Unity物理演算とは

Physicsは、現実世界の物理法則をシミュレートする機能です。重力、衝突、力などを自動計算します。

物理演算でできること

  • 🎾 重力による落下
  • 💥 リアルな衝突反応
  • 🚗 車や物体の動き
  • ⚽ ボールの跳ね返り
  • 🎯 弾道計算

Rigidbody - 物理挙動の基本

Rigidbodyの追加

1. GameObjectを選択
2. Add Component > Physics > Rigidbody
3. Inspector で設定を調整

主要なプロパティ

  • Mass(質量): 物体の重さ(デフォルト: 1)
  • Drag(空気抵抗): 移動の抵抗(0-∞)
  • Angular Drag: 回転の抵抗
  • Use Gravity: 重力の有効/無効
  • Is Kinematic: 物理演算を無効化(スクリプトで制御)

Constraints(制約)

// 位置の固定
Freeze Position: X, Y, Z

// 回転の固定
Freeze Rotation: X, Y, Z

例: 2DゲームではFreeze Rotation Z以外をチェック。

力の加え方

AddForce(力を加える)

public class ForceExample : MonoBehaviour
{
    private Rigidbody rb;
    public float forcePower = 10.0f;

    void Start()
    {
        rb = GetComponent();
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // 継続的な力
            rb.AddForce(Vector3.up * forcePower, ForceMode.Force);
            
            // 瞬間的な力(ジャンプなど)
            rb.AddForce(Vector3.up * forcePower, ForceMode.Impulse);
        }
    }
}

ForceModeの種類

  • Force: 継続的な力(質量を考慮)
  • Impulse: 瞬間的な力(ジャンプ、爆発)
  • Acceleration: 継続的な加速度(質量無視)
  • VelocityChange: 瞬間的な速度変化(質量無視)

速度の直接設定

void FixedUpdate()
{
    // 速度を直接設定
    rb.velocity = new Vector3(5, 0, 0);
    
    // Y軸速度はそのまま、XZ軸のみ設定
    rb.velocity = new Vector3(5, rb.velocity.y, 0);
    
    // 角速度
    rb.angularVelocity = new Vector3(0, 2, 0);
}

Collider - 当たり判定

主要なColliderの種類

  • Box Collider: 箱型(壁、床、立方体)
  • Sphere Collider: 球型(ボール、爆弾)
  • Capsule Collider: カプセル型(キャラクター)
  • Mesh Collider: メッシュ形状(複雑な地形)

Colliderの設定

// Inspectorで設定
Is Trigger: チェックなし = 物理衝突あり
           チェックあり = すり抜ける(イベントのみ)

Material: 物理マテリアル(摩擦、弾性)
Center: 中心位置のオフセット
Size/Radius: サイズ

Trigger vs Collision

// Trigger(すり抜ける)
void OnTriggerEnter(Collider other) { }
void OnTriggerStay(Collider other) { }
void OnTriggerExit(Collider other) { }

// Collision(物理衝突)
void OnCollisionEnter(Collision collision) { }
void OnCollisionStay(Collision collision) { }
void OnCollisionExit(Collision collision) { }

Physical Material - 物理マテリアル

作成と設定

1. Project > 右クリック > Create > Physic Material
2. 名前を付ける(例: Bouncy)
3. Colliderの Material に設定

主要なプロパティ

  • Dynamic Friction(動摩擦): 0-1(0=滑る、1=止まる)
  • Static Friction(静止摩擦): 0-1
  • Bounciness(弾性): 0-1(0=弾まない、1=完全弾性)
  • Friction Combine: 摩擦の合成方法
  • Bounce Combine: 弾性の合成方法

例: ボールの設定

Dynamic Friction: 0.3
Static Friction: 0.3
Bounciness: 0.8
Bounce Combine: Maximum

実用的な物理演算例

例1: ジャンプ

public class Jump : MonoBehaviour
{
    private Rigidbody rb;
    public float jumpForce = 5.0f;
    private bool isGrounded = true;

    void Start()
    {
        rb = GetComponent();
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space) && isGrounded)
        {
            rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
            isGrounded = false;
        }
    }

    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Ground"))
        {
            isGrounded = true;
        }
    }
}

例2: 車の移動

public class CarController : MonoBehaviour
{
    private Rigidbody rb;
    public float acceleration = 10.0f;
    public float turnSpeed = 50.0f;

    void Start()
    {
        rb = GetComponent();
        rb.centerOfMass = new Vector3(0, -0.5f, 0); // 重心を下げる
    }

    void FixedUpdate()
    {
        float moveInput = Input.GetAxis("Vertical");
        float turnInput = Input.GetAxis("Horizontal");

        // 前進
        rb.AddForce(transform.forward * moveInput * acceleration);

        // 回転
        if (Mathf.Abs(moveInput) > 0.1f)
        {
            transform.Rotate(0, turnInput * turnSpeed * Time.fixedDeltaTime, 0);
        }
    }
}

例3: 爆発エフェクト

public class Explosion : MonoBehaviour
{
    public float explosionForce = 500.0f;
    public float explosionRadius = 5.0f;

    void Explode()
    {
        Collider[] colliders = Physics.OverlapSphere(transform.position, explosionRadius);

        foreach (Collider hit in colliders)
        {
            Rigidbody rb = hit.GetComponent();

            if (rb != null)
            {
                rb.AddExplosionForce(explosionForce, transform.position, explosionRadius);
            }
        }
    }
}

例4: プレイヤーの移動(滑らか)

public class SmoothMovement : MonoBehaviour
{
    private Rigidbody rb;
    public float moveSpeed = 5.0f;
    public float maxSpeed = 10.0f;

    void Start()
    {
        rb = GetComponent();
    }

    void FixedUpdate()
    {
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");

        Vector3 movement = new Vector3(h, 0, v).normalized * moveSpeed;

        // 速度制限
        if (rb.velocity.magnitude < maxSpeed)
        {
            rb.AddForce(movement);
        }
    }
}

Raycast - レイキャスト

基本的な使い方

void Update()
{
    Ray ray = new Ray(transform.position, transform.forward);
    RaycastHit hit;

    if (Physics.Raycast(ray, out hit, 10.0f))
    {
        Debug.Log("Hit: " + hit.collider.gameObject.name);
        Debug.Log("Distance: " + hit.distance);
        Debug.Log("Point: " + hit.point);
    }
}

レイの可視化

void Update()
{
    Vector3 forward = transform.TransformDirection(Vector3.forward) * 10;
    Debug.DrawRay(transform.position, forward, Color.green);
}

地面判定

bool IsGrounded()
{
    return Physics.Raycast(transform.position, Vector3.down, 0.1f);
}

レイヤーマスクで特定オブジェクトのみ

LayerMask mask = LayerMask.GetMask("Enemy", "Obstacle");

if (Physics.Raycast(ray, out hit, 100.0f, mask))
{
    // EnemyかObstacleレイヤーにのみ反応
}

OverlapSphere - 範囲内の検出

基本的な使い方

void DetectNearbyObjects()
{
    Collider[] hitColliders = Physics.OverlapSphere(transform.position, 5.0f);

    foreach (Collider collider in hitColliders)
    {
        Debug.Log("Nearby: " + collider.gameObject.name);
    }
}

実用例: 範囲攻撃

public class AreaAttack : MonoBehaviour
{
    public float attackRadius = 3.0f;
    public int damage = 50;

    void Attack()
    {
        Collider[] enemies = Physics.OverlapSphere(transform.position, attackRadius);

        foreach (Collider enemy in enemies)
        {
            if (enemy.CompareTag("Enemy"))
            {
                enemy.GetComponent()?.TakeDamage(damage);
            }
        }
    }

    void OnDrawGizmosSelected()
    {
        // Sceneビューで範囲を可視化
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, attackRadius);
    }
}

Joint - ジョイント

Fixed Joint(固定)

// 2つのオブジェクトを固定
FixedJoint joint = gameObject.AddComponent();
joint.connectedBody = otherRigidbody;

Hinge Joint(蝶番)

ドア、橋、振り子などに使用。

HingeJoint hinge = GetComponent();
hinge.axis = new Vector3(0, 1, 0); // Y軸回転
hinge.useLimits = true;
JointLimits limits = new JointLimits();
limits.min = 0;
limits.max = 90;
hinge.limits = limits;

Spring Joint(バネ)

SpringJoint spring = gameObject.AddComponent();
spring.connectedBody = targetRigidbody;
spring.spring = 50.0f;
spring.damper = 5.0f;

パフォーマンス最適化

1. FixedUpdateを使う

// ❌ 悪い例
void Update()
{
    rb.AddForce(force);
}

// ✅ 良い例
void FixedUpdate()
{
    rb.AddForce(force);
}

2. Mesh Colliderは慎重に

  • Mesh Collider同士の衝突は重い
  • 静的オブジェクトにのみ使用推奨
  • 可能ならBox/Sphere/Capsule Colliderを使う

3. Rigidbodyのスリープ

// 動いていないRigidbodyは自動的にスリープ
rb.sleepThreshold = 0.1f; // デフォルト: 0.005

4. レイヤーの衝突マトリクス

Edit > Project Settings > Physics

// 不要な衝突を無効化してパフォーマンス向上

トラブルシューティング

オブジェクトが貫通する

  • Continuous Collision Detection を使う
rb.collisionDetectionMode = CollisionDetectionMode.Continuous;

ジャンプが二段ジャンプになる

// OnCollisionEnterではなくRaycastで地面判定
bool IsGrounded()
{
    return Physics.Raycast(transform.position, Vector3.down, 0.1f);
}

回転がおかしい

// 不要な回転を固定
Rigidbody > Constraints > Freeze Rotation にチェック

物理演算が不安定

Edit > Project Settings > Time
Fixed Timestep: 0.02 (50fps) または 0.01667 (60fps)

まとめ

Unity物理演算を理解すれば、リアルで楽しいゲームが作れます

重要ポイント

  • Rigidbodyで物理挙動を追加
  • Colliderで当たり判定
  • AddForceで力を加える
  • FixedUpdate()で物理演算を制御
  • Raycastで精密な検出

次のステップ

  • Character Controllerの使用
  • 3D物理と2D物理の違い
  • カスタム物理マテリアル
  • 高度なジョイント活用