Fix what showed up when the tutorials were actually built and run
Two readers were given the library, the art, the chapter, and no reference
program, and told to compile and run. Both got a working game. These are the
defects that only came out because a compiler and a frame loop were involved --
four earlier read-only reviews had missed every one.
libakstdlib's header was never named. The chapters used aksl_strncpy,
aksl_snprintf and aksl_atoi throughout and said only "libakstdlib is documented
in deps/libakstdlib"; the obvious guess from the function prefix is <aksl.h> and
it is wrong. That was the only compile error the sidescroller reader hit, and it
was the chapter's fault. Both chapters now carry a per-file include table and say
which header it is.
The sidescroller's asset directory defaulted to ".". The chapter said the code
"falls back to `.`" without showing the #ifndef or that assetdir is initialised
from the macro, so a reader gets a game that only finds its art when launched
from exactly the right directory.
The sidescroller's summary line reported 0.0,0.0 airborne while the screenshot
showed the player standing on the ground. Teardown runs in main's CLEANUP block
and releases the actor pool before the summary prints. The chapter published an
example output a reader could not reproduce; the capture that makes it true is
now shown beside it.
The JRPG's CMake block interpolated ${JRPG_REPO_ROOT}, which is this repository's
own variable and undefined for anybody else -- so the font path came out wrong
and akgl_text_loadfont failed long after everything else had loaded. It is
${CMAKE_CURRENT_SOURCE_DIR} now, and the chapter says why both definitions are
needed rather than one.
Chapter 21 described --demo and a summary line in its build section that no step
wrote. Both are now a section: the script table, playback through
akgl_controller_handle_event rather than the handlers, the fixed clock step, and
printing the position before teardown. It also now says libakgl keeps no frame
counter, which a reader assumed it did.
Both map sections now say to draw it in Tiled and point at chapter 13, naming the
fields the loader needs that Tiled fills in automatically -- both readers
hand-wrote a .tmj before being given one and hit exactly those.
Excerpts across docs/ go from 190 to 198.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KzBDV2fqgnUAcqCKqKvc71
This commit is contained in:
@@ -134,19 +134,36 @@ target_compile_definitions(sidescroller
|
||||
|
||||
The parts that matter:
|
||||
|
||||
Each `.c` file includes `sidescroller.h` plus whatever it calls directly. `main.c` adds
|
||||
`akgl/character.h`, `akgl/controller.h`, `akgl/heap.h`, `akgl/registry.h`, `akgl/renderer.h`,
|
||||
`akgl/sprite.h` and `akgl/text.h`; `player.c` adds `akgl/controller.h`, `akgl/heap.h`,
|
||||
`akgl/registry.h`, `akgl/util.h` and `<math.h>`; `actors.c` adds `akgl/heap.h`,
|
||||
`akgl/registry.h` and `<math.h>`. libakgl's headers are self-contained, so including the one
|
||||
that declares what you are calling is always enough.
|
||||
Each `.c` file includes `sidescroller.h` plus whatever it calls directly:
|
||||
|
||||
| File | Adds |
|
||||
|---|---|
|
||||
| `main.c` | `<string.h>`, `akstdlib.h`, `SDL3_image/SDL_image.h`, `SDL3_mixer/SDL_mixer.h`, `SDL3_ttf/SDL_ttf.h`, `akgl/character.h`, `akgl/controller.h`, `akgl/heap.h`, `akgl/registry.h`, `akgl/renderer.h`, `akgl/sprite.h`, `akgl/text.h`, `akgl/tilemap.h` |
|
||||
| `player.c` | `<math.h>`, `akstdlib.h`, `akgl/controller.h`, `akgl/heap.h`, `akgl/physics.h`, `akgl/registry.h`, `akgl/util.h` |
|
||||
| `actors.c` | `<math.h>`, `akstdlib.h`, `akgl/heap.h`, `akgl/registry.h` |
|
||||
|
||||
**libakstdlib's header is `<akstdlib.h>`**, not `<aksl.h>` — its functions are prefixed
|
||||
`aksl_` but the file is not. libakerror's is `<akerror.h>`. libakgl's own headers are
|
||||
self-contained, so including the one that declares what you are calling is always enough.
|
||||
|
||||
The build file:
|
||||
|
||||
- You link all four SDL libraries even though this game has no text and no sound, because
|
||||
libakgl itself is built against them.
|
||||
- `SS_ASSET_DIR` is baked in at compile time, so the program can be run from any working
|
||||
directory. The code falls back to `"."` if it is not defined.
|
||||
directory. Give it a fallback so the file still compiles without CMake, and **use it as the
|
||||
default**: a program that defaults to `"."` only finds its assets when it happens to be
|
||||
launched from the right directory.
|
||||
|
||||
```c excerpt=examples/sidescroller/main.c
|
||||
#ifndef SS_ASSET_DIR
|
||||
#define SS_ASSET_DIR "."
|
||||
#endif
|
||||
```
|
||||
|
||||
```c excerpt=examples/sidescroller/main.c
|
||||
char *assetdir = SS_ASSET_DIR;
|
||||
```
|
||||
|
||||
### The header
|
||||
|
||||
@@ -649,7 +666,14 @@ reads. Load a character before its sprites and it fails on the first name it can
|
||||
|
||||
## 5. Draw a level
|
||||
|
||||
Open Tiled, make a new map, and set it up like this:
|
||||
**Draw the map in [Tiled](https://mapeditor.org) and save it as `.tmj`.** The rest of this
|
||||
section is what to set in the editor; Tiled writes the JSON. [Chapter 13](13-tilemaps.md) is
|
||||
the reference for the format itself, and for what libakgl reads out of it and what it
|
||||
ignores — read that before hand-editing a `.tmj`, because the loader needs several fields
|
||||
Tiled fills in automatically (a root-level `width` and `height`, an `id` on every layer, and
|
||||
`tilecount`, `columns`, `imagewidth` and `imageheight` on every tileset).
|
||||
|
||||
Make a new map and set it up like this:
|
||||
|
||||
| Setting | Value |
|
||||
|---|---|
|
||||
@@ -1625,6 +1649,19 @@ It prints where the player finished:
|
||||
sidescroller: 240 frames, 0 of 4 coins, 0 deaths, player at 136.0,160.0 grounded
|
||||
```
|
||||
|
||||
**Capture that position at the end of `run()`, not in `main`.** Teardown happens in `main`'s
|
||||
`CLEANUP` block and releases the actor pool, so by the time the summary is printed
|
||||
`ss_game.player` points at a slot that has been given back — and the line reports `0.0,0.0`:
|
||||
|
||||
```c excerpt=examples/sidescroller/main.c
|
||||
if ( ss_game.player != NULL ) {
|
||||
ss_game.final_x = ss_game.player->x;
|
||||
ss_game.final_y = ss_game.player->y;
|
||||
}
|
||||
```
|
||||
|
||||
That is what `final_x` and `final_y` on `ss_Game` are for.
|
||||
|
||||
`grounded` and a sensible `y` are how you know collision ran. If the player is hundreds of
|
||||
pixels below the level, revisit step 7.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user