An Asteroids-like game, running in a terminal, written in PHP.
TermAsteroid is a horizontal scrolling Asteroids-like game which has the following particularities:
- it runs in a terminal emulator.
- it is fully implemented in PHP (except for the alternative and optional rendering backend described below).
- it features 2 rendering backends (and can switch between the 2 at runtime):
- one implemented in PHP.
- one implemented in C and called through FFI.
- it highlights:
- the benefit of using FFI in order to reimplement a tight loop, as long as it significantly outweighs the PHP / FFI communication overhead. This is the case here since the main data movement is between the renderer and PHP's output buffer.
- PHP's JIT benefits for such (CPU-bound) application and especially how it boosts the PHP rendering backend, making it 2 times faster and reducing the frame time by 40%.
- PHP's cycle collector fairness for such application. Back in PHP 5 or even early PHP 7 versions, it was still a big issue for long-running processes with high load of object creations & destructions.
- it renders early 90s style 2D graphics featuring:
- 300x144 true color (24bpp) screen reaching 2+ million of pixel changes per second.
- tens of animated medium/large sprites rendered per frame.
- transparency, distortion and persistence effects.
- procedural bitmap / animation generation via Perlin noise.
- pre-rendered sprite rotations.
- adaptive performance in order to preserve a minimal framerate of 35 FPS.
- GNU/Linux distro
- Docker
- A fast terminal emulator with unicode & true color support and an adjusted font size so that a maximized window will render at least 300 columns x 77 rows
- xterm meets these requirements and is embedded in the Docker image
git clone https://github.com/NoiseByNorthwest/term-asteroids.git
cd term-asteroids
make run
The first
make run
execution will take some time (building the Docker image, warming up some caches...) before starting the game, but the next executions will start instantly.
- UP arrow: move the spaceship up
- DOWN arrow: move the spaceship down
- LEFT arrow: move the spaceship left
- RIGHT arrow: move the spaceship right
- Esc or q: quit
- s: reset
Survive as long as possible:
- use the arrow keys to move the spaceship
- avoid collision with the asteroids
- collide with bonuses, they give one third of your health and improve one of your 3 weapons
Default mode (native renderer + JIT)
make run
Run it without JIT
make run.nojit
Run it with PHP renderer
make run.full_php
Run it with PHP renderer and without JIT
make run.full_php.no_jit
The dev mode gives nearly infinite health and more controls (see below). The difficulty level also increases faster.
make run.dev
Additional controls:
- r: toggle renderer
- a: toggle adaptive performance
- w: show bounding and hit boxes
- d: increase blue laser's level
- c: decrease blue laser's level
- f: increase plasma ball's level
- v: decrease plasma ball's level
- g: increase energy beam's level
- b: decrease energy beam's level
This game comes with a benchmark mode which allows to highlight the performance differences between the four optimization levels (PHP/native renderer combined with JIT on/off).
To run all benchmark and generate the Markdown report:
make run.benchmark.all
PHP version: 8.2.6
CPU: Intel(R) Core(TM) i7-9850H CPU @ 2.60GHz
Native Renderer + JIT | Native Renderer | PHP Renderer + JIT | PHP Renderer | |
---|---|---|---|---|
Execution time | 20.7s | 20.9s | 20.7s | 20.9s |
Rendered frames | 909 | 746 | 609 | 369 |
Average frame time | 22.7ms | 28.0ms | 33.9ms | 56.7ms |
Average framerate | 44.0 FPS | 35.7 FPS | 29.5 FPS | 17.6 FPS |
Average gameplay+physic time | 7.0ms | 11.0ms | 7.8ms | 13.5ms |
Average rendering time | 15.8ms | 17.0ms | 26.1ms | 43.2ms |
Average drawing time | 1.7ms | 1.9ms | 7.6ms | 20.7ms |
Average update time | 1.1ms | 1.2ms | 5.4ms | 11.1ms |
Average flushing time | 13.0ms | 13.9ms | 13.1ms | 11.4ms |
Time breakdown explanation:
- frame time: the elapsed time between 2 frames (i.e. the inverse of frame rate). It is compound of gameplay+physic and rendering times.
- gameplay+physic time: this the time spent doing anything other than rendering the new frame, it mainly includes the gameplay & physic (moves, collisions) management.
- rendering time: this is the time spent for the new frame rendering, it is compound of drawing, update and flushing times.
- drawing time: the time spent drawing something (mainly bitmaps) to the frame buffer.
- update time: the time spent generating the stream of characters to the buffered output in order to update the terminal based screen.
- flushing time: the time spent flushing the output buffer. It may counterintuitively increase with a faster rendering loop since a faster rendering loop means a higher character changes throughput and thus more work for the terminal (and consequently a higher output blocking time).