diff --git a/animation/spine_ik/.gitattributes b/animation/spine_ik/.gitattributes new file mode 100644 index 00000000..187cdf4a --- /dev/null +++ b/animation/spine_ik/.gitattributes @@ -0,0 +1,42 @@ +# Defold Protocol Buffer Text Files (https://github.com/github/linguist/issues/5091) +*.animationset linguist-language=JSON5 +*.atlas linguist-language=JSON5 +*.camera linguist-language=JSON5 +*.collection linguist-language=JSON5 +*.collectionfactory linguist-language=JSON5 +*.collectionproxy linguist-language=JSON5 +*.collisionobject linguist-language=JSON5 +*.cubemap linguist-language=JSON5 +*.display_profiles linguist-language=JSON5 +*.factory linguist-language=JSON5 +*.font linguist-language=JSON5 +*.gamepads linguist-language=JSON5 +*.go linguist-language=JSON5 +*.gui linguist-language=JSON5 +*.input_binding linguist-language=JSON5 +*.label linguist-language=JSON5 +*.material linguist-language=JSON5 +*.mesh linguist-language=JSON5 +*.model linguist-language=JSON5 +*.particlefx linguist-language=JSON5 +*.render linguist-language=JSON5 +*.sound linguist-language=JSON5 +*.sprite linguist-language=JSON5 +*.spinemodel linguist-language=JSON5 +*.spinescene linguist-language=JSON5 +*.texture_profiles linguist-language=JSON5 +*.tilemap linguist-language=JSON5 +*.tilesource linguist-language=JSON5 + +# Defold JSON Files +*.buffer linguist-language=JSON + +# Defold GLSL Shaders +*.fp linguist-language=GLSL +*.vp linguist-language=GLSL + +# Defold Lua Files +*.editor_script linguist-language=Lua +*.render_script linguist-language=Lua +*.script linguist-language=Lua +*.gui_script linguist-language=Lua diff --git a/animation/spine_ik/.gitignore b/animation/spine_ik/.gitignore new file mode 100644 index 00000000..0f4d6130 --- /dev/null +++ b/animation/spine_ik/.gitignore @@ -0,0 +1,11 @@ +/.editor_settings +/.internal +/build +.externalToolBuilders +.DS_Store +Thumbs.db +.lock-wscript +*.pyc +.project +.cproject +builtins \ No newline at end of file diff --git a/animation/spine_ik/README.md b/animation/spine_ik/README.md new file mode 100644 index 00000000..3cc9075c --- /dev/null +++ b/animation/spine_ik/README.md @@ -0,0 +1,33 @@ +--- +title: Handle Spine IK constraints +brief: Learn how to control Spine IK constraints by assigning a game object as the IK target. +author_ids: + - evgenii-starostin +scripts: area.script, cursor.script, manipulator.script +thumbnail: thumbnail.webp +tags: animation +--- +This example demonstrates how to control Spine Inverse Kinematics (IK) constraints in Defold by assigning a game object as the IK target. + +Move the mouse or touch in the active area (white rectangle in the background) to move the robot's arm with IK constraints. + + +## Project Structure +``` +- root + - active_area.go + - collisionobject + - area.script + - sprite + - cursor.go + - collisionobject + - cursor.script + - manipulator.script + - spinemodel +``` + +## How It Works +The `cursor.go` game object is controlled by the user and has a collision object configured as a trigger. +When the cursor overlaps with the trigger in `active_area.go`, it sends a message to `manipulator.script`. +The script uses the Defold Spine API to assign `cursor.go` as the target of an IK constraint with spine.set_ik_target(). +When the target is no longer needed, spine.reset_ik_target() resets the constraint to its original target position. diff --git a/animation/spine_ik/game.project b/animation/spine_ik/game.project new file mode 100644 index 00000000..5d2bc407 --- /dev/null +++ b/animation/spine_ik/game.project @@ -0,0 +1,25 @@ +[bootstrap] +main_collection = /main/main.collectionc + +[script] +shared_state = 1 + +[display] +width = 960 +height = 640 +high_dpi = 1 + +[android] +input_method = HiddenInputField + +[physics] +gravity_y = -1000.0 +scale = 0.01 + +[html5] +scale_mode = stretch + +[project] +title = spine_ik +dependencies#0 = https://github.com/defold/extension-spine/archive/refs/tags/4.9.4.zip + diff --git a/animation/spine_ik/input/game.input_binding b/animation/spine_ik/input/game.input_binding new file mode 100644 index 00000000..8ed1d4e4 --- /dev/null +++ b/animation/spine_ik/input/game.input_binding @@ -0,0 +1,4 @@ +mouse_trigger { + input: MOUSE_BUTTON_1 + action: "touch" +} diff --git a/animation/spine_ik/main/area.script b/animation/spine_ik/main/area.script new file mode 100644 index 00000000..3e5ebfa7 --- /dev/null +++ b/animation/spine_ik/main/area.script @@ -0,0 +1,6 @@ +local NORMAL_ALPHA = 0.8 +local ALPHA_PROPERTY = hash("tint.w") + +function init(self) + go.set("#sprite", ALPHA_PROPERTY, NORMAL_ALPHA) +end diff --git a/animation/spine_ik/main/base.atlas b/animation/spine_ik/main/base.atlas new file mode 100644 index 00000000..6f61ccba --- /dev/null +++ b/animation/spine_ik/main/base.atlas @@ -0,0 +1,25 @@ +images { + image: "/main/images/base_bot.png" +} +images { + image: "/main/images/base_top.png" +} +images { + image: "/main/images/blue_pin_min_bot.png" +} +images { + image: "/main/images/blue_pin_min_top.png" +} +images { + image: "/main/images/blue_pin_top.png" +} +images { + image: "/main/images/circle_pin_front.png" +} +images { + image: "/main/images/red_stick_bot.png" +} +images { + image: "/main/images/rect.png" +} +extrude_borders: 2 diff --git a/animation/spine_ik/main/cursor.script b/animation/spine_ik/main/cursor.script new file mode 100644 index 00000000..c3f8113a --- /dev/null +++ b/animation/spine_ik/main/cursor.script @@ -0,0 +1,22 @@ +local MSG_TRIGGER = hash("trigger_response") +local MSG_PARENT = hash("parent") + +function init(self) + msg.post(".", "acquire_input_focus") +end + +function final(self) + msg.post(".", "release_input_focus") +end + +function on_input(self, action_id, action) + if action.x and action.y then + go.set_position(vmath.vector3(action.x, action.y, 1)) + end +end + +function on_message(self, message_id, message, sender) + if message_id == MSG_TRIGGER then + msg.post("/root#manipulator", MSG_PARENT, { parent = message.enter }) + end +end diff --git a/animation/spine_ik/main/images/base_bot.png b/animation/spine_ik/main/images/base_bot.png new file mode 100644 index 00000000..f751a6e6 Binary files /dev/null and b/animation/spine_ik/main/images/base_bot.png differ diff --git a/animation/spine_ik/main/images/base_top.png b/animation/spine_ik/main/images/base_top.png new file mode 100644 index 00000000..5955fc3f Binary files /dev/null and b/animation/spine_ik/main/images/base_top.png differ diff --git a/animation/spine_ik/main/images/blue_pin_min_bot.png b/animation/spine_ik/main/images/blue_pin_min_bot.png new file mode 100644 index 00000000..f4140f84 Binary files /dev/null and b/animation/spine_ik/main/images/blue_pin_min_bot.png differ diff --git a/animation/spine_ik/main/images/blue_pin_min_top.png b/animation/spine_ik/main/images/blue_pin_min_top.png new file mode 100644 index 00000000..3c79d416 Binary files /dev/null and b/animation/spine_ik/main/images/blue_pin_min_top.png differ diff --git a/animation/spine_ik/main/images/blue_pin_top.png b/animation/spine_ik/main/images/blue_pin_top.png new file mode 100644 index 00000000..c104999c Binary files /dev/null and b/animation/spine_ik/main/images/blue_pin_top.png differ diff --git a/animation/spine_ik/main/images/circle_pin_front.png b/animation/spine_ik/main/images/circle_pin_front.png new file mode 100644 index 00000000..b7dfbdbd Binary files /dev/null and b/animation/spine_ik/main/images/circle_pin_front.png differ diff --git a/animation/spine_ik/main/images/rect.png b/animation/spine_ik/main/images/rect.png new file mode 100644 index 00000000..ca8aa034 Binary files /dev/null and b/animation/spine_ik/main/images/rect.png differ diff --git a/animation/spine_ik/main/images/red_stick_bot.png b/animation/spine_ik/main/images/red_stick_bot.png new file mode 100644 index 00000000..ff3fbdea Binary files /dev/null and b/animation/spine_ik/main/images/red_stick_bot.png differ diff --git a/animation/spine_ik/main/main.collection b/animation/spine_ik/main/main.collection new file mode 100644 index 00000000..14067b3e --- /dev/null +++ b/animation/spine_ik/main/main.collection @@ -0,0 +1,128 @@ +name: "main" +scale_along_z: 0 +embedded_instances { + id: "root" + children: "active_area" + children: "cursor" + data: "components {\n" + " id: \"manipulator\"\n" + " component: \"/main/manipulator.script\"\n" + "}\n" + "embedded_components {\n" + " id: \"spinemodel\"\n" + " type: \"spinemodel\"\n" + " data: \"spine_scene: \\\"/main/spine/manipulator.spinescene\\\"\\n" + "default_animation: \\\"idle\\\"\\n" + "skin: \\\"\\\"\\n" + "material: \\\"/defold-spine/assets/spine.material\\\"\\n" + "create_go_bones: true\\n" + "\"\n" + " position {\n" + " x: 480.0\n" + " }\n" + "}\n" + "" +} +embedded_instances { + id: "cursor" + data: "components {\n" + " id: \"cursor\"\n" + " component: \"/main/cursor.script\"\n" + "}\n" + "embedded_components {\n" + " id: \"collisionobject\"\n" + " type: \"collisionobject\"\n" + " data: \"type: COLLISION_OBJECT_TYPE_TRIGGER\\n" + "mass: 0.0\\n" + "friction: 0.1\\n" + "restitution: 0.5\\n" + "group: \\\"cursor\\\"\\n" + "mask: \\\"area\\\"\\n" + "embedded_collision_shape {\\n" + " shapes {\\n" + " shape_type: TYPE_BOX\\n" + " position {\\n" + " }\\n" + " rotation {\\n" + " }\\n" + " index: 0\\n" + " count: 3\\n" + " id: \\\"box\\\"\\n" + " }\\n" + " data: 2.5\\n" + " data: 2.5\\n" + " data: 10.0\\n" + "}\\n" + "event_collision: false\\n" + "event_contact: false\\n" + "\"\n" + "}\n" + "" + position { + x: 689.0 + y: 369.0 + } +} +embedded_instances { + id: "active_area" + data: "components {\n" + " id: \"area\"\n" + " component: \"/main/area.script\"\n" + "}\n" + "embedded_components {\n" + " id: \"collisionobject\"\n" + " type: \"collisionobject\"\n" + " data: \"type: COLLISION_OBJECT_TYPE_TRIGGER\\n" + "mass: 0.0\\n" + "friction: 0.1\\n" + "restitution: 0.5\\n" + "group: \\\"area\\\"\\n" + "mask: \\\"cursor\\\"\\n" + "embedded_collision_shape {\\n" + " shapes {\\n" + " shape_type: TYPE_BOX\\n" + " position {\\n" + " }\\n" + " rotation {\\n" + " }\\n" + " index: 0\\n" + " count: 3\\n" + " id: \\\"base\\\"\\n" + " }\\n" + " data: 150.0\\n" + " data: 100.0\\n" + " data: 10.0\\n" + "}\\n" + "event_collision: false\\n" + "event_contact: false\\n" + "\"\n" + "}\n" + "embedded_components {\n" + " id: \"sprite\"\n" + " type: \"sprite\"\n" + " data: \"default_animation: \\\"rect\\\"\\n" + "material: \\\"/builtins/materials/sprite.material\\\"\\n" + "slice9 {\\n" + " x: 8.0\\n" + " y: 8.0\\n" + " z: 8.0\\n" + " w: 8.0\\n" + "}\\n" + "size {\\n" + " x: 300.0\\n" + " y: 200.0\\n" + "}\\n" + "size_mode: SIZE_MODE_MANUAL\\n" + "textures {\\n" + " sampler: \\\"texture_sampler\\\"\\n" + " texture: \\\"/main/base.atlas\\\"\\n" + "}\\n" + "\"\n" + "}\n" + "" + position { + x: 480.0 + y: 360.0 + z: -0.5 + } +} diff --git a/animation/spine_ik/main/manipulator.script b/animation/spine_ik/main/manipulator.script new file mode 100644 index 00000000..fdb0ce06 --- /dev/null +++ b/animation/spine_ik/main/manipulator.script @@ -0,0 +1,17 @@ +local IK_BONE = hash("RED_IK") +local SPINE_COMPONENT = "#spinemodel" +local MSG_PARENT = hash("parent") + +function init(self) + self.target = go.get_id("cursor") +end + +function on_message(self, message_id, message, sender) + if message_id == MSG_PARENT then + if message.parent then + spine.set_ik_target(SPINE_COMPONENT, IK_BONE, self.target) + else + spine.reset_ik_target(SPINE_COMPONENT, IK_BONE) + end + end +end diff --git a/animation/spine_ik/main/spine/manipulator.spinejson b/animation/spine_ik/main/spine/manipulator.spinejson new file mode 100644 index 00000000..2c0415ed --- /dev/null +++ b/animation/spine_ik/main/spine/manipulator.spinejson @@ -0,0 +1 @@ +{"skeleton":{"hash":"OCs+C0uVJzw","spine":"4.2.43","x":-180.19,"y":1.91,"width":350.73,"height":556.54,"images":"../../manipulator/export/images/","audio":"./audio"},"bones":[{"name":"root"},{"name":"base","parent":"root"},{"name":"red_stick","parent":"base","length":157.89,"rotation":117.63,"x":-42.06,"y":111.65},{"name":"red_stick2","parent":"red_stick","length":138.98,"rotation":-68.81,"x":158.5,"y":1.16},{"name":"RED_IK","parent":"root","rotation":-12.89,"x":-25.06,"y":356.13,"color":"ff3f00ff","icon":"ik"},{"name":"blue_pin_top_1","parent":"red_stick2","length":114.16,"rotation":21.09,"x":170.1,"y":22.02,"color":"abe323ff"},{"name":"blue_pin_top_2","parent":"blue_pin_top_1","length":105.84,"rotation":-95.52,"x":136.17,"y":-3.08,"color":"abe323ff"},{"name":"blue_pin_bot_1","parent":"red_stick2","length":107.39,"rotation":-103.72,"x":152.81,"y":-0.67,"color":"abe323ff"},{"name":"blue_pin_bot_2","parent":"blue_pin_bot_1","length":92.62,"rotation":91.04,"x":141.91,"y":-2.49,"color":"abe323ff"}],"slots":[{"name":"base_bot","bone":"base","attachment":"base_bot"},{"name":"circle_pin_clone_back","bone":"red_stick","attachment":"circle_pin_front"},{"name":"circle_pin_clone_top_front","bone":"red_stick2","attachment":"circle_pin_front"},{"name":"red_stick_bot","bone":"red_stick","attachment":"red_stick_bot"},{"name":"base_top","bone":"base","attachment":"base_top"},{"name":"red_stick_top","bone":"red_stick2","attachment":"red_stick_top"},{"name":"blue_pin_top","bone":"blue_pin_top_1","attachment":"blue_pin_top"},{"name":"blue_pin_bot","bone":"blue_pin_bot_1","attachment":"blue_pin_top"},{"name":"circle_pin_front","bone":"red_stick","attachment":"circle_pin_front"},{"name":"circle_pin_clone_back_top","bone":"blue_pin_top_1","attachment":"circle_pin_front"},{"name":"circle_pin_clone_back_bot","bone":"blue_pin_bot_1","attachment":"circle_pin_front"},{"name":"blue_pin_min_top","bone":"blue_pin_top_2","attachment":"blue_pin_min_top"},{"name":"blue_pin_min_bot","bone":"blue_pin_bot_2","attachment":"blue_pin_min_bot"},{"name":"circle_pin_clone_front","bone":"red_stick2","attachment":"circle_pin_front"},{"name":"circle_pin_clone_front_top","bone":"blue_pin_top_1","attachment":"circle_pin_front"},{"name":"circle_pin_clone_front_bot","bone":"blue_pin_bot_1","attachment":"circle_pin_front"}],"ik":[{"name":"RED_IK","bones":["red_stick","red_stick2"],"target":"RED_IK","bendPositive":false}],"physics":[{"name":"blue_pin_bot_1","order":2,"bone":"blue_pin_bot_1","x":0.2,"y":0.2,"rotate":1,"inertia":0.5,"damping":0.85},{"name":"blue_pin_bot_2","order":4,"bone":"blue_pin_bot_2","x":0.1,"y":0.1,"rotate":1,"inertia":0.5,"damping":0.85},{"name":"blue_pin_top_1","order":1,"bone":"blue_pin_top_1","x":0.2,"y":0.2,"rotate":1,"inertia":0.5,"damping":0.85},{"name":"blue_pin_top_2","order":3,"bone":"blue_pin_top_2","x":0.1,"y":0.1,"rotate":1,"inertia":0.5,"damping":0.85}],"skins":[{"name":"default","attachments":{"base_bot":{"base_bot":{"x":27.86,"y":67.91,"width":227,"height":132}},"base_top":{"base_top":{"x":-44.95,"y":67.91,"width":227,"height":132}},"blue_pin_bot":{"blue_pin_top":{"x":55.9,"y":5.09,"rotation":108.94,"width":94,"height":143}},"blue_pin_min_bot":{"blue_pin_min_bot":{"x":60.8,"y":-4.81,"rotation":-36.14,"width":109,"height":85}},"blue_pin_min_top":{"blue_pin_min_top":{"x":71.53,"y":5.56,"rotation":25.62,"width":113,"height":78}},"blue_pin_top":{"blue_pin_top":{"x":60.58,"y":-5.57,"rotation":-69.9,"width":94,"height":143}},"circle_pin_clone_back":{"circle_pin_front":{"x":138.61,"y":-20.2,"rotation":-117.63,"width":116,"height":107}},"circle_pin_clone_back_bot":{"circle_pin_front":{"x":138.92,"y":27.3,"scaleX":0.806,"scaleY":0.806,"rotation":54.9,"width":116,"height":107}},"circle_pin_clone_back_top":{"circle_pin_front":{"x":136.26,"y":-35.98,"scaleX":0.8555,"scaleY":0.8555,"rotation":-69.9,"width":116,"height":107}},"circle_pin_clone_front":{"circle_pin_front":{"x":154.9,"y":7.59,"rotation":-48.81,"width":116,"height":107}},"circle_pin_clone_front_bot":{"circle_pin_front":{"x":124.71,"y":9.42,"scaleX":0.8586,"scaleY":0.8586,"rotation":54.9,"width":116,"height":107}},"circle_pin_clone_front_top":{"circle_pin_front":{"x":129.37,"y":-12.52,"scaleX":0.8824,"scaleY":0.8824,"rotation":-69.9,"width":116,"height":107}},"circle_pin_clone_top_front":{"circle_pin_front":{"x":167.4,"y":-5.06,"rotation":-48.81,"width":116,"height":107}},"circle_pin_front":{"circle_pin_front":{"x":153.2,"y":-0.55,"rotation":-117.63,"width":116,"height":107}},"red_stick_bot":{"red_stick_bot":{"x":69.41,"y":-8.5,"rotation":-117.63,"width":141,"height":181}},"red_stick_top":{"red_stick_top":{"path":"red_stick_bot","x":59.08,"y":-11.55,"rotation":-115.12,"width":141,"height":181}}}}],"animations":{"idle":{"bones":{"RED_IK":{"translate":[{"curve":[0.444,0,0.889,-2.95,0.444,0,0.889,-12.87]},{"time":1.3333,"x":-2.95,"y":-12.87,"curve":[1.778,-2.95,2.222,0,1.778,-12.87,2.222,0]},{"time":2.6667}]}}},"parent":{}}} \ No newline at end of file diff --git a/animation/spine_ik/main/spine/manipulator.spinescene b/animation/spine_ik/main/spine/manipulator.spinescene new file mode 100644 index 00000000..23b34784 --- /dev/null +++ b/animation/spine_ik/main/spine/manipulator.spinescene @@ -0,0 +1,2 @@ +spine_json: "/main/spine/manipulator.spinejson" +atlas: "/main/base.atlas" diff --git a/animation/spine_ik/thumbnail.webp b/animation/spine_ik/thumbnail.webp new file mode 100644 index 00000000..fa399950 Binary files /dev/null and b/animation/spine_ik/thumbnail.webp differ