Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sample-third-person-playground
Submodule sample-third-person-playground added at b0c22f
36 changes: 36 additions & 0 deletions sprite/events_on_frames/assets/character.atlas
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
animations {
id: "walk"
images {
image: "/assets/free knight images/__Run/frame_0000.png"
}
images {
image: "/assets/free knight images/__Run/frame_0001.png"
}
images {
image: "/assets/free knight images/__Run/frame_0002.png"
}
images {
image: "/assets/free knight images/__Run/frame_0003.png"
}
images {
image: "/assets/free knight images/__Run/frame_0004.png"
}
images {
image: "/assets/free knight images/__Run/frame_0005.png"
}
images {
image: "/assets/free knight images/__Run/frame_0006.png"
}
images {
image: "/assets/free knight images/__Run/frame_0007.png"
}
images {
image: "/assets/free knight images/__Run/frame_0008.png"
}
images {
image: "/assets/free knight images/__Run/frame_0009.png"
}
playback: PLAYBACK_LOOP_FORWARD
fps: 12
}
extrude_borders: 2
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions sprite/events_on_frames/assets/ui.font
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
font: "/builtins/fonts/vera_mo_bd.ttf"
material: "/builtins/fonts/font-df.material"
size: 25
characters: " !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
styles {
name: "default"
}
styles {
name: "link"
markup: "<color=#1972e5><ul>"
}
styles {
name: "link:hover"
markup: "<color=#4ca5ff>"
}
styles {
name: "link:active"
markup: "<color=#0c4cb2>"
}
vector_font_mode: VECTOR_FONT_MODE_SDF
runtime: true
43 changes: 43 additions & 0 deletions sprite/events_on_frames/example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
title: Flipbook frame events - Footsteps
brief: Detect selected frames while a sprite flipbook animation plays and loops.
author_ids:
- pawel-jarosz
scripts: frame_events.script
thumbnail: thumbnail.webp
tags: sprite, animation, sound
---

This example triggers events (label information and footstep sound) on given frames of a looping Sprite animation.
Click or tap to pause and then again to resume the flipbook.

## Setup

The main collection contains a character game object with:
- a sprite component that plays `walk` from `character.atlas` by default,
- a script component `frame_events.script`,
- 2 sound components with footstep audio

Additionally there is a game object with labels for UI information.

![Editor collection setup](setup.png)

## How It Works

`FRAME_EVENTS` and `FRAME_EVENTS_FOOTSTEPS` contains a map of selected frame numbers with assigned actions (label text and sound component URL to play).

In `update()`, the script converts the normalized sprite `cursor` and `frame_count` into a 1-based current frame number.

When the frame changes, the script checks the frames crossed in between, including wraparound from the last to the first frame.

If a frame has an event, a label briefly flashes to indicate the event occured and sound of a footstep is played.

The normal flipbook completion callback doesn't fire during continuous looping.

This detects frame changes once per update.

## Credits

Free Fantasy Knight by Nauris Amatnieks, License: https://aamatniekss.itch.io/fantasy-knight-free-pixelart-animated-character

Footsteps sounds (cropped) by Fission9, CC0: https://freesound.org/people/Fission9/sounds/521589/
70 changes: 70 additions & 0 deletions sprite/events_on_frames/example/frame_events.script
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
-- Run an event when the looping flipbook passes one of these frames.
local FRAME_EVENTS = {
[1] = "RIGHT STEP ",
[7] = " LEFT STEP",
}

local FRAME_EVENTS_FOOTSTEPS = {
[1] = "#footstep_right",
[7] = "#footstep_left",
}

local function trigger_event(frame)
-- Check if there is an event for the given frame
local event = FRAME_EVENTS[frame]
if not event then
return
end

-- Set event text to the label
go.set("ui#event", "text", event)

-- Make the label flash shortly and then slowly disappear
go.cancel_animations("ui#event", "color.w")
go.set("ui#event", "color.w", 1)
go.animate("ui#event", "color.w", go.PLAYBACK_ONCE_FORWARD, 0, go.EASING_OUTQUAD, 0.6)

-- Play a footstep sound for a given frame event
sound.play(FRAME_EVENTS_FOOTSTEPS[frame])
end

function init(self)
-- Acquire input focus to respond to click/tap
msg.post(".", "acquire_input_focus")

-- Start the animation of the sprite
sprite.play_flipbook("#sprite", "walk")

-- Get the sprite count from the sprite animation
self.frame_count = go.get("#sprite", "frame_count")

-- Initialize data
self.previous_frame = 1
self.playing = true

-- Hide the event label
go.set("ui#event", "color.w", 0)
end

function update(self, dt)
-- Get the position of the cursor of the sprite animation
local cursor = go.get("#sprite", "cursor")

-- Get the next frame number
local frame = math.min(self.frame_count, math.floor(cursor * self.frame_count) + 1)

-- Trigger an event only when the frame changes.
if frame ~= self.previous_frame then
trigger_event(frame)
self.previous_frame = frame
go.set("ui#frame", "text", ("FRAME %02d / %02d"):format(frame, self.frame_count))
end
end

function on_input(self, action_id, action)
-- Toggle playing on mouse click or touch input
if action.pressed and action_id == hash("touch") then
self.playing = not self.playing
go.set("#sprite", "playback_rate", self.playing and 1 or 0)
end
end
98 changes: 98 additions & 0 deletions sprite/events_on_frames/example/main.collection
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
name: "main"
scale_along_z: 0
embedded_instances {
id: "character"
data: "components {\n"
" id: \"controller\"\n"
" component: \"/example/frame_events.script\"\n"
"}\n"
"embedded_components {\n"
" id: \"footstep_left\"\n"
" type: \"sound\"\n"
" data: \"sound: \\\"/assets/footsteps sounds/footstep_left.wav\\\"\\n"
"\"\n"
"}\n"
"embedded_components {\n"
" id: \"footstep_right\"\n"
" type: \"sound\"\n"
" data: \"sound: \\\"/assets/footsteps sounds/footstep_right.wav\\\"\\n"
"\"\n"
"}\n"
"embedded_components {\n"
" id: \"sprite\"\n"
" type: \"sprite\"\n"
" data: \"default_animation: \\\"walk\\\"\\n"
"material: \\\"/builtins/materials/sprite.material\\\"\\n"
"textures {\\n"
" sampler: \\\"texture_sampler\\\"\\n"
" texture: \\\"/assets/character.atlas\\\"\\n"
"}\\n"
"\"\n"
"}\n"
""
position {
x: 360.0
y: 420.0
}
scale3 {
x: 4.0
y: 4.0
}
}
embedded_instances {
id: "ui"
data: "embedded_components {\n"
" id: \"title\"\n"
" type: \"label\"\n"
" data: \"size {\\n"
" x: 480.0\\n"
" y: 32.0\\n"
"}\\n"
"text: \\\"Click or tap to pause/continue\\\"\\n"
"font: \\\"/assets/ui.font\\\"\\n"
"material: \\\"/builtins/fonts/label-df.material\\\"\\n"
"\"\n"
" position {\n"
" y: 200.0\n"
" }\n"
"}\n"
"embedded_components {\n"
" id: \"frame\"\n"
" type: \"label\"\n"
" data: \"size {\\n"
" x: 480.0\\n"
" y: 32.0\\n"
"}\\n"
"color {\\n"
" y: 0.9019608\\n"
" z: 0.6\\n"
"}\\n"
"text: \\\"Frame\\\"\\n"
"font: \\\"/assets/ui.font\\\"\\n"
"material: \\\"/builtins/fonts/label-df.material\\\"\\n"
"\"\n"
" position {\n"
" y: -240.0\n"
" }\n"
"}\n"
"embedded_components {\n"
" id: \"event\"\n"
" type: \"label\"\n"
" data: \"size {\\n"
" x: 480.0\\n"
" y: 32.0\\n"
"}\\n"
"text: \\\"Event\\\"\\n"
"font: \\\"/assets/ui.font\\\"\\n"
"material: \\\"/builtins/fonts/label-df.material\\\"\\n"
"\"\n"
" position {\n"
" y: -300.0\n"
" }\n"
"}\n"
""
position {
x: 360.0
y: 420.0
}
}
33 changes: 33 additions & 0 deletions sprite/events_on_frames/game.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[project]
title = Flipbook Frame Events
version = 0.1

[bootstrap]
main_collection = /example/main.collectionc

[display]
width = 720
height = 720
high_dpi = 1

[input]
game_binding = /input/game.input_bindingc

[html5]
scale_mode = no_scale
show_fullscreen_button = 0
show_made_with_defold = 0
heap_size = 64

[render]
clear_color_red = 0.160156
clear_color_green = 0.164063
clear_color_blue = 0.183594

[sprite]
subpixels = 0

[graphics]
default_texture_min_filter = nearest
default_texture_mag_filter = nearest

8 changes: 8 additions & 0 deletions sprite/events_on_frames/input/game.input_binding
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
mouse_trigger {
input: MOUSE_BUTTON_1
action: "touch"
}
touch_trigger {
input: TOUCH_MULTI
action: "touch"
}
50 changes: 50 additions & 0 deletions sprite/events_on_frames/install_assets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
"""Install original CC0 art from Kenney's Simplified Platformer Pack."""
from io import BytesIO
from pathlib import Path
from urllib.request import Request, urlopen
from zipfile import ZipFile

ROOT = Path(__file__).resolve().parent
DESTINATION = ROOT / "assets" / "kenney"
FILES = ("platformChar_idle.png", "platformChar_walk1.png", "platformChar_walk2.png")
SOURCES = (
"https://kenney.nl/media/pages/assets/simplified-platformer-pack/2d06327041-1677693200/kenney_simplified-platformer-pack.zip",
"https://opengameart.org/sites/default/files/kenney_simplifiedPlatformer.zip",
)


def download_pack():
for source in SOURCES:
try:
request = Request(source, headers={"User-Agent": "Mozilla/5.0"})
with urlopen(request, timeout=30) as response:
content = response.read()
return ZipFile(BytesIO(content))
except Exception as error:
print(f"Could not download from {source}: {error}")
raise SystemExit("Could not download Kenney CC0 art. See ASSET_SOURCE.md for the source links.")


def main():
if all((DESTINATION / name).exists() for name in FILES):
print("Kenney assets are already installed.")
return

with download_pack() as archive:
names = archive.namelist()
for name in FILES:
# The archive contains several formats; select the 96x96 PNG frames.
choices = [p for p in names if p.endswith("/" + name) or p == name]
choices.sort(key=lambda p: ("double" in p.lower(), "retina" in p.lower(), len(p)))
if not choices:
raise SystemExit(f"Missing {name} in Kenney archive; layout may have changed.")
DESTINATION.mkdir(parents=True, exist_ok=True)
(DESTINATION / name).write_bytes(archive.read(choices[0]))
print(f"Installed {name} from {choices[0]}")

print("Done. Open game.project in Defold.")


if __name__ == "__main__":
main()
Binary file added sprite/events_on_frames/setup.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added sprite/events_on_frames/thumbnail.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading