Probably bug: bullet around a planet

I have a bullet whinging around a planet by constructing a bullet parent, which is centered on the planet’s center, and has a rigidbody on it that limits the XYZ position. Seemed logical at the time… Then I added a child to this object, at a distance, the pos is 0, 0, 10, where 10 is the radius at which the bullet should orbit around the planet. On THAT child object, I place the mesh. Now I can add torque to the rigidbody bullet parent and whoosh off the bullet goes around the planet. So far so good.

Now, I add a capsule collider on the child bullet and set its layers. Now when I add the bullet (parent) to the scene and set the parent’s pos and rot, and add the torque, the bullet simply spins in place. Its XYZ positions in the window show it’s spinning around the planet in a circle, but it really just sits in space and spins. I think this is a bug?

Should I not have a rigidbody on the bullet’s parent? I figured this was an interesting way to have a bullet be deflectable as its moving around the planet, but since its parent’s XYZ is fixed, I figured (somehow), Unity’s physics would deflect it around while still in orbit?

I tried to get a fixed joint working, but couldn’t figure it out.

9418076--1319516--upload_2023-10-18_22-44-52.png

Rigidbody don’t do that by any built-in feature so how is that happening? Or do you mean you’ve set its XYZ constraints so it cannot move? I presume that’s what you mean.

So this child is nothing to do with physics, it’s just a Transform child with a mesh on it. The fact that it’s rendering at the same location as its parent can only mean its local position is (0,0,0).

I presume then this is a prefab and that when instantiating, you’re specifying the new position/rotation via the Instantitate call and not modifying the Transform; something you’re not supposed to do.

The above just seems like it’s easy to debug and narrow down. Remove the RB and just set the Transform rotation, heck, just change it in the inspector.

btw, why does everyone naturally think it’s a bug in Unity when something they don’t understand happens? :wink:

Nope, this is just how physics behave even in the real world: objects always rotate around their center of mass, not just some arbitrary point. You’re assuming the center of mass of an object is always at the transform position which is seldom the case.

So when you add the capsule collider to your bullet, unity recalculates the rigidbody’s inertia tensor (aka mass for rotations) and its center of mass, which is where its mass is concentrated. All mass being in the capsule, the correct location for the center of mass is at the capsule’s center.

Just manually set the rigidbody’s center of mass to the center of the planet after adding the capsule.

Edit: Note this won’t result in accurate bullet physics at all, since you want the center of mass of the bullet to be…well, inside the bullet at the very least. Using a spring joint to keep the bullet at a fixed distance of the planet center would be far better.

Ah yes, of course. I hadn’t considered that. I got caught up in all the parent/child, collider/no-collider stuff. :slight_smile:

Arkano22: FTW: manually set the center of mass to the center of the planet. Admittedly, what I’m trying to do is pretty weird: Make bullets fly around a planet, as if they’re in orbit, constrained to a fixed distance from the center. Unity doesn’t have a way to do this with physics, and anyhow, it’s not supposed to be realistic. If i wanted to bullet to disappear when it hit something that would be different, but I want a powerful bullet to take a chunk out of an asteroid, deflect, and eventually be able to hit something else. Thus, I need to constrain it and at the same time allow it to be a rigidbody. I think I came up with a solution using a very (very) rigid spring, that seems to work well enough. My problem with that solution is trying to keep the bullet tangential to the orbit while at the same time allowing it to be a rigidbody and deflect this way and that, and at that same time also have it ‘tethered’ to the planet center.

By “realistic” here I meant vaguely accurate collision response, which you’ll need if you want the bullet to bounce off other objects. The center of mass method only works if you want the bullet to follow a straight geodesic trajectory (that is, “straight” across the planet’s surface) and then die off once it hits anything, since the bullet can’t re-orient itself when hitting objects.

As soon as you need the bullet to ricochet, you need its center of mass to be at the center of the bullet for it to rotate around itself and point in its new direction.

How about:

  • Parent the bullet to a rigidbody at the center of the planet (just like you are doing right now) and place the bullet’s COM at the planet’s center.
  • Set the bullet’s transform rotation using its velocity vector and the vector towards the center of the planet. Something like:
bullet.transform.rotation = Quaternion.LookRotation(transform.position - previousPosition, transform.position - planet.transform.position);

This way you have a bullet that always faces its movement direction and can bounce off other objects, tethered to the planet’s center while kept tangential to the planet’s surface without the need for spring joints (which you seem to dislike ;)).

I tried getting rid of the parent and just making a lonely little bullet w/ rigidbody. No parent. Then adding code to it to apply in/out force to keep it at a fixed distance to the planet. This causes ringing. I don’t know if I should be adding an impulse force, and/or if I should be dampening it, and if I should be doing it in FixedUpdate. Plus, if this force is applied NEXT frame, should I anticipate where it’s going to be next frame, in order to calculate the force?

   void ForceAdjustOrbit()
   {
       float f = transform.position.magnitude;
       {
           float fTimeDelta = Time.deltaTime;
           // xdelta = a * fTimeDelta * fTimeDelta
           // a = xDelta / ( fTimeDelta * fTimeDelta )
           // F = m * a
           float xDelta = DistanceToPlanet - f; // if xDelta > 0, object is too far in
           float fAccel = xDelta / (fTimeDelta * fTimeDelta);
           float fForce = m_pRB.mass * fAccel;
           fForce *= 0.00001f; // something about the force calculation is way too big.
           m_pRB.AddForce(transform.position.normalized * fForce, ForceMode.Impulse);
           Debug.Log("Dist = " + xDelta + ", Adding force " + transform.position.normalized * fForce);
       }
   }

You’re just reimplementing a spring joint, but in a less efficient and more complicated way. Using a joint is a much easier/simpler way of achieving the same.

Final code…

    void ForceAdjustOrbit()
    {
        // spring equation
        // Fs = -k * fDeltaOrbitDist

        Vector3 vPriorForce = m_pRB.GetAccumulatedForce();
        // we want the only force in the planet's direction to be a corrective force.
        float fPriorForceTowardsPlanet = Vector3.Dot(transform.position, vPriorForce);
        float fDistToPlanet = transform.position.magnitude;
        float fDeltaOrbitDist = fDistToPlanet - OrbitRadius; // > 0, object is too far out
        float fForceSpring = -m_fSpringK * fDeltaOrbitDist;
        float fForceToAdd = fForceSpring - fPriorForceTowardsPlanet;
       m_pRB.AddForce(transform.position.normalized * fForceToAdd, ForceMode.Force);

        // get the velocity in the planet direction and dampen it
        float fPriorVelTowardsPlanet = Vector3.Dot(transform.position, m_pRB.velocity);
        float fVelocityChange = -m_fSpringDrag * fPriorVelTowardsPlanet;
        m_pRB.AddForce(transform.position.normalized * fVelocityChange, ForceMode.VelocityChange);
    }

Question: Is the SpringJoint implemented in hardware by the graphics card? If that’s the case, my spring equation is by comparison, lame. If it’s not, my spring equation is probably faster?

No, it’s Physx software.

The Transform position is NOT the authority here, the Rigidbody is. Use its position and rotation. The two won’t be the same when using interpolation for instance.

oh sure use the RB’s pos & rot. Good point. Gee, I thought the Physx stuff was on the hardware card all this time.
I am wondering about bullets “in orbit” and colliding with other things, but needing to stay the same radius away from the planet. If the bullets all have RB’s, can I per frame in FixedUpdate() get the RB’s velocity in the direction towards the planet, and simply cancel it out by adding a Velocity delta to the RB’s velocity? Would this ruin collisions or just make them less realistic?

Modifying the rigidbody’s velocity would just change its trajectory before collision detection is performed, it won’t affect collisions at all. Just make sure you don’t replace the velocity entirely and just add to / subtract from it.

Keep in mind that what you describe is still very much a spring. You can’t just zero out the velocity component towards the center of the planet and leave the tangential part, as this would result in the bullet slowly drifting away from it. The velocity you need to add/subtract to keep the distance to the planet fixed is:

F = m * a
F = - k * x
a = - k * x / m
v = - k * x / m * dt;

Plus some velocity based damping if you want to get rid of the springy beavior. At the end of the day all AddForce() does is change the velocity for you, you’d obtain the exact same results by changing the velocity manually.

SpringJoint is not implemented in hardware, nothing is except the lowest-level stuff. But it is implemented in the unmanaged side of things (C++) so it’s still faster than your script. The fact that the physics engine is multithreaded also skews the balance in favor of joints.

As a rule of thumb, most built-in functionality is faster than any custom C# script. Keep in mind that C# calls have to cross the barrier between managed and unmanaged code, that’s already a non-negligible cost in many cases.

I’m just paranoid of multiple “big weight” physics things. Since I haven’t seen any talk about how a spring is performed down in the native code, it scares me to use one unless I actually need spring behavior. In my case, I kind of don’t. In fact, I don’t want spring behavior at all, I just want to limit the motion onto the shell around the planet.

I had this bright idea this morning to use rigidbodies for everything, and yet per frame, cancel out any AccumulatedForce that is towards or away from the planet center, and cancel out any velocity in that direction, AND to reset the position to stay on the planet. Now which of those prior acts will violate the collision rebounds I’m trying to get? If I want collisions to happen “semi-realistically, but not perfectly” on the surface of the planet, would resetting those above things prevent this, or just prevent them in the ray towards the center of the planet?

:slight_smile:

If you’re worried about performance, profile. It takes just a few minutes to determine which solution is faster based on actual data.

In physics, limiting the motion of an object is usually done via springs of various kinds. This is because most engines apply constraints at the velocity level (that is, they’re only allowed to modify velocity in order to enforce specific conditions).

Now, modifying velocity only can lead to positional drifting (in your case, removing all velocity towards/away from the planet leaves tangential velocity only, that would cause the bullet to drift away from the planet). This is why something called Baumgarte stabilization exists: re-inject some of the positional error back into the constraint, to give an extra “oomph” to the constraint in order to compensate. Another option is to additionally constrain (project) positions after the velocity solve, this moves objects around without actually modifying their velocity so basically teleporting stuff around which isn’t physically correct, but gets the job done.

Unity uses PhysX as its default physics engine. You can find additional details in the PhysX docs: Joints — NVIDIA PhysX SDK 3.4.0 Documentation

This is basically what joints do: constrain the velocity and then project the position onto the constraint manifold to prevent drifting (if positional projection is enabled, see: Configurable Joint - Unity Manual). You’re kinda reinventing the wheel here, there’s nothing wrong with it except that it takes extra work and the result might be slower.

None of them, if done correctly. Collision detection / response takes both positions and velocities into account. As I said above, positional projection is kind of “hacky” but as long as you don’t teleport the object a long distance (passing trough other objects against which it would normally collide) you should be fine.