Unity 물리 조인트 완전 가이드
Unity의 물리 조인트 시스템은 두 Rigidbody를 물리적으로 연결해 현실적인 운동을 구현합니다. 문, 체인, 래그돌, 로봇 팔 등의 관절 구조에 활용됩니다. PhysX 기반의 기존 Joint와 Unity 2020.1+의 ArticulationBody 두 계열이 있습니다.
1. HingeJoint — 경첩 관절
섹션 제목: “1. HingeJoint — 경첩 관절”// 문 열기 구현[RequireComponent(typeof(Rigidbody))]public class Door : MonoBehaviour{ private HingeJoint _hinge;
void Awake() { _hinge = GetComponent<HingeJoint>();
// 회전축 설정 (로컬 Y축) _hinge.axis = Vector3.up;
// 회전 제한 설정 var limits = new JointLimits { min = 0f, max = 90f, bounciness = 0.1f, bounceMinVelocity = 0.5f }; _hinge.limits = limits; _hinge.useLimits = true;
// 스프링으로 자동 닫힘 var spring = new JointSpring { spring = 50f, damper = 10f, targetPosition = 0f }; _hinge.spring = spring; _hinge.useSpring = true; }
public void Open(float force) { var motor = new JointMotor { targetVelocity = 120f, // deg/s force = force, freeSpin = false }; _hinge.motor = motor; _hinge.useMotor = true; }}2. ConfigurableJoint — 범용 조인트
섹션 제목: “2. ConfigurableJoint — 범용 조인트”public class RobotArm : MonoBehaviour{ [SerializeField] private Rigidbody _upperArm; [SerializeField] private Rigidbody _foreArm;
private ConfigurableJoint _elbow;
void Awake() { _elbow = _foreArm.gameObject.AddComponent<ConfigurableJoint>(); _elbow.connectedBody = _upperArm;
// X축만 회전 허용 (나머지 잠금) _elbow.xMotion = ConfigurableJointMotion.Locked; _elbow.yMotion = ConfigurableJointMotion.Locked; _elbow.zMotion = ConfigurableJointMotion.Locked; _elbow.angularXMotion = ConfigurableJointMotion.Limited; _elbow.angularYMotion = ConfigurableJointMotion.Locked; _elbow.angularZMotion = ConfigurableJointMotion.Locked;
// 각도 제한 var limit = new SoftJointLimit { limit = 120f, bounciness = 0f }; _elbow.highAngularXLimit = limit; limit.limit = -10f; _elbow.lowAngularXLimit = limit;
// 드라이브 (목표 각도 추종) var drive = new JointDrive { positionSpring = 1000f, positionDamper = 100f, maximumForce = float.MaxValue }; _elbow.angularXDrive = drive; }
public void SetElbowAngle(float degrees) { _elbow.targetRotation = Quaternion.Euler(degrees, 0f, 0f); }}3. ArticulationBody — 로봇/래그돌 전용
섹션 제목: “3. ArticulationBody — 로봇/래그돌 전용”// ArticulationBody는 안정적인 관절 시뮬레이션에 최적// 기존 Joint보다 폭발/떨림 현상이 적음public class ArticulatedLeg : MonoBehaviour{ void Start() { var hip = GetComponent<ArticulationBody>(); var thigh = transform.GetChild(0) .GetComponent<ArticulationBody>();
// 관절 타입: 구형 관절 thigh.jointType = ArticulationJointType.SphericalJoint;
// 각 축 운동 설정 var drive = new ArticulationDrive { stiffness = 10000f, damping = 1000f, forceLimit = 10000f, target = 0f }; thigh.xDrive = drive; thigh.yDrive = drive; thigh.zDrive = drive;
// 회전 한계 var limit = new ArticulationReducedSpace(45f); thigh.xDrive = new ArticulationDrive { lowerLimit = -45f, upperLimit = 45f, stiffness = 10000f, damping = 1000f, forceLimit = 10000f }; }}4. 래그돌 시스템
섹션 제목: “4. 래그돌 시스템”public class RagdollController : MonoBehaviour{ private Rigidbody[] _bodies; private CharacterJoint[] _joints; private Animator _animator;
void Awake() { _bodies = GetComponentsInChildren<Rigidbody>(); _joints = GetComponentsInChildren<CharacterJoint>(); _animator = GetComponent<Animator>(); SetRagdoll(false); }
public void SetRagdoll(bool active) { _animator.enabled = !active;
foreach (var rb in _bodies) { rb.isKinematic = !active; rb.detectCollisions = active; } }
public void Die(Vector3 force, Vector3 hitPoint) { SetRagdoll(true);
// 피격 지점에 힘 적용 var hitBody = GetNearestBody(hitPoint); hitBody.AddForceAtPosition(force, hitPoint, ForceMode.Impulse); }
private Rigidbody GetNearestBody(Vector3 point) { return _bodies .OrderBy(b => Vector3.Distance(b.position, point)) .First(); }}5. 체인/로프 시뮬레이션
섹션 제목: “5. 체인/로프 시뮬레이션”public class Chain : MonoBehaviour{ [SerializeField] private GameObject _linkPrefab; [SerializeField] private int _linkCount = 10; [SerializeField] private float _linkLength = 0.3f;
void Start() { Rigidbody prev = GetComponent<Rigidbody>();
for (int i = 0; i < _linkCount; i++) { var link = Instantiate(_linkPrefab, transform.position - Vector3.up * _linkLength * (i + 1), Quaternion.identity);
var rb = link.GetComponent<Rigidbody>(); var joint = link.AddComponent<HingeJoint>(); joint.connectedBody = prev; joint.axis = Vector3.right;
// 스윙 제한 var limits = new JointLimits { min = -45f, max = 45f }; joint.limits = limits; joint.useLimits = true;
prev = rb; } }}6. 조인트 디버깅 팁
섹션 제목: “6. 조인트 디버깅 팁”// 에디터에서 조인트 시각화void OnDrawGizmos(){ if (_hinge == null) return;
Gizmos.color = Color.yellow; Gizmos.DrawLine(transform.position, transform.position + transform.TransformDirection(_hinge.axis));
// 조인트 앵커 표시 Gizmos.color = Color.red; Gizmos.DrawWireSphere( transform.TransformPoint(_hinge.anchor), 0.05f);}안정적인 관절이 필요하면 ArticulationBody, 레거시 호환이나 단순 구조는 HingeJoint/ConfigurableJoint를 사용하세요. 모든 조인트는 연결된 두 Rigidbody의 질량 비율이 10:1을 넘으면 수치적으로 불안정해지므로 Mass 값을 비슷하게 맞추고, Edit > Project Settings > Physics > Default Solver Iterations를 높여 안정성을 확보하세요.