Skip to content

Commit d84e825

Browse files
Site changes
1 parent ec38ebd commit d84e825

560 files changed

Lines changed: 3097 additions & 537 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

_data/examplesindex.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
11
[
2+
{
3+
"author": "Defold Foundation",
4+
"brief": "Learn how to play 3D animations from a GLB model using skinned model material.",
5+
"category": "animation",
6+
"layout": "example",
7+
"opengraph_image": "https://www.defold.com/examples/animation/3d_animations/thumbnail.webp",
8+
"path": "animation/3d_animations",
9+
"scripts": "main.script, main.gui_script",
10+
"tags": "animation, model, gui, input, message-passing",
11+
"thumbnail": "thumbnail.webp",
12+
"title": "3D Animations - Skinned Model",
13+
"twitter_image": "https://www.defold.com/examples/animation/3d_animations/thumbnail.webp"
14+
},
215
{
316
"author": "Defold Foundation",
417
"brief": "This example shows how to use the delay parameter of `go.animate()` to create a wave effect.",
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
local TOUCH = hash("touch") -- <1>
2+
local MSG_PLAY_MODEL_ANIMATION = hash("play_model_animation") -- <2>
3+
local MODEL_SCRIPT = "/character#main" -- <3>
4+
5+
local ANIMATIONS = { -- <4>
6+
"A_TPose",
7+
"Crouch_Fwd_Loop",
8+
"Crouch_Idle_Loop",
9+
"Dance_Loop",
10+
"Death01",
11+
"Driving_Loop",
12+
"Fixing_Kneeling",
13+
"Hit_Chest",
14+
"Hit_Head",
15+
"Idle_Loop",
16+
"Idle_Talking_Loop",
17+
"Idle_Torch_Loop",
18+
"Interact",
19+
"Jog_Fwd_Loop",
20+
"Jump_Land",
21+
"Jump_Loop",
22+
"Jump_Start",
23+
"PickUp_Table",
24+
"Pistol_Aim_Down",
25+
"Pistol_Aim_Neutral",
26+
"Pistol_Aim_Up",
27+
"Pistol_Idle_Loop",
28+
"Pistol_Reload",
29+
"Pistol_Shoot",
30+
"Punch_Cross",
31+
"Punch_Jab",
32+
"Push_Loop",
33+
"Roll",
34+
"Roll_RM",
35+
"Sitting_Enter",
36+
"Sitting_Exit",
37+
"Sitting_Idle_Loop",
38+
"Sitting_Talking_Loop",
39+
"Spell_Simple_Enter",
40+
"Spell_Simple_Exit",
41+
"Spell_Simple_Idle_Loop",
42+
"Spell_Simple_Shoot",
43+
"Sprint_Loop",
44+
"Swim_Fwd_Loop",
45+
"Swim_Idle_Loop",
46+
"Sword_Attack",
47+
"Sword_Attack_RM",
48+
"Sword_Idle",
49+
"Walk_Formal_Loop",
50+
"Walk_Loop",
51+
}
52+
53+
local function set_button_enabled(button_node, enabled)
54+
local color = enabled and vmath.vector4(1, 1, 1, 1) or vmath.vector4(0.7, 0.7, 0.7, 1)
55+
gui.set_color(button_node, color) -- <5>
56+
end
57+
58+
function init(self)
59+
msg.post(".", "acquire_input_focus") -- <6>
60+
61+
self.buttons = {} -- <7>
62+
self.selected_index = nil
63+
64+
for index, animation_id in ipairs(ANIMATIONS) do -- <8>
65+
local button_id = "button_" .. string.format("%02d", index)
66+
local text_id = "button_text_" .. string.format("%02d", index)
67+
local button_node = gui.get_node(button_id)
68+
local text_node = gui.get_node(text_id)
69+
70+
gui.set_text(text_node, animation_id) -- <9>
71+
set_button_enabled(button_node, true)
72+
73+
self.buttons[index] = { -- <10>
74+
node = button_node,
75+
animation_id = animation_id,
76+
}
77+
end
78+
end
79+
80+
function on_input(self, action_id, action)
81+
if action_id ~= TOUCH or not action.pressed then -- <11>
82+
return false
83+
end
84+
85+
for index, button in ipairs(self.buttons) do
86+
if gui.pick_node(button.node, action.x, action.y) then -- <12>
87+
if self.selected_index then
88+
set_button_enabled(self.buttons[self.selected_index].node, true)
89+
end
90+
91+
self.selected_index = index
92+
set_button_enabled(button.node, false) -- <13>
93+
94+
msg.post(MODEL_SCRIPT, MSG_PLAY_MODEL_ANIMATION, { -- <14>
95+
animation_id = hash(button.animation_id),
96+
animation_name = button.animation_id,
97+
})
98+
99+
return true
100+
end
101+
end
102+
103+
return false
104+
end
105+
106+
-- <1> The input binding uses the touch action for both mouse clicks and touch input in this example.
107+
-- <2> This is the same message id used by main.script, so both scripts agree on the command name.
108+
-- <3> GUI scripts cannot address model components directly in a clean way here, so the GUI sends a message to the character script instead.
109+
-- <4> The animation list is ordered to match the generated GUI buttons: button_01 plays the first clip, button_02 the second clip, and so on.
110+
-- <5> The selected button is dimmed by changing the box node color. The text nodes stay unchanged and remain readable on the separate text layer.
111+
-- <6> The GUI must acquire input focus before on_input() receives click/touch events.
112+
-- <7> Store runtime references to the GUI nodes once during init instead of resolving them every click.
113+
-- <8> Button and text node ids are generated from the animation index, matching the button_01/button_text_01 naming convention in main.gui.
114+
-- <9> The visible label is filled from the animation list, so the GUI file does not need to hardcode animation names in every text node.
115+
-- <10> Each button entry stores the clickable box node and the animation id it should request.
116+
-- <11> Ignore all non-press input events. This prevents the same click/touch from triggering repeatedly during release or movement phases.
117+
-- <12> gui.pick_node() tests whether the click position is inside the button box node.
118+
-- <13> Re-enable the previously selected button and dim the newly selected one to show which animation is currently active.
119+
-- <14> Send the selected animation to the model-owning script. The animation is sent as a hash for model.play_anim(), with the string kept only as readable debug/context data.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
local MSG_PLAY_MODEL_ANIMATION = hash("play_model_animation") -- <1>
2+
local DEFAULT_ANIMATION = hash("Sword_Idle") -- <2>
3+
4+
local function play_animation(animation_id)
5+
model.play_anim("#model", animation_id, go.PLAYBACK_LOOP_FORWARD) -- <3>
6+
end
7+
8+
function init(self)
9+
play_animation(DEFAULT_ANIMATION) -- <4>
10+
end
11+
12+
function on_message(self, message_id, message, sender)
13+
if message_id == MSG_PLAY_MODEL_ANIMATION then -- <5>
14+
play_animation(message.animation_id)
15+
end
16+
end
17+
18+
-- <1> This message id is shared with the GUI script. The GUI does not call the model API directly; it asks this script to play an animation.
19+
-- <2> The character starts in a known default pose/animation before the user selects anything from the GUI.
20+
-- <3> model.play_anim() is called on the model component attached to the same game object as this script. The animation id must match a clip imported from the GLB.
21+
-- <4> Start the default animation when the game object is initialized.
22+
-- <5> React only to the animation-selection message. The selected animation id is sent by the GUI script in the message table.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"content":[{"name":"game.projectc","size":4776,"pieces":[{"name":"game0.projectc","offset":0}]},{"name":"game.arci","size":3168,"pieces":[{"name":"game0.arci","offset":0}]},{"name":"game.arcd","size":1448400,"pieces":[{"name":"game0.arcd","offset":0}]},{"name":"game.dmanifest","size":3303,"pieces":[{"name":"game0.dmanifest","offset":0}]}],"total_size":1459647}
1.38 MB
Binary file not shown.
3.09 KB
Binary file not shown.
3.23 KB
Binary file not shown.

0 commit comments

Comments
 (0)