The last article was a bit long so we’re going to move forward in smaller increments.
Let’s start with a discussion on how movement works in games. The 18th century Swiss mathematician, physicist, astronomer, logician and engineer (quite a resume) Leonhard Euler devised a way for us to predict where an object’s going to be based on two things: its current position and its speed. We actually used it in our previous article in the update() method of our Square class:
this.x += 1 * movementMultiplier;
this.y += 1.5 * movementMultiplier;
Euler integration works wonders in games where speed is constant and it’s also very easy on the processor; unfortunately you’ll quickly find out it’s not quite enough if you’re planning on building a physics engine… it becomes highly inaccurate. Let’s visualize this:
In the image above, the distance between two peaks on the grid is ~30px, the speed of our ball is 60px every frame and we’re running this for 10 frames (at 60fps that’s 1/6 of a second). This seems fine… the movement is constant so it should be easy to work with, right?… well, not really; let’s add a wall in there:
Between frames 5 and 6 you can see an impressively drawn wall; what’s even more impressive is that with the current logic, that ball will pass through the wall like it’s not even there. This might be cool if you’re trying to achieve teleportation, not so much if you’re reaching for collision.
In a game were detecting multiple object collisions is necessary, if you take into account that each object has it’s own trajectory, speed and external forces acting on it… Euler integration proves insufficient.
Another way would be using one of the Runge–Kutta methods but they can put a strain on the processor; it’s perfect for precision physics calculations but probably overkill for our purposes.
The go to method in most games would be to use the equations of the French physicist Loup Verlet, specifically Verlet integration. The main difference is that with Verlet integration you’re calculating the speed of an object and not its position in the next frame. You do this by subtracting the previous position from the current position.
I keep saying speed so I’m going to correct myself; we’re actually calculating velocity. The difference between speed an velocity matters to us, because speed is ignorant of direction, whereas velocity is a vector quantity and it is direction aware; it’s a really small and hugely important distinction.
let velocity = currentPosition - previousPosition;
position += velocity;
Does this mean our collision problem goes away? Not really but it makes it easier to rectify; we can calculate the next state based on our current and previous states, enabling us to resolve any conflicts before we render.
We’ll create a VerletModel in the game engine and this will allow for other objects to use it if needed; for now it should:
- expose getters and setters for the x and y velocities
- expose getters and setters for the x and y position values
This model could potentially hold variables for other things like: gravity, friction, rotation and so on or it could be extended by another class that adds these features. The reasoning behind this is simple: all games have movement but not all games need complex physics.
Let’s see an example; we’ll create another square that moves as described above and add a bit of acceleration and friction logic to our model. A classic MVC approach should do the trick:
- SquareModel class which will extend VerletModel: it will store all data related to our square as well as an update() method which will overwrite the default one in VerletModel.
You can see we’re using the x, y, vx and vy setters and getters from VerletModel to calculate friction which we then add to the acceleration.
- SquareView class which will extend Scene (for now; as this should extend DisplayObject, which we don’t have yet)
The SquareView receives an instance of the SquareModel and SquareCtrl which it updates in its own update() method. You’ll also see that we’re storing a scene reference on the model, as this is needed to properly wrap the square to its parent scene. This is annoying and should be done automatically but we won’t worry about that yet.
- SquareCtrl class which will extend Keyboard
The SquareCtrl has one job: to change the acceleration based on the currently pressed key. When we release a key, the friction is set to 0.94. This value will be multiplied with our velocity and the result added to our acceleration.
In the end we’ll get a friction effect which slows down the object on every frame.
The acceleration increases by 0.5 on every frame if we keep the key pressed. We do have a bit of code duplication in our update() method but it’s fine for the purposes of this demo.
Let’s put everything together:
Here’s a demo of this in action (use the arrow keys):
Source code:
At this point you might be thinking: Aren’t we just adding more complexity to our code? Well yes, the complexity is increasing; some of it can’t be avoided and part of it is due to our MVC approach.
As always, there are improvements to be made here; for starters, the way the scene reference is added on the model is suboptimal, we also have code duplication in our controller and quite a bit of logic in our MVC.
We’ll slowly move away from MVC and go towards a Flux architecture as these articles progress but all things considered we did learn more about how movement works in games.
Next time we’ll create a DisplayObject class and cover: sprite sheets, sprites, animated sprites and frame rate control.