Java Countdown Timer - java

Hi I am trying to create a countdown stopwatch timer for my gym.
Here is what I have for the actual displaying and decrementing of the time: (bear in mind I used my normal stopwatch one and just edited it briefly to decrement. It is still first pass and I still want to thread it all at some stage but for now this should work. )
new Timer(1,new ActionListener(){
public void actionPerformed(ActionEvent e){
int seconds = (int)(System.currentTimeMillis()-watchStart)/1000;
int days = seconds / 86400;
//these are just initialization values for testing
int startHour, startMin, startSec;
startHour = 5;
startMin = 5;
startSec = 0;
int hours = (seconds / 3600) - (days * 24);
int min = (seconds / 60) - (days * 1440) - (hours * 60);
int sec = seconds % 60;
String s = String.format("%02d:%02d:%02d", startHour - hours, startMin - min, ((startSec == 0) ? startSec = 60 : startSec) - sec );
displayTimeLabel.setText(s);
}
});
Now my issue is:
a) It is not decrementing the minute to start with (if it starts at say 5 minutes)
b) If I start it at 30 seconds it will go into negative until it reaches a full minute then decrements the minute. ( I think I will need a method like if == 0 then minute - 1? but I fear that will break the actual count?)
Thanks.
Edit: Full program:
package countdown;
import java.awt.*;
import static java.awt.Frame.MAXIMIZED_BOTH;
import javax.swing.Timer;
import javax.swing.*;
import java.awt.event.*;
public class CountDown extends JFrame implements ActionListener{
private int hour;
private int minute;
private int second;
// The component that shows the elapsed time.
private JLabel displayTimeLabel;
private long watchStart, watchEnd;
private Timer theChronometer;
// Keeps track of time when pausing the Timer.
private long pausedTime;
// Lets the program know if starting or resuming
private boolean paused = false;
// Button that changes from "Start" to "Resume" depending on pause status.
private JButton activateTimerButton;
// run the program
public static void main(String[] args) {
CountDown count = new CountDown();
count.setVisible(true);
count.setLocationRelativeTo(null);
}
public CountDown(){
// initialize
super();
setExtendedState(MAXIMIZED_BOTH);
setLayout(new BorderLayout());
setLayout(new GridLayout(2,1));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("CrossFit Slam");
setBackground(Color.black);
setForeground(Color.white);
Font largeFontBOLD = new Font("Calibri", Font.BOLD,20);
Font largeFontPLAIN = new Font("Calibri", Font.PLAIN,200);
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout());
activateTimerButton = new JButton("Start");// will display resume when the watch is paused
JButton stopTimerButton = new JButton("Stop");
JButton pauseTimerButton = new JButton("Pause");
// register buttons to generate events when clicked
activateTimerButton.addActionListener(this);
stopTimerButton.addActionListener(this);
pauseTimerButton.addActionListener(this);
// the display for elapsed time
displayTimeLabel = new JLabel("00:00:00");
displayTimeLabel.setHorizontalAlignment(JLabel.CENTER);
buttonPanel.setBackground(Color.black);
buttonPanel.setForeground(Color.white);
displayTimeLabel.setFont(largeFontPLAIN);
displayTimeLabel.setForeground(Color.white);
displayTimeLabel.setBackground(Color.black);
activateTimerButton.setFont(largeFontBOLD);
stopTimerButton.setFont(largeFontBOLD);
pauseTimerButton.setFont(largeFontBOLD);
displayTimeLabel.setOpaque(true);
buttonPanel.add(activateTimerButton);
buttonPanel.add(stopTimerButton);
buttonPanel.add(pauseTimerButton);
add(displayTimeLabel);
add(buttonPanel, BorderLayout.PAGE_END);
theChronometer =
new Timer(1,new ActionListener(){
public void actionPerformed(ActionEvent e){
int seconds = (int)(System.currentTimeMillis()-watchStart)/1000;
int days = seconds / 86400;
int startHour, startMin, startSec;
startHour = 5;
startMin = 5;
startSec = 0;
int hours = (seconds / 3600) - (days * 24);
int min = (seconds / 60) - (days * 1440) - (hours * 60);
int sec = seconds % 60;
String s = String.format("%02d:%02d:%02d", startHour - hours, startMin - min, ((startSec == 0) ? startSec = 60 : startSec) - sec );
displayTimeLabel.setText(s);
}
});
}
public void actionPerformed(ActionEvent e){
if(e.getActionCommand().equals("Stop")){theChronometer.stop();}
else if(e.getActionCommand().equals("Start") || e.getActionCommand().equals("Resume")){
if(!paused){
watchStart = System.currentTimeMillis();
theChronometer.start();
}
else{
watchStart = System.currentTimeMillis()+pausedTime;
pausedTime = 0;
theChronometer.start();
paused = false;
activateTimerButton.setText("Start");
}
}
else if(e.getActionCommand().equals("Pause")){
long now = System.currentTimeMillis();
pausedTime -= (now - watchStart);
theChronometer.stop();
paused = true;
activateTimerButton.setText("Resume");
}
}
}

What you're trying to is easily achieved using standard libraries, in particular the Date class.
DateFormat f = new SimpleDateFormat("HH:mm:ss");
f.setTimeZone(TimeZone.getTimeZone("GMT"));
long startingTime = TimeUnit.HOURS.toMillis(5) + TimeUnit.MINUTES.toMillis(5);
public void actionPerformed(ActionEvent e) {
long elapsed = System.currentTimeMillis() - startTimestamp;
long displayTs = startingTime - elapsed;
String out;
if (displayTs >= 0) {
out = f.format(new Date(displayTs));
} else {
out = "-" + f.format(new Date(-displayTs));
}
displayTimeLabel.setText(out);
}
All of this is in the standard Java libraries and the Javadoc on the methods/classes should provide insight on what's happening.

Related

How to improve the framerate in simple java game loop?

I'm working on a java 2d game, using this simple game loop to cap the FPS and UpdatesPS to 60:
public void run() {
final int MAX_FPS = 60;
final int MAX_UPS = 60;
final double fOPTIMAL_TIME = 1000000000 / MAX_FPS;
final double uOPTIMAL_TIME = 1000000000 / MAX_UPS;
double uDeltaTime = 0, fDeltaTime = 0;
int frames = 0, updates = 0;
long startTime = System.nanoTime();
long timer = System.currentTimeMillis();
// GameLOOP starts here
while (running) {
long currentTime = System.nanoTime();
uDeltaTime += (currentTime - startTime);
fDeltaTime += (currentTime - startTime);
startTime = currentTime;
if (uDeltaTime >= uOPTIMAL_TIME) {
gameUpdate();
updates++;
uDeltaTime -= uOPTIMAL_TIME;
}
if (fDeltaTime >= fOPTIMAL_TIME) {
gameRender();
gameDraw();
frames++;
fDeltaTime -= fOPTIMAL_TIME;
}
if (System.currentTimeMillis() - timer >= 1000) {
fps = frames; //saves the current FPS
ups = updates; //saves the current UPS
updates = 0;
frames = 0;
timer += 1000;
}
}
}
The loop works, but I get only 30 FPS for the first ~10 Seconds after starting the game.
After I wait, the FPS raises up to the wanted 60. I don't have a problem to wait a few seconds to let the program stabilize and reach the wanted framerate. But I can't find the reason like a methode who drops the FPS because it's fetching a big file after startup.
Do you have any idea why my engine needs so long to stabilize the framerate?
Thanks for your help!
I think this should do the trick:
public static void run()
{
final int desiredFPS = 60;
final int desiredUPS = 60;
final long updateThreshold = 1000000000 / desiredUPS;
final long drawThreshold = 1000000000 / desiredFPS;
long lastFPS = 0, lastUPS = 0, lastFPSUPSOutput = 0;
int fps = 0, ups = 0;
loop:
while(true)
{
if((System.nanoTime() - lastFPSUPSOutput) > 1000000000)
{
System.out.println("FPS: " + (double)fps);
System.out.println("UPS: " + (double)ups);
fps = 0;
ups = 0;
lastFPSUPSOutput = System.nanoTime();
}
if((System.nanoTime() - lastUPS) > updateThreshold)
{
lastUPS = System.nanoTime();
updateGame();
ups++;
}
if((System.nanoTime() - lastFPS) > drawThreshold)
{
lastFPS = System.nanoTime();
drawGame();
fps++;
}
// Calculate next frame, or skip if we are running behind
if(!((System.nanoTime() - lastUPS) > updateThreshold || (System.nanoTime() - lastFPS) > drawThreshold))
{
long nextScheduledUP = lastUPS + updateThreshold;
long nextScheduledDraw = lastFPS + drawThreshold;
long minScheduled = Math.min(nextScheduledUP, nextScheduledDraw);
long nanosToWait = minScheduled - System.nanoTime();
// Just in case
if(nanosToWait <= 0)
continue loop;
try
{
Thread.sleep(nanosToWait / 1000000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
Edit: I fixed the issue now! The issue was that I was saving the lastFPS/lastUPS after the scene was updated/drawn, and when I set both lastUPS/lastFPS before the scene is drawn/updated, we get the desired fps!Another neat thing about this code is that it doesn't consume a whole cpu core(I measured the difference, your code was consuming 100%, whilest my code only consumed about 10%. If you want to measure it yourself, please note that for some reason, the core on which the code is executed regularly switches(at least this was the case when I measured the code))By the way if you use LWJGL (or have direct access to a windowing library like GLFW) you can activate V-Sync, which would cut your fps down to 60 fps.

how to use millis() to display images every x seconds?

I have an array of candy images which are randomly chosen. There are candies falling and I want to make a new one fall every 5 seconds, and I know I have to use millis() - but how would I implement it into my program? I tried using millis() like so:
int time = millis();
if (time<5*1000)
{
image(goodCandy[rand], randX, goodY, randCandyW, randCandyH);
goodY = goodY + (candySpeed * yDirCandy);
time = millis();
}
But it only appears for 5 seconds then goes away.
I also tried:
int time = millis();
if (millis() - time >= 5000)
{
image(goodCandy[rand], randX, goodY, randCandyW, randCandyH);
goodY = goodY + (candySpeed * yDirCandy);
time = millis();
}
But it didn't work.
Here's the simplified code:
PImage [] goodCandy = new PImage [3];
int candySpeed = 20;
int yDirCandy = 1;
int candyY = 10;
int candyX = 200;
int candyW = 187;
int candyH = 121;
int randCandyW = 100;
int randCandyH = 100;
int goodY = -200;
int rand=(int) (2*Math.random()) +1;
int randX = (int) (1500*Math.random())+20;
void setup() {
for (int i=0; i<goodCandy.length; i++) {
goodCandy[i] = loadImage("goodCandy" + i + ".png");
}
void draw() {
if (current=="play") {
loadStuff();
}
}
void loadStuff() {
image(candy, candyX, candyY, candyW, candyH); //original candy
candyY = candyY + (candySpeed * yDirCandy);
int time = millis();
if (millis() - time >= 5000)
{
image(goodCandy[rand], randX, goodY, randCandyW, randCandyH);
goodY = goodY + (candySpeed * yDirCandy);
time = millis();
}
//for (int i=0; i<time; i++) {
// image(goodCandy[rand], randX, goodY, randCandyW, randCandyH);
// goodY = goodY + (candySpeed * yDirCandy);
// time = millis();
//}
}
Any ideas how I could make millis() work so I can have a random candy falling every 5 seconds?
Thanks
Please try to get into the habit of breaking your problem down into smaller pieces and only taking on those pieces one at a time. For example, you should probably start with a simpler sketch that just shows a random circle every 5 seconds.
Here's a small example that shows how you would use the millis() function to draw something every 5 seconds:
int lastCircleTime = 0;
void draw() {
if (millis() > lastCircleTime + 5*1000) {
ellipse(random(width), random(height), 20, 20);
lastCircleTime = millis();
}
}
If you're still having trouble, please post a MCVE showing exactly which step you're stuck on. Note that this should not be your whole sketch. It should be a small example like this one. Good luck.

java delta time problems with canvas

hi am trying to make a small game using canvas and bitmaps i want my game to run the same on all devices i found delta time is the best practice for this but for some reason when i try to implement it into my code i have display issues for example am trying to move my coluds in the sky but when i add the delta they all disapere i dont know if im doing it wrong so please can sombody help me heres the code
private float c1x = 0.0f;
private float c2x = cloudWidth;
private float c3x = cloudWidth * 2;
private float cloudSpeed = 0.1f;
private long curentTime;
private long lastTime = 0;
private double delta;
#Override
public void run(){
while(running){
if(!holder.getSurface().isValid()){
continue;
}
curentTime = System.nanoTime();
delta = curentTime - lastTime;
lastTime = curentTime;
cloudMovement();
canvas = holder.lockCanvas();
canvas.drawBitmap(bg, 0, 0, null);
canvas.drawBitmap(sun, 20, 20, null);
canvas.drawBitmap(cloud1, c1x, c1y, null);
canvas.drawBitmap(cloud2, c2x, c2y, null);
canvas.drawBitmap(cloud3, c3x, c3y, null);
holder.unlockCanvasAndPost(canvas);
}
}
private void cloudMovement(){
if(c1x <= 0 - cloudWidth){
c1x = w;
c1y = y.nextInt(rand);
}
if(c2x <= 0 - cloudWidth){
c2x = w;
c2y = y.nextInt(rand);
}
if(c3x <= 0 - cloudWidth){
c3x = w;
c3y = y.nextInt(rand);
}
c1x-=cloudSpeed * delta;
c2x-=cloudSpeed * delta;
c3x-=cloudSpeed * delta;
}
You could use a global FPS mechanism instead which forces a steady FPS on your game :)
If you track the FPS the game will run the same way on any device and you dont need to include delta-times on all update processes.
Here's a code snippet from a FpsTracker i used in an old project:
private static final long SECOND = 1000;
private static final long TARGET_FPS = 40;
private static final long FRAME_PERIOD = SECOND / TARGET_FPS;
private long time = System.currentTimeMillis();
/**
*
* #param startTime
* #return <code>true</code> if the interval between startTime and the time
* when this method was called is smaller or equal to the given
* frame period.
*
* Will return <code>false</code> if the interval was longer.
*/
public boolean doFpsCheck(long startTime) {
if (System.currentTimeMillis() - time >= SECOND) {
time = System.currentTimeMillis();
}
long sleepTime = FRAME_PERIOD
- (System.currentTimeMillis() - startTime);
if (sleepTime >= 0) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
//TODO handle this properly
e.printStacktrace()
}
return true;
} else {
return false;
}
}
If this method returns false it means that your operations took longer that the timeperiod you gave to one frame. You can react to this by checking the doFpsCheckreturn parameter.
Implementing this in your code would look like this:
#Override
public void run()
{
while(running)
{
if(!holder.getSurface().isValid())
{
continue;
}
startTime = System.currentTimeMillis();
cloudMovement();
canvas = holder.lockCanvas();
canvas.drawBitmap(bg, 0, 0, null);
canvas.drawBitmap(sun, 20, 20, null);
canvas.drawBitmap(cloud1, c1x, c1y, null);
canvas.drawBitmap(cloud2, c2x, c2y, null);
canvas.drawBitmap(cloud3, c3x, c3y, null);
holder.unlockCanvasAndPost(canvas);
doFpsCheck(startTime);
}
}
By the way - it is good practice to devide your game loop into pro sub processes, one being the update process, the other being the draw process.
For many different Bitmaps you should consider extracting the fields and functionalities into seperate classes containing a draw(Canvas c) and update() method. So you wont get a trillion fields on your main class.

JFrame.repaint() stops getting called in a Java Canvas

EDIT: Problem solved, turns out I needed to call main.repaint() instead of frame.repaint()
I am using a class that extends Canvas and overrides the 'paint(Graphics graphics)' method. In a loop which activates 60 times a second (it works like it's supposed to), I have called frame.repaint() (the canvas is correctly added to the frame). The paint method gets called about 4 or 5 times, then stops getting called. My other method in the loop, does not stop however, proving that it's the frame.repaint() method.
To make the problem clear, the JFrame.repaint() method stops getting called after 4 or 5 attempts within a second.
To prove this, I've increased an integer every second in my update method (which is getting called 60 times per second) and I'm using that as the x cordanite as a rectangle in my frame, which should make the rectangle larger each second. The rectangle paint's for 2 seconds or so, then stops growing, however the integer is still increasing. One thing to keep in mind that the rectangle does draw for the first few times, indicating that it's some sort of issue with the frame.
Is there a better way to call the paint(Graphics graphics) method? Do
I have some flaw in my code?
Sorry if my explanation was confusing, but I attached the code below (and in a pastebin file that you can find here: http://pastebin.com/WNnK54gq)
I have been looking for the past few hour's, and haven't found any replacement for the frame.repant() method.
Thanks in advanced!
public class Main extends Canvas {
//Static Variables
public static Main main;
public static String name = "Game";
public static double version = 1.0;
public static int FPS;
//Object Variables
private JFrame frame;
private boolean running;
private int screenX;
private int screenY;
private int x = 0;
//Constructor
public Main() {
setSize(new Dimension(500, 500));
}
//Main Method
public static void main(String[] args) {
main = new Main();
main.init();
}
//Object Methods
private void init() {
frame = new JFrame(name);
frame.setSize(500, 500);
frame.setResizable(false);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(main);
loop();
}
public void loop() {
running = true;
int fps = 0;
long timer = System.currentTimeMillis();
long lastTime = System.nanoTime();
final double ns = 1000000000.0 / 60;
double delta = 0;
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 1) {
if (fps <= 60) {
fps++;
update();
frame.repaint();
delta--;
}
}
if (System.currentTimeMillis() - timer > 1000) {
timer += 1000;
log("Running at " + fps + " FPS and UPS");
FPS = fps;
fps = 0;
}
}
}
public void update() {
screenX = frame.getWidth();
screenY = frame.getHeight();
x++;
if (x >= 500) x = 0;
log("update");
//update gametstate
}
public void log(String string) {
System.out.println("[" + name + "] [" + version + "] " + string);
}
public void log() {
System.out.println("[" + name + "] [" + version + "]");
}
#Override
public void paint(Graphics graphics) {
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, screenX, screenY);
//update gamestate
graphics.setColor(Color.BLUE);
graphics.fillRect(0, 200, x, 300);
log("rendered");
}
while (delta >= 1) {
if (fps <= 60) {
fps++;
update();
frame.repaint();
delta--;
}
}
Once fps hits 61, it will stop rendering or updating, because you never set fps back to 0.
while (delta >= 1) {
if (fps <= 60) {
fps++;
update();
frame.repaint();
delta--;
if(fps == 60) fps = 0;
}
}
You need to set fps back to 0.

JScrollPane - Smooth Scrolling

I have a JScrollPane with a moderately high block increment (125). I would like to apply smooth/slow scrolling to it so it doesn't jump (or skip) when scrolling. How can I do this?
I was thinking of scrolling like Windows 8.
Any help would be greatly appreciated!
You could use a javax.swing.Timer during the scroll to achieve the smooth scrolling effect. If you are triggering this from outside the component, somthing like this will work (where component is the component within the JScrollPane):
final int target = visible.y;
final Rectangle current = component.getVisibleRect();
final int start = current.y;
final int delta = target - start;
final int msBetweenIterations = 10;
Timer scrollTimer = new Timer(msBetweenIterations, new ActionListener() {
int currentIteration = 0;
final long animationTime = 150; // milliseconds
final long nsBetweenIterations = msBetweenIterations * 1000000; // nanoseconds
final long startTime = System.nanoTime() - nsBetweenIterations; // Make the animation move on the first iteration
final long targetCompletionTime = startTime + animationTime * 1000000;
final long targetElapsedTime = targetCompletionTime - startTime;
#Override
public void actionPerformed(ActionEvent e) {
long timeSinceStart = System.nanoTime() - startTime;
double percentComplete = Math.min(1.0, (double) timeSinceStart / targetElapsedTime);
double factor = getFactor(percentComplete);
current.y = (int) Math.round(start + delta * factor);
component.scrollRectToVisible(current);
if (timeSinceStart >= targetElapsedTime) {
((Timer) e.getSource()).stop();
}
}
});
scrollTimer.setInitialDelay(0);
scrollTimer.start();
The getFactor method is a conversion from linear to an easing function and would be implemented as one of these depending on how you want it to feel:
private double snap(double percent) {
return 1;
}
private double linear(double percent) {
return percent;
}
private double easeInCubic(double percent) {
return Math.pow(percent, 3);
}
private double easeOutCubic(double percent) {
return 1 - easeInCubic(1 - percent);
}
private double easeInOutCubic(double percent) {
return percent < 0.5
? easeInCubic(percent * 2) / 2
: easeInCubic(percent * -2 + 2) / -2 + 1;
}
This could probably be adapted to work within a component too so when the user scrolls it does something along these lines.
Or, if possible, you could use JavaFX which has much better support for animation than Swing.

Categories

Resources