1
\$\begingroup\$

How can I move and rotate an object along with another using vector math?

diagram

This is using Godot 3.1 using a KinematicBody2D as a ship with a StaticBody2D as a planet.

\$\endgroup\$
1
  • \$\begingroup\$ Note that a game engine generally expects a static body to be, well, static, unmoving. The physics systems will often optimize for this case - doing potentially costly work to inject it into an acceleration structure when it's placed/removed, so future collision queries can be lightning fast during the majority of frames when this object doesn't move. When you move it, you break that assumption, and that costly work has to be done again. If you plan to move the object, it's often better to make it kinematic, to say "I'll move this myself, so optimize it as a mover but don't move it for me" \$\endgroup\$
    – DMGregory
    Aug 10, 2019 at 9:31

3 Answers 3

2
\$\begingroup\$

The engine is geared towards making this easy by parenting. Though it may not be conceptually correct in your case, it's the most godot friendly way of doing it. Just add the ship as a child node of the planet when it lands, and detach it from the planet when it leaves.

As long as the parent-child relation is a chain of spatial nodes (if 3D) or Node2d (if 2D) they will share and propagate transforms.

If for a reason you want a node to be a child of a Spatial or Node2d without inheriting their movement, you just have to make sure that the "independent" node is child of a bare "Node" (the gray/white ones in the UI, instead of the blue/red). This would interrupt the transform propagation

\$\endgroup\$
1
  • 2
    \$\begingroup\$ It's also a good idea to save the global transform before moving an object to a new parent node. So that you can put it back at its original position & transform instead of letting it suddenly jump to a new position relative to the new parent. \$\endgroup\$
    – Romen
    Sep 16, 2020 at 20:37
1
\$\begingroup\$

Disclaimer: This solution assumes knowledge of linear algebra. Additionally, I'm new to these problems myself and my solution might be erroneous or incomplete. Any comments or edits are more than welcome.

Any transformation of objects in 2(3)D space is done via what is called an affine transformation matrix. This matrix is usually a 3x3 (or 4x4 in 3D) matrix that contains/combines the following transformations:

  1. Scaling
  2. Rotating
  3. Translating (moving)

All of the transformations are applied to each of the points that make up the object(s) in space. Affine transformations have a specific property where whichever set of points they transform, their relative positions remain unchanged.

In order to perform a transformation on an object you must first generate the corresponding matrix that will do this (in your picture example this would be a matrix containing only a rotation around the Z axis located in the middle of the orange square). With the matrix at hand, you can now apply it to each point that represents your objects in space (both the orange and the blue squares). In order to apply a transformation matrix you multiply it with the vector that represents the position of the point (in 3D the vector has 3 components namely x,y and z), \$M*V\$ where \$M\$ is a 4x4 transformation matrix and \$V=[x,y,z]^T\$ (the \$T\$ exponent denotes the transpose of the matrix). Applying this transformation to all the points in space will rotate them but their relative positions will remain unchanged (as per the affine transformation property) which means that the blue square position relative to the orange square will not change.

\$\endgroup\$
0
\$\begingroup\$

Oxy is correct that parenting is the easy way. And, yes, I'm aware I'm answering years later. Yet, if that is not convinient, we can still do this.

The KinematicBody2D (which I'll call ship) and the StaticBody2D (which I'll call planet) has their own transforms:

var ship_transform := ship.global_transform
var planet_transform := planet.global_transform

We are looking for a relative_transform, such that:

planet_transform * relative_transform = ship_transform

In other words:

var relative_transform := planet_transform.affine_inverse() * ship_transform

Once you have that, you can rotate the planet… Consider using a KinematicBody2D for the planet… Anyway, for example:

planet.rotation_degrees += somavelue

Which would change the transform of the planet. In fact, if you prefer, you could work with the transform directly:

planet_transform = planet_transform * Transform2D.IDENTITY.rotated(rad2deg(somevalue))
planet.global_transform = planet_transform

This also gives you the option to not set planet.global_transform, so we would be doing this as if it rotated. You would probably want to keep track of the rotation elsewhere in that case.

And then we can computed the updated transform for the ship:

ship_transform = planet_transform * relative_transform
ship.global_transform = ship_transform

Notice that if the planet_transform didn't change, shipl_transform would not change either. Because the planet_transform and planet_transform.affine_inverse() (part of relative_transform) cancel each other out.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .