diff --git a/physics/box2d_weld_joint/all.texture_profiles b/physics/box2d_weld_joint/all.texture_profiles new file mode 100644 index 00000000..5b0d7762 --- /dev/null +++ b/physics/box2d_weld_joint/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/physics/box2d_weld_joint/box2d_v2.appmanifest b/physics/box2d_weld_joint/box2d_v2.appmanifest new file mode 100644 index 00000000..b3abe8c8 --- /dev/null +++ b/physics/box2d_weld_joint/box2d_v2.appmanifest @@ -0,0 +1,95 @@ +platforms: + arm64-ios: + context: + excludeLibs: [] + excludeSymbols: [] + symbols: [] + libs: [] + frameworks: [] + linkFlags: [] + x86_64-ios: + context: + excludeLibs: [] + excludeSymbols: [] + symbols: [] + libs: [] + frameworks: [] + linkFlags: [] + armv7-android: + context: + excludeLibs: [] + excludeJars: [] + excludeSymbols: [] + symbols: [] + libs: [] + linkFlags: [] + jetifier: true + arm64-android: + context: + excludeLibs: [] + excludeJars: [] + excludeSymbols: [] + symbols: [] + libs: [] + linkFlags: [] + jetifier: true + arm64-osx: + context: + excludeLibs: [] + excludeSymbols: [] + symbols: [] + libs: [] + frameworks: [] + linkFlags: [] + x86_64-osx: + context: + excludeLibs: [] + excludeSymbols: [] + symbols: [] + libs: [] + frameworks: [] + linkFlags: [] + x86_64-linux: + context: + excludeLibs: [] + excludeSymbols: [] + symbols: [] + libs: [] + linkFlags: [] + arm64-linux: + context: + excludeLibs: [] + excludeSymbols: [] + symbols: [] + libs: [] + linkFlags: [] + x86-win32: + context: + excludeLibs: [] + excludeSymbols: [] + symbols: [] + libs: [] + linkFlags: [] + x86_64-win32: + context: + excludeLibs: [] + excludeSymbols: [] + symbols: [] + libs: [] + linkFlags: [] + wasm-web: + context: + excludeLibs: [] + excludeJsLibs: [] + excludeSymbols: [] + symbols: [] + libs: [] + linkFlags: [] + wasm_pthread-web: + context: + excludeLibs: [] + excludeJsLibs: [] + excludeSymbols: [] + symbols: [] + libs: [] + linkFlags: [] diff --git a/physics/box2d_weld_joint/box2d_v3.appmanifest b/physics/box2d_weld_joint/box2d_v3.appmanifest new file mode 100644 index 00000000..9751deca --- /dev/null +++ b/physics/box2d_weld_joint/box2d_v3.appmanifest @@ -0,0 +1,100 @@ +platforms: + armv7-ios: + context: + excludeLibs: [physics, box2d_defold, script_box2d_defold] + excludeSymbols: [] + libs: [physics_2d, box2d, script_box2d, physics_3d] + arm64-ios: + context: + excludeLibs: [physics, box2d_defold, script_box2d_defold] + excludeSymbols: [] + symbols: [] + libs: [physics_2d, box2d, script_box2d, physics_3d] + frameworks: [] + linkFlags: [] + x86_64-ios: + context: + excludeLibs: [physics, box2d_defold, script_box2d_defold] + excludeSymbols: [] + symbols: [] + libs: [physics_2d, box2d, script_box2d, physics_3d] + frameworks: [] + linkFlags: [] + armv7-android: + context: + excludeLibs: [physics, box2d_defold, script_box2d_defold] + excludeJars: [] + excludeSymbols: [] + symbols: [] + libs: [physics_2d, box2d, script_box2d, physics_3d] + linkFlags: [] + jetifier: true + arm64-android: + context: + excludeLibs: [physics, box2d_defold, script_box2d_defold] + excludeJars: [] + excludeSymbols: [] + symbols: [] + libs: [physics_2d, box2d, script_box2d, physics_3d] + linkFlags: [] + jetifier: true + arm64-osx: + context: + excludeLibs: [physics, box2d_defold, script_box2d_defold] + excludeSymbols: [] + symbols: [] + libs: [physics_2d, box2d, script_box2d, physics_3d] + frameworks: [] + linkFlags: [] + x86_64-osx: + context: + excludeLibs: [physics, box2d_defold, script_box2d_defold] + excludeSymbols: [] + symbols: [] + libs: [physics_2d, box2d, script_box2d, physics_3d] + frameworks: [] + linkFlags: [] + x86_64-linux: + context: + excludeLibs: [physics, box2d_defold, script_box2d_defold] + excludeSymbols: [] + symbols: [] + libs: [physics_2d, box2d, script_box2d, physics_3d] + linkFlags: [] + arm64-linux: + context: + excludeLibs: [physics, box2d_defold, script_box2d_defold] + excludeSymbols: [] + symbols: [] + libs: [physics_2d, box2d, script_box2d, physics_3d] + linkFlags: [] + x86-win32: + context: + excludeLibs: [libphysics, libbox2d_defold, script_box2d_defold] + excludeSymbols: [] + symbols: [] + libs: [libphysics_2d.lib, libbox2d.lib, libscript_box2d.lib, libphysics_3d.lib] + linkFlags: [] + x86_64-win32: + context: + excludeLibs: [libphysics, libbox2d_defold, script_box2d_defold] + excludeSymbols: [] + symbols: [] + libs: [libphysics_2d.lib, libbox2d.lib, libscript_box2d.lib, libphysics_3d.lib] + linkFlags: [] + wasm-web: + context: + excludeLibs: [physics, box2d_defold, script_box2d_defold] + excludeJsLibs: [] + excludeSymbols: [] + symbols: [] + libs: [physics_2d, box2d, script_box2d, physics_3d] + linkFlags: [] + wasm_pthread-web: + context: + excludeLibs: [physics, box2d_defold, script_box2d_defold] + excludeJsLibs: [] + excludeSymbols: [] + symbols: [] + libs: [physics_2d, box2d, script_box2d, physics_3d] + linkFlags: [] diff --git a/physics/box2d_weld_joint/example.md b/physics/box2d_weld_joint/example.md new file mode 100644 index 00000000..f8f5cccf --- /dev/null +++ b/physics/box2d_weld_joint/example.md @@ -0,0 +1,63 @@ +--- +tags: physics, box2d +title: Box2D Weld Joint +brief: Create Box2D weld joints from script so separate dynamic bodies behave like one rigid assembly. +author: Defold Foundation +scripts: box2d_weld_joint.script +thumbnail: thumbnail.webp +--- + +This example creates two Box2D weld joints at runtime between three separate dynamic square bodies. +They form an L-shaped cluster and are put inside a static wall box. +The weld joints keep the pieces together, while the whole assembly collides and bounces. + +Click or touch the screen to kick one of the welded bodies upward and watch the whole cluster react. + +There are no differences in scripting between Box2D V2 and V3 in this example. + +## What You'll Learn + +- How to get Box2D body handles from Defold collision objects +- How to create rigid weld joints with `b2d.joint.create_weld()` +- How local weld anchors define where two bodies are attached +- How changing one welded body's velocity affects the whole connected cluster + +## Setup + +The collection contains +- `controller` game object with the script and a label with information text +- three game objects: `center`, `right`, and `lower` with dynamic collision objects and visual sprite representations +- a single `walls` game object with static collision object that surrounds the play area with floor, ceiling, and wall shapes. + +The 3 separate Defold collision objects are arranged in an L shape, +with 80 px square sprites and matching box collision shapes. +Each block has restitution set to 1.0 so the welded cluster keeps bouncing against the surrounding walls. +Because a weld joint does not turn the blocks into one true compound rigid body, +they remain separate dynamic Box2D bodies connected by a constraint solver, +so even if a block has restitution: 1.0, energy can still be lost thorugh weld joint constraint or damping. + +![setup](setup.png) + +The `game.project` of this example is configured to build with `/box2d_v3.appmanifest` by default. +To test V2 locally after downloading the example, +change `Native Extensions -> App Manifest` in `game.project` to `/box2d_v2.appmanifest`. + +## How It Works + +The script starts by getting the bodies owned by the collection-authored collision objects using `b2d.get_body()`. +The script stores the handles for `center`, `right`, and `lower`. + +Then it calls `b2d.joint.create_weld()` twice - one weld joint from `center` to `right`, and another from `center` to `lower`. +Each weld joint uses `local_anchor_a` and `local_anchor_b` so the neighboring squares are joined at their touching edges. + +The example uses only the rigid weld fields shared by both backends: +`local_anchor_a`, `local_anchor_b`, `reference_angle`, and `collide_connected`. +V2 also supports `frequency` and `damping_ratio` for soft welds, +while V3 uses separate linear and angular hertz/damping fields. +Those backend-specific spring fields are intentionally omitted here so the same script can run on both backends. + +Input focus lets player clicks or touches the screen. +The script changes the linear and angular velocity of the `right` block. +The weld joints transfer that motion through the connected bodies, +so the whole L-shaped assembly kicks upward and spins while remaining rigid. + diff --git a/physics/box2d_weld_joint/example/box2d_weld_joint.collection b/physics/box2d_weld_joint/example/box2d_weld_joint.collection new file mode 100644 index 00000000..97ef6248 --- /dev/null +++ b/physics/box2d_weld_joint/example/box2d_weld_joint.collection @@ -0,0 +1,256 @@ +name: "box2d_weld_joint" +scale_along_z: 0 +embedded_instances { + id: "controller" + data: "components {\n" + " id: \"script\"\n" + " component: \"/example/box2d_weld_joint.script\"\n" + "}\n" + "embedded_components {\n" + " id: \"label\"\n" + " type: \"label\"\n" + " data: \"size {\\n" + " x: 640.0\\n" + " y: 96.0\\n" + "}\\n" + "text: \\\"Click or touch to move the welded blocks up\\\"\\n" + "font: \\\"/fonts/text28.font\\\"\\n" + "material: \\\"/builtins/fonts/label-df.material\\\"\\n" + "\"\n" + " position {\n" + " x: 360.0\n" + " y: 670.0\n" + " }\n" + "}\n" + "" +} +embedded_instances { + id: "center" + data: "embedded_components {\n" + " id: \"collisionobject\"\n" + " type: \"collisionobject\"\n" + " data: \"type: COLLISION_OBJECT_TYPE_DYNAMIC\\n" + "mass: 1.0\\n" + "friction: 0.15\\n" + "restitution: 1.0\\n" + "group: \\\"default\\\"\\n" + "mask: \\\"default\\\"\\n" + "embedded_collision_shape {\\n" + " shapes {\\n" + " shape_type: TYPE_BOX\\n" + " position {\\n" + " }\\n" + " rotation {\\n" + " }\\n" + " index: 0\\n" + " count: 3\\n" + " }\\n" + " data: 40.0\\n" + " data: 40.0\\n" + " data: 10.0\\n" + "}\\n" + "linear_damping: 0.02\\n" + "angular_damping: 0.02\\n" + "\"\n" + "}\n" + "embedded_components {\n" + " id: \"sprite\"\n" + " type: \"sprite\"\n" + " data: \"default_animation: \\\"pixel_orange\\\"\\n" + "material: \\\"/builtins/materials/sprite.material\\\"\\n" + "size {\\n" + " x: 80.0\\n" + " y: 80.0\\n" + "}\\n" + "size_mode: SIZE_MODE_MANUAL\\n" + "textures {\\n" + " sampler: \\\"texture_sampler\\\"\\n" + " texture: \\\"/textures/textures.atlas\\\"\\n" + "}\\n" + "\"\n" + "}\n" + "" + position { + x: 360.0 + y: 360.0 + } +} +embedded_instances { + id: "right" + data: "embedded_components {\n" + " id: \"collisionobject\"\n" + " type: \"collisionobject\"\n" + " data: \"type: COLLISION_OBJECT_TYPE_DYNAMIC\\n" + "mass: 1.0\\n" + "friction: 0.15\\n" + "restitution: 1.0\\n" + "group: \\\"default\\\"\\n" + "mask: \\\"default\\\"\\n" + "embedded_collision_shape {\\n" + " shapes {\\n" + " shape_type: TYPE_BOX\\n" + " position {\\n" + " }\\n" + " rotation {\\n" + " }\\n" + " index: 0\\n" + " count: 3\\n" + " }\\n" + " data: 40.0\\n" + " data: 40.0\\n" + " data: 10.0\\n" + "}\\n" + "linear_damping: 0.02\\n" + "angular_damping: 0.02\\n" + "\"\n" + "}\n" + "embedded_components {\n" + " id: \"sprite\"\n" + " type: \"sprite\"\n" + " data: \"default_animation: \\\"pixel_blue\\\"\\n" + "material: \\\"/builtins/materials/sprite.material\\\"\\n" + "size {\\n" + " x: 80.0\\n" + " y: 80.0\\n" + "}\\n" + "size_mode: SIZE_MODE_MANUAL\\n" + "textures {\\n" + " sampler: \\\"texture_sampler\\\"\\n" + " texture: \\\"/textures/textures.atlas\\\"\\n" + "}\\n" + "\"\n" + "}\n" + "" + position { + x: 440.0 + y: 360.0 + } +} +embedded_instances { + id: "lower" + data: "embedded_components {\n" + " id: \"collisionobject\"\n" + " type: \"collisionobject\"\n" + " data: \"type: COLLISION_OBJECT_TYPE_DYNAMIC\\n" + "mass: 1.0\\n" + "friction: 0.15\\n" + "restitution: 1.0\\n" + "group: \\\"default\\\"\\n" + "mask: \\\"default\\\"\\n" + "embedded_collision_shape {\\n" + " shapes {\\n" + " shape_type: TYPE_BOX\\n" + " position {\\n" + " }\\n" + " rotation {\\n" + " }\\n" + " index: 0\\n" + " count: 3\\n" + " }\\n" + " data: 40.0\\n" + " data: 40.0\\n" + " data: 10.0\\n" + "}\\n" + "linear_damping: 0.02\\n" + "angular_damping: 0.02\\n" + "\"\n" + "}\n" + "embedded_components {\n" + " id: \"sprite\"\n" + " type: \"sprite\"\n" + " data: \"default_animation: \\\"pixel_blue\\\"\\n" + "material: \\\"/builtins/materials/sprite.material\\\"\\n" + "size {\\n" + " x: 80.0\\n" + " y: 80.0\\n" + "}\\n" + "size_mode: SIZE_MODE_MANUAL\\n" + "textures {\\n" + " sampler: \\\"texture_sampler\\\"\\n" + " texture: \\\"/textures/textures.atlas\\\"\\n" + "}\\n" + "\"\n" + "}\n" + "" + position { + x: 360.0 + y: 280.0 + } +} +embedded_instances { + id: "walls" + data: "embedded_components {\n" + " id: \"collisionobject\"\n" + " type: \"collisionobject\"\n" + " data: \"type: COLLISION_OBJECT_TYPE_STATIC\\n" + "mass: 0.0\\n" + "friction: 0.1\\n" + "restitution: 0.9\\n" + "group: \\\"default\\\"\\n" + "mask: \\\"default\\\"\\n" + "embedded_collision_shape {\\n" + " shapes {\\n" + " shape_type: TYPE_BOX\\n" + " position {\\n" + " y: -380.0\\n" + " }\\n" + " rotation {\\n" + " }\\n" + " index: 0\\n" + " count: 3\\n" + " id: \\\"floor\\\"\\n" + " }\\n" + " shapes {\\n" + " shape_type: TYPE_BOX\\n" + " position {\\n" + " y: 380.0\\n" + " }\\n" + " rotation {\\n" + " }\\n" + " index: 3\\n" + " count: 3\\n" + " id: \\\"ceiling\\\"\\n" + " }\\n" + " shapes {\\n" + " shape_type: TYPE_BOX\\n" + " position {\\n" + " x: -380.0\\n" + " }\\n" + " rotation {\\n" + " }\\n" + " index: 6\\n" + " count: 3\\n" + " id: \\\"wall_left\\\"\\n" + " }\\n" + " shapes {\\n" + " shape_type: TYPE_BOX\\n" + " position {\\n" + " x: 380.0\\n" + " }\\n" + " rotation {\\n" + " }\\n" + " index: 9\\n" + " count: 3\\n" + " id: \\\"wall_right\\\"\\n" + " }\\n" + " data: 360.0\\n" + " data: 20.0\\n" + " data: 10.0\\n" + " data: 360.0\\n" + " data: 20.0\\n" + " data: 10.0\\n" + " data: 20.0\\n" + " data: 400.0\\n" + " data: 10.0\\n" + " data: 20.0\\n" + " data: 400.0\\n" + " data: 10.0\\n" + "}\\n" + "\"\n" + "}\n" + "" + position { + x: 360.0 + y: 360.0 + } +} diff --git a/physics/box2d_weld_joint/example/box2d_weld_joint.script b/physics/box2d_weld_joint/example/box2d_weld_joint.script new file mode 100644 index 00000000..35ed48c8 --- /dev/null +++ b/physics/box2d_weld_joint/example/box2d_weld_joint.script @@ -0,0 +1,53 @@ +local BLOCK_HALF_SIZE = 40 + +local function create_weld(self, body_a, body_b, local_anchor_a, local_anchor_b) + local joint = b2d.joint.create_weld(body_a, body_b, { + -- Local anchors mark the touching edges that should stay locked together. + local_anchor_a = local_anchor_a, + local_anchor_b = local_anchor_b, + + -- The blocks start with the same rotation, so their desired relative angle is zero. + reference_angle = 0.0, + collide_connected = false, + }) + + table.insert(self.joints, joint) +end + +local function create_welds(self) + -- Weld the right block to the center block at their shared vertical edge. + create_weld(self, self.center, self.right, vmath.vector3(BLOCK_HALF_SIZE, 0, 0), vmath.vector3(-BLOCK_HALF_SIZE, 0, 0)) + + -- Weld the lower block to the center block at their shared horizontal edge. + create_weld(self, self.center, self.lower, vmath.vector3(0, -BLOCK_HALF_SIZE, 0), vmath.vector3(0, BLOCK_HALF_SIZE, 0)) +end + +function init(self) + self.center = b2d.get_body("/center#collisionobject") + self.right = b2d.get_body("/right#collisionobject") + self.lower = b2d.get_body("/lower#collisionobject") + + self.joints = {} + + create_welds(self) + + msg.post(".", "acquire_input_focus") +end + +function final(self) + for _, joint in ipairs(self.joints) do + b2d.joint.destroy(joint) + end + + msg.post(".", "release_input_focus") +end + +local TOUCH = hash("touch") + +function on_input(self, action_id, action) + if action_id == TOUCH and action.pressed then + -- The welds transfer this velocity change through the whole L-shaped cluster. + b2d.body.set_linear_velocity(self.right, vmath.vector3(math.random(-300,300), 900, 0)) + b2d.body.set_angular_velocity(self.right, math.random(-10,10)) + end +end diff --git a/physics/box2d_weld_joint/fonts/SourceSansPro-Semibold.ttf b/physics/box2d_weld_joint/fonts/SourceSansPro-Semibold.ttf new file mode 100644 index 00000000..50205948 Binary files /dev/null and b/physics/box2d_weld_joint/fonts/SourceSansPro-Semibold.ttf differ diff --git a/physics/box2d_weld_joint/fonts/text28.font b/physics/box2d_weld_joint/fonts/text28.font new file mode 100644 index 00000000..3d9c4a8e --- /dev/null +++ b/physics/box2d_weld_joint/fonts/text28.font @@ -0,0 +1,5 @@ +font: "/fonts/SourceSansPro-Semibold.ttf" +material: "/builtins/fonts/font-df.material" +size: 28 +output_format: TYPE_DISTANCE_FIELD +characters: " !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" diff --git a/physics/box2d_weld_joint/game.project b/physics/box2d_weld_joint/game.project new file mode 100644 index 00000000..328e7b11 --- /dev/null +++ b/physics/box2d_weld_joint/game.project @@ -0,0 +1,39 @@ +[bootstrap] +main_collection = /example/box2d_weld_joint.collectionc + +[script] +shared_state = 1 + +[display] +width = 720 +height = 720 +high_dpi = 1 + +[android] +input_method = HiddenInputField + +[physics] +gravity_y = -900.0 +scale = 0.01 +use_fixed_timestep = 1 + +[html5] +show_fullscreen_button = 0 +show_made_with_defold = 0 +scale_mode = no_scale +heap_size = 64 + +[input] +game_binding = /builtins/input/all.input_bindingc + +[project] +title = Box2D Weld Joint + +[native_extension] +app_manifest = /box2d_v2.appmanifest + +[render] +clear_color_red = 0.160156 +clear_color_green = 0.164063 +clear_color_blue = 0.183594 + diff --git a/physics/box2d_weld_joint/setup.png b/physics/box2d_weld_joint/setup.png new file mode 100644 index 00000000..30918ed4 Binary files /dev/null and b/physics/box2d_weld_joint/setup.png differ diff --git a/physics/box2d_weld_joint/textures/pixel_blue.png b/physics/box2d_weld_joint/textures/pixel_blue.png new file mode 100644 index 00000000..2db50d55 Binary files /dev/null and b/physics/box2d_weld_joint/textures/pixel_blue.png differ diff --git a/physics/box2d_weld_joint/textures/pixel_orange.png b/physics/box2d_weld_joint/textures/pixel_orange.png new file mode 100644 index 00000000..9fb97e44 Binary files /dev/null and b/physics/box2d_weld_joint/textures/pixel_orange.png differ diff --git a/physics/box2d_weld_joint/textures/pixel_white.png b/physics/box2d_weld_joint/textures/pixel_white.png new file mode 100644 index 00000000..0e34154f Binary files /dev/null and b/physics/box2d_weld_joint/textures/pixel_white.png differ diff --git a/physics/box2d_weld_joint/textures/textures.atlas b/physics/box2d_weld_joint/textures/textures.atlas new file mode 100644 index 00000000..324df8de --- /dev/null +++ b/physics/box2d_weld_joint/textures/textures.atlas @@ -0,0 +1,10 @@ +images { + image: "/textures/pixel_white.png" +} +images { + image: "/textures/pixel_blue.png" +} +images { + image: "/textures/pixel_orange.png" +} +extrude_borders: 2 diff --git a/physics/box2d_weld_joint/thumbnail.webp b/physics/box2d_weld_joint/thumbnail.webp new file mode 100644 index 00000000..557521d2 Binary files /dev/null and b/physics/box2d_weld_joint/thumbnail.webp differ