256 lines
6.6 KiB
Markdown
Raw Permalink Normal View History

---
layout: pattern
title: Game Loop
folder: game-loop
permalink: /patterns/game-loop/
categories: Behavioral
language: en
tags:
- Game programming
---
2020-08-29 21:21:01 +03:00
## Intent
A game loop runs continuously during gameplay. Each turn of the loop, it processes user input
without blocking, updates the game state, and renders the game. It tracks the passage of time to
control the rate of gameplay.
2020-07-22 21:34:44 +03:00
This pattern decouples progression of game time from user input and processor speed.
2020-08-29 21:21:01 +03:00
## Applicability
This pattern is used in every game engine.
## Explanation
2020-08-29 21:21:01 +03:00
2020-07-22 21:34:44 +03:00
Real world example
2020-08-29 21:21:01 +03:00
> Game loop is the main process of all the game rendering threads. It's present in all modern games.
> It drives input process, internal status update, rendering, AI and all the other processes.
2020-07-22 21:34:44 +03:00
In plain words
2020-08-29 21:21:01 +03:00
> Game Loop pattern ensures that game time progresses in equal speed in all different hardware
> setups.
2020-07-22 21:34:44 +03:00
Wikipedia says
2020-08-29 21:21:01 +03:00
> The central component of any game, from a programming standpoint, is the game loop. The game loop
> allows the game to run smoothly regardless of a user's input, or lack thereof.
2020-07-22 21:34:44 +03:00
**Programmatic Example**
2020-08-29 21:21:01 +03:00
Let's start with something simple. Here's `Bullet` class. Bullets will move in our game. For
demonstration purposes it's enough that it has 1-dimensional position.
2020-07-22 21:34:44 +03:00
```java
public class Bullet {
private float position;
public Bullet() {
position = 0.0f;
}
public float getPosition() {
return position;
}
public void setPosition(float position) {
this.position = position;
}
}
```
2020-08-29 21:21:01 +03:00
`GameController` is responsible for moving objects in the game, including the aforementioned bullet.
2020-07-22 21:34:44 +03:00
```java
public class GameController {
protected final Bullet bullet;
public GameController() {
bullet = new Bullet();
}
public void moveBullet(float offset) {
var currentPosition = bullet.getPosition();
bullet.setPosition(currentPosition + offset);
}
public float getBulletPosition() {
return bullet.getPosition();
}
}
```
2020-08-29 21:21:01 +03:00
Now we introduce the game loop. Or actually in this demo we have 3 different game loops. Let's see
the base class `GameLoop` first.
2020-07-22 21:34:44 +03:00
```java
public enum GameStatus {
RUNNING, STOPPED
}
public abstract class GameLoop {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
protected volatile GameStatus status;
protected GameController controller;
private Thread gameThread;
public GameLoop() {
controller = new GameController();
status = GameStatus.STOPPED;
}
public void run() {
status = GameStatus.RUNNING;
2020-08-29 21:21:01 +03:00
gameThread = new Thread(this::processGameLoop);
2020-07-22 21:34:44 +03:00
gameThread.start();
}
public void stop() {
status = GameStatus.STOPPED;
}
public boolean isGameRunning() {
return status == GameStatus.RUNNING;
}
protected void processInput() {
try {
var lag = new Random().nextInt(200) + 50;
Thread.sleep(lag);
} catch (InterruptedException e) {
logger.error(e.getMessage());
}
}
protected void render() {
var position = controller.getBulletPosition();
logger.info("Current bullet position: " + position);
}
protected abstract void processGameLoop();
}
2020-08-29 21:21:01 +03:00
```
Here's the first game loop implementation, `FrameBasedGameLoop`:
2020-07-22 21:34:44 +03:00
2020-08-29 21:21:01 +03:00
```java
2020-07-22 21:34:44 +03:00
public class FrameBasedGameLoop extends GameLoop {
@Override
protected void processGameLoop() {
while (isGameRunning()) {
processInput();
update();
render();
}
}
protected void update() {
controller.moveBullet(0.5f);
}
}
```
2020-08-29 21:21:01 +03:00
Finally, we show all the game loops in action.
2020-07-22 21:34:44 +03:00
```java
try {
LOGGER.info("Start frame-based game loop:");
var frameBasedGameLoop = new FrameBasedGameLoop();
frameBasedGameLoop.run();
Thread.sleep(GAME_LOOP_DURATION_TIME);
frameBasedGameLoop.stop();
LOGGER.info("Stop frame-based game loop.");
LOGGER.info("Start variable-step game loop:");
var variableStepGameLoop = new VariableStepGameLoop();
variableStepGameLoop.run();
Thread.sleep(GAME_LOOP_DURATION_TIME);
variableStepGameLoop.stop();
LOGGER.info("Stop variable-step game loop.");
LOGGER.info("Start fixed-step game loop:");
var fixedStepGameLoop = new FixedStepGameLoop();
fixedStepGameLoop.run();
Thread.sleep(GAME_LOOP_DURATION_TIME);
fixedStepGameLoop.stop();
LOGGER.info("Stop variable-step game loop.");
} catch (InterruptedException e) {
LOGGER.error(e.getMessage());
}
```
2020-08-29 21:21:01 +03:00
Program output:
```java
Start frame-based game loop:
Current bullet position: 0.5
Current bullet position: 1.0
Current bullet position: 1.5
Current bullet position: 2.0
Current bullet position: 2.5
Current bullet position: 3.0
Current bullet position: 3.5
Current bullet position: 4.0
Current bullet position: 4.5
Current bullet position: 5.0
Current bullet position: 5.5
Current bullet position: 6.0
Stop frame-based game loop.
Start variable-step game loop:
Current bullet position: 6.5
Current bullet position: 0.038
Current bullet position: 0.084
Current bullet position: 0.145
Current bullet position: 0.1805
Current bullet position: 0.28
Current bullet position: 0.32
Current bullet position: 0.42549998
Current bullet position: 0.52849996
Current bullet position: 0.57799995
Current bullet position: 0.63199997
Current bullet position: 0.672
Current bullet position: 0.778
Current bullet position: 0.848
Current bullet position: 0.8955
Current bullet position: 0.9635
Stop variable-step game loop.
Start fixed-step game loop:
Current bullet position: 0.0
Current bullet position: 1.086
Current bullet position: 0.059999995
Current bullet position: 0.12999998
Current bullet position: 0.24000004
Current bullet position: 0.33999994
Current bullet position: 0.36999992
Current bullet position: 0.43999985
Current bullet position: 0.5399998
Current bullet position: 0.65999967
Current bullet position: 0.68999964
Current bullet position: 0.7299996
Current bullet position: 0.79999954
Current bullet position: 0.89999944
Current bullet position: 0.98999935
Stop variable-step game loop.
```
## Class diagram
2020-08-29 21:21:01 +03:00
![alt text](./etc/game-loop.urm.png "Game Loop pattern class diagram")
2020-08-29 21:21:01 +03:00
## Credits
* [Game Programming Patterns - Game Loop](http://gameprogrammingpatterns.com/game-loop.html)
2020-07-22 21:34:44 +03:00
* [Game Programming Patterns](https://www.amazon.com/gp/product/0990582906/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0990582906&linkId=1289749a703b3fe0e24cd8d604d7c40b)
* [Game Engine Architecture, Third Edition](https://www.amazon.com/gp/product/1138035459/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1138035459&linkId=94502746617211bc40e0ef49d29333ac)