Skip to main content

Click and Drag RigidBody2D

·724 words·4 mins

I’m making an optics game, building on an idea from several years ago, overhead ray tracing. I’ve done the Godot tutorial. Now it’s time to start prototyping.

For the first prototype, I just want to be able to drag blocks around in a 2D space, have them respond to collisions. Those blocks will later become lenses and mirrors.

There’ll be more prototypes later:

  • Drawing emitted rays and have them collide with blocks
  • Drawing reflected and refracted rays
  • Making the classic convex and concave lens shapes
  • Placing a sensor that detects rays

But one thing at a time.

Questions for later (so they don’t distract me now)

  • What size screen should I target with the prototypes?
  • What’s the best way to export animations of interactions?

Ooh, found a recipe for drag and dropping RigidBody2Ds. https://kidscancode.org/godot_recipes/4.x/physics/rigidbody_drag_drop/index.html

Not getting click events on the RigidBody2D nodes.

  • Make sure it is “Input Pickable” in the properties/inspector

Ah. The tutorial must’ve been for an older version of Godot. Instead of _on_input_event() what I needed was _input_event(). Now it works!

Now, dropping these blocks gives them some velocity and they can pass that onto each other as well. Right now that results in them going off screen. Lets bound them to the screen.

Looks like the way to do that is with StaticBody. I’d have to position static bodies around the outside of the viewport. I’ll probably want more control over the actual bounds so it’d make sense to specify a rectangle using the UI and then add the static bodies programmatically.

…ended up doing it manually for now. It requires StaticBody2D > CollisionShape2D > Shape2D and that’s a lot of layers of potential for mistakes.

I made the bodies huge so even fast-moving objects won’t penetrate.

There are some other things I’d like to clean up:

  • When picking up the object, it gets centered on the mouse instead of gripping it from the point on the object where the click occurred.
  • I’d like to have really high friction. It’s fun throwing the blocks around but not really what I’m going for.

I’d also like to be able to rotate the blocks. What’s the UI gonna be for that? Optics Factory used a control point. Pros: fine control, similar to image editors. Cons: feels like an image editor.

What if clicks in the middle of the object drag it and clicks further out rotate it? It’s intuitive and more physical. Not an editor but a game.

The center of mass is just the origin. That might be a problem later on but for the most part, I think I’m dealing with objects whose center of mass doesn’t change so no problem.

Dragging the object from the point where it was clicked was easy. Just record the offset from the object origin when clicked and subtract it when setting the new position. Cool.

Damping is taken care of by the RigidBody2D properties Angular > Damp and Linear > Damp.

Rotating based on the mouse is gonna be trickier. I think at each frame I’d want to calculate the distance between the point on the block which was clicked (grip point) and the new location of the mouse. Before updating the position of the block, rotate it so the angle from the origin to the grip point and the angle from the origin to the mouse are more similar. Rotate directly or apply angular velocity?

Also, I need to watch out for the point on the circle that goes from -π to π. Or 0 to 2π. Otherwise it’ll start rotating in the wrong direction.

Can I do it with just vectors instead to avoid that? Add the two vectors together but scale the vector from origin to the mouse. The resulting vector can be the new rotation.

Alright. That took some whiteboarding to wrap my head around the maths but it’s looking nice.

One little trick I was proud of: when clicking close to the origin and dragging the block rotates too quickly. It looks unnatural and the interaction is jarring. So I calculate the distance of the grip point from the origin and if it’s large, the block rotates quickly. If it’s small the block rotates more slowly, facilitated by the delta argument in rotate_toward().

That method also happens to take care of the 0 to 2π transition error, btw.