Add two tutorial games that build, run in CI, and cannot drift
examples/sidescroller and examples/jrpg are complete programs, built with the library and exercised headless by ctest. The chapters quote them with `c excerpt=examples/...` blocks rather than restating the code, so a chapter cannot drift from a program that compiles -- the excerpt check fails the moment the source moves. 34 excerpts in one chapter, 21 in the other. The two are complementary. The sidescroller is the physics tutorial: gravity, a jump, coins, hazards. The JRPG is the content-pipeline tutorial: a town map, NPCs spawned from map objects, four-way per-facing animation, a text box, a follower. Both smoke tests drive real SDL_Events through akgl_controller_handle_event and step the physics clock at a fixed 1/60s rather than sleeping, so a scripted run is deterministic and finishes in under five seconds. Writing them is what turned up most of the defects recorded in the next commit, because a game exercises paths a unit test does not. Each workaround says in the chapter which library gap forced it: - collision is written in a custom movementlogicfunc, because akgl_physics_arcade_collide raises AKERR_API and akgl_physics_simulate never calls collide at all; - the sidescroller cancels the step's own gravity when it blocks downward, because otherwise a quarter-pixel of penetration makes the *horizontal* sweep report blocked and the character walks backwards a tile at a time; - both clear movement_controls_face on every map-spawned actor, because the default facefunc leaves a stopped actor with no facing bit, no sprite, and no draw; - the JRPG's follower gets a renderfunc that nulls obj->parent for the duration of the draw, because a child's offset is counted twice. Assets are CC0 from three Kenney packs, vendored with per-pack licence text, per-file provenance, and the geometry contract in docs/tutorials/assets/README.md. CC0 specifically rather than merely free: a reader who copies a tutorial into their own game inherits no obligation. scripts/fetch_tutorial_assets.sh refreshes them in the shape mkcontrollermappings.sh was fixed into for 0.5.0 -- it checks curl's status, refuses a pack page that does not say CC0, verifies the archive and the staged dimensions, and leaves the tracked bytes untouched on any failure. Both failure paths were tested, and a no-op refresh is byte-identical. Co-Authored-By: Claude Code <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
70
docs/tutorials/assets/jrpg/character_jrpg_elder.json
Normal file
70
docs/tutorials/assets/jrpg/character_jrpg_elder.json
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"name": "jrpg_elder",
|
||||
"speedtime": 150,
|
||||
"speed_x": 60.0,
|
||||
"speed_y": 60.0,
|
||||
"acceleration_x": 400.0,
|
||||
"acceleration_y": 400.0,
|
||||
"sprite_mappings": [
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_DOWN"
|
||||
],
|
||||
"sprite": "jrpg_elder_idle_down"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_DOWN",
|
||||
"AKGL_ACTOR_STATE_MOVING_DOWN"
|
||||
],
|
||||
"sprite": "jrpg_elder_walk_down"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_LEFT"
|
||||
],
|
||||
"sprite": "jrpg_elder_idle_left"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_LEFT",
|
||||
"AKGL_ACTOR_STATE_MOVING_LEFT"
|
||||
],
|
||||
"sprite": "jrpg_elder_walk_left"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_RIGHT"
|
||||
],
|
||||
"sprite": "jrpg_elder_idle_right"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_RIGHT",
|
||||
"AKGL_ACTOR_STATE_MOVING_RIGHT"
|
||||
],
|
||||
"sprite": "jrpg_elder_walk_right"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_UP"
|
||||
],
|
||||
"sprite": "jrpg_elder_idle_up"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_UP",
|
||||
"AKGL_ACTOR_STATE_MOVING_UP"
|
||||
],
|
||||
"sprite": "jrpg_elder_walk_up"
|
||||
}
|
||||
]
|
||||
}
|
||||
70
docs/tutorials/assets/jrpg/character_jrpg_player.json
Normal file
70
docs/tutorials/assets/jrpg/character_jrpg_player.json
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"name": "jrpg_player",
|
||||
"speedtime": 150,
|
||||
"speed_x": 60.0,
|
||||
"speed_y": 60.0,
|
||||
"acceleration_x": 400.0,
|
||||
"acceleration_y": 400.0,
|
||||
"sprite_mappings": [
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_DOWN"
|
||||
],
|
||||
"sprite": "jrpg_player_idle_down"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_DOWN",
|
||||
"AKGL_ACTOR_STATE_MOVING_DOWN"
|
||||
],
|
||||
"sprite": "jrpg_player_walk_down"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_LEFT"
|
||||
],
|
||||
"sprite": "jrpg_player_idle_left"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_LEFT",
|
||||
"AKGL_ACTOR_STATE_MOVING_LEFT"
|
||||
],
|
||||
"sprite": "jrpg_player_walk_left"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_RIGHT"
|
||||
],
|
||||
"sprite": "jrpg_player_idle_right"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_RIGHT",
|
||||
"AKGL_ACTOR_STATE_MOVING_RIGHT"
|
||||
],
|
||||
"sprite": "jrpg_player_walk_right"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_UP"
|
||||
],
|
||||
"sprite": "jrpg_player_idle_up"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_UP",
|
||||
"AKGL_ACTOR_STATE_MOVING_UP"
|
||||
],
|
||||
"sprite": "jrpg_player_walk_up"
|
||||
}
|
||||
]
|
||||
}
|
||||
70
docs/tutorials/assets/jrpg/character_jrpg_shopkeeper.json
Normal file
70
docs/tutorials/assets/jrpg/character_jrpg_shopkeeper.json
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"name": "jrpg_shopkeeper",
|
||||
"speedtime": 150,
|
||||
"speed_x": 60.0,
|
||||
"speed_y": 60.0,
|
||||
"acceleration_x": 400.0,
|
||||
"acceleration_y": 400.0,
|
||||
"sprite_mappings": [
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_DOWN"
|
||||
],
|
||||
"sprite": "jrpg_shopkeeper_idle_down"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_DOWN",
|
||||
"AKGL_ACTOR_STATE_MOVING_DOWN"
|
||||
],
|
||||
"sprite": "jrpg_shopkeeper_walk_down"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_LEFT"
|
||||
],
|
||||
"sprite": "jrpg_shopkeeper_idle_left"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_LEFT",
|
||||
"AKGL_ACTOR_STATE_MOVING_LEFT"
|
||||
],
|
||||
"sprite": "jrpg_shopkeeper_walk_left"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_RIGHT"
|
||||
],
|
||||
"sprite": "jrpg_shopkeeper_idle_right"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_RIGHT",
|
||||
"AKGL_ACTOR_STATE_MOVING_RIGHT"
|
||||
],
|
||||
"sprite": "jrpg_shopkeeper_walk_right"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_UP"
|
||||
],
|
||||
"sprite": "jrpg_shopkeeper_idle_up"
|
||||
},
|
||||
{
|
||||
"state": [
|
||||
"AKGL_ACTOR_STATE_ALIVE",
|
||||
"AKGL_ACTOR_STATE_FACE_UP",
|
||||
"AKGL_ACTOR_STATE_MOVING_UP"
|
||||
],
|
||||
"sprite": "jrpg_shopkeeper_walk_up"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
docs/tutorials/assets/jrpg/jingle_start.ogg
Normal file
BIN
docs/tutorials/assets/jrpg/jingle_start.ogg
Normal file
Binary file not shown.
BIN
docs/tutorials/assets/jrpg/npc_elder.png
Normal file
BIN
docs/tutorials/assets/jrpg/npc_elder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 729 B |
BIN
docs/tutorials/assets/jrpg/npc_shopkeeper.png
Normal file
BIN
docs/tutorials/assets/jrpg/npc_shopkeeper.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 783 B |
BIN
docs/tutorials/assets/jrpg/player.png
Normal file
BIN
docs/tutorials/assets/jrpg/player.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 640 B |
16
docs/tutorials/assets/jrpg/sprite_jrpg_elder_idle_down.json
Normal file
16
docs/tutorials/assets/jrpg/sprite_jrpg_elder_idle_down.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "npc_elder.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_elder_idle_down",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 250,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
0
|
||||
]
|
||||
}
|
||||
16
docs/tutorials/assets/jrpg/sprite_jrpg_elder_idle_left.json
Normal file
16
docs/tutorials/assets/jrpg/sprite_jrpg_elder_idle_left.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "npc_elder.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_elder_idle_left",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 250,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
3
|
||||
]
|
||||
}
|
||||
16
docs/tutorials/assets/jrpg/sprite_jrpg_elder_idle_right.json
Normal file
16
docs/tutorials/assets/jrpg/sprite_jrpg_elder_idle_right.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "npc_elder.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_elder_idle_right",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 250,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
6
|
||||
]
|
||||
}
|
||||
16
docs/tutorials/assets/jrpg/sprite_jrpg_elder_idle_up.json
Normal file
16
docs/tutorials/assets/jrpg/sprite_jrpg_elder_idle_up.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "npc_elder.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_elder_idle_up",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 250,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
9
|
||||
]
|
||||
}
|
||||
19
docs/tutorials/assets/jrpg/sprite_jrpg_elder_walk_down.json
Normal file
19
docs/tutorials/assets/jrpg/sprite_jrpg_elder_walk_down.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "npc_elder.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_elder_walk_down",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 150,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0
|
||||
]
|
||||
}
|
||||
19
docs/tutorials/assets/jrpg/sprite_jrpg_elder_walk_left.json
Normal file
19
docs/tutorials/assets/jrpg/sprite_jrpg_elder_walk_left.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "npc_elder.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_elder_walk_left",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 150,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
4,
|
||||
3,
|
||||
5,
|
||||
3
|
||||
]
|
||||
}
|
||||
19
docs/tutorials/assets/jrpg/sprite_jrpg_elder_walk_right.json
Normal file
19
docs/tutorials/assets/jrpg/sprite_jrpg_elder_walk_right.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "npc_elder.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_elder_walk_right",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 150,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
7,
|
||||
6,
|
||||
8,
|
||||
6
|
||||
]
|
||||
}
|
||||
19
docs/tutorials/assets/jrpg/sprite_jrpg_elder_walk_up.json
Normal file
19
docs/tutorials/assets/jrpg/sprite_jrpg_elder_walk_up.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "npc_elder.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_elder_walk_up",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 150,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
10,
|
||||
9,
|
||||
11,
|
||||
9
|
||||
]
|
||||
}
|
||||
16
docs/tutorials/assets/jrpg/sprite_jrpg_player_idle_down.json
Normal file
16
docs/tutorials/assets/jrpg/sprite_jrpg_player_idle_down.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "player.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_player_idle_down",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 250,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
0
|
||||
]
|
||||
}
|
||||
16
docs/tutorials/assets/jrpg/sprite_jrpg_player_idle_left.json
Normal file
16
docs/tutorials/assets/jrpg/sprite_jrpg_player_idle_left.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "player.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_player_idle_left",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 250,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
3
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "player.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_player_idle_right",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 250,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
6
|
||||
]
|
||||
}
|
||||
16
docs/tutorials/assets/jrpg/sprite_jrpg_player_idle_up.json
Normal file
16
docs/tutorials/assets/jrpg/sprite_jrpg_player_idle_up.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "player.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_player_idle_up",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 250,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
9
|
||||
]
|
||||
}
|
||||
19
docs/tutorials/assets/jrpg/sprite_jrpg_player_walk_down.json
Normal file
19
docs/tutorials/assets/jrpg/sprite_jrpg_player_walk_down.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "player.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_player_walk_down",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 150,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0
|
||||
]
|
||||
}
|
||||
19
docs/tutorials/assets/jrpg/sprite_jrpg_player_walk_left.json
Normal file
19
docs/tutorials/assets/jrpg/sprite_jrpg_player_walk_left.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "player.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_player_walk_left",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 150,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
4,
|
||||
3,
|
||||
5,
|
||||
3
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "player.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_player_walk_right",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 150,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
7,
|
||||
6,
|
||||
8,
|
||||
6
|
||||
]
|
||||
}
|
||||
19
docs/tutorials/assets/jrpg/sprite_jrpg_player_walk_up.json
Normal file
19
docs/tutorials/assets/jrpg/sprite_jrpg_player_walk_up.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "player.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_player_walk_up",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 150,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
10,
|
||||
9,
|
||||
11,
|
||||
9
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "npc_shopkeeper.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_shopkeeper_idle_down",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 250,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
0
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "npc_shopkeeper.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_shopkeeper_idle_left",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 250,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
3
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "npc_shopkeeper.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_shopkeeper_idle_right",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 250,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
6
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "npc_shopkeeper.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_shopkeeper_idle_up",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 250,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
9
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "npc_shopkeeper.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_shopkeeper_walk_down",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 150,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "npc_shopkeeper.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_shopkeeper_walk_left",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 150,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
4,
|
||||
3,
|
||||
5,
|
||||
3
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "npc_shopkeeper.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_shopkeeper_walk_right",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 150,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
7,
|
||||
6,
|
||||
8,
|
||||
6
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"spritesheet": {
|
||||
"filename": "npc_shopkeeper.png",
|
||||
"frame_width": 32,
|
||||
"frame_height": 32
|
||||
},
|
||||
"name": "jrpg_shopkeeper_walk_up",
|
||||
"width": 32,
|
||||
"height": 32,
|
||||
"speed": 150,
|
||||
"loop": true,
|
||||
"loopReverse": false,
|
||||
"frames": [
|
||||
10,
|
||||
9,
|
||||
11,
|
||||
9
|
||||
]
|
||||
}
|
||||
BIN
docs/tutorials/assets/jrpg/tiles.png
Normal file
BIN
docs/tutorials/assets/jrpg/tiles.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
1356
docs/tutorials/assets/jrpg/town.tmj
Normal file
1356
docs/tutorials/assets/jrpg/town.tmj
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user