UnityのSpringJointコンポーネントについて詳しく知りたいです。以下の点について教えてください:
こんにちは!UnityのSpringJointコンポーネントについて、基本から応用まで解説します。
SpringJointは、物理挙動を持つ2つのRigidbodyオブジェクト間に弾性的な接続を作成するコンポーネントです。バネのように伸び縮みする動作を実現でき、ロープやゴム、サスペンション、振り子などの表現に適しています。
// C#スクリプトでSpringJointを追加する例
using UnityEngine;
public class SpringJointExample : MonoBehaviour
{
public GameObject connectedObject;
void Start()
{
// SpringJointコンポーネントを追加
SpringJoint joint = gameObject.AddComponent<SpringJoint>();
// 接続先のRigidbodyを設定
joint.connectedBody = connectedObject.GetComponent<Rigidbody>();
// 基本パラメータの設定
joint.spring = 10.0f; // バネの強さ
joint.damper = 0.2f; // 減衰値(揺れの収束速度)
joint.minDistance = 0.0f; // 最小距離
joint.maxDistance = 2.0f; // 最大距離
}
}
public class SimpleRope : MonoBehaviour
{
public GameObject ropeStart;
public GameObject ropeEnd;
public int segments = 10;
public float ropeWidth = 0.1f;
void Start()
{
CreateRope();
}
void CreateRope()
{
GameObject prevSegment = ropeStart;
for (int i = 0; i < segments; i++)
{
// ロープセグメントを作成
GameObject segment = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
segment.transform.localScale = new Vector3(ropeWidth, 0.5f, ropeWidth);
// 位置を設定
float t = (float)(i + 1) / (segments + 1);
segment.transform.position = Vector3.Lerp(ropeStart.transform.position, ropeEnd.transform.position, t);
// Rigidbodyを追加
Rigidbody rb = segment.AddComponent<Rigidbody>();
rb.mass = 0.1f;
// SpringJointで前のセグメントと接続
SpringJoint joint = segment.AddComponent<SpringJoint>();
joint.connectedBody = prevSegment.GetComponent<Rigidbody>();
joint.spring = 100.0f;
joint.damper = 5.0f;
prevSegment = segment;
}
// 最後のセグメントをropeEndに接続
SpringJoint endJoint = ropeEnd.AddComponent<SpringJoint>();
endJoint.connectedBody = prevSegment.GetComponent<Rigidbody>();
endJoint.spring = 100.0f;
endJoint.damper = 5.0f;
}
}
public class SimpleWheelSuspension : MonoBehaviour
{
public GameObject wheelObject;
public float restLength = 0.5f;
private SpringJoint suspension;
void Start()
{
// ホイールにRigidbodyがあることを確認
Rigidbody wheelRb = wheelObject.GetComponent<Rigidbody>();
if (wheelRb == null)
{
wheelRb = wheelObject.AddComponent<Rigidbody>();
}
// サスペンション用のSpringJoint作成
suspension = gameObject.AddComponent<SpringJoint>();
suspension.connectedBody = wheelRb;
suspension.anchor = Vector3.zero;
suspension.connectedAnchor = Vector3.zero;
// サスペンションパラメータ設定
suspension.spring = 100.0f; // バネの強さ
suspension.damper = 10.0f; // 振動減衰
suspension.minDistance = 0.0f; // 最小距離
suspension.maxDistance = 0.2f; // 最大距離
// 初期位置設定
wheelObject.transform.position = transform.position - new Vector3(0, restLength, 0);
}
}
public class GrapplingHook : MonoBehaviour
{
public Camera playerCamera;
public GameObject hookPrefab;
public float hookSpeed = 20.0f;
public float pullForce = 50.0f;
private GameObject activeHook;
private SpringJoint joint;
private Rigidbody playerRb;
void Start()
{
playerRb = GetComponent<Rigidbody>();
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
ShootHook();
}
if (Input.GetMouseButtonUp(0))
{
ReleaseHook();
}
}
void ShootHook()
{
RaycastHit hit;
if (Physics.Raycast(playerCamera.transform.position, playerCamera.transform.forward, out hit, 100.0f))
{
activeHook = Instantiate(hookPrefab, hit.point, Quaternion.identity);
// ターゲットに固定
if (hit.rigidbody)
{
FixedJoint fixJoint = activeHook.AddComponent<FixedJoint>();
fixJoint.connectedBody = hit.rigidbody;
}
// プレイヤーにSpringJoint追加
joint = gameObject.AddComponent<SpringJoint>();
joint.connectedBody = activeHook.GetComponent<Rigidbody>();
// SpringJointの設定
joint.spring = pullForce;
joint.damper = 7.0f;
joint.minDistance = 0.0f;
joint.maxDistance = 0.1f;
joint.autoConfigureConnectedAnchor = false;
joint.connectedAnchor = Vector3.zero;
}
}
void ReleaseHook()
{
if (activeHook)
{
Destroy(activeHook);
}
if (joint)
{
Destroy(joint);
}
}
}
Spring (バネの強さ)
Damper (減衰値)
Min Distance (最小距離)
Max Distance (最大距離)
// 減衰値(Damper)を増やす
joint.damper = joint.damper * 2.0f;
// または臨界減衰値を計算して適用
float criticalDamping = 2.0f * Mathf.Sqrt(joint.spring);
joint.damper = criticalDamping;
// 物理ステップの設定を調整
void Start()
{
// 物理演算の安定性向上
Physics.defaultSolverIterations = 20;
Physics.defaultSolverVelocityIterations = 20;
// もしくはタイムステップを小さくする
Time.fixedDeltaTime = 0.01f;
}
// Break Forceを無限に設定
joint.breakForce = Mathf.Infinity;
joint.breakTorque = Mathf.Infinity;
| Joint種類 | 主な特徴 | 使用例 | |-----------|----------|--------| | SpringJoint | 弾性的な接続、バネのように動作 | ロープ、サスペンション、ゴム | | FixedJoint | 剛体的な接続、相対位置固定 | 壊れない接続、複合オブジェクト | | HingeJoint | 回転軸に沿った動きのみ許可 | ドア、蝶番、振り子 | | CharacterJoint | 制限付き回転、人体関節向け | キャラクターの腕や脚 | | ConfigurableJoint | 高度にカスタマイズ可能な接続 | 複雑な機械、特殊な物理挙動 |
public class SoftBody : MonoBehaviour
{
public int resolution = 5;
public float radius = 1.0f;
public float springStrength = 100.0f;
public float damping = 5.0f;
private List<GameObject> nodes = new List<GameObject>();
void Start()
{
CreateSoftBody();
}
void CreateSoftBody()
{
// 頂点作成
for (int i = 0; i < resolution; i++)
{
float theta = (2 * Mathf.PI / resolution) * i;
Vector3 pos = new Vector3(Mathf.Cos(theta) * radius, 0, Mathf.Sin(theta) * radius);
GameObject node = GameObject.CreatePrimitive(PrimitiveType.Sphere);
node.transform.localScale = Vector3.one * 0.2f;
node.transform.position = transform.position + pos;
Rigidbody rb = node.AddComponent<Rigidbody>();
rb.mass = 0.1f;
nodes.Add(node);
}
// 中心点作成
GameObject center = GameObject.CreatePrimitive(PrimitiveType.Sphere);
center.transform.position = transform.position;
Rigidbody centerRb = center.AddComponent<Rigidbody>();
nodes.Add(center);
// SpringJointで接続
for (int i = 0; i < resolution; i++)
{
// 周囲の点と接続
int nextIndex = (i + 1) % resolution;
CreateSpringJoint(nodes[i], nodes[nextIndex], springStrength, damping);
// 中心と接続
CreateSpringJoint(nodes[i], nodes[resolution], springStrength, damping);
}
}
void CreateSpringJoint(GameObject a, GameObject b, float spring, float damper)
{
SpringJoint joint = a.AddComponent<SpringJoint>();
joint.connectedBody = b.GetComponent<Rigidbody>();
joint.spring = spring;
joint.damper = damper;
// 初期距離を保持
float distance = Vector3.Distance(a.transform.position, b.transform.position);
joint.minDistance = distance * 0.8f;
joint.maxDistance = distance * 1.2f;
}
}
public class DynamicSpringAdjuster : MonoBehaviour
{
public SpringJoint targetJoint;
public float minSpring = 10.0f;
public float maxSpring = 1000.0f;
public float minDamper = 0.1f;
public float maxDamper = 50.0f;
void Update()
{
// 入力に基づいてバネ定数を調整
if (Input.GetKey(KeyCode.UpArrow))
{
targetJoint.spring = Mathf.Min(targetJoint.spring * 1.01f, maxSpring);
}
if (Input.GetKey(KeyCode.DownArrow))
{
targetJoint.spring = Mathf.Max(targetJoint.spring * 0.99f, minSpring);
}
// 入力に基づいて減衰値を調整
if (Input.GetKey(KeyCode.RightArrow))
{
targetJoint.damper = Mathf.Min(targetJoint.damper * 1.01f, maxDamper);
}
if (Input.GetKey(KeyCode.LeftArrow))
{
targetJoint.damper = Mathf.Max(targetJoint.damper * 0.99f, minDamper);
}
// 現在の値を表示
Debug.Log($"Spring: {targetJoint.spring:F2}, Damper: {targetJoint.damper:F2}");
}
}
この回答は、Unity 2022.3 LTS を基準に作成されています。新しいバージョンでは仕様が変更されている可能性があるため、適宜公式ドキュメントを参照してください。