Rigidbody constraints in local space?

The position and rotation constraints you can set for rigidbodies are in world space. Is there a way to constrain a rigidbody’s position and rotation in local space? I couldn’t find an answer with Google, just a lot of other people asking the same question.

I realize this question is old, but I recently solved this problem myself, if any other people come stumbling across this thread.

Khada’s response is correct, but in order to actually achieve this, you can use the following code (C#).

Vector3 localVelocity = transform.InverseTransformDirection(rigidbody.velocity);
localVelocity.x = 0;
localVelocity.z = 0;
		
rigidbody.velocity = transform.TransformDirection(localVelocity);

This translates the rigidbody’s velocity into local space, constrains the values you need, then translates it back. This particular code constrains x and z movement in local space.

In short, no. Rigid bodies don’t do calculations in local space and in general an object with a rigid body shouldn’t be the child of another object.

What you can do is constrain the axes that you want to behave differently and code the behavior yourself.

Just in case anyone comes across this old question, in Unity 5 rigidbody constraints are applied in local space:

I noticed that setting local position to zero was effective to constrain a rigidbody’s position along specific axis :

public class FreezeLocalAxes : MonoBehaviour {
    private Rigidbody _rigidbody;
    private float _localX = 0;
    private float _localY = 0;
    private float _localZ = 0;
    public bool _freezeAlongX = false;
    public bool _freezeAlongY = false;
    public bool _freezeAlongZ = false;
    // Use this for initialization
    void Start () {
        _rigidbody = gameObject.GetComponent<Rigidbody>();

	}

void Update () {
            _localX = transform.localPosition.x;
            _localY = transform.localPosition.y;
            _localZ = transform.localPosition.z;
            
            if (_freezeAlongX) _localX = 0;
            if (_freezeAlongY) _localY = 0;
            if (_freezeAlongZ) _localZ = 0;
            gameObject.transform.localPosition = new Vector3(_localX, _localY, _localZ);
    }
}

If you want to freeze the rotation along a specific axis, just mark them in Rigidbody component because they are already in local coordinates (in contrast with ‘freeze position’ which is in world coordinate system).

You can attach following behaviour script to your rigidbody game object to keep it always at a plane or beam cutting / pointing at a target transform:

using System;
using UnityEngine;

[RequireComponent( typeof( Rigidbody ) )]
public class CameraRail : MonoBehaviour
{
    [Flags]
    public enum LockFlags
    {
        None,
        Horizontal,
        Vertical,
        Both
    }
    
    [Range( 0, 25 )]
    public float Force = 1;
    
    public LockFlags Lock = LockFlags.Both;
    
    public Transform Target;
    
    private Rigidbody _rigidbody;
    
    private Transform _transform;
    
    public void Awake()
    {
        _rigidbody = GetComponent<Rigidbody>();
        _transform = GetComponent<Transform>();
    }
    
    public void Update()
    {
        if ( Lock == LockFlags.None || Force <= 0 ) return;
        
        bool lockX = ( Lock & LockFlags.Horizontal ) == LockFlags.Horizontal;
        bool lockY = ( Lock & LockFlags.Vertical ) == LockFlags.Vertical;
        
        Vector3 velocity = _transform.InverseTransformDirection( _rigidbody.velocity );
        if ( lockX ) velocity.x = 0;
        if ( lockY ) velocity.y = 0;
        _rigidbody.velocity = _transform.TransformDirection( velocity );
        
        Vector3 distance = Target.position - _transform.position;
        Vector3 idealPosition = Target.position - _transform.forward * distance.magnitude;
        Vector3 correction = idealPosition - _transform.position;
        
        correction = _transform.InverseTransformDirection( correction );
        if ( !lockX ) correction.x = 0;
        if ( !lockY ) correction.y = 0;
        correction.z = 0;
        correction = _transform.TransformDirection( correction );
        
        _rigidbody.velocity += correction * Force;
    }
}

Hi, I’m testing @OhNoDinos solution on Unity 2019.3.X but it doesn’t always work well. So I’ve created another solution to limit local space that may work better.

I use FixedUpdate() instead of Update() to synchronize with physics calculations.

Here is the script I used (just attach it to the gameObject to which you want to apply the local position constraint)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class localPositionConstraints : MonoBehaviour
{
    Rigidbody rb;
    public bool lockX = false;
    public bool lockY = false;
    public bool lockZ = false;
    Vector3 initialLocalTransform;
    private void Awake()
    {
        rb = GetComponent<Rigidbody>();
        initialLocalTransform = transform.localPosition;
    }

    private void FixedUpdate()
    {
        if(rb != null)
        {
            Vector3 lockedPosition = transform.localPosition;
            if (lockX)
                lockedPosition.x = initialLocalTransform.x;
            if (lockY)
                lockedPosition.y = initialLocalTransform.y;
            if (lockZ)
                lockedPosition.z = initialLocalTransform.z;
            transform.localPosition = lockedPosition;

        }
    }
}

just use a joint constraint.

Can someone explain how to do this using angular velocity?