PDA

View Full Version : Problems getting a consistant movement rate



andrew2110
15-08-2006, 09:09 PM
Hello everyone! I have begun creating a MMORPG side scrolling game and have run in to a bit of a problem.

My problem is that when run on different computers, the sprites move at different speeds. First off this was because I was basing the movement rates on the dxtimer component but now thanks to these forums I've learnt this is very bad. I've now come up with the function below to generate xspeeds and yspeeds based on timegettime() but obviously i've gone horribly wrong somewhere as this does not work as sprites move at different rates still.

Passed into the function below are variables:

now:=timegettime();
last:=timegettime from previous frame
xspeed:=speed of sprite which is between -3 and +3.

My thinking behind this was that I could get the framerate by dividing 1000 by the difference between the two times. I could get the target distance (distance that the sprite should move in around 1 second) by multiplying xspeed by 50. Finally I could work out the actual speed by dividing framerate by target distance to give how many pixels the sprite needs to move that frame to reach the target distance...



function getxs(now,last:cardinal;xspeed:single):single;
var
framerate:smallint;
targetdistance:single;
begin
result:=0;
if now-last>0 then
begin
framerate:=1000 div (now-last);
if xspeed<>0 then
begin
targetdistance&#58;=xspeed*50;
result&#58;=targetdistance / framerate;
end;
end;
end;


Obviously im doing something pretty wrong here so any advice would be more than welcome!

Thanks!

Huehnerschaender
15-08-2006, 09:34 PM
Just don't handle the framerate at all...

You will never get an exact framerate (which says how much frames will be displayed in one second!) between 2 frames, cause you don't have a full second.

Just handle the time.

If, for example, a speed of +3 means, that your character moves, lets say 30 pixels (this of course can be exchanged with any vectorlength or whatever), then you know how much you have to move in 1/10 second, do you?

Got the idea?

Just set the speed to the amount of units (pixels or whatever) you want to move your char within a second.
Then you can calculate the time between the last and the current frame and multiply the speed with the result.

Example:
Char should run 50 pixels/second (Speed = 50)

Time between last and current frame = 0.043 sec

Move char 50*0.043 (=2.15) pixels this frame

You can handle your animations the same way!

Just exchange Speed with the animationframecount/second then.

Hope this helps...

andrew2110
15-08-2006, 10:24 PM
thanks so much for your help, it makes sense now and I feel a bit stupid! Trying to write a function to get rid of dependancy on framerates but inside it calculating the frame rate :oops: ... Anyway thanks a lot, will adjust the program accordingly and let you know how it goes.

Thanks,
Andrew Starnes

Huehnerschaender
15-08-2006, 10:46 PM
Yeah, I would be glad to know if it helped...

And: Don't feel stupid :) This is just one of many questions/problems many people have when writing a game... there are so many other things which seem to be difficult, but in fact, the most things are easy, once you got a push in the right direction... and thats what this community is for... pushing us!

Greetings,
Dirk

andrew2110
16-08-2006, 07:42 AM
I've made the changes to the functions and now they work great! However I still have one problem. On every frame at the moment, the effects of gravity are applied to the sprites speed. Obviously as these effects are based on frame rates it makes sprites appear to be moving at different speeds on different computers. However as these functions also handle collisions I dont see any other alternative but to call these functions every frame.

Below is my code for the objects vertical physics which also tests for collisions underneath the character.

The thinking behind this function is:

if players going upwards:
set onfloor flag to false
decrease yspeed by multiplying it by 0.91
if yspeed is very small, set to 0. Otherwise we would never get to 0!
(ignore collision possibility if players going upwards)

if players going downwards:
if the players not colliding with anything:
set onfloor flag to false
set speed to 0.5 if speed was 0 so multiplying will actually change something
if speed is less than 5, multiply it by 1.1

Anyway im sure theres a fairly simple solution using the difference in times instead of just constant values of 0.91 and 1.1 but am struggling to figure this out for myself :(



procedure TPlayerChar.VPhysics&#40;map&#58;TMap&#41;;
begin
if yspeed<0 then
begin
onfloor&#58;=false;
yspeed&#58;=yspeed*0.91;
if yspeed>-0.5 then yspeed&#58;=0;
end else
if yspeed>=0 then
if not collisions.VCollision&#40;map,getys&#40;currenttime,lastti me,yspeed&#41;,xpos,ypos&#41; then
begin
onfloor&#58;=false;
if yspeed=0 then yspeed&#58;=0.5 else
if yspeed<5 then yspeed&#58;=yspeed*1.1;
end else
begin
onfloor&#58;=true;
yspeed&#58;=0;
end;
end;


Any comments on this code, ie. if im doing something a bit silly, are more than welcome!

Thanks!

Andreaz
16-08-2006, 08:10 AM
Best tip i can give you is to always think in the terms of time, ie pixels/second for speed, pixels/second^2 for acceleration and every other place.

Say your gravity is 20 pixels per second downwards then you'l you'll update the speed like this:


Sprite.Speed.Y:= Sprite.Speed.Y + Sprite.Acceleration * DeltaTime;

Sprite.Postion.Y:= Sprite.Position.Y + Sprite.Speed.Y * DeltaTime;


where DeltaTime is the time between two frames in seconds.

And yes you have to call the collision routines each frame, so make shure they are quite fast.

One problem with time based movement is that it's very easy to get stuck inside a wall as you move the sprite different ammount each update and not a fixed amount of pixels. If you go fast enough you can even walk through walls if the collision detection algorithms doesnt compensate for this.

Andreaz
16-08-2006, 08:13 AM
Oh and one small thought on your VPhysics function:

What if the player jumps and touches a wall, this will make onfloor:=true; will he be able to "powerjump" to climb the wall in that case ?

czar
16-08-2006, 08:52 AM
A problem with time based movement is when deltatime gets very big.

If the computer glitches, e.g. accessing dvd or whatever, you might get a very large deltatime so the chaneg in position might be huge. The problem is that this leaves it open to objects passing or falling through other objects.

You collision detection needs to handle this.

What I also do is instead of using TimeGetTime as teh basis for movement I use a variable (longint) that initially gets set to TimeGetTime. For every frame add the deltatime to the variable.

If you then open a menu or whatever you stop adding deltatime to the variable and effectively everything in your game pauses. Quite useful if you build it in from the beginning.

Cheers

R

Huehnerschaender
16-08-2006, 12:09 PM
Andreaz already pointed out the solution for your problem.

Every calculation which changes the position or something else, must be multiplied with the delta time. This way it doesn't matter if one frame is 1 sec or 0.003 secs.

Of course the other problem has to be handled (collisions with big steps). But thats a general problem in collision handling.

andrew2110
16-08-2006, 12:38 PM
Hello and thanks for all the replies! I've added a new function to calculate the gravity based on time instead of frame rates and it now appears to be working so thanks! Collision detection is not really a problem at the moment because it scans x pixels in the direction of movement for a wall, where x is the distance in pixels that the sprite will move that frame.

My problems now I think are to do with the way I am sending the data over the network but plan to deal with this by making a thread listen to any updates from the server instead of polling the server every n frames for an update which means that things still dont look right, but I'm pretty sure thanks to everybodys help here, my initial problem has been solved so thankyou so much!