Slyth2727
|
 |
«
Posted
2013-02-13 21:09:30 » |
|
For my 2 dimensional Tower Defense game I decided to try it out on a different computer. This turned out to be a disaster... The game runs completely different. randomely lags and everything goes much faster. I am guessing it is the way I do my animations and game loop. Here is the code for the loop: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public void run() { while (inRealGame) { if (inGame) { if (!isFirst) { room.physic(); mobSpawner(); for (int i = 0; i < currLevel.mobs.length; i++) { if (currLevel.mobs[i].inGame) { currLevel.mobs[i].physic(); } } if (currLevel.levelOver) { getNextLevel(currLevel); } } } try { Thread.sleep(gameSpeed); } catch (InterruptedException e) { e.printStackTrace(); } repaint();
} } |
And the way I do my animations is I have two variables and an if loop seeing if the first lvariables has become larger than the other and if it has than set that to zero, switch the image. If not increment the variable by one. Any ideas??
|
|
|
|
Rorkien
|
 |
«
Reply #1 - Posted
2013-02-13 21:35:21 » |
|
I don't see anything on this code that could lead to such behaviors, except for Thread.sleep() You shouldn't trust that little bugger. He asks for a time, but guess what: He can't count! So you should do that for him. In the meantime, check THIS out. TL;DR: Thread.sleep is not always going to be accurate, plus if you've got any other threads hogging processor it won't necessarily allow your thread to resume in time.
|
|
|
|
Slyth2727
|
 |
«
Reply #2 - Posted
2013-02-13 21:56:51 » |
|
Yes, I have read that thread through and through before but I cant figure out why its acting like this!
|
|
|
|
Games published by our own members! Check 'em out!
|
|
Varkas
|
 |
«
Reply #3 - Posted
2013-02-13 22:12:52 » |
|
If gameSleep isn't much bigger than the actual time spent in your code, you start to notice the different run speeds. You should sleep for (time consumed - time between frames) instead of a fixed time, to get closer to a constant speed. If the difference becomes negative you have a performance problem.
|
|
|
|
Rorkien
|
 |
«
Reply #4 - Posted
2013-02-13 22:14:03 » |
|
Basically, When you call Thread.sleep(2), it's not guaranteed it will sleep for 2000ns. Sometimes it will sleep for 1000ns, or 3000ns, or something between or even beyond those values, depending on OS or hardware. This is why you have to check how much time passed since you last called sleep(), and change the sleep amount to fix the difference between what you want and what you got
PS: Have you tried moving repaint(); to the line before the try/catch block? I just noticed you are rendering stuff after sleeping.
|
|
|
|
Grunnt
|
 |
«
Reply #5 - Posted
2013-02-13 22:15:13 » |
|
Are you using the time delta between frames to update your game world? I dont see an anything in your code that actually compensates for differences in processing speeds of different computers. Maybe you could read up on timing, e.g. http://gafferongames.com/game-physics/fix-your-timestep
|
|
|
|
Slyth2727
|
 |
«
Reply #6 - Posted
2013-02-14 00:40:05 » |
|
ahh yes I have heard of this! I knew it had something to do with the different specs of computers and all but I wasnt sure on how to fix it! I shall read up on this and get back to you, thanks
|
|
|
|
ra4king
|
 |
«
Reply #7 - Posted
2013-02-15 04:49:06 » |
|
Just subtract the time it took to update the logic and repaint the screen from the "gameSpeed" variable, that is all 
|
|
|
|
Grunnt
|
 |
«
Reply #8 - Posted
2013-02-15 09:17:14 » |
|
Which is quite ugly, but it may work, kinda 
|
|
|
|
Grunnt
|
 |
«
Reply #9 - Posted
2013-02-15 15:20:33 » |
|
|
|
|
|
Games published by our own members! Check 'em out!
|
|
lithos
|
 |
«
Reply #10 - Posted
2013-02-15 18:27:57 » |
|
Different Operating Systems also have different allowed sleep times. For instance on Older windows you can get a min sleep time of 15MS or worse. On any Operating System the amount of time your thread will sleep will randomly vary as well, sometimes the OS/JVM will just decide now is the time to sleep an extra 30 MS.
What I end up doing for my game loop is counting the time between each loop. and then using a FOR statement that counts down the total time for each 15MS for update logic; so if the thread slept for 30 MS I run twice, if it slept for 5MS I don't run at that step. However I always draw the screen even if it was too short of a step.
|
There are no such things as bugs... Only happy accidents.
|
|
|
Slyth2727
|
 |
«
Reply #11 - Posted
2013-02-15 22:07:40 » |
|
alright so I did some research on the pages suggested, thanks for the links, very helpful! Now I have another question: For one of the game loops I see delta being plugged into the update position method and update player sound and all that stuff... How would I implement that into the for loops that time my animations? Or is there a different way I should be animating?
|
|
|
|
sproingie
|
 |
«
Reply #12 - Posted
2013-02-16 02:20:19 » |
|
Variable timesteps in game logic are bonkers, resulting in simulations that aren't stable. Just avoid the hell out of it and take the framerate hit instead.
|
|
|
|
masteryoom
|
 |
«
Reply #13 - Posted
2013-02-16 02:32:12 » |
|
Have a look at Eli's tutorial on game loops! 
|
|
|
|
Slyth2727
|
 |
«
Reply #14 - Posted
2013-02-20 01:56:14 » |
|
Sorry for the late reply, had some crazy stuff going on. How would I "take the framerate hit" instead of using variable timestep, which I assume is the loop method that I am using right now.
|
|
|
|
Grunnt
|
 |
«
Reply #15 - Posted
2013-02-20 14:32:55 » |
|
Sorry for the late reply, had some crazy stuff going on. How would I "take the framerate hit" instead of using variable timestep, which I assume is the loop method that I am using right now. I think he meant that using a fixed timestep by fixing the framerate. I don't think you're using any kind of timestep in your loop at the moment, as far as I can see. In a sense that means you're using a fixed timestep already, only you're not limiting the framerate. For example, at some computers your game may run at 60FPS resulting in a timestep for each update of 1/60 = 0.0166 seconds. On another computer your game may run at 120ms, resulting in a timestep for each update of 1/120 = 0.00833 seconds. In other words, the world will change twice as fast when the computer runs twice as fast. One way to avoid this is by simply limiting the FPS. Which means that on a system that could run 120FPS your game would be limited to 60FPS (and do nothing for the rest of the time). For this I personally I like the loop that r4king's posted some time ago, it's here.
|
|
|
|
Slyth2727
|
 |
«
Reply #16 - Posted
2013-02-21 00:02:59 » |
|
Makes total sense now, thank you. The only issue is that in his loop I see "update(delta)". How do I use delta to change the positions of my mobs, and the animations of things switching images? Here is the moving method for the mobs: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| public void physic() { if (walkFrame >= walkSpeed) { if (direction == right) { x += 1; } else if (direction == upward) { y -= 1; } else if (direction == downward) { y += 1; } else if (direction == left) { x -= 1; }
mobWalk += 1;
if (mobWalk == Screen.room.blockSize) { if (direction == right) { xC += 1; hasRight = true; } else if (direction == left) { xC -= 1; hasLeft = true; } else if (direction == upward) { yC -= 1; hasUpward = true; } else if (direction == downward) { yC += 1; hasDownward = true; }
if (!hasUpward) { try { if (Screen.room.block[yC + 1][xC].groundID == Value.groundRoad) { direction = downward; } } catch (Exception e) { } }
if (!hasDownward) { try { if (Screen.room.block[yC - 1][xC].groundID == Value.groundRoad) { direction = upward; } } catch (Exception e) { } }
if (!hasLeft) { try { if (Screen.room.block[yC][xC + 1].groundID == Value.groundRoad) { direction = right; } } catch (Exception e) { } }
if (!hasRight) { try { if (Screen.room.block[yC][xC - 1].groundID == Value.groundRoad) { direction = left; } } catch (Exception e) { } } if (Screen.room.block[yC][xC].airID == Value.airCastle) { deleteMob(); loseHealth(); }
mobWalk = 0; hasUpward = false; hasDownward = false; hasLeft = false; hasRight = false; } walkFrame = 0; } else { walkFrame += 1; } } |
and here is the animation method for an explosion: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public void switchImage(Graphics g) { if (animIndex == 0) { changeImage(Screen.tileset_explosion[Value.explosion2]); animIndex = 1; return; } else if (animIndex == 1) { changeImage(Screen.tileset_explosion[Value.explosion3]); animIndex = 2; return; } else if (animIndex == 2) { changeImage(Screen.tileset_explosion[Value.explosion4]); animIndex = 3; return; } else if (animIndex == 3) { changeImage(Screen.tileset_explosion[Value.explosion5]); animIndex = 4; return; } else if (animIndex == 4) { changeImage(Screen.tileset_explosion[Value.explosion6]); animIndex = 5; return; } else if (animIndex == 5) { changeImage(Screen.tileset_explosion[Value.explosion7]); animIndex = 6; return; } else if (animIndex == 6) { changeImage(Screen.tileset_explosion[Value.explosion8]); animIndex = 7; return; } else if (animIndex == 7) { changeImage(Screen.tileset_explosion[Value.explosion9]); animIndex = 8; return; } else if (animIndex == 8) { changeImage(Screen.tileset_explosion[Value.explosion10]); animIndex = 9; return; } else if (animIndex == 9) { changeImage(Screen.tileset_explosion[Value.explosion11]); inGame = false; return; } } |
|
|
|
|
Jimmt
|
 |
«
Reply #17 - Posted
2013-02-21 00:05:45 » |
|
Those repeated "if" statements hurt my eyes...use an array or something  Anyways, can't you just use a gif?
|
|
|
|
Slyth2727
|
 |
«
Reply #18 - Posted
2013-02-21 00:22:42 » |
|
Yeah man I feel you... Wrote this at like 2:30 and never revised it against my better judgement. Never thought of a using a gif though, mind explaining?
|
|
|
|
Grunnt
|
 |
«
Reply #19 - Posted
2013-02-21 08:44:04 » |
|
The delta is the timestep, which you need to integrate into your calculations for movement, animation etc so that with a higher FPS the movements and animations take smaller steps (instead of more steps of the same size). Here's how I would do this in your movement code, for example: 1 2 3 4 5 6 7 8 9 10
| public void physic(float delta) { if (walkFrame >= walkSpeed) { if (direction == right) { x += WALK_SPEED * delta; } else if (direction == upward) { y -= WALK_SPEED * delta; } else if (direction == downward) { y += WALK_SPEED * delta;
|
And a slightly different approach in your animation code. Assuming the Value.explosion<number> refers to a sequence of numbers you could write something like: 1 2 3 4 5 6 7 8
| switchImage(Graphics g, float delta) { animationInterval -= delta; if (animationInterval <= 0) { changeImage(Screen.tileset_explosion[animationIndexValue]); animationIndexValue = (animationIndexValue + 1) % NUMBER_OF_ANIMATION_FRAMES; animationInterval = ANIMATION_INTERVAL; } } |
Now the nice thing about this approach is that you make things like walking and animation speed explicit. This means that by changing values such as ANIMATION_INTERVAL or WALK_SPEED you can tune animation and movement speed. Also, this means that your movement and animation will become independent on processor speed. One thing to keep in mind, and which has been mentioned before: you can run into problems with this if the delta becomes too big. If you look at the simple calculation above you will see that with a very high delta the character will move a very long distance in one step, which may mean it warps outside of the screen, or right past an obstacle which it should have collided with. The discussions in the blogs that were mentioned earlier in this thread discuss this phenomenon, and how to avoid it (which is most easily done by "fixing" or maximizing the FPS, which puts an upper limit on the value of delta). Edit: typo
|
|
|
|
Slyth2727
|
 |
«
Reply #20 - Posted
2013-02-21 12:06:02 » |
|
Very well written explanation, thank you very much. I will implement this as soon as possible  . EDIT: So I did this, followed Eli's tutorial (Very helpful by the way) and everything is up and running, but the walking for the mob is all messed up. I used to say to add 1/-1 to the x or y for the movement. That was a very smooth walking speed. Now it is all jerky, jumping forward when I use MOVESPEED * delta. I know this has something to do with delta... Any suggestions? EDIT 2: When I replace MOVESPEED * delta with just a 1 like it used to be there is still no smooth walking speed. So it has something to do with the loop I am using. Here is the code foe anyone interested: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public void run() { long lastLoopTime = System.nanoTime(); final int TARGET_FPS = 60; final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
while (inRealGame) { if (inGame) { long now = System.nanoTime(); long updateLength = now - lastLoopTime; lastLoopTime = now; double delta = updateLength / ((double) OPTIMAL_TIME);
lastFpsTime += updateLength; fps++;
if (lastFpsTime >= 1000000000) { System.out.println("(FPS: " + fps + ")"); lastFpsTime = 0; fps = 0; }
gamePhysic(delta); try { Thread.sleep((lastLoopTime - System.nanoTime() + OPTIMAL_TIME) / 1000000); } catch (Exception e) { e.printStackTrace(); } } repaint(); } }
public void gamePhysic(double delta) { if (!isFirst) { room.physic(); mobSpawner(); for (int i = 0; i < currLevel.mobs.length; i++) { if (currLevel.mobs[i].inGame) { currLevel.mobs[i].physic((int) delta); } } if (currLevel.levelOver) { getNextLevel(currLevel); } } } |
And the moving for the mob (superclass) : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| public void physic(int delta) { if (walkFrame >= walkSpeed) { if (direction == right) { x += MOVESPEED; } else if (direction == upward) { y -= MOVESPEED; } else if (direction == downward) { y += MOVESPEED; } else if (direction == left) { x -= MOVESPEED; } mobWalk += 1;
if (mobWalk == Screen.room.blockSize) { if (direction == right) { xC += MOVESPEED; hasRight = true; } else if (direction == left) { xC -= MOVESPEED; hasLeft = true; } else if (direction == upward) { yC -= MOVESPEED; hasUpward = true; } else if (direction == downward) { yC += MOVESPEED; hasDownward = true; }
if (!hasUpward) { try { if (Screen.room.block[yC + 1][xC].groundID == Value.groundRoad) { direction = downward; } } catch (Exception e) { } }
if (!hasDownward) { try { if (Screen.room.block[yC - 1][xC].groundID == Value.groundRoad) { direction = upward; } } catch (Exception e) { } }
if (!hasLeft) { try { if (Screen.room.block[yC][xC + 1].groundID == Value.groundRoad) { direction = right; } } catch (Exception e) { } }
if (!hasRight) { try { if (Screen.room.block[yC][xC - 1].groundID == Value.groundRoad) { direction = left; } } catch (Exception e) { } }
if (Screen.room.block[yC][xC].airID == Value.airCastle) { deleteMob(); loseHealth(); }
mobWalk = 0; hasUpward = false; hasDownward = false; hasLeft = false; hasRight = false; } walkFrame = 0; } else { walkFrame += 1; } } |
|
|
|
|
Grunnt
|
 |
«
Reply #21 - Posted
2013-02-21 12:40:55 » |
|
I can't really say much without more information. Can you show us some key parts of your code?
|
|
|
|
Slyth2727
|
 |
«
Reply #22 - Posted
2013-02-22 01:01:30 » |
|
Post updated with information ...
|
|
|
|
Slyth2727
|
 |
«
Reply #23 - Posted
2013-02-23 00:46:51 » |
|
Ok, I figured out something. If I raise the variable "targetfps" to something higher, the movement is much, much smoother. The only problem is that the game should be totally smooth at 60 FPS, which is what it is set to in the example. 60 FPS is more than enough to be smooth. Also, when I set the move speed of the mob to more than one, it jumps pixels instead of smoothly going faster, like I would like it to. Anyone got a better way of doing all this?
|
|
|
|
HeroesGraveDev
|
 |
«
Reply #24 - Posted
2013-02-23 08:01:59 » |
|
Ok, I figured out something. If I raise the variable "targetfps" to something higher, the movement is much, much smoother. The only problem is that the game should be totally smooth at 60 FPS, which is what it is set to in the example. 60 FPS is more than enough to be smooth. Also, when I set the move speed of the mob to more than one, it jumps pixels instead of smoothly going faster, like I would like it to. Anyone got a better way of doing all this?
If it moves >1 per frame, then it will obviously more >1 pixel. The human eye will see anything >=24 fps as moving. All the extra frames just smooth everything out in case an object is moving too fast for the brain to realise that it's the same object just in a different place. The player won't notice. You can't make anything go faster or slower without skipping/stopping on pixels. "Smoothly going faster" is not going to happen on a screen made up of pixels.
|
|
|
|
HeroesGraveDev
|
 |
«
Reply #25 - Posted
2013-02-23 08:06:56 » |
|
Also, your loop is faulty. You are sleeping for (System.nanoTime()-lastLoopTime+OPTIMAL_TIME)/1000000 |
which basically means you are sleeping for a whole frame plus whatever the differnce between nanoTime and lastLoopTime. This means you will get deltas of >2 and frame rates of <30fps.
|
|
|
|
Slyth2727
|
 |
«
Reply #26 - Posted
2013-02-23 16:29:49 » |
|
alright, makes sense. If the sleep time is faulty, what do I need to change?
|
|
|
|
Slyth2727
|
 |
«
Reply #27 - Posted
2013-02-23 17:25:04 » |
|
And another strange something thats happening. The reason that the mobs hardly move at all when I use MOVESPEED * delta is because the delta I am passing it is like this: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 0.9676847 0.8476047 0.8783945 0.9083911 0.9012535 0.8945824 0.9042859 0.9009269 0.8978947 0.9003671 0.8992475 0.9031663 0.9527097 0.8525964 0.8930429 0.9051722 0.9019999 0.8964018 0.9016734 0.90172 0.9006471 0.896635 0.9050323 0.9174881 0.9091376 0.8735427 |
This is the output to the console when I print out delta.
|
|
|
|
HeroesGraveDev
|
 |
«
Reply #28 - Posted
2013-02-23 20:05:11 » |
|
And another strange something thats happening. The reason that the mobs hardly move at all when I use MOVESPEED * delta is because the delta I am passing it is like this:
-snip-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public void gamePhysic(double delta) { if (!isFirst) { room.physic(); mobSpawner(); for (int i = 0; i < currLevel.mobs.length; i++) { if (currLevel.mobs[i].inGame) { currLevel.mobs[i].physic((int) delta); } } if (currLevel.levelOver) { getNextLevel(currLevel); } } }
|
You are casting the delta to an int, which means that it will be zero unless you have lag, in which case it will be 1 or more. As you know, multiplying by zero = zero. So as long as you don't get lag, nothing moves. Don't cast the delta to an int and you should be fine.
|
|
|
|
Slyth2727
|
 |
«
Reply #29 - Posted
2013-02-23 20:45:43 » |
|
Wow. I feel very silly. It works fine now, thanks! Now I want to go further though, and the way I used to have it, I could adjust the gameSpeed variable to make the game go faster. I'm guessing there is no real easy way to do this now though, correct?
|
|
|
|
|