Community Challenge #6: Box2D scripting 📦

Hi!
I am working on a Box2dPlayground, so a small editor where you choose prefabs physics objects/joints and you build a scene, then press “play” and see what happens (trying to implement also an “Actor” concept where you chose an actor type (plateformer, top down, vehicle) then you choose an object and can controle it with this behaviour.

I am no way near finishing it, but hope makes me live..

Anyway, I am struggling a bit on the API documentation to get what is Box2d v2 and what is v3..?
Does it exist a doc with only Box2d v3 API?
(But all the new examples really helps, thanks for that)

Another problem I faced, is that you cant create a box2d body from scratch, you have to already have a game object having a collision object, then you can reference this collision object to obtain the corresponding box2d body. Then you can create shapes on it.
Is it right? Or can we create body objects from scratch without game object?
You cannot also create a world from scratch and controle its step() function, correct?

And since you cant create a gameobject from scratch (I mean adding components to it), this limits a bit my data-driven code. But I will do with factories.

One of my problem is that I have a build error when I setup a collision object with no shape on it… so you always have to add a box shape at least in the Defold editor, then destroy it/inactivate it afterwards.
Is this right?

2 Likes

I’m very mad about this documentation too, but we didn’t yet figured out a good split for this, docs simply weren’t prepared for versions of some APIs. The API references are module-based rather than backend-based.

The main difference between Box2D V2 and V3 is regarding the fixture (V2) vs shapes (V3):

Backend Main collision geometry API
Box2D V2 / legacy fixtures: b2d.body.get_fixtures(), b2d.fixture.*, b2d.body.create_fixture()
Box2D V3 shapes: b2d.body.get_shapes(), b2d.shape.*, b2d.body.create_shape()

In Box2D V3 we get also useful IDs for body references. I would personally recommend using only V3 API.

You can also get more informations from the official Box2D documentation:


Yes, you’re right here. Having a collisionobject component is a minimum in current setup. The new b2d.* API gives runtime access to the Box2D world, bodies, shapes, fixtures, joints and queries, but it does not currently expose a fully standalone Box2D workflow where you create your own world, create bodies from scratch, and step the world manually. In Defold, the Box2D body is still owned by a Defold collision object component.

You can though create a very generic “placeholder” game object with component and use factory to spawn as many as you like:

local id = factory.create("#body_factory", position)

and then using the b2d API modify its parameters, shapes and types:

local body = b2d.get_body("#collisionobject")

-- Optionally remove placeholder shape, then add runtime-defined shapes.
b2d.body.create_shape(body, {
    type = b2d.shape.SHAPE_TYPE_BOX,
    hx = 32,
    hy = 16,
    density = 1.0,
    friction = 0.3,
})

E.g. in the last example I change the “static” collisionobject into sensor (Defold’s trigger) using the b2d function in the code :wink:

You figured it out already :smiley:

Agree here, b2d.body.create_shape() is explicitly meant to attach new shapes to an existing body at runtime, so it would be very reasonable to want a “body container” collision object with no initial shape, but this is not possible now (you can create a Feature Request on Github for this though!).

Note, I’m not an expert here, so this is only from my experience with it as a user, I’m not aware of the implications and the decisions behind such an approach, but I believe it was best for the first iterations, and Mathias @JCash who created the APIs is on vacation right now.

1 Like

New example - Prismatic Joint:

2 Likes

Box2D Physics Playground is a small interactive physics sandbox . You can spawn boxes and circles, apply vertical or horizontal impulses, adjust object friction, switch bodies between dynamic and static, create and remove distance and rope joints, and reset the sandbox at any time. It was built as a simple, hands-on demonstration of Defold’s new Box2D scripting features.

The project demonstrates several of Defold’s new Box2D APIs, including body manipulation, fixture editing, mass data handling, and runtime joint creation/destruction.

Playable build: Box2D Physics Playground in Defold by FadingShadow

Source code: GitHub - Pica-Eduard-Ionut/Box2D-Physics-Playground-in-Defold: Submission for the "Community Challenge #6: Box2D scripting" challenge ¡ GitHub

Demo: https://www.youtube.com/watch?v=7k-H_npqKXA

Disclaimer: I forgot to make sure it the click controls work properly in fullscreen so it’s advised to play in the given viewport of the build on itchio. Been a long while since my last project in Defold.

3 Likes

Congratulations @Eduard !

I only didn’t figured how to create the joints in it :thinking: I tried clicking around, but seemingly no constraint was visible when I played the simulation :cry:


New example, this time with weld joints:

2 Likes

I tried using a place holder with my hill climb example. I created the new shapes and then i tried to delete the original ‘place holder’ shape. This caused a crash. So i just ended up making the place holder very small and left it in the middle of the game object.

1 Like

I also bumped into this. I think the crash happens because the collisionobject always needs at least one shape I. It so you could try deleting it after other shapes have been added to it.

1 Like

Agreed.

I was trying out the b2d. joints. But the documentation does not say the that b2d.joint.create_gear() and b2d.joint.create_pulley are only v2 not v3. I only discovered this after reading the box2d website.

2 Likes

Ouch you beat me to it :laughing:
It is great example, not to much code but efficient. Interesting how you detect mouse click by spawning a non persistant gameobject with a collision object.

1 Like

I saw that the html build on the site is a bit buggy in the upper sides of the screen since it doesnt take the screen coordinates exactly.

But the joints are only visible if debugging is turned on. I didnt spawn objects to represent them. I made it to showcase the API only, and the joints are made by clicking 2 existing objects in the scene.

Thanks!

1 Like

Ok, now I can create them!

1 Like

Ok, here’s the last example I managed to publish during this challenge - this one showcases how to change the shape and mass dynamically in code :wink:

3 Likes

It’s a simple and safe approach to get object ids, using clicks without spawning objects is tricky in my opinion since I’m not sure you can detect the clicked object that way. Using a temporary collision box spawned on click is sure to be right since its two objects interracting in the game world and you can easily use the messaging system to get object ids and forward them to the needed script.

2 Likes

Hello,

I am trying to set user data on a box2d Body, and get it back, the API says there exists


and

but I have an error and theses functions doesn’t seem to exists in Box2d v3. …?

My goal is to get back the GameObject Id from a box2d body… is this possible?

it does exists in the box2d v3 manual:

In debug mode, if I just evaluate b2d.body object, I get this… and no get_user_data !

b2d.body
{ -- <table: 0x0204c4c18bc0>
  B2_DYNAMIC_BODY = 2,
  B2_KINEMATIC_BODY = 1,
  B2_STATIC_BODY = 0,
  apply_angular_impulse = <function: 0x0204c4c1a0d0>,
  apply_force = <function: 0x0204c4c2b840>,
  apply_force_to_center = <function: 0x0204c4c2b8b0>,
  apply_linear_impulse = <function: 0x0204c4c2b9a0>,
  apply_linear_impulse_to_center = <function: 0x0204c4c2ba20>,
  apply_torque = <function: 0x0204c4c2b930>,
  compute_aabb = <function: 0x0204c4c19a30>,
  create_chain = <function: 0x0204c4c18cf0>,
  create_shape = <function: 0x0204c4c18c80>,
  destroy_shape = <function: 0x0204c4c19aa0>,
  enable_contact_events = <function: 0x0204c4c191c0>,
  enable_hit_events = <function: 0x0204c4c19240>,
  enable_sleep = <function: 0x0204c4c1a860>,
  get_angle = <function: 0x0204c4c19550>,
  get_angular_damping = <function: 0x0204c4c19e10>,
  get_angular_velocity = <function: 0x0204c4c19c10>,
  get_contact_data = <function: 0x0204c4c199b0>,
  get_force = <function: 0x0204c4c2b500>,
  get_gravity_scale = <function: 0x0204c4c19670>,
  get_inertia = <function: 0x0204c4c19310>,
  get_joints = <function: 0x0204c4c19940>,
  get_linear_damping = <function: 0x0204c4c19d10>,
  get_linear_velocity = <function: 0x0204c4c19b10>,
  get_linear_velocity_from_local_point = <function: 0x0204c4c2b7b0>,
  get_linear_velocity_from_world_point = <function: 0x0204c4c2b720>,
  get_local_center = <function: 0x0204c4c2b480>,
  get_local_center_of_mass = <function: 0x0204c4c2b400>,
  get_local_point = <function: 0x0204c4c2b630>,
  get_local_vector = <function: 0x0204c4c2b6a0>,
  get_mass = <function: 0x0204c4c192a0>,
  get_mass_data = <function: 0x0204c4c19400>,
  get_name = <function: 0x0204c4c18c40>,
  get_position = <function: 0x0204c4c18f40>,
  get_rotational_inertia = <function: 0x0204c4c19380>,
  get_shapes = <function: 0x0204c4c195c0>,
  get_sleep_threshold = <function: 0x0204c4c1a8d0>,
  get_transform = <function: 0x0204c4c18fb0>,
  get_type = <function: 0x0204c4c19770>,
  get_world = <function: 0x0204c4c1a150>,
  get_world_center = <function: 0x0204c4c198d0>,
  get_world_center_of_mass = <function: 0x0204c4c19850>,
  get_world_point = <function: 0x0204c4c2b540>,
  get_world_vector = <function: 0x0204c4c2b5b0>,
  is_active = <function: 0x0204c4c19110>,
  is_awake = <function: 0x0204c4c19ff0>,
  is_bullet = <function: 0x0204c4c19f10>,
  is_fixed_rotation = <function: 0x0204c4c1a6e0>,
  is_sleeping_allowed = <function: 0x0204c4c18d60>,
  is_sleeping_enabled = <function: 0x0204c4c1a7e0>,
  is_valid = <function: 0x0204c4c18a90>,
  reset_mass_data = <function: 0x0204c4c194e0>,
  set_active = <function: 0x0204c4c19150>,
  set_angular_damping = <function: 0x0204c4c19e90>,
  set_angular_velocity = <function: 0x0204c4c19c90>,
  set_awake = <function: 0x0204c4c1a060>,
  set_bullet = <function: 0x0204c4c19f80>,
  set_fixed_rotation = <function: 0x0204c4c1a760>,
  set_gravity_scale = <function: 0x0204c4c196f0>,
  set_linear_damping = <function: 0x0204c4c19d90>,
  set_linear_velocity = <function: 0x0204c4c19b90>,
  set_mass_data = <function: 0x0204c4c19470>,
  set_name = <function: 0x0204c4c18e00>,
  set_sleep_threshold = <function: 0x0204c4c1a950>,
  set_sleeping_allowed = <function: 0x0204c4c18eb0>,
  set_target_transform = <function: 0x0204c4c19090>,
  set_transform = <function: 0x0204c4c19020>,
  set_type = <function: 0x0204c4c197e0>
}
1 Like

Sorry, it’s not implemented, we also noticed this, please follow the issue:

:scream: :scream: :scream: all my design was based on this :sweat_smile:
hummm… ok I will need to pass the game object id every where or store a global table for lookups..
:melting_face:

1 Like

I’m not sure if it can help you, but there is a `b2d.body.set/get_name` API, though it only accepts a string.

1 Like

Yes, I also had this problem when creating the overlaps with queries and raycasts example, and solved it by creating a table with id look-ups:

Also, is there anyone else currently developing something for the challenge and needs one more day to submit? :wink:

You mean one more month? :face_with_peeking_eye:
I think I am too ambitious but too busy at the same time to deliver anything on schedule :sweat_smile: