40. The Engine at a Glance¶
The game chapters told the engine's story in the order the games needed it: high scores with Mines, the game loop with Paddle, flow fields with Towers. That is the right order to learn it in, and the wrong order to look something up in. Part IV is the manual: the same engine, sorted by what it is for.
Each chapter of this part covers one area. Each section in it describes one feature: what it is for, when to choose it and when something else fits better, how it works inside, and its methods. A line at the top of each section names the package, the chapter that introduced the feature, and the source files on GitHub, so you can go from the manual to the story and to the code. The tests of the engine are its best examples; most sections show one.
| Chapter | Packages | For |
|---|---|---|
| 41. Loop, Time and Drawing | loop, time, swing |
The game loop, rules with a pace of their own, countdowns, the canvas, full screen, frame times |
| 42. Sprites and Effects | sprite, shape, anim, fx |
Pictures that copy fast, a ground painted only where it changed, sheets and animation, turned pictures, figures of lines, tweens, particles |
| 43. Input | input |
Keyboard, actions instead of keys, mouse, gamepads |
| 44. Movement, Physics and Collision | math, motion, physics, collision, pool, entity |
Vectors and angles, flight paths, falling and jumping, flying on jets and landing, what hits what (down to the pixel), many things at once |
| 45. Maps, Camera and Fog | tile, tiled, iso, camera, fog, minimap |
Tile maps, reach and networks on them, maps from an editor, the isometric view, worlds larger than the screen, what a side sees |
| 46. Paths, Groups and Decisions | path, road, select, ai, event |
Finding the way for one or for many, roads between flags, choosing units, states, the computer's view of the map, chain reactions |
| 47. Chance, Data and Saving | random, replay, undo, data, economy, save, score |
Repeatable chance, replays, undo, data files, money and production, saving, high scores |
| 48. Scenes, Menus and Sound | scene, swing, menu, audio |
Screens of a game, the standard menus and dialogs, menus over the game, sound and music, speech |
What the engine is, and is not¶
The engine is a library, not a framework. It never calls your game unless you hand it something to call, it has no base class to extend and no main loop you must fit into, except the GameLoop if you want one. It knows nothing about bricks, ghosts or crystal: every class in it is used by at least one game, and none of them knows which.
It also has no editor, no scene graph and no physics engine in the usual sense. What it has is small classes, most of them a page or two long, that each solve one problem completely and are tested. A game takes the ones it needs and writes the rest itself.
Ideas that run through the engine¶
A few rules hold for nearly every class. Knowing them makes the rest of the manual shorter.
The rules never see Swing. Everything the domain of a game uses (vectors, tile maps, paths, timers, the economy) is plain Java without java.awt, so the rules can be tested in milliseconds without a screen (chapter 2). Only swing, sprite, fx, shape, fog.FogPainter, minimap, menu drawing and audio touch the screen or the sound card. collision.PixelMask uses Java2D, but only to draw shapes into an image in memory, which works headless; the rules may use it.
No class reads the clock by itself. FixedStep, Ticker, Timers, Jump, Camera.follow, Production and the others are told how many seconds passed. GameClock, Keyboard and Tween take their time from a function you pass, System::nanoTime in the game and a number the test moves in tests. So a test can play a minute of a game in a millisecond, and every second of it is the same each time.
Logical coordinates. A game draws as if the screen were always the same size, 640 × 400 units in most games of this book. The GameCanvas scales that picture to the window, and the Mouse reports positions in the same units. No rule, and no drawing code, ever sees the window's real size (chapter 20).
Two threads, one owner. Swing calls the game on its event thread; the game loop runs the rules and the drawing on a thread of its own. The input classes stand between them: the event thread reports, the game loop takes over the state with poll() once per step. Anything else that must cross from Swing to the game goes through GameLoop.post. Everything the rules touch then belongs to the loop thread alone and needs no locks (chapter 20).
Draw between two steps. The rules move in fixed steps, the screen shows frames whenever it can. Things that move keep their previous position (Body, Walker, Camera, Particles), and the drawing places them alpha of the way from the last step to the current one (chapter 20).
The same game every time. Chance comes from a SeededRandom with a seed, ties are broken by a fixed order (Direction, the order things were made or started in), and events are handled first in, first out. With the same seed and the same input, a game runs the same way on every computer: that is what makes replays, tests of whole games and loaded saves work (chapter 22).
No garbage in the frame loop. Classes that run hundreds of times per frame (Particles, Pool, Grid, Outline, Minimap, FogPainter) make their objects once and reuse them. The garbage collector then has nothing to do while the game runs, and never pauses it (chapter 21).
Errors name the place. Data written by people is read with errors that say where the mistake is: waves.txt:12: "swoop-lef" is not a path. TextData, Catalog, TileMap.read, TiledMap and Snapshot all do this (chapter 24).
Use it on one thread. Unless a class says otherwise, it is not thread-safe and belongs to the game loop. The classes that cross threads say so: Keyboard, Mouse, Gamepad, FrameTimes, GameLoop.post, Mixer.play and GameCanvas.setRenderMode.
I need … : use …¶
| I need | Use | Section |
|---|---|---|
| Rules that run 120 times a second, frames as fast as the screen allows | GameLoop |
41 |
| Rules that move in ticks of their own (a snake's cell, a falling row) | Ticker |
41 |
| Many countdowns: fuses, cooldowns, build times | Timers |
41 |
| A year of seasons, which one began | Seasons |
41 |
| The time of one game, for a high score | GameClock |
41 |
| A drawing surface in fixed logical coordinates | GameCanvas |
41 |
| A map that fills a wide screen instead of black bars | GameCanvas.setMaxLogicalWidth, Camera.resize |
41, 45 |
| Full screen with F11, without ending or pausing the game | FullScreen |
41 |
| Animation in a Swing component of a game that waits for the player | Animator, Tween |
41, 42 |
| A picture from a file or drawn in code, fast at any size | Sprite |
42 |
| A big ground drawn once, then only where the view moved or the map changed | ScrollingLayer |
42 |
| Many frames in one image, and which one to show | SpriteSheet, Animation |
42, 42 |
| A picture that turns | Rotations or Outline |
42 |
| Splinters, sparks, dust | Particles |
42 |
| "Does the player move up?" from keys or a pad | Controls |
43 |
| The mouse in game units | Mouse |
43 |
| Gamepads on Linux and Windows | Gamepads, Gamepad |
43 |
| Things that fly at an angle | Vector, Angles |
44 |
| A field without edges | Wrap |
44 |
| A smooth curve to fly along at an even speed | FlightPath |
44 |
| Animals that graze around a home, and flee | Wander |
44 |
| Falling, standing, jumping on tiles | Body, TileCollider, Jump |
44 |
| A craft on jets against gravity, fuel, air, a soft or hard landing | Thrusters, Body.drag, Touchdown |
44 |
| Collision of fast boxes, of circles, of many things | Sweep, Circle, Grid |
44 |
| Collision exact to the pixel, for shapes that are neither boxes nor circles | PixelMask |
44 |
| Shots with a limit, without garbage | Pool |
44 |
| Things whose behaviour changes while they live | Entity, Entities |
44 |
| A level on a grid, written as text | TileMap |
45 |
| Walking corridors from tile centre to tile centre | Walker |
45 |
| Land owned around buildings, a border that grows | Territory |
45 |
| Services that reach a circle, how strongly | Coverage |
45 |
| Power or water that flows from sources to loads | SupplyGrid |
45 |
| Levels drawn in the Tiled editor | TiledMap |
45 |
| A map seen from a corner, diamonds and walls, the mouse back to its tile | IsoProjection |
45 |
| Buildings of any size that cover each other right | DepthSort |
45 |
| A world larger than the screen | Camera, Parallax |
45 |
| What a side has seen and sees now | FogOfWar, FogPainter |
45 |
| The whole map in a corner | Minimap, DiamondMinimap |
45, 45 |
| The way for one, or for many to one goal | AStar, DistanceMap, FlowField |
46 |
| Roads from flag to flag, the next road for a ware | RoadNetwork |
46 |
| Groups that arrive side by side | Formation, FieldCache |
46 |
| Click and box selection, groups on number keys | Selection, BoxSelect |
46 |
| A character with modes | StateMachine |
46 |
| Where a side is strong or weak | InfluenceMap |
46 |
| Chain reactions | EventQueue |
46 |
| Chance that repeats | SeededRandom |
47 |
| Replays, undo | Replay, History |
47, 47 |
| Levels, waves, units as text files | TextData, Catalog |
47, 47 |
| Money and things that take time to make | Credits, Production |
47 |
| Goods by kind, and what a workshop makes of them | Stock, Recipe |
47 |
| Progress, or a whole game, on disk | SaveFile, Snapshot |
47 |
| High scores | HighScores |
47 |
| Title, match, pause and end screens | Scene, SceneStack |
48 |
| New Game, Full Screen, Exit with the usual keys | GameMenus |
48 |
| A menu on the map, beside what it is about | OverlayMenu |
48 |
| Sound effects and music | Sound, Tune, Mixer |
48 |
| Characters who talk, without recordings | Speech, Phoneme |
48 |
The packages by chapter¶
The order in which the engine grew, and where each part is explained step by step:
| Chapter | Game | Packages and classes it brought |
|---|---|---|
| 10 | Mines | score (HighScores, Score, Ranking, PlayerName), swing.GameMenus, swing.HighScoreDialog, swing.GameTexts |
| 11 | Solitaire | undo.History, anim (Easing, Tween), swing.Animator, time.GameClock |
| 20 | Paddle | loop (GameLoop, FixedStep), swing.GameCanvas, swing.FullScreen, input (KeyState, Keyboard, Controls), scene, audio (Sound, Mixer) |
| 21 | Bricks | collision (Box, Sweep), sprite.Sprite, fx.Particles, loop.FrameTimes, swing.RenderMode, input.Mouse |
| 22 | Snake | loop.Ticker, random.SeededRandom, replay.Replay |
| 23 | Rocks | math (Vector, Angles, Wrap), shape.Outline, pool.Pool, collision.Circle |
| 24 | Swarm | sprite (SpriteSheet, Animation), entity, motion.FlightPath, data.TextData, collision.Grid, audio.Tune |
| 25 | Maze | tile (TileMap, Cell, Direction, Walker), ai.StateMachine, path (DistanceMap, AStar, Passable) |
| 26 | Blast | input (Gamepad, Gamepads, LinuxJoysticks, XInput), time.Timers, event.EventQueue |
| 27 | Jump | camera (Camera, Parallax), physics (Body, Solidity, TileCollider, Jump), tiled, save.SaveFile |
| 28 | Hover Cab | audio (Speech, Phoneme), collision.PixelMask, physics (Thrusters, Touchdown, Body.drag) |
| 30 | Towers | path.FlowField, data.Catalog, menu.OverlayMenu, sprite.Rotations |
| 31 | Sands | select, path (Formation, FieldCache), fog, minimap, economy, ai.InfluenceMap, save.Snapshot |
| 32 | Colony | road.RoadNetwork, economy (Stock, Recipe), time.Seasons, tile.Territory, motion.Wander |
| 33 | Town | iso (IsoProjection, DepthSort, DiamondMinimap), tile (Coverage, SupplyGrid), sprite.ScrollingLayer, the wheel of input.Mouse |
Many classes grew later, when another game needed a little more: Camera learned the mouse in Towers, Mouse the wheel in Town, Sprite its opaque caches in Jump and Towers, SeededRandom to be saved in Sands. The sections of this part describe each class as it is now.
Using the engine in a game¶
A game declares the engine as an ordinary dependency, com.github.datazuul:amade-game-engine from JitPack, pinned to a commit of the engine. Its settings.gradle builds a checkout of the engine next to the game instead, when there is one, so a change in the engine is tried in a game before it is merged (chapter 0). A game and its engine change therefore go in as two pull requests: the engine's first, then the game's, which pins the engine's new commit.
The engine brings amade-commons along: the theme, the start-up, the XDG directories and the texts. Gamepads on Windows call XInput through java.lang.foreign, so a game that uses them runs with --enable-native-access=ALL-UNNAMED, in its tests, its run task, its packaged launcher and its jar's manifest (chapter 26).