can anyone help with my problem? In my game loop for game refreshing and redrawing, sometimes variable "wait" returned negative value. How its possible?
while(running) {
start = System.nanoTime();
update();
draw();
drawToScreen();
elapsed = System.nanoTime() - start;
wait = targetTime - (elapsed / 1000000);
try {
System.out.println(wait);
Thread.sleep(wait);
} catch (Exception e) {
e.printStackTrace();
}
}
elapsed = System.nanoTime() - start;
wait = targetTime - (elapsed / 1000000);
wait is not the value of System.nanotime(); wait being negative just means targetTime - (elapsed / 1000000) is negative
nano = 1 billionth = 1/1000000000 so if targetTime is in seconds you probably don't want 1000000
It is possible that the elapsed time is greater than the refresh interval (targetTime). In this case, check if the elapsed is +ve, and sleep only then.
Related
I have a java game wherein there is a countdown timer from 60 secs. It works fine, but when I pause the game and return back to play state. the original time is now reduced because I found out that the timer still keeps running from the system no matter what gameState I'm in. How do I fix this?
I tried storing the remainingTime to a pauseTime variable whenever I switch states, and just subtract it and stuff. But my efforts seem to failed.
// GET ELAPSED TIME
if(gp.gameState == gp.playState && remainingTime >= 0) {
soundCounter--;
elapsedTime = System.currentTimeMillis() - startTime;
remainingTime = totalTime - elapsedTime;
if(remainingTime <= 0) {
remainingMilliseconds = 0;
remainingSeconds = 0;
remainingMinutes = 0;
// GAME OVER
gp.gameOver();
} else {
remainingMilliseconds = remainingTime % 1000;
remainingSeconds = (remainingTime / 1000) % 60;
remainingMinutes = (remainingTime / 1000) / 60;
}
}
// DRAW TIMER
timeString = String.format("%02d:%02d:%03d", remainingMinutes, remainingSeconds, remainingMilliseconds);
Your problem is that you are calculating your elapsed time forever since the first startTime.
One of the solution would be to calculate a delta (difference) time between loop iterations.
Here some very primitive code that should get you started:
public static void main(String[] args) {
boolean paused = false;
long lastRun = System.currentTimeMillis();
long elapsed = 0;
System.out.println("Game start");
while (true) {
long now = System.currentTimeMillis();
long delta = now - lastRun;
if (!paused && delta > 0) {
elapsed += delta;
// Do game stuff
System.out.println("Elapsed: " + elapsed);
if (elapsed >= 5000) return;
lastRun = now;
}
}
}
I'm trying to build a stopwatch with hundreth of second precision. I have this code that runs every 10 milliseconds but I am having trouble converting it to a 0(min):0(sec):00 format.
timer.post(new Runnable() {
#Override
public void run() {
time += 1;
txtView.setText(convertTimeToText(time));
timer.postDelayed(this, 10);
}
});
private String convertTimeToText(int time) {
String convertedTime = time / 6000 + ":" + (time / 100) % 60
+ ":" + (time / 10) % 10 + time % 10;
return convertedTime;
}
I need help with the convertTimeToText(int time){} that formats time.
EDIT:
Thanks to Ole V.V. and WJS for the formatting answer and how to fix the delay I was having, this is the code I came up with if anybody needs it, so far it works well, maybe using System.nanoTime() will get you more accurate results but for my use its fine.
public void start(){
final long timeMillisWhenStarted = System.currentTimeMillis();
if(!isRunning){
isRunning = true;
timer.post(new Runnable() {
#Override
public void run() {
long millisNow = System.currentTimeMillis();
long time = millisNow - timeMillisWhenStarted;
yourtxtView.setText(convertTimeToText(time));
timer.postDelayed(this, 10);
}
});
}
}
private String convertTimeToText(long time){
long hundredths = time / 10;
long sec = hundredths / 100;
long min = sec / 60;
return String.format("%02d:%02d.%02d", min % 60, sec % 60, hundredths % 100);
}
See if this helps. The remainders weren't being computed correctly.
For 12340 hundreds of seconds that would be 123.40 seconds
so 12340 / 6000 = 2 for minutes
12340 % 6000 gets what's left which is 340
so 340 /100 = 3 seconds
leaving 340 % 100= 40 hundredths
public static void main(String[] args) {
// n = 16 mins 4 seconds and 99 hundredths
int n = (16 * 6000) + (4 * 100) + 99;
System.out.println(convertTimeToText(n));
}
private static String convertTimeToText(int time) {
int mins = time / 6000;
time %= 6000; // get remaining hundredths
int seconds = time / 100;
int hundredths = time %= 100; // get remaining hundredths
// format the time. The leading 0's mean to pad single
// digits on the left with 0. The 2 is a field width
return String.format("%02d:%02d:%02d", mins, seconds,
hundredths);
}
This prints
16:04:99
Use the standard library
Your timer isn’t accurate. Your observed delay comes from there. Read the time off some clock each time rather than adding 1/100 second. Use for example System.currentTimeMillis(), System.nanoTime() or Instant.now(). Keep the reading from when your stopwatch was started and subtract for getting the current stopwatch value.
Should performing a system call 100 times per second be too expensive (which I don’t expect), do it for example for every 30 ticks to adjust your stopwatch back to the correct time.
Next if you are using Java 9 or higher, use the Duration class for converting from the the time (whether in milliseconds or nanoseconds) to minutes and seconds. If you do the conversion by hand as you tried, it’s error-prone and hard to read, which — I believe — was one reason for your question.
For example:
long millisNow = System.currentTimeMillis();
Duration time = Duration.ofMillis(millisNow - timeMillisWhenStarted);
String convertedTime = String.format("%d:%02d.%02d",
time.toMinutes(), time.toSecondsPart(), time.toMillisPart() / 10);
I'm working on optimizing a little java program I made for Christmas. I was testing the amount of time it takes to run the methods and one of them, Snow_AI, is taking 1234567 nano seconds to run.
The issues is with the counter method:
public boolean Count() {
if (CreateCnt > CreateTime) {
CreateCnt = 0;
return true;
} else {
CreateCnt = CreateCnt + 1;
}
return false;
}
This is how I'm making the calls:
MethTmr1 = System.nanoTime();
if (Snw.Count()) {
MethTmr = System.nanoTime();
Snw.Snow_AI();
MethTPS = 1000000000 / (System.nanoTime() - MethTmr);
}
try{
MethTPS1 = 1000000000 / (System.nanoTime() - MethTmr1);
}catch(Exception e){}
when I move the timing calls inside the If statement it changed the time to run to less than 5000. Anyone know why the counter method is causing this to happen?
Your measurement is flawed.
Check this: Why is System.nanoTime() way slower (in performance) than System.currentTimeMillis()?
And then look at your code:
MethTmr1 = System.nanoTime();
if (Snw.Count()) {
//expensive! Added to outer time
MethTmr = System.nanoTime();
Snw.Snow_AI();
//expensive! Added to outer time
MethTPS = 1000000000 / (System.nanoTime() - MethTmr);
}
try{
MethTPS1 = 1000000000 / (System.nanoTime() - MethTmr1);
}catch(Exception e){}
Baseline. Your MethTPS1 includes your (fast) getCount(), your Snow_AI() and two tcalls to System.nanoTime()
Try it this way once:
MethTmr1 = System.nanoTime();
if (Snw.Count()) {
//expensive! Added to outer time
//MethTmr = System.nanoTime();
Snw.Snow_AI();
//expensive! Added to outer time
//MethTPS = 1000000000 / (System.nanoTime() - MethTmr);
}
try{
MethTPS1 = 1000000000 / (System.nanoTime() - MethTmr1);
}catch(Exception e){}
I'm following a tutorial and below is the run method to generate logic and frame updates. I understand how the ticks are updated 60 ticks/second but I don't understand how we adjust frame per second here.
Right now with Thread.sleep(2), frame per second is around 460. Without it the numbers go way up, around 10 million updates per second. The code Thread.sleep(2) suspends the thread for only 2 milliseconds right? Why/how does Thread.sleep exactly work here to drop it so low?
Isn't it simpler to create a nsPerFrame = 1000000000D/ (FPS)D to set whatever FPS I want the way he did with ticks?
public void run(){
long lastTime = System.nanoTime();
double nsPerTick = 1000000000D / 60D;
int frames = 0;
int ticks = 0;
long lastTimer = System.currentTimeMillis();
double delta = 0;
while(running){
long now = System.nanoTime();
delta += (now - lastTime) / nsPerTick;
lastTime = now;
while(delta >= 1){
ticks++;
tick();
delta-= 1;
}
try{
Thread.sleep(2);
} catch(InterruptedException e){
e.printStackTrace();
}
frames++;
render();
if(System.currentTimeMillis() - lastTimer >= 1000){
lastTimer += 1000;
System.out.println(ticks + "," + frames);
frames = 0;
ticks = 0;
}
}
}
Well sleep(2) is called repeatedly in your running-loop so the hard upper limit in this case is 500 cycles per second (1000 ms divided by 2 ms), and your measured 460 fps is pretty close to that.
Sure enough you can adjust the sleep duration according to your needs and if needed stuff it into the somewhat higher-precision method Thread#sleep(long, int) where the second parameter is "nanos" (take a look at the docu for caveats!).
The formula for your case is FPS = 1000 ms / sleep duration in ms. From which follows: sleep duration in ms = 1000 ms / FPS.
I'm trying to keep my game at 60fps, but I'm getting strange results from my code like "2-8000 fps" Why isn't this staying at 60?
public static void main(String[] args) {
joglplat m = new joglplat();
while(true){
long startTime = System.nanoTime() / 1000000;
try
{
// 123456: 6 zeros => 16ms
long nsToSleep = 16000000 - (System.nanoTime() - lastFrame);
System.out.println("ns: " + nsToSleep);
lastFrame = System.nanoTime();
if(nsToSleep > 0)
{
System.out.println("ns2: " + (nsToSleep/1000));
System.out.println("ns3: " + (nsToSleep%1000));
Thread.sleep(nsToSleep/16000000, (int)(nsToSleep % 1000));
}
else
{
Thread.yield(); // Only necessary if you want to guarantee that
// the thread yields the CPU between every frame
}
}
catch(Exception e){
e.printStackTrace();
}
m.controls();
m.update();
m.repaint();
System.out.println("framerate: " + (System.nanoTime() / 1000000 - startTime));
}
}
Your output is the number of seconds your program has run for, not framerate. You should be dividing your frame count (which you aren't collecting) by the total time run.
To get the frame count, simply add a new variable outside of your game loop, and increment it each time through...
public static void main(String[] args) {
long frames = 0;
joglplat m = new joglplat();
while(true){
frames++;
// other code here
System.out.println("framerate: " + ((System.nanoTime() / 1000000 - startTime) / frames ) );
}
}
Note, however, that this will give you the average framerate throughout the entire execution of your program. Two other options you have are to get the instantaneous framerate, and the average framerate over the past N frames.
All styles in one (untested/uncompiled, so might have some errors, but should get you started in the right direction):
public static void main(String[] args) {
long startTime = System.nanoTime();
long lastFrameTime = startTime;
long frames = 0;
int framesToAverage = 10;
long[] frameTimes = new long[framesToAverage];
joglplat m = new joglplat();
while(true){
// logic here
long currentFrameDuration = System.nanoTime() - lastFrame;
lastFrameTime = System.nanoTime();
long instantFramerate = currentFrameDuration / 1000000;
int currentFrameIndex = frames % frameTimes.length;
frameTimes[currentFrameIndex] = currentFrameDuration;
frames++;
long averageFramerate = ( ( lastFrameTime - startTime) / frames ) / 1000000;
long instantFramerate = currentFrameDuration / 1000000;
if( frames > frameTimes.length ) { // if it isn't, we don't have enough data yet
int firstFrameIndex = currentFrameIndex + 1;
if( firstFrameIndex > frameTimes.length ) {
firstFrameIndex = 0;
}
long averageFrameratePerN = ( ( frameTimes[currentFrameIndex] - frameTimes[firstFrameindex] ) / frameTimes.length ) / 1000000;
}
// yield/sleep here
}
}
My suspicion is that this is caused by the inaccuracy of Thread.sleep():
Causes the currently executing thread
to sleep (cease execution) for the
specified number of milliseconds plus
the specified number of nanoseconds,
subject to the precision and accuracy
of system timers and schedulers. The
thread does not lose ownership of any
monitors.
Is there any reason why you have to hold the framerate down like this? Perhaps you can explain more fully what you are trying to accomplish?