how do I keep a gameobject with a rigidBody+boxcollider upright ,yet still affected by physics?
Here’s a simple script I wrote for constraining the position and/or rotation of an object. Add it onto your object and check all 3 constrain rotation boxes.
If you want the object to be able to rotate about the Y (vertical) axis, just constrain X and Z rotation. It will probably act a bit weird if you try to constrain just one rotation axis, so either constrain 0, 2, or all 3.
using UnityEngine;
using System.Collections;
public class SimpleConstraint : MonoBehaviour {
Vector3 startPosition;
Vector3 startRotation;
public bool constrainX;
public bool constrainY;
public bool constrainZ;
public bool constrainRotationX;
public bool constrainRotationY;
public bool constrainRotationZ;
// Use this for initialization
void Start () {
startPosition = transform.position;
startRotation = transform.eulerAngles;
}
// Update is called once per frame
void Update () {
Vector3 currentPosition = transform.position;
Vector3 currentRotation = transform.eulerAngles;
if (constrainX)
{
currentPosition = new Vector3(startPosition.x, currentPosition.y, currentPosition.z);
rigidbody.velocity = new Vector3(0, rigidbody.velocity.y, rigidbody.velocity.z);
}
if (constrainY)
{
currentPosition = new Vector3(currentPosition.x, startPosition.y, currentPosition.z);
rigidbody.velocity = new Vector3(rigidbody.velocity.x, 0, rigidbody.velocity.z);
}
if (constrainZ)
{
currentPosition = new Vector3(currentPosition.x, currentPosition.y, startPosition.z);
rigidbody.velocity = new Vector3(rigidbody.velocity.x, rigidbody.velocity.y, 0);
}
transform.position = currentPosition;
if (constrainRotationX)
{
currentRotation = new Vector3(startRotation.x, currentRotation.y, currentRotation.z);
rigidbody.angularVelocity =
new Vector3(0, rigidbody.angularVelocity.y, rigidbody.angularVelocity.z);
}
if (constrainRotationY)
{
currentRotation = new Vector3(currentRotation.x, startRotation.y, currentRotation.z);
rigidbody.angularVelocity =
new Vector3(rigidbody.angularVelocity.x, 0, rigidbody.angularVelocity.z);
}
if (constrainRotationZ)
{
currentRotation = new Vector3(currentRotation.x, currentRotation.y, startRotation.z);
rigidbody.angularVelocity =
new Vector3(rigidbody.angularVelocity.x, rigidbody.angularVelocity.y, 0);
}
transform.eulerAngles = currentRotation;
}
}
excellent thanks!
Wouldn’t it be easier to just freeze rotation on the rigid body?
I didn’t know about that until after I posted this… Yes, it would be much easier! But this script at least allows you to keep free rotation on one axis if you want.
I was going to ask if your object simply wasn’t supposed to rotate at all… or if you wanted it to be more like one of those child punching bag things that pop right back up when you hit them.
@phort
Thanks for sharing that script,it will be very useful for something i am working on.
In my case for the most part, I am allowing movement in one direction (z) and rotation in (y) but optimally would like the position constraint to be along a path (which follows x and z) vs one specific axis.
Any suggestions?
I shamelessly stole the concept of a time-based vertical attractor from the Havok physics code for vehicles.
Here’s my code straight from my game, so you’ll need to modify to make it fit your own purpose. The concept is that it finds the local up axis, compares it to the actual world up axis, and then does a very long blend between the two if they mismatch. In my game, I use it so my ground vehicles prefer remaining upright. Unrealistic, but a cheap way to make sure my wheeled vehicles are prone to landing on all four feet.
If you set the time frame rather short, then your object will always “bounce” its local Y+ axis towards the world Y+ axis.
You could easily modify to work on different axis, or multiple.
// Get our local Y+ vector (relative to world)
Vector3 localUp = transform.up ;
// Now build a rotation taking us from our current Y+ vector towards the actual world Y+
Quaternion vertical = Quaternion.FromToRotation (localUp, Vector3.up) * rigidbody.rotation ;
// How far are we tilted off the ideal Y+ world rotation?
float angleVerticalDiff = Quaternion.Angle (rigidbody.rotation, vertical) ;
if (angleVerticalDiff > 30.0f) // Greater than 30 degrees, start the vertical attractor
{
// Slerp blend based on our current rotation
rigidbody.MoveRotation (Quaternion.Slerp (rigidbody.rotation, vertical, Time.deltaTime * verticalAttractorTime)) ;
}