- Home /
[Physics] Compensating for Moving/Rotating Terrain
Hey all, and thanks for the read!
First, some background info:
I'm working on a space physics game, using a custom Character Controller based on Unity's Rigidbody System. The game takes place on a series of spherical Planets rotating in World Space, and the Player Object is a Rigidbody Sphere Collider, which is parented and unparented to the Planets as it moves and jumps. The center of the closest Planet to the Player is set as the current Gravity Source, and the Gravity Direction Vector always points toward the current Source. To check for the ground, the Player raycasts in the Gravity Direction to check for the Planet (or another collider object), and sets the Sphere's height above the Raycast Hit location. The Player is also always rotated so that their Y-Axis points away from the Gravity Direction, and their raycast is always pointing toward the planet.
Video Demo via Dropbox: PhysicsTrouble.mp4
Here's the problem:
he planets will be moving and rotating in complex ways in World Space all the time, so to keep the Player stuck to a Planet's surface, they are Parented to a Planet Object whenever they land on them. When the player jumps off the surface (Jump Force applied away from the Gravity Direction), the Player also un-parents themselves from the Planet.
However, as soon as they are un-parented, the Planet starts to spin under their feet, and if they didn't jump high enough to escape, they will land in a different location than they jumped from. I'm fine with the terrain moving a bit after significant airtime, but I still want to account for the angular momentum the Player Object would be experiencing when they jump, so that they land in more or less the same area even if they aren't Parented to the Planet.
How I'm trying to solve this:
The way I'm trying to correct for this is by checking the Player's colliderPosition each FixedUpdate. When the Player un-parents a planet, I calculate the deltaPosition Vector pointing from the lastColliderPosition toward the current colliderPosition, and apply that Vector as a Rigidbody.AddForce acting on the Player Object.
Right now, it isn't enough to compensate for movement unless it's applied as ForceType.Impulse and multiplied by 5000. Also, the deltaPosition also takes normal player movement into account, meaning it causes a running jump to fling them twice as far when moving with the planet's rotation, which isn't really desirable.
Does anyone with the Math or Unity Physics Experience have ideas for how to better simulate momentum to compensate for a Planet's movement or rotation, without strongly affecting game performance? Alternatively, are their relatively simple ways to keep the Player "attached" to an object like a Planet's surface without using Transform Parenting?
// If character is pressing Jump while grounded, enter Jumping state, and add JumpForce inverse to current gravity.
if (Input.GetButtonDown("Jump"))
{
isGrounded = false;
isJumping = true;
isParented = false;
transform.parent = null;
rigidbody.velocity += (gravityInverse * jumpForce);
jumpTime = 0f;
// Calculate deltaPosition and deltaRotation since last FixedUpdate, and apply that force.
rigidbody.AddForce( (colliderPosition - lastColliderPosition) * 5000, ForceMode.Impulse );
}
Parenting rigidbodies in this way is usually a bad idea. I guess I've seen situations where it works out, but I don't envy the potential frustrations. I see the dilemma you're facing though. Interesting problem!
A more physically accurate model might be a pain to work with because it introduces new problems, but it will eli$$anonymous$$ate others. You can use what I think of as arcade physics - a special branch of actual physics where you totally fudge realism in exchange for fun and responsiveness - but the principles remain the same.
You could find your desired gravity vector by averaging the influence of nearby bodies, and apply tangential force informed by the rotation of the nearest body to keep the player "in place" while they're on or near the surface. The influence of this force can scale with the distance from the surface using whatever curve delivers the most desirable result.
Note that this logic doesn't necessarily have to be driven by the unity physics system, which is designed for real-world, real-scale physics. You can theoretically mix and match between your own rolled physics simulation logic and Unity's physics sim for different jobs, depending on which offers the best results.
Thanks for the reply! I'm all up for fudging realism, too; the game is using idealized physics as it is, and I might try averaging Gravity Sources that way. Also, if there's a cleaner solution than Transform Parenting to keeping a physics object like the Player attached at a fixed point on a moving, rotating sphere, and which allows them to still move over the surface, etc. I would be all for it. It's the best I got right now, though.
Anyway, I'm still dealing with the issue of how to impart momentum from the Planet to the Player when they break parenting or jump. Considering using a bit of Trigonometry to get just the rotation of a cross-section of the sphere (based on the angle of elevation from the equator), but the math and implementation is giving me trouble.
The basis of what you're asking for is the player's tangential velocity, or the tangential velocity of a point on a rotating circle. I started typing out an explanation for this, but I realized it's been better stated elsewhere. Found a decent short article on getting tangential velocity. It's easy, and you already have access to all the knowns, though there may be some to-radian conversions to make: http://www.dummies.com/how-to/content/calculating-tangential-velocity-on-a-curve.html
Getting the player "attached" to the surface so they rotate with it (without parenting) can also be accomplished using the tangential velocity of the player's position relative to the planet center.
When they go airborne, they should continue having their velocity informed by the tangential velocity, but it no longer has to be the exact tangential velocity of their relative position. Ins$$anonymous$$d it can start out as slightly greater-than to achieve the "lets clear the terrain and kinda-sorta maintain our relative position" phase, then taper off to nil as they leave the planet's gravity influence, or return to 1:1 if they become grounded again.
Just a heads up, parenting any non-kinematic body to any object which moves or rotates will "fight" with the physics simulation. It may not be readily apparent depending on the scenario, but the purity of the physics simulation will be compromised unless the body is made kinematic upon attachment. You can work around this fighting - or just accept all the problems that go with it - but Unity's physics simulation will not react to these situations in a physically accurate manner.
Your answer
Follow this Question
Related Questions
Throwing a Spear 1 Answer
Unity3D: Get Velocity after collision 0 Answers
Rigidbody character controller can't walk on stairs 0 Answers
3D Hook With Swing Physics 0 Answers