Tag Archives: game development

Game Development: The Engine (Game Loop)

All computer games, when you boil them down to their simplest form, consist of a notion called the ‘game loop’. When you consider what a game really is you realize that it’s just a looping construct thats continuously receiving inputs and drawing outputs. The simplest loop looks like this:

  1. Calculate time elapsed since last loop.
  2. Accept inputs from the user(s).
  3. Decide what to do based upon those inputs, including updating entity positions.
  4. Draw everything to the screen.
  5. Repeat the loop.

I’ve been working on a game in my spare time (ok, so I’m ALWAYS working on a game in my spare time, shutup, let’s get back on topic). I spent a fair amount of time simplifying my game loop so that any developer could come along and immediately understand what it’s doing. As it turns out, this process of simplification is a great way to decouple things into a better design. Here’s what I’ve ended up with so far:

private boolean running;
private long lastLoopTimestamp;
private final Renderer renderer;
private final RateLimiter rateLimiter;

public Engine(Renderer renderer, RateLimiter rateLimiter) {
	this.renderer = renderer;
	this.rateLimiter = rateLimiter;
}

public void run() {
	
	this.running = true;

	while(running) {
		
		long elapsedTimeMillis = 
                  this.calculateLoopElapsedTime();

		this.processInput();
		this.think(elapsedTimeMillis);
		this.drawWorld();

		this.rateLimiter.blockAsNeeded(
                  System.currentTimeMillis());
	}
	
}

private long calculateLoopElapsedTime() {
	
	long now = System.currentTimeMillis();
	long elapsed = now - this.lastLoopTimestamp;
	this.lastLoopTimestamp = now;
	
	return elapsed;

}

The basic idea is that the Engine class here knows nothing about processing input, thinking about the logic, or rendering the world to the screen. It does however orchestrate all of those things (omitted the contents of the orchestration methods for brevity). The only thing the Engine cares about is knowing how to run, and how fast.

I think this is a pretty clean design. I’ll post more later about how I decoupled what’s being rendered to (ultimately a Canvas).

Collision Detection in 2D Games

Implementing per-pixel collision detection in a 2D game can be accomplished using a variety of methods:

  1. Sprite image rotation, bounds intersection
  2. Sprite translation, per-pixel comparison of of non-alpha-transparent pixels
  3. Sprite translation, maintaining a bounding box, per-pixel comparison of of non-alpha-transparent pixels

The first method assumes that you are rendering your image verbatim, and the image is actually what you are manipulating. This is typically the way you’d be doing things in a Java Swing game. The bounding box of your sprite is effectively the bounding box of your image.

Here’s what that might look like:

    if (sprite.boundingBox.intersects(otherSprite.boundingBox)) {
        //collision!
    }

The second method utilizes matrix transforms (location, rotation, scaling) to figure out where and how to render pixels. This transformation is the most common scenario in C# XNA game development. Since you are essentially plotting pixels right to the screen, no bounding box exists. Each time your game loop runs, it’s necessary to ask the sprite where its pixels currently are and compare them with collidable pixels in the scene. This is VERY expensive, since you have to ask all your sprites on the screen for their current pixel information, ask your player sprite for its pixel information, and then do combinatorial math to determine if any non-alpha-transparent pixels are true for both sets of pixels.

Here’s what that might look like:

public bool CollidesWith(Sprite entitySprite)
{
    Matrix myMatrix = this.Sprite.GetMatrixTransform();
    Matrix theirMatrix = entitySprite.GetMatrixTransform();

    Color[,] myColorArray = this.Sprite.ColorArray;
    Color[,] theirColorArray = entitySprite.ColorArray;

    Matrix myMatrixToTheirs = myMatrix * Matrix.Invert(theirMatrix);

    int myWidth = myColorArray.GetLength(0);
    int myHeight = myColorArray.GetLength(1);
    int theirWidth = theirColorArray.GetLength(0);
    int theirHeight = theirColorArray.GetLength(1);

    for (int x1 = 0; x1 < myWidth; x1++)
    {
        for (int y1 = 0; y1 < myHeight; y1++)
        {
            Vector2 pos1 = new Vector2(x1, y1);
            Vector2 pos2 = Vector2.Transform(pos1, myMatrixToTheirs);

            int x2 = (int)pos2.X;
            int y2 = (int)pos2.Y;
            if ((x2 >= 0) && (x2 < theirWidth) &&
                (y2 >= 0) && (y2 < theirHeight) &&
                (myColorArray[x1, y1].A > 0) && 
                (theirColorArray[x2, y2].A > 0))
            {
                return true;
            }
        }
    }

    return false;
}

The third method is essentially the previous method, with the optimization of a bounding box being maintained around your drawn pixels. Checking for a collision has an early-exit optimization, in that you first check which sprites bounding boxes intersect with eachother, and then only do the per-pixel collision detection on those sprites. This is a much better way to do things, and many orders of magnitude faster.

Here’s what that might look like:

public bool CollidesWith(Sprite entitySprite)
{
    if (!entitySprite.BoundingBox.Intersects(this.Sprite.BoundingBox))
    {
        return false;
    }

    ...
    //rest of above method here
    ...
}

Pixel, Texel, Voxel

I learned a few new terms today for graphics programming. Pixel is something everyone knows of course but Texel and Voxel are less known.

  • A pixel is the single smallest point addressable on your screen.
  • A texel is the smallest unit of whatever it is that you are drawing to the screen. It’s pixels are inferred based upon many factors (for example, scaling).
  • A voxel is the smallest unit of 3D space that you are drawing. All the voxels being drawn in a 3D object are inferred based upon relation to other voxels (you specify points to define a volume).

Tank Age: Movement

I’ve been developing a top-down tank game, and I needed a way to accurately represent the movement of a vehicle with independently mobile treads. Obviously controls such as left/right/up/down needed to be polled from the keyboard, but their specific interaction with eachother and the rate of movement corresponding to a key being continuously held down needs to be representative of gas/brakes, etc. Additionally, when a tank turns, its speed is negatively affected by the nature of treads; To turn a tank slows or reverses one tread, thus causing a differential in the speed one tread is traveling in relation to another. Here’s what I came up with:

    private void gameLoopInput()
    {
        Tank tank = _state.getTank();

        if(_input.left())
            tank.rotateLeft();
        if(_input.right())
            tank.rotateRight();

        if((_input.left() == false) && (_input.right() == false))
            if(_input.up())
                tank.increaseDirectionalForce();

        if(_input.down())
            tank.decreaseDirectionalForce();

        if(_input.up() == false && _input.down() == false)
            tank.decayDirectionalForce();
    }

The specifics behind the rotation methods of the tank sprite:

    public void rotateLeft()
    {
        if(_velocity > 80)
            _angle = clampAngle(_angle - Constants.ROTATE_ANGLE_FAST);
        else
            _angle = clampAngle(_angle - Constants.ROTATE_ANGLE_SLOW);
    }

    public void rotateRight()
    {
        if(_velocity > 80)
            _angle = clampAngle(_angle + Constants.ROTATE_ANGLE_FAST);
        else
            _angle = clampAngle(_angle + Constants.ROTATE_ANGLE_SLOW);
    }

    private double clampAngle(double angle)
    {
        double result = angle % 360;
        if (result < 0)
            result = result + 360;
        return result;
    }

And the wrappers around those methods for the tank, as well as the other tank methods:

    public void increaseDirectionalForce()
    {
        long millis = System.currentTimeMillis();
        if((millis - Constants.SPEED_DELAY) < _lastDirectionalForceChangeMillis)
            return;

        if(_directionalForce < 10)
        {
            _directionalForce++;
            _lastDirectionalForceChangeMillis = System.currentTimeMillis();
        }
    }

    public void decreaseDirectionalForce()
    {
        long millis = System.currentTimeMillis();
        if((millis - Constants.SPEED_DELAY) < _lastDirectionalForceChangeMillis)
            return;

        if(_directionalForce > -10)
        {
            _directionalForce--;
            _lastDirectionalForceChangeMillis = System.currentTimeMillis();
        }
    }

    public void decayDirectionalForce()
    {
        long millis = System.currentTimeMillis();
        if(millis - Constants.SPEED_DELAY < _lastDirectionalForceChangeMillis)
            return;

        if(_directionalForce > 0)
        {
            _directionalForce--;
            _lastDirectionalForceChangeMillis = System.currentTimeMillis();
        }
        else if(_directionalForce < 0)
        {
            _directionalForce++;
            _lastDirectionalForceChangeMillis = System.currentTimeMillis();
        }
    }

    public void rotateLeft()
    {
        _sprite.rotateLeft();

        long millis = System.currentTimeMillis();
        if(millis - Constants.SPEED_DELAY < _lastDirectionalForceChangeMillis)
            return;

        if(_directionalForce > 5)
            _directionalForce--;

        _lastDirectionalForceChangeMillis = System.currentTimeMillis();
    }

    public void rotateRight()
    {
        _sprite.rotateRight();

        long millis = System.currentTimeMillis();
        if(millis - Constants.SPEED_DELAY < _lastDirectionalForceChangeMillis)
            return;

        if(_directionalForce > 5)
            _directionalForce--;

        _lastDirectionalForceChangeMillis = System.currentTimeMillis();
    }