How are you guys getting stutter-free movement?

Monkey Forums/Monkey Programming/How are you guys getting stutter-free movement?

Chroma(Posted 2015) [#1]
Been prototyping a game and I can't get reliable stutter-free movement. Is there a secret out there? Are you guys putting the update for your sprites in the OnRender method? I tried that and it doesn't seem to work either.


gasmonso(Posted 2015) [#2]
It would help if you specify what target and provide a little code :-)


Samah(Posted 2015) [#3]
Are you keeping track of the number of milliseconds that have passed since the last frame?
This is generally how I do delta timing, where I store velocities as distance per second. Another common way of doing it is to assume you have a fixed framerate, then have an "adjustment" multiplier to cater for any frame drops.
Disclaimer: I haven't tried compiling this.
Class MyApp Extends App
	Field lastMillis:Int ' value of Millisecs() from the previous frame
	Field thisMillis:Int ' value of Millisecs() from the current frame
	Field deltaMillis:Int ' the difference between this and the previous frame
	
	' sprite starts at 400,400 with a velocity of 100,50 units per second
	Field spr:Sprite = New Sprite(400,400,100,50)
	
	Method OnUpdate()
		' update timing
		lastMillis = thisMillis
		thisMillis = Millisecs()
		deltaMillis = thisMillis - lastMillis
		
		' update the sprite with the number of millis that have passed since the last update
		spr.Update(deltaMillis)
	End
	
	Method OnRender()
		Cls()
		spr.Draw()
	End
End

Class Sprite
	Field x:Float, y:Float
	Field dx:Float, dy:Float ' units per second
	
	Method New(x:Float, y:Float, dx:Float, dy:Float)
		Self.x = x
		Self.y = y
		Self.dx = dx
		Self.dy = dy
	End
	
	Method Draw()
		' do your sprite drawing
	End
	
	Method Update(deltaMillis:Int)
		' convert millis to seconds
		Local secs:Float = deltaMillis / 1000.0
		' update position based on units per second and seconds passed since last frame
		x += dx * secs
		y += dy * secs
	End
End



ImmutableOctet(SKNG)(Posted 2015) [#4]
@Chroma: I'll point you to this thread. I can back port that demo to Mojo 1 if you're interested.

The demo looks like this:


I've written a delta-timing module myself, and it may be useful to you. It has been tweaked since that thread, and it now interpolates a bit better. It's worth looking at, even as a reference.

There are multiple ways of keeping game logic consistent, and the most used methods are FRL (Fixed-rate-logic) and delta-timing/tweening (Usually together). FRL usually involves dropping frames, and keeping game updates as close to the ideal framerate as possible (In other words, a fixed update rate). And delta-timing being a way to generate a scalar to use on frame-dependent calculations. As long as the scalar is accurate to the framerate, adjusting accordingly, things will be in approximately the same place they would be at the ideal framerate. And since you'd be dealing with floating point, it's not a big issue. With delta-timing, you'll want to set limits on the scalar, as really low framerates would make fast objects teleport through walls. With a good collision engine, ray casting can be performed, but it can only help to a certain degree. This is where FRL is best suited; lower framerates. FRL doesn't really scale with higher framerates, but delta-timing obviously does. With FRL, you're basically limiting 'OnRender'. But some people want to run your game at higher framerates. Since delta-timing is done by detecting the timing differences between two or more frames, we can scale based on our own expected frame timings. At 60 FPS, this is 16.6666... milliseconds, or 1000 / 60 (Length of a second divided by the framerate).

So, if we apply this scalar to our game logic, we get:


So, assuming your game runs at 60 FPS, but the machine can only run it at 30 FPS, the scalar is ~2.0. Meaning the amount added per-frame would be 1.0 in the above example. But what if we apply this to 15 FPS? Since we're multiplying, we're going to be adding 2.0 to our vector's X position. Scale this up to something fast, like say, 4.5, and you have 9.0 at 30FPS, or 18.0 at 15 FPS, which could mean you go through, or inside of a smaller object. Not to get into building a 2D physics engine, but FRL/frame-dropping is necessary for a lot of games. The only exception is forcing your game to run in slow motion. Having your game properly take its time isn't too bad on singleplayer games. Just keep in mind that multiplayer games don't usually have this luxury.

Though its timing system was quite complicated, I'll point to how DOOM handled this in multiplayer games. DOOM would synchronize updates practically every frame, but it would do this by sending differentials. So, if your machine couldn't keep up well enough, it would kick you from the game. Obviously, DOOM didn't do this the best possible way, but it did have an accurate way of handling things. If someone, say, ran through a wall, the server would see this, and see that the client would conform, or be kicked.

With singleplayer games, this is less of an issue from a timing perspective, but it is an issue from a stability perspective. So, no matter how you handle timing in your game, just keep stability in mind above anything else. Players running through walls isn't exactly intended.

From what I understand, Mojo does have a bit of an FRL setup going, but it's best to test these things yourself, and build your own timing system if necessary. For your average game, you should be good with delta-timing. I hope this post (Along with the thread I linked before) helps you make the best decisions when building your game.


Samah(Posted 2015) [#5]
Personally I dislike the idea of frame-based timing, because I don't like to define my velocities as "units per frame". Time-based deltas also make it easier to segment vectors for collision detection/response. Each to their own.


nullterm(Posted 2015) [#6]
Time-based deltas have their advantages. It's more flexible across platforms/hardware, but more potential to miss bullet-through-paper type collisions. If you have alot of things going on in your game eating up CPU, this is the better more flexible approach.

The catch is because you might get variations each frame (16ms 14ms 18ms 15ms 10ms 20ms 16ms 14ms 13ms ...) your animation/physics might not be as perfectly smooth. This is because the CPU doing logic/animation/scene management may not be perfectly synched with the screen refresh.

Fixed delta have their advantages. Verlet physics integration is easier. If you can get consistent 30/60fps then your animation/physics is more consistent motion wise.

Disadvantage being if you have a update & render pass that takes longer than 16ms(60fps) or 33ms(30fps) then things start to trip up. So you need to be lean & mean with everything you are doing at runtime.


I've been working a VR contract, and switched to fixed delta (60fps). It means a more responsive/immersive experience with head tracking and rendering, which is way more noticeable than on a monitor/phone/tablet. But you really need to keep your scene simple to ensure quick render times.

Depends on the game/experience. And technical requirements of collision detection and physics. Most physics engines like fixed steps.


Samah(Posted 2015) [#7]
@nullterm: The catch is because you might get variations each frame...

By "frame" I mean "update frame" not "render frame", in which case there will be the same variations in having a delta modifier float. The difference is that I work with units per second rather than units per frame. It still runs (usually) with fixed steps.
position += unitsPerFrame * deltaModifier

As opposed to:
position += unitsPerSecond * timeSinceLastUpdateFrame

If your update rate is faster than your render FPS, timeSinceLastUpdateFrame will be far less than 16ms.

Edit: Check the example I posted earlier. You can see I'm doing the time-based stuff in OnUpdate, not OnRender.