diff --git a/animation/animation_states/all.texture_profiles b/animation/animation_states/all.texture_profiles new file mode 100644 index 0000000..5b0d776 --- /dev/null +++ b/animation/animation_states/all.texture_profiles @@ -0,0 +1,18 @@ +path_settings { + path: "**" + profile: "Default" +} +profiles { + name: "Default" + platforms { + os: OS_ID_GENERIC + formats { + format: TEXTURE_FORMAT_RGBA + compression_level: BEST + compression_type: COMPRESSION_TYPE_DEFAULT + } + mipmaps: false + max_texture_size: 0 + premultiply_alpha: true + } +} diff --git a/animation/animation_states/animation_states_collection.png b/animation/animation_states/animation_states_collection.png new file mode 100644 index 0000000..4754e32 Binary files /dev/null and b/animation/animation_states/animation_states_collection.png differ diff --git a/animation/animation_states/assets/SourceSansPro-Semibold.ttf b/animation/animation_states/assets/SourceSansPro-Semibold.ttf new file mode 100755 index 0000000..5020594 Binary files /dev/null and b/animation/animation_states/assets/SourceSansPro-Semibold.ttf differ diff --git a/animation/animation_states/assets/example.input_binding b/animation/animation_states/assets/example.input_binding new file mode 100644 index 0000000..36126eb --- /dev/null +++ b/animation/animation_states/assets/example.input_binding @@ -0,0 +1,20 @@ +key_trigger { + input: KEY_SPACE + action: "jump" +} +key_trigger { + input: KEY_C + action: "crouch" +} +key_trigger { + input: KEY_X + action: "attack" +} +key_trigger { + input: KEY_RIGHT + action: "right" +} +key_trigger { + input: KEY_LEFT + action: "left" +} diff --git a/animation/animation_states/assets/images/AttackLeft1.png b/animation/animation_states/assets/images/AttackLeft1.png new file mode 100644 index 0000000..ebd13e0 Binary files /dev/null and b/animation/animation_states/assets/images/AttackLeft1.png differ diff --git a/animation/animation_states/assets/images/AttackLeft2.png b/animation/animation_states/assets/images/AttackLeft2.png new file mode 100644 index 0000000..b30a782 Binary files /dev/null and b/animation/animation_states/assets/images/AttackLeft2.png differ diff --git a/animation/animation_states/assets/images/AttackLeft3.png b/animation/animation_states/assets/images/AttackLeft3.png new file mode 100644 index 0000000..b69bdb6 Binary files /dev/null and b/animation/animation_states/assets/images/AttackLeft3.png differ diff --git a/animation/animation_states/assets/images/AttackLeft4.png b/animation/animation_states/assets/images/AttackLeft4.png new file mode 100644 index 0000000..06fe05c Binary files /dev/null and b/animation/animation_states/assets/images/AttackLeft4.png differ diff --git a/animation/animation_states/assets/images/AttackLeft5.png b/animation/animation_states/assets/images/AttackLeft5.png new file mode 100644 index 0000000..00c5120 Binary files /dev/null and b/animation/animation_states/assets/images/AttackLeft5.png differ diff --git a/animation/animation_states/assets/images/AttackLeft6.png b/animation/animation_states/assets/images/AttackLeft6.png new file mode 100644 index 0000000..72284f9 Binary files /dev/null and b/animation/animation_states/assets/images/AttackLeft6.png differ diff --git a/animation/animation_states/assets/images/AttackRight1.png b/animation/animation_states/assets/images/AttackRight1.png new file mode 100644 index 0000000..07b3be9 Binary files /dev/null and b/animation/animation_states/assets/images/AttackRight1.png differ diff --git a/animation/animation_states/assets/images/AttackRight2.png b/animation/animation_states/assets/images/AttackRight2.png new file mode 100644 index 0000000..75a4bef Binary files /dev/null and b/animation/animation_states/assets/images/AttackRight2.png differ diff --git a/animation/animation_states/assets/images/AttackRight3.png b/animation/animation_states/assets/images/AttackRight3.png new file mode 100644 index 0000000..4b360d0 Binary files /dev/null and b/animation/animation_states/assets/images/AttackRight3.png differ diff --git a/animation/animation_states/assets/images/AttackRight4.png b/animation/animation_states/assets/images/AttackRight4.png new file mode 100644 index 0000000..798e6ec Binary files /dev/null and b/animation/animation_states/assets/images/AttackRight4.png differ diff --git a/animation/animation_states/assets/images/Crouch1.png b/animation/animation_states/assets/images/Crouch1.png new file mode 100644 index 0000000..f7c8bc9 Binary files /dev/null and b/animation/animation_states/assets/images/Crouch1.png differ diff --git a/animation/animation_states/assets/images/Crouch2.png b/animation/animation_states/assets/images/Crouch2.png new file mode 100644 index 0000000..aea8b62 Binary files /dev/null and b/animation/animation_states/assets/images/Crouch2.png differ diff --git a/animation/animation_states/assets/images/Crouch3.png b/animation/animation_states/assets/images/Crouch3.png new file mode 100644 index 0000000..f7c8bc9 Binary files /dev/null and b/animation/animation_states/assets/images/Crouch3.png differ diff --git a/animation/animation_states/assets/images/CrouchAttack1.png b/animation/animation_states/assets/images/CrouchAttack1.png new file mode 100644 index 0000000..d840860 Binary files /dev/null and b/animation/animation_states/assets/images/CrouchAttack1.png differ diff --git a/animation/animation_states/assets/images/CrouchAttack2.png b/animation/animation_states/assets/images/CrouchAttack2.png new file mode 100644 index 0000000..48aa2af Binary files /dev/null and b/animation/animation_states/assets/images/CrouchAttack2.png differ diff --git a/animation/animation_states/assets/images/CrouchAttack3.png b/animation/animation_states/assets/images/CrouchAttack3.png new file mode 100644 index 0000000..88a5ebd Binary files /dev/null and b/animation/animation_states/assets/images/CrouchAttack3.png differ diff --git a/animation/animation_states/assets/images/CrouchAttack4.png b/animation/animation_states/assets/images/CrouchAttack4.png new file mode 100644 index 0000000..0d4267d Binary files /dev/null and b/animation/animation_states/assets/images/CrouchAttack4.png differ diff --git a/animation/animation_states/assets/images/CrouchWalk1.png b/animation/animation_states/assets/images/CrouchWalk1.png new file mode 100644 index 0000000..1e332bc Binary files /dev/null and b/animation/animation_states/assets/images/CrouchWalk1.png differ diff --git a/animation/animation_states/assets/images/CrouchWalk2.png b/animation/animation_states/assets/images/CrouchWalk2.png new file mode 100644 index 0000000..56c0ed0 Binary files /dev/null and b/animation/animation_states/assets/images/CrouchWalk2.png differ diff --git a/animation/animation_states/assets/images/CrouchWalk3.png b/animation/animation_states/assets/images/CrouchWalk3.png new file mode 100644 index 0000000..f9505fd Binary files /dev/null and b/animation/animation_states/assets/images/CrouchWalk3.png differ diff --git a/animation/animation_states/assets/images/CrouchWalk4.png b/animation/animation_states/assets/images/CrouchWalk4.png new file mode 100644 index 0000000..1b0321a Binary files /dev/null and b/animation/animation_states/assets/images/CrouchWalk4.png differ diff --git a/animation/animation_states/assets/images/CrouchWalk5.png b/animation/animation_states/assets/images/CrouchWalk5.png new file mode 100644 index 0000000..e4c9449 Binary files /dev/null and b/animation/animation_states/assets/images/CrouchWalk5.png differ diff --git a/animation/animation_states/assets/images/CrouchWalk6.png b/animation/animation_states/assets/images/CrouchWalk6.png new file mode 100644 index 0000000..8fe7f63 Binary files /dev/null and b/animation/animation_states/assets/images/CrouchWalk6.png differ diff --git a/animation/animation_states/assets/images/CrouchWalk7.png b/animation/animation_states/assets/images/CrouchWalk7.png new file mode 100644 index 0000000..ec9308c Binary files /dev/null and b/animation/animation_states/assets/images/CrouchWalk7.png differ diff --git a/animation/animation_states/assets/images/CrouchWalk8.png b/animation/animation_states/assets/images/CrouchWalk8.png new file mode 100644 index 0000000..a9eb760 Binary files /dev/null and b/animation/animation_states/assets/images/CrouchWalk8.png differ diff --git a/animation/animation_states/assets/images/Fall1.png b/animation/animation_states/assets/images/Fall1.png new file mode 100644 index 0000000..970173d Binary files /dev/null and b/animation/animation_states/assets/images/Fall1.png differ diff --git a/animation/animation_states/assets/images/Fall2.png b/animation/animation_states/assets/images/Fall2.png new file mode 100644 index 0000000..cdb50ee Binary files /dev/null and b/animation/animation_states/assets/images/Fall2.png differ diff --git a/animation/animation_states/assets/images/Fall3.png b/animation/animation_states/assets/images/Fall3.png new file mode 100644 index 0000000..8429a03 Binary files /dev/null and b/animation/animation_states/assets/images/Fall3.png differ diff --git a/animation/animation_states/assets/images/Idle1.png b/animation/animation_states/assets/images/Idle1.png new file mode 100644 index 0000000..9bae85f Binary files /dev/null and b/animation/animation_states/assets/images/Idle1.png differ diff --git a/animation/animation_states/assets/images/Idle10.png b/animation/animation_states/assets/images/Idle10.png new file mode 100644 index 0000000..9bae85f Binary files /dev/null and b/animation/animation_states/assets/images/Idle10.png differ diff --git a/animation/animation_states/assets/images/Idle2.png b/animation/animation_states/assets/images/Idle2.png new file mode 100644 index 0000000..c8dee75 Binary files /dev/null and b/animation/animation_states/assets/images/Idle2.png differ diff --git a/animation/animation_states/assets/images/Idle3.png b/animation/animation_states/assets/images/Idle3.png new file mode 100644 index 0000000..c43f51d Binary files /dev/null and b/animation/animation_states/assets/images/Idle3.png differ diff --git a/animation/animation_states/assets/images/Idle4.png b/animation/animation_states/assets/images/Idle4.png new file mode 100644 index 0000000..c54b50b Binary files /dev/null and b/animation/animation_states/assets/images/Idle4.png differ diff --git a/animation/animation_states/assets/images/Idle5.png b/animation/animation_states/assets/images/Idle5.png new file mode 100644 index 0000000..3183f4d Binary files /dev/null and b/animation/animation_states/assets/images/Idle5.png differ diff --git a/animation/animation_states/assets/images/Idle6.png b/animation/animation_states/assets/images/Idle6.png new file mode 100644 index 0000000..600428f Binary files /dev/null and b/animation/animation_states/assets/images/Idle6.png differ diff --git a/animation/animation_states/assets/images/Idle7.png b/animation/animation_states/assets/images/Idle7.png new file mode 100644 index 0000000..a774efd Binary files /dev/null and b/animation/animation_states/assets/images/Idle7.png differ diff --git a/animation/animation_states/assets/images/Idle8.png b/animation/animation_states/assets/images/Idle8.png new file mode 100644 index 0000000..7fc454d Binary files /dev/null and b/animation/animation_states/assets/images/Idle8.png differ diff --git a/animation/animation_states/assets/images/Idle9.png b/animation/animation_states/assets/images/Idle9.png new file mode 100644 index 0000000..9bae85f Binary files /dev/null and b/animation/animation_states/assets/images/Idle9.png differ diff --git a/animation/animation_states/assets/images/Jump1.png b/animation/animation_states/assets/images/Jump1.png new file mode 100644 index 0000000..53d2a4a Binary files /dev/null and b/animation/animation_states/assets/images/Jump1.png differ diff --git a/animation/animation_states/assets/images/Jump2.png b/animation/animation_states/assets/images/Jump2.png new file mode 100644 index 0000000..e22aac9 Binary files /dev/null and b/animation/animation_states/assets/images/Jump2.png differ diff --git a/animation/animation_states/assets/images/Jump3.png b/animation/animation_states/assets/images/Jump3.png new file mode 100644 index 0000000..da2fd43 Binary files /dev/null and b/animation/animation_states/assets/images/Jump3.png differ diff --git a/animation/animation_states/assets/images/JumpToFall1.png b/animation/animation_states/assets/images/JumpToFall1.png new file mode 100644 index 0000000..4cb8f07 Binary files /dev/null and b/animation/animation_states/assets/images/JumpToFall1.png differ diff --git a/animation/animation_states/assets/images/JumpToFall2.png b/animation/animation_states/assets/images/JumpToFall2.png new file mode 100644 index 0000000..7b9e8b9 Binary files /dev/null and b/animation/animation_states/assets/images/JumpToFall2.png differ diff --git a/animation/animation_states/assets/images/Run1.png b/animation/animation_states/assets/images/Run1.png new file mode 100644 index 0000000..f0e0c3d Binary files /dev/null and b/animation/animation_states/assets/images/Run1.png differ diff --git a/animation/animation_states/assets/images/Run10.png b/animation/animation_states/assets/images/Run10.png new file mode 100644 index 0000000..95ea0f6 Binary files /dev/null and b/animation/animation_states/assets/images/Run10.png differ diff --git a/animation/animation_states/assets/images/Run2.png b/animation/animation_states/assets/images/Run2.png new file mode 100644 index 0000000..0519675 Binary files /dev/null and b/animation/animation_states/assets/images/Run2.png differ diff --git a/animation/animation_states/assets/images/Run3.png b/animation/animation_states/assets/images/Run3.png new file mode 100644 index 0000000..b4a4a35 Binary files /dev/null and b/animation/animation_states/assets/images/Run3.png differ diff --git a/animation/animation_states/assets/images/Run4.png b/animation/animation_states/assets/images/Run4.png new file mode 100644 index 0000000..a99b42f Binary files /dev/null and b/animation/animation_states/assets/images/Run4.png differ diff --git a/animation/animation_states/assets/images/Run5.png b/animation/animation_states/assets/images/Run5.png new file mode 100644 index 0000000..421e030 Binary files /dev/null and b/animation/animation_states/assets/images/Run5.png differ diff --git a/animation/animation_states/assets/images/Run6.png b/animation/animation_states/assets/images/Run6.png new file mode 100644 index 0000000..bbfd48e Binary files /dev/null and b/animation/animation_states/assets/images/Run6.png differ diff --git a/animation/animation_states/assets/images/Run7.png b/animation/animation_states/assets/images/Run7.png new file mode 100644 index 0000000..7e60f58 Binary files /dev/null and b/animation/animation_states/assets/images/Run7.png differ diff --git a/animation/animation_states/assets/images/Run8.png b/animation/animation_states/assets/images/Run8.png new file mode 100644 index 0000000..21a8cf4 Binary files /dev/null and b/animation/animation_states/assets/images/Run8.png differ diff --git a/animation/animation_states/assets/images/Run9.png b/animation/animation_states/assets/images/Run9.png new file mode 100644 index 0000000..e548d00 Binary files /dev/null and b/animation/animation_states/assets/images/Run9.png differ diff --git a/animation/animation_states/assets/images/TurnAround1.png b/animation/animation_states/assets/images/TurnAround1.png new file mode 100644 index 0000000..8aa535d Binary files /dev/null and b/animation/animation_states/assets/images/TurnAround1.png differ diff --git a/animation/animation_states/assets/images/TurnAround2.png b/animation/animation_states/assets/images/TurnAround2.png new file mode 100644 index 0000000..cdfaf29 Binary files /dev/null and b/animation/animation_states/assets/images/TurnAround2.png differ diff --git a/animation/animation_states/assets/images/TurnAround3.png b/animation/animation_states/assets/images/TurnAround3.png new file mode 100644 index 0000000..8cefd1f Binary files /dev/null and b/animation/animation_states/assets/images/TurnAround3.png differ diff --git a/animation/animation_states/assets/knight.atlas b/animation/animation_states/assets/knight.atlas new file mode 100644 index 0000000..8594d44 --- /dev/null +++ b/animation/animation_states/assets/knight.atlas @@ -0,0 +1,197 @@ +animations { + id: "idle" + images { + image: "/assets/images/Idle1.png" + } + images { + image: "/assets/images/Idle10.png" + } + images { + image: "/assets/images/Idle2.png" + } + images { + image: "/assets/images/Idle3.png" + } + images { + image: "/assets/images/Idle4.png" + } + images { + image: "/assets/images/Idle5.png" + } + images { + image: "/assets/images/Idle6.png" + } + images { + image: "/assets/images/Idle7.png" + } + images { + image: "/assets/images/Idle8.png" + } + images { + image: "/assets/images/Idle9.png" + } + playback: PLAYBACK_LOOP_FORWARD + fps: 15 +} +animations { + id: "run" + images { + image: "/assets/images/Run1.png" + } + images { + image: "/assets/images/Run2.png" + } + images { + image: "/assets/images/Run3.png" + } + images { + image: "/assets/images/Run4.png" + } + images { + image: "/assets/images/Run5.png" + } + images { + image: "/assets/images/Run6.png" + } + images { + image: "/assets/images/Run7.png" + } + images { + image: "/assets/images/Run8.png" + } + images { + image: "/assets/images/Run9.png" + } + images { + image: "/assets/images/Run10.png" + } + playback: PLAYBACK_LOOP_FORWARD + fps: 15 +} +animations { + id: "turn_around" + images { + image: "/assets/images/TurnAround1.png" + } + images { + image: "/assets/images/TurnAround2.png" + } + images { + image: "/assets/images/TurnAround3.png" + } + fps: 15 +} +animations { + id: "jump" + images { + image: "/assets/images/Jump1.png" + } + images { + image: "/assets/images/Jump2.png" + } + images { + image: "/assets/images/Jump3.png" + } + images { + image: "/assets/images/JumpToFall1.png" + } + images { + image: "/assets/images/JumpToFall2.png" + } + images { + image: "/assets/images/Fall1.png" + } + images { + image: "/assets/images/Fall2.png" + } + images { + image: "/assets/images/Fall3.png" + } + fps: 15 +} +animations { + id: "attack" + images { + image: "/assets/images/AttackRight1.png" + } + images { + image: "/assets/images/AttackRight2.png" + } + images { + image: "/assets/images/AttackRight3.png" + } + images { + image: "/assets/images/AttackRight4.png" + } + fps: 15 +} +animations { + id: "to_crouch" + images { + image: "/assets/images/Crouch1.png" + } + playback: PLAYBACK_ONCE_FORWARD + fps: 15 +} +animations { + id: "crouch_attack" + images { + image: "/assets/images/CrouchAttack1.png" + } + images { + image: "/assets/images/CrouchAttack2.png" + } + images { + image: "/assets/images/CrouchAttack3.png" + } + images { + image: "/assets/images/CrouchAttack4.png" + } + fps: 15 +} +animations { + id: "crouch_walk" + images { + image: "/assets/images/CrouchWalk1.png" + } + images { + image: "/assets/images/CrouchWalk2.png" + } + images { + image: "/assets/images/CrouchWalk3.png" + } + images { + image: "/assets/images/CrouchWalk4.png" + } + images { + image: "/assets/images/CrouchWalk5.png" + } + images { + image: "/assets/images/CrouchWalk6.png" + } + images { + image: "/assets/images/CrouchWalk7.png" + } + images { + image: "/assets/images/CrouchWalk8.png" + } + playback: PLAYBACK_LOOP_FORWARD + fps: 15 +} +animations { + id: "crouch_idle" + images { + image: "/assets/images/Crouch2.png" + } + playback: PLAYBACK_LOOP_FORWARD + fps: 15 +} +animations { + id: "from_crouch" + images { + image: "/assets/images/Crouch3.png" + } + playback: PLAYBACK_ONCE_FORWARD + fps: 15 +} +extrude_borders: 2 diff --git a/animation/animation_states/assets/text32.font b/animation/animation_states/assets/text32.font new file mode 100644 index 0000000..3922b42 --- /dev/null +++ b/animation/animation_states/assets/text32.font @@ -0,0 +1,4 @@ +font: "/assets/SourceSansPro-Semibold.ttf" +material: "/builtins/fonts/font.material" +size: 32 +characters: " !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" diff --git a/animation/animation_states/atlas.png b/animation/animation_states/atlas.png new file mode 100644 index 0000000..7a1a20d Binary files /dev/null and b/animation/animation_states/atlas.png differ diff --git a/animation/animation_states/example.md b/animation/animation_states/example.md new file mode 100644 index 0000000..cb4a37a --- /dev/null +++ b/animation/animation_states/example.md @@ -0,0 +1,102 @@ +--- +tags: animation, state_machine, input +title: Animation State Machine +brief: This example demonstrates how to create a character animation system using a Finite State Machine (FSM) with smooth transitions between different character states. +author: The Defold Foundation +scripts: knight.script +thumbnail: thumbnail.png +--- + +# A simple Finite State Machine for animations. + +This example shows how to create a responsive character animation system using a **Finite State Machine (FSM)**. The character can smoothly transition between different states like idle, running, jumping, attacking, and crouching based on player input. This is a fundamental technique used in most 2D platformers and action games. + +## What You'll Learn? + +- How to implement a state machine for character animations +- How to handle complex input combinations and priorities## Key Concepts + +**State Machine**: A design pattern where an object can be in only one state at a time, with clear rules for transitioning between states. + +**Input Priority**: A system that determines which actions take precedence when multiple keys are pressed simultaneously. + +**Animation Transitions**: Smooth changes between different animations, often with intermediate "transition" animations.## Key Concepts + +**State Machine**: A design pattern where an object can be in only one state at a time, with clear rules for transitioning between states. + +**Input Priority**: A system that determines which actions take precedence when multiple keys are pressed simultaneously. + +**Animation Transitions**: Smooth changes between different animations, often with intermediate "transition" animations. +- How to create smooth transitions between animation states +- How to make sprites flip direction based on movement +- How to add visual effects (like jump animations) +- How to communicate between game objects using messages + +## Setup + +The example consists of two main game objects: + +knight +: The animated character. Contains: + - A *Sprite* component with the knight character image and animations. + - A *Script* component (`knight.script`) that implements the state machine logic, handles input, and manages animation transitions. + +gui +: The user interface. Contains: + - A *GUI* component (`control.gui`) that has 6 nodes displaying states and text description for the example. + - A *GUI Script* component (`control.gui_script`) that receives messages from the knight and updates the visual state indicators. + +> **Note:** +> The GUI in this example is not required for understanding the state machine logic, it only visually shows the active animation state. You can view the GUI source in the project files on Github still though. + +![animation_states_collection](animation_states_collection.png) + +## Animation Atlas + +The sprite component uses a flipbook animation that is set up in an atlas: + +> For this example we used the Free Knight Character by Nauris 'aamatniekss' available here: https://aamatniekss.itch.io/fantasy-knight-free-pixelart-animated-character + +![atlas](atlas.png) + +The atlas contains multiple animations for different character states: +- **idle**: Standing still animation +- **run**: Running animation (looped) +- **jump**: Jumping animation (plays once) +- **attack**: Attacking animation (plays once) +- **turn_around**: Turning animation (plays once) +- **crouch_idle**: Crouching idle anim **Note:** ation +- **crouch_walk**: Crouch walking animation +- **crouch_attack**: Crouch attacking animation +- **to_crouch**: Transition from standing to crouching +- **from_crouch**: Transition from crouching to standing + +## Input Bindings + +| Key | Action | +|-----------------|--------------------------------| +| **Left Arrow / Right Arrow** | Move left/right | +| **Space** | Jump | +| **X** | Attack | +| **C** | Crouch (hold to stay crouched) | + +![input_bindings](input_bindings.png) + + +## How It Works? + +The character uses a **finite state machine** - a programming pattern where the character can only be in one "state" at a time. Each state can define certain things like: +- Which animation to play +- Whether the animation loops or plays once +- What happens when different keys are pressed +- What state to go to when the animation finishes + +The system processes input with **priorities**: Attack > Jump > Movement > Crouch/Stand > Turning. This ensures that important actions (like attacking) can interrupt less important ones (like walking). + +## Key Concepts + +**State Machine**: A design pattern where an object can be in only one state at a time, with clear rules for transitioning between states. + +**Input Priority**: A system that determines which actions take precedence when multiple keys are pressed simultaneously. + +**Animation Transitions**: Smooth changes between different animations, often with intermediate "transition" animations. \ No newline at end of file diff --git a/animation/animation_states/example/animation_states.collection b/animation/animation_states/example/animation_states.collection new file mode 100644 index 0000000..d632bda --- /dev/null +++ b/animation/animation_states/example/animation_states.collection @@ -0,0 +1,37 @@ +name: "default" +scale_along_z: 0 +embedded_instances { + id: "knight" + data: "components {\n" + " id: \"knight\"\n" + " component: \"/example/knight.script\"\n" + "}\n" + "embedded_components {\n" + " id: \"sprite\"\n" + " type: \"sprite\"\n" + " data: \"default_animation: \\\"idle\\\"\\n" + "material: \\\"/builtins/materials/sprite.material\\\"\\n" + "textures {\\n" + " sampler: \\\"texture_sampler\\\"\\n" + " texture: \\\"/assets/knight.atlas\\\"\\n" + "}\\n" + "\"\n" + "}\n" + "" + position { + x: 360.0 + y: 600.0 + } + scale3 { + x: 5.0 + y: 5.0 + } +} +embedded_instances { + id: "gui" + data: "components {\n" + " id: \"control\"\n" + " component: \"/example/control.gui\"\n" + "}\n" + "" +} diff --git a/animation/animation_states/example/control.gui b/animation/animation_states/example/control.gui new file mode 100644 index 0000000..747cccf --- /dev/null +++ b/animation/animation_states/example/control.gui @@ -0,0 +1,242 @@ +script: "/example/control.gui_script" +fonts { + name: "text48" + font: "/assets/text32.font" +} +nodes { + position { + x: 500.0 + y: 150.0 + } + size { + x: 120.0 + y: 70.0 + } + color { + x: 0.302 + y: 0.4 + z: 0.8 + } + type: TYPE_BOX + id: "run" + layer: "box" + inherit_alpha: true + size_mode: SIZE_MODE_AUTO +} +nodes { + size { + x: 120.0 + y: 70.0 + } + type: TYPE_TEXT + text: "run" + font: "text48" + id: "text" + parent: "run" + layer: "text" + inherit_alpha: true +} +nodes { + position { + x: 200.0 + y: 150.0 + } + size { + x: 120.0 + y: 70.0 + } + color { + x: 0.302 + y: 0.4 + z: 0.8 + } + type: TYPE_BOX + id: "idle" + layer: "box" + inherit_alpha: true + size_mode: SIZE_MODE_AUTO +} +nodes { + size { + x: 120.0 + y: 70.0 + } + type: TYPE_TEXT + text: "idle" + font: "text48" + id: "text1" + parent: "idle" + layer: "text" + inherit_alpha: true +} +nodes { + position { + x: 350.0 + y: 150.0 + } + size { + x: 120.0 + y: 70.0 + } + color { + x: 0.302 + y: 0.4 + z: 0.8 + } + type: TYPE_BOX + id: "turn_around" + layer: "box" + inherit_alpha: true + size_mode: SIZE_MODE_AUTO +} +nodes { + size { + x: 120.0 + y: 70.0 + } + type: TYPE_TEXT + text: "turn" + font: "text48" + id: "text2" + parent: "turn_around" + layer: "text" + inherit_alpha: true +} +nodes { + position { + x: 500.0 + y: 250.0 + } + size { + x: 120.0 + y: 70.0 + } + color { + x: 0.302 + y: 0.4 + z: 0.8 + } + type: TYPE_BOX + id: "jump" + layer: "box" + inherit_alpha: true + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + y: 3.0 + } + size { + x: 120.0 + y: 70.0 + } + type: TYPE_TEXT + text: "[Space]\n" + "jump" + font: "text48" + id: "text3" + parent: "jump" + layer: "text" + inherit_alpha: true + text_leading: 0.8 +} +nodes { + position { + x: 200.0 + y: 250.0 + } + size { + x: 120.0 + y: 70.0 + } + color { + x: 0.302 + y: 0.4 + z: 0.8 + } + type: TYPE_BOX + id: "attack" + layer: "box" + inherit_alpha: true + size_mode: SIZE_MODE_AUTO +} +nodes { + size { + x: 120.0 + y: 70.0 + } + type: TYPE_TEXT + text: "[X]\n" + "attack" + font: "text48" + id: "text4" + parent: "attack" + layer: "text" + inherit_alpha: true + text_leading: 0.8 +} +nodes { + position { + x: 350.0 + y: 250.0 + } + size { + x: 120.0 + y: 70.0 + } + color { + x: 0.302 + y: 0.4 + z: 0.8 + } + type: TYPE_BOX + id: "crouch" + layer: "box" + inherit_alpha: true + size_mode: SIZE_MODE_AUTO +} +nodes { + size { + x: 120.0 + y: 70.0 + } + type: TYPE_TEXT + text: "[C]\n" + "crouch" + font: "text48" + id: "text5" + parent: "crouch" + layer: "text" + inherit_alpha: true + text_leading: 0.8 +} +nodes { + position { + x: 350.0 + y: 50.0 + } + size { + x: 120.0 + y: 70.0 + } + color { + x: 0.2 + y: 0.302 + z: 0.702 + } + type: TYPE_TEXT + text: "Use arrow keys, space, X and C." + font: "text48" + id: "description" + layer: "text" + inherit_alpha: true + text_leading: 0.8 +} +layers { + name: "box" +} +layers { + name: "text" +} +material: "/builtins/materials/gui.material" +adjust_reference: ADJUST_REFERENCE_PARENT diff --git a/animation/animation_states/example/control.gui_script b/animation/animation_states/example/control.gui_script new file mode 100644 index 0000000..47dc744 --- /dev/null +++ b/animation/animation_states/example/control.gui_script @@ -0,0 +1,61 @@ +-- Update button visual state +local function update_button_visual(node, pressed) + if pressed then + -- Set pressed color: #6680e6 + gui.set_color(node, vmath.vector4(0.4, 0.5, 0.9, 1.0)) + else + -- Set default color: #4d66cc + gui.set_color(node, vmath.vector4(0.3, 0.4, 0.8, 1.0)) + end +end + +function init(self) + -- Get node references + self.idle_node = gui.get_node("idle") + self.run_node = gui.get_node("run") + self.turn_around_node = gui.get_node("turn_around") + self.jump_node = gui.get_node("jump") + self.attack_node = gui.get_node("attack") + self.crouch_node = gui.get_node("crouch") + + -- Start with idle highlighted (default state) + update_button_visual(self.idle_node, true) +end + +function on_message(self, message_id, message) + -- Handle animation state changes + if message_id == hash("animation_state_changed") then + -- Reset all buttons to unpressed state + update_button_visual(self.idle_node, false) + update_button_visual(self.run_node, false) + update_button_visual(self.turn_around_node, false) + update_button_visual(self.jump_node, false) + update_button_visual(self.attack_node, false) + update_button_visual(self.crouch_node, false) + + -- Get state name + local state = message.state + + -- Highlight the current state button + if state:find("idle") then + update_button_visual(self.idle_node, true) + end + if state:find("run") then + update_button_visual(self.run_node, true) + end + if state:find("turn_around") then + update_button_visual(self.turn_around_node, true) + end + if state:find("jump") then + update_button_visual(self.jump_node, true) + end + if state:find("attack") then + update_button_visual(self.attack_node, true) + end + + -- Highlight crouch button for crouching states + if state:find("^crouching") or state == "to_crouch" then + update_button_visual(self.crouch_node, true) + end + end +end diff --git a/animation/animation_states/example/knight.script b/animation/animation_states/example/knight.script new file mode 100644 index 0000000..f57e27c --- /dev/null +++ b/animation/animation_states/example/knight.script @@ -0,0 +1,315 @@ +-- ============================================================================ +-- KNIGHT ANIMATION STATE MACHINE - Beginner Friendly Example +-- ============================================================================ +-- This script demonstrates how to create a character animation system using +-- a simple implementation of a Finite State Machine (FSM) in Defold. + +-- Input action hashes - these connect keyboard/gamepad buttons to our code +-- In Defold, we use hash() to convert strings to efficient identifiers +local INPUT = { + JUMP = hash("jump"), + CROUCH = hash("crouch"), + ATTACK = hash("attack"), + LEFT = hash("left"), + RIGHT = hash("right") +} + +-- ============================================================================ +-- STATE MACHINE CONFIGURATION +-- ============================================================================ +-- This table defines ALL possible states our character can be in. +-- Think of it as a "rule book" that tells the game: +-- - What animation to play in each state +-- - Whether the animation should loop or play once +-- - What should happen when the player presses different buttons +-- +-- Each state is like a "mode" the character is in. For example: +-- - "standing_idle" = character is standing still, playing idle animation, looped +-- +-- The "on_" properties define what happens when inputs are pressed, e.g.: +-- - on_attack = what state to go to when attack button is pressed +-- - on_move = what state to go to when movement keys are pressed +-- - default_next = what state to go to when animation finishes (for non-looped animations) + +local STATE_CONFIG = { + -- STANDING STATES - Character is upright and can move freely + -- These are the "normal" states when the character is standing + standing_idle = { + animation = "idle", -- Play the "idle" animation from the sprite atlas + is_looped = true, -- Keep playing this animation over and over + on_crouch = "to_crouch", -- If crouch key pressed, go to "to_crouch" state + on_attack = "standing_attack", -- If attack key pressed, go to "standing_attack" state + on_jump = "standing_jump", -- If jump key pressed, go to "standing_jump" state + on_move = "standing_run", -- If movement keys pressed, go to "standing_run" state + on_turn = "standing_turn" -- If character turns around, go to "standing_turn" state + }, + standing_run = { + animation = "run", -- Play the running animation + is_looped = true, -- Loop the running animation continuously + on_crouch = "to_crouch", -- Can still crouch while running + on_attack = "standing_attack", -- Can attack while running + on_jump = "standing_jump", -- Can jump while running + on_stop = "standing_idle", -- When movement stops, go back to idle + on_turn = "standing_turn" -- When turning around, play turn animation + }, + standing_jump = { + animation = "jump", -- Play the jump animation + is_looped = false, -- Play jump animation only once + default_next = "standing_idle" -- When jump animation finishes, go back to idle + }, + standing_attack = { + animation = "attack", -- Play the attack animation + is_looped = false, -- Play attack animation only once + default_next = "standing_idle" -- When attack finishes, go back to idle + }, + standing_turn = { + animation = "turn_around", -- Play the turn around animation + is_looped = false, -- Play turn animation only once + default_next = "standing_idle", -- When turn finishes, go to idle + on_turn = "standing_turn" -- If turning again while already turning, keep turning + }, + + -- CROUCHING STATES - Character is in low position, limited movement + -- When crouching, the character can't jump but can still move and attack + crouching_idle = { + animation = "crouch_idle", -- Play the crouching idle animation + is_looped = true, -- Loop the crouch idle animation + on_stand = "to_standing", -- If crouch key released, start standing up + on_attack = "crouching_attack", -- Can attack while crouching + on_move = "crouching_run" -- Can move while crouching (crouch walk) + }, + crouching_run = { + animation = "crouch_walk", -- Play the crouch walking animation + is_looped = true, -- Loop the crouch walk animation + on_stand = "to_standing", -- Can stand up while crouch walking + on_attack = "crouching_attack", -- Can attack while crouch walking + on_stop = "crouching_idle" -- When movement stops, go to crouch idle + }, + crouching_attack = { + animation = "crouch_attack", -- Play the crouch attack animation + is_looped = false, -- Play attack animation only once + default_next = "crouching_idle", -- When attack finishes, go to crouch idle + on_stand = "to_standing", -- Can stand up even while attacking + }, + + -- TRANSITION STATES - Intermediate animations between major state changes + -- These states handle the smooth transition between standing and crouching + to_crouch = { + animation = "to_crouch", -- Play the "going into crouch" animation + is_looped = false, -- Play transition animation only once + default_next = "crouching_idle" -- When transition finishes, go to crouch idle + }, + to_standing = { + animation = "from_crouch", -- Play the "standing up from crouch" animation + is_looped = false, -- Play transition animation only once + default_next = "standing_idle" -- When transition finishes, go to standing idle + } +} + +-- ============================================================================ +-- MOVEMENT AND DIRECTION LOGIC +-- ============================================================================ + +--- Updates movement state and sprite direction based on input +--- This function figures out: +--- 1. Is the character moving? (left or right key pressed) +--- 2. Which direction is the character facing? (left or right) +--- 3. Did the character just turn around? (for turn animation) +--- @param self table Script instance with input flags +local function update_movement_state(self) + -- Start by assuming the character is not moving + self.is_moving = false + + -- Remember the previous facing direction to detect turns + local previous_is_flipped = self.is_flipped + + -- Check movement input and update facing direction + if self[INPUT.LEFT] and not self[INPUT.RIGHT] then + -- Left key is pressed and right key is not pressed + self.is_moving = true + self.is_flipped = true -- Character faces left (sprite is flipped) + elseif self[INPUT.RIGHT] and not self[INPUT.LEFT] then + -- Right key is pressed and left key is not pressed + self.is_moving = true + self.is_flipped = false -- Character faces right (sprite is not flipped) + end + -- If both keys are pressed or neither is pressed, character doesn't move + + -- Detect if the character just turned around - used to trigger the "turn around" animation + self.is_turning = self.is_flipped ~= previous_is_flipped +end + + +-- ============================================================================ +-- STATE TRANSITION LOGIC +-- ============================================================================ + +--- Determines the next state based on current input and state configuration +--- This is the "brain" of our state machine - it decides what state to go to next +--- +--- INPUT PRIORITY SYSTEM (in order of importance): +--- 1. Attack - Highest priority, can interrupt most other actions +--- 2. Jump - High priority, can interrupt movement +--- 3. Movement - Medium priority, handles start/stop moving +--- 4. Crouch/Stand - Medium priority, changes posture +--- 5. Turn - Lowest priority, only when changing direction +--- +--- @param self table Script instance with input flags and current state +--- @return string|nil Next state name or nil if no transition needed +local function get_next_state(self) + -- Get current input state and configuration + local is_crouching = self[INPUT.CROUCH] -- Is crouch key currently pressed? + local config = STATE_CONFIG[self.state] -- Get rules for current state + local next_state = nil -- Will hold the next state to go to + + -- PRIORITY 1: ATTACK INPUT (Highest Priority) + -- Attack can interrupt almost any other action + if self[INPUT.ATTACK] then + next_state = config.on_attack -- Go to attack state if current state allows it + end + + -- PRIORITY 2: JUMP INPUT (High Priority) + -- Jump can interrupt movement but not attack + if self[INPUT.JUMP] then + next_state = config.on_jump -- Go to jump state if current state allows it + end + + -- PRIORITY 3: MOVEMENT STATE CHANGES (Medium Priority) + -- Handle starting to move or stopping movement + if self.is_moving and config.on_move then + next_state = config.on_move -- Character is moving and current state has a "move" transition + elseif not self.is_moving and config.on_stop then + next_state = config.on_stop -- Character stopped moving and current state has a "stop" transition + end + + -- PRIORITY 4: CROUCH/STAND STATE CHANGES (Medium Priority) + -- Handle posture changes (standing vs crouching) + if is_crouching and config.on_crouch then + next_state = config.on_crouch -- Crouch key is pressed and current state allows crouching + elseif not is_crouching and config.on_stand then + next_state = config.on_stand -- Crouch key is released and current state allows standing + end + + -- PRIORITY 5: DIRECTION CHANGE (Lowest Priority) + -- Handle turning around (only when changing direction) + if self.is_turning and config.on_turn then + next_state = config.on_turn -- Character just turned around and current state has turn animation + end + + -- Return the next state (or nil if no transition is needed) + return next_state +end + +-- ============================================================================ +-- VISUAL LAYER - Handles all visual effects and animations +-- ============================================================================ + +--- Updates all visual elements based on current character state +--- This function is responsible for making the character look correct on screen: +--- - Playing the right animation for the current state +--- - Flipping the sprite to face the right direction +--- - Creating special effects (like the jump animation) +--- - Updating the GUI to show current state +--- @param self table Script instance with current state and flip information +local function update_visuals(self) + -- Get the configuration for the current state + local config = STATE_CONFIG[self.state] + + -- Play the animation for the current state + sprite.play_flipbook("#sprite", config.animation) + + -- Visualize the jump effect + -- (When jumping, we add a visual effect by moving the character up and down) + if self.state == "standing_jump" then + local pos = go.get_position() + -- Animate the Y position to simulate a jump visually + go.animate(".", "position.y", go.PLAYBACK_ONCE_PINGPONG, pos.y + 50, go.EASING_INOUTCUBIC, 0.6) + else + -- If not jumping, make sure any jump animation is cancelled and reset the character to ground level + go.cancel_animations(".", "position.y") + local pos = go.get_position() + go.set_position(vmath.vector3(pos.x, 600, pos.z)) -- 600 is our ground level Y position + end + + -- Update the GUI - send a message to the GUI component to update the UI + msg.post("gui", "animation_state_changed", { + state = self.state + }) +end + +-- ============================================================================ +-- DEFOLD LIFECYCLE FUNCTIONS +-- ============================================================================ + +--- Initializes the knight character when the game starts +--- It sets up the initial state and prepares the character for input +--- @param self table Script instance - this is automatically provided by Defold +function init(self) + -- Set up initial state machine state as "standing_idle" + self.state = "standing_idle" + + -- Set up movement and direction flags + self.is_flipped = false -- Character starts facing right (not flipped) + self.is_moving = false -- Character starts not moving + self.is_turning = false -- Character starts not turning + + -- Initialize all input flags - start with all keys "not pressed" (false) + self[INPUT.LEFT] = false -- Left arrow key + self[INPUT.RIGHT] = false -- Right arrow key + self[INPUT.JUMP] = false -- Space bar + self[INPUT.ATTACK] = false -- Attack button (X) + self[INPUT.CROUCH] = false -- Crouch button (C) + + -- Display the initial state visually + update_visuals(self) + + -- Enable input handling + msg.post(".", "acquire_input_focus") +end + +--- Handles input events from keyboard every time the player presses or releases a key +--- It updates our input tracking and triggers state transitions +--- @param self table Script instance +--- @param action_id hash Which input was pressed (like "jump", "attack", etc.) +--- @param action table Contains information about the input (pressed/released) +function on_input(self, action_id, action) + -- Update input state - keep track of which keys are currently being pressed: + if action.pressed then + self[action_id] = true -- Key was just pressed down + elseif action.released then + self[action_id] = false -- Key was just released + end + + -- Process state machine: + update_movement_state(self) -- Update movement and direction state based on input + local next_state = get_next_state(self) -- Decide what state to go to next + + -- If we determined a new state is needed, switch to it and update visuals: + if next_state then + self.state = next_state -- Change to the new state + update_visuals(self) -- Update the visual appearance + end +end + +--- Handles messages from other game objects +--- We use it to handle messages that comes to the script when animations finish playing +--- @param self table Script instance +--- @param message_id hash What type of message this is +function on_message(self, message_id, message) + -- This message is sent when a non-looped animation finishes playing (like attack, jump, or turn animations) + if message_id == hash("animation_done") then + + -- Flip the sprite horizontally when the character just finished turning + if message.id == hash("turn_around") then + sprite.set_hflip("#sprite", self.is_flipped) + end + + -- Process state machine: + update_movement_state(self) -- Update movement and direction state based on input + local next_state = get_next_state(self) -- Decide what state to go to next + + -- Switch to the next state (or default next) and update visuals + self.state = next_state or STATE_CONFIG[self.state].default_next + update_visuals(self) + end +end diff --git a/animation/animation_states/game.project b/animation/animation_states/game.project new file mode 100644 index 0000000..976de2b --- /dev/null +++ b/animation/animation_states/game.project @@ -0,0 +1,67 @@ +[project] +title = Defold-examples +version = 0.1 + +[bootstrap] +main_collection = /example/animation_states.collectionc + +[input] +game_binding = /assets/example.input_bindingc +repeat_interval = 0.05 + +[display] +width = 720 +height = 720 +high_dpi = 1 + +[physics] +scale = 0.02 +gravity_y = -500.0 + +[script] +shared_state = 1 + +[collection_proxy] +max_count = 256 + +[label] +subpixels = 1 + +[sprite] +subpixels = 1 +max_count = 32765 + +[windows] +iap_provider = + +[android] +package = com.defold.examples + +[ios] +bundle_identifier = com.defold.examples + +[osx] +bundle_identifier = com.defold.examples + +[html5] +show_fullscreen_button = 0 +show_made_with_defold = 0 +scale_mode = no_scale +heap_size = 64 + +[graphics] +texture_profiles = /all.texture_profiles +default_texture_min_filter = nearest +default_texture_mag_filter = nearest + +[collection] +max_instances = 32765 + +[particle_fx] +max_emitter_count = 1024 + +[render] +clear_color_blue = 1.0 +clear_color_green = 1.0 +clear_color_red = 1.0 + diff --git a/animation/animation_states/input_bindings.png b/animation/animation_states/input_bindings.png new file mode 100644 index 0000000..1034d9f Binary files /dev/null and b/animation/animation_states/input_bindings.png differ diff --git a/animation/animation_states/thumbnail.png b/animation/animation_states/thumbnail.png new file mode 100644 index 0000000..31f0637 Binary files /dev/null and b/animation/animation_states/thumbnail.png differ