Unstable speed of gameloop - java

I'm making a simple breakout game in OpenGL(-es) on Android. Initially I had the updating of the game's state and the drawing calls in the same loop: onDrawFrame. Now I decided to split up the two, only leaving the rendering calls in onDrawFrame, and the gamestate was managed in another Thread:
public void run() {
Log.d("GameLogicThread", "GameLogicThread started");
final long UPDATE_INTERVAL = 1000000000 / 30;
long endingTime;
int timeElapsed;
long startingTime = System.nanoTime();
while (!running) {// wait for it...
}
while (running) {
endingTime = System.nanoTime();
timeElapsed = (int) (endingTime - startingTime);
Log.d("timeElapsed",Integer.toString(timeElapsed));
if (timeElapsed < UPDATE_INTERVAL-timeElapsed){
try {
Thread.sleep(timeElapsed);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
startingTime = System.nanoTime();
Game.updateGame(timeElapsed);
}
EDIT
I now have changed the code like this ^^, but it still doesn't work correctly..
Is it something in the loop itself that's wrong, or should I look outside (Probably not, since it worked great before moving the code). What should I do?

There are a few logical errors which are pointed out/discussed in the comments below:
endingTime = System.currentTimeMillis();
timeElapsed = (int) (endingTime - startingTime);
// Why is the "elapsed" time being waited? Hmm.
// If *any* wait is being done (I'd recommend sleep(0) for starters)
// it should be the MAXIMUM desired cycle time MINUS the
// currently used cycle time (MINUS some fudge factor).
if (timeElapsed < UPDATE_INTERVAL) // I dislike hanging blocks...
try {
Thread.sleep(timeElapsed);
} catch (InterruptedException e) {
e.printStackTrace();
}
startingTime = System.currentTimeMillis();
// The game needs to know the TOTAL time elapsed since
// the last update, not the time "until before the yield".
// This will likely be passed fictitiously small values as
// it is only time the the LAST updateGame took to run.
Game.updateGame(timeElapsed);
I would never expect to see timeElapsed (passed to updateGame) below say 10ms with sleep(...) and the corrected time calculations.
However, it may not have the required precision (increasing the minimum cycle length to say 1/30 sec, which would result from the fixed math, would make this less important): see Cristian Vrabie's answer for a suggestion on a higher-resolution timer. (There may be some better 3rd-party alternatives designed just for this -- there is in "normal" Java -- I don't program Android ;-)
Happy coding.

The loop doesn't look broken to me. The move to separate threads is definitely a good one or you would have serious problems when the rendering of a frame takes too long.
Have you tried to use nanoTime() for more accuracy?

Related

How to run a For Loop for 60 seconds maximum irrespective of the size of the loop and complete all the iterations

for(i=1;i<list.size();i++){
//do something
//For Eg: move marker to a new position on the map
}
I want the above loop to complete all the iterations irrespective of the size of the list and also want the entire task to run for 1 minute. (60 seconds)
I don't really know if this is what you want but I hope this helps.
import java.util.concurrent.TimeUnit;
for(i=1;i<list.size();i++){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Execute thing you want to be executed every second
}
As explanation: you iterate through the for loop and the thread waits for one second before executing the code after the TimeUnit.SECONDS.sleep(1);.
If the list's size is 60 it would therefore take a minute for the loop to end.
Edit: It has occurred to me that it might be smarter to do a try-catch around the sleep function.
You can, for example, use System.nanoTime() to measure the duration of your loop, and then use TimeUnit.NANOSECONDS.sleep(...) to make it wait for the rest of time like this:
long start = System.nanoTime();
long desiredDuration = 60 * 1000 * 1000;
// your loop goes here
long duration = System.nanoTime() - start;
if (duration < desiredDuration)
TimeUnit.NANOSECONDS.sleep(desiredDuration - duration);
The best possible solution is to compute the desired time first and then run the loop to that extent.
long finish=System.currentTimeMillis() + 60000;
while(System.currentTimeMillis() != finish)
{
//statements;
//statements;
}
If you are trying to equip the CPU and keep it idle for this time the process is known as busy waiting but is not considered convenient in many cases so i recommend to use Thread.sleep(duration) for this purpose.
Would like to receive further queries from your side.
To spread N amount of invocations uniformly across a minute, you'll have to set the delay in between the invocations to the value 60/(N-1). The -1 is optional but causes the first and last invocations to be exactly 60 seconds apart. (just like how a ladder with N rungs has N-1 spaces)
Of course, using sleep() with the number calculated above is not only subject to round-off errors, but also drift, because you do stuff between the delays, and that stuff also takes time.
A more accurate solution is to subtract the time at which each invocation should occur (defined by startTime + 60*i/(N-1)) from the current time. Reorder and reformulate those formulas and you can subtract the 'time that should have elapsed for the next invocation' from the already elapsed time.
Of course 'elapsed time' should be calculated using System.nanoTime() and not System.currentTimeMillis() as the latter can jump when the clock changes or the computer resumes from stand-by.
For this example I changed 60 seconds to 6 seconds so you can more easily see what's going on when you run it.
public static void main(String... args) throws Exception {
int duration = 6; // seconds
List<Double> list = IntStream.range(0, 10).mapToDouble(i->ThreadLocalRandom.current().nextDouble()).boxed().collect(Collectors.toList());
long startTime = System.nanoTime();
long elapsed = 0;
for (int i = 0; i < list.size(); i++) { // Bug fixed: start at 0, not at 1.
if (i > 0) {
long nextInvocation = TimeUnit.NANOSECONDS.convert(duration, TimeUnit.SECONDS) * i / (list.size() - 1);
long sleepAmount = nextInvocation - elapsed;
TimeUnit.NANOSECONDS.sleep(sleepAmount);
}
elapsed = System.nanoTime() - startTime;
doSomething(elapsed, list.get(i));
}
}
private static void doSomething(long elapsedNanos, Double d) {
System.out.println(elapsedNanos / 1.0e9f + "\t" + d);
}
Of course when the task you preform per list element takes longer than 60/(N-1) seconds, you get contention and the 'elapsed time' deadlines are always exceeded. With this algorithm the total time just taking longer than a mnute. However if some earlier invocations exceed the deadline, and later invocations take much less time than 60/(N-1), this algorithm will show 'catch-up' behavior. This can be partially solved by sleeping at least a minimum amount even when sleepAmount is less.
Check out this.
long start = System.currentTimeMillis();
long end = start + 60*1000; // 60 seconds * 1000 ms/sec
int i = 0;
while (System.currentTimeMillis() < end)
{
// do something, iterate your list
i++;
if (i == list.size()) { // check size of the list if iteration is completed
// if time has not yet expired, sleep for the rest of the time
Thread.sleep(end - System.currentTimeMillis());
}
}
Do not forget checking size of the list.

Why this while loop cannot print 1,000 times per seconds?

The following Java method is meant to print the number i by nLoopsPerSecond times per second for seconds seconds:
public void test(int nLoopsPerSecond, int seconds) {
double secondsPerLoop = 1.0/(double)nLoopsPerSecond;
long startTime = System.currentTimeMillis();
long currentTime;
int i = 0;
while ((currentTime = System.currentTimeMillis()) < startTime + seconds*1000) {
System.out.println(i++);
while (System.currentTimeMillis() < currentTime + secondsPerLoop*1000);
}
}
With the following call:
test(1000,1);
I expect this method to do the System.out.println(i++); 1,000 times, but I only got 63.
When I try to see how many seconds it actually use per loop with this code
public void test(int nLoopsPerSecond, int seconds) {
double secondsPerLoop = 1.0/(double)nLoopsPerSecond;
long startTime = System.currentTimeMillis();
long currentTime;
int i = 0;
while ((currentTime = System.currentTimeMillis()) < startTime + seconds*1000) {
while (System.currentTimeMillis() < currentTime + secondsPerLoop*1000);
System.out.println(System.currentTimeMillis() - currentTime);
}
}
I expect it to print 1 milliseconds each loop, but it prints 15 or 16 milliseconds.
Please suggest what is wrong in my code.
Are you running on Windows, perhaps? System.currentTimeMillis() consults the underlying operating system clock, which is only 60Hz on many versions of Windows.
Try System.nanoTime() since you are not measuring time since the epoch. System.currentTimeMillis vs System.nanoTime
That's probably because the processing takes up some time. The processor does not solely dedicate its time to the execution of your program, it performs several other functions in the background. So you might get different results based on the processor load.
Your output console is not fast enough. You do not mention how you run your test and where the output goes. The speed of the terminal and buffers (not) used will limit how fast can the program output data. If running unbuffered, your program will always have to wait, until the new line is printed on screen. If the console waits for screen redraw and screen is redrawn at 60Hz, you've got your 16ms/line and about 60 lines per second.
Running your code without inner loop inside IntelliJ Idea, I get about 140.000 lines per second (and Idea warns me, that it is not displaying every line, as my output is too fast).
With the inner loop, I get about 800-900 lines. That happens because the process may be scheduled out of cpu, or blocked because of something else, like swapping. (If I simplify a lot, usually desktop OSes schedule in 1ms granularity.)

Interrupt Loop after some Time in Java

I am looking for a way to interrupt a loop in Java without multithreading.
public class Foo{
public Foo(){
}
public void bar(long timeLimit) {
long endTime = System.currentTimeMillis() + (timeLimit * 1000);
while (System.currentTimeMillis() < endTime) {
// Some really long and complicated computation
}
}
}
At the moment I realized that which various (System.currentTimeMillis() < timeLimit) calls to check during the computation if there is time left but I guess that eats up some time and I am also facing the problem that if a loop starts in time, the computation might not be done in time.
Amending the timeLimit (let's say only using 40 % of the time) accordingly also does not work, because I cannot predict how long some computations take.
Which options are there?

LWJGL timer not working?

I keep getting a lot of "Delta: 0.0"s in my console and ever few lines I get something like "Delta: 9.999275207519531E-4", what's happening?
Timer timer = new Timer();
float lastTime = 0.0f;
timer.resume();
while (!Display.isCloseRequested())
{
timer.tick();
System.out.println("Delta: " + (timer.getTime() - lastTime));
lastTime = timer.getTime();
Display.update();
}
Display.destroy();
Don't ever use java's Timer class in your game loops. It isn't precise enough to use reliably. Instead, check out this link to see a couple of reliable game loops written in java.
One little note, just a warning. Your timing mechanism is flawed a bit.
Considering this code runs line by line as it is written, you'll "loose" time. This
System.out.println("Delta: " + (timer.getTime() - lastTime));
lastTime = timer.getTime();
code does the following:
1. Getting current time.
2. Doing some math.
3. Calling String constructor.
4. Performing String concatenation.
5. Writing current time to the lastTime variable.
Note that current time in the 1 and 5 cases are different. That means that this time is "lost" from the "Delay: xx" output.
If you continue to use (timer.getTime() - lastTime) technics in your code for the purpose of getting time passed from the previous iteration, you will surely run into problem where different events thinks that time passed from the previous iteration is different. I recommend you to use the following code for timing:
private double delta;
private long timing;
public void updateTime()
{
long newTime = System.nanoTime();
this.delta = (newTime - this.timing) / 1_000_000_000.0;
this.timing = newTime;
}
public double getDelta() {
return this.delta;
}
where updateTime() is called once per cycle and getDelta() is called every time you want to get time passed from the previous iteration.

Would this lower frame rate?

I am making a game, and the requirement is to make it have at least 30FPS and shouldn't drop below. Would what I have below achieve this? Or am I off somewhere? Much help would be appreciated.
private long period = 6 * 1000000;
private static final int DELAYS_BEFORE_YIELD = 5;
long before, after, difference, sleep, oversleep = 0;
int delays = 0;
while (running)
{
before = System.nanoTime();
after = System.nanoTime();
difference = after - before;
if (sleep < period && sleep > 0)
{
try
{
Thread.sleep(sleep / 35000L);
oversleep = 0;
}
catch (InterruptedException e)
{
}
}
else if (difference > period)
{
oversleep = difference - period;
}
else if (++delays >= DELAYS_BEFORE_YIELD)
{
Thread.yield();
oversleep = 0;
delays = 0;
}
else
{
oversleep = 0;
}
}
You can set an upper bound to frame rate but not a lower bound that is guaranteed to be always followed.
You can make a function be called no more than 30 times per second but you can't be sure it will be called at least 30 times per second. At 30 fps you have 0.03s of time that will be distributed between your threads and usually the drawing one is the heaviest between them all (unless you have complex operations like AI or whatever but that should be solved by lowering their rate or precomputing what can be precomputed).
If time of draw + time of logic > 0.03 then there is no way to make your game run at least at 30fps.
Good that you are asking early, because one better uses a timer I think. And that turns things inside out. (It could be done your way though.)
Sorry for this non-answer, but I think it is a worthwhile advice.
Look into some game/animation frameworks for their approach.

Categories

Resources