Unity でトルネードの物理学を作成する

このチュートリアルでは、Unity 内に竜巻シミュレーションを作成します。

Sharp Coder ビデオプレーヤー

Unity このチュートリアルで使用されるバージョン: Unity 2018.3.0f2 (64 ビット)

ステップ 1: 必要なスクリプトをすべて作成する

このチュートリアルには 2 つのスクリプトが必要です。

SC_キャッチ.cs

//This script is attached automatically to each Object caught in Tornado

using UnityEngine;

public class SC_Caught : MonoBehaviour
{
    private SC_Tornado tornadoReference;
    private SpringJoint spring;
    [HideInInspector]
    public Rigidbody rigid;

    // Use this for initialization
    void Start()
    {
        rigid = GetComponent<Rigidbody>();
    }

    // Update is called once per frame
    void Update()
    {
        //Lift spring so objects are pulled upwards
        Vector3 newPosition = spring.connectedAnchor;
        newPosition.y = transform.position.y;
        spring.connectedAnchor = newPosition;
    }

    void FixedUpdate()
    {
        //Rotate object around tornado center
        Vector3 direction = transform.position - tornadoReference.transform.position;
        //Project
        Vector3 projection = Vector3.ProjectOnPlane(direction, tornadoReference.GetRotationAxis());
        projection.Normalize();
        Vector3 normal = Quaternion.AngleAxis(130, tornadoReference.GetRotationAxis()) * projection;
        normal = Quaternion.AngleAxis(tornadoReference.lift, projection) * normal;
        rigid.AddForce(normal * tornadoReference.GetStrength(), ForceMode.Force);

        Debug.DrawRay(transform.position, normal * 10, Color.red);
    }

    //Call this when tornadoReference already exists
    public void Init(SC_Tornado tornadoRef, Rigidbody tornadoRigidbody, float springForce)
    {
        //Make sure this is enabled (for reentrance)
        enabled = true;

        //Save tornado reference
        tornadoReference = tornadoRef;

        //Initialize the spring
        spring = gameObject.AddComponent<SpringJoint>();
        spring.spring = springForce;
        spring.connectedBody = tornadoRigidbody;

        spring.autoConfigureConnectedAnchor = false;

        //Set initial position of the caught object relative to its position and the tornado
        Vector3 initialPosition = Vector3.zero;
        initialPosition.y = transform.position.y;
        spring.connectedAnchor = initialPosition;
    }

    public void Release()
    {
        enabled = false;
        Destroy(spring);
    }
}

SC_Tornado.cs

//Tornado script controls tornado physics

using System.Collections.Generic;
using UnityEngine;

public class SC_Tornado : MonoBehaviour
{
    [Tooltip("Distance after which the rotation physics starts")]
    public float maxDistance = 20;

    [Tooltip("The axis that the caught objects will rotate around")]
    public Vector3 rotationAxis = new Vector3(0, 1, 0);

    [Tooltip("Angle that is added to the object's velocity (higher lift -> quicker on top)")]
    [Range(0, 90)]
    public float lift = 45;

    [Tooltip("The force that will drive the caught objects around the tornado's center")]
    public float rotationStrength = 50;

    [Tooltip("Tornado pull force")]
    public float tornadoStrength = 2;

    Rigidbody r;

    List<SC_Caught> caughtObject = new List<SC_Caught>();

    // Start is called before the first frame update
    void Start()
    {
        //Normalize the rotation axis given by the user
        rotationAxis.Normalize();

        r = GetComponent<Rigidbody>();
        r.isKinematic = true;
    }

    void FixedUpdate()
    {
        //Apply force to caught objects
        for (int i = 0; i < caughtObject.Count; i++)
        {
            if(caughtObject[i] != null)
            {
                Vector3 pull = transform.position - caughtObject[i].transform.position;
                if (pull.magnitude > maxDistance)
                {
                    caughtObject[i].rigid.AddForce(pull.normalized * pull.magnitude, ForceMode.Force);
                    caughtObject[i].enabled = false;
                }
                else
                {
                    caughtObject[i].enabled = true;
                }
            }
        }
    }

    void OnTriggerEnter(Collider other)
    {
        if (!other.attachedRigidbody) return;
        if (other.attachedRigidbody.isKinematic) return;

        //Add caught object to the list
        SC_Caught caught = other.GetComponent<SC_Caught>();
        if (!caught)
        {
            caught = other.gameObject.AddComponent<SC_Caught>();
        }

        caught.Init(this, r, tornadoStrength);

        if (!caughtObject.Contains(caught))
        {
            caughtObject.Add(caught);
        }
    }

    void OnTriggerExit(Collider other)
    {
        //Release caught object
        SC_Caught caught = other.GetComponent<SC_Caught>();
        if (caught)
        {
            caught.Release();

            if (caughtObject.Contains(caught))
            {
                caughtObject.Remove(caught);
            }
        }
    }

    public float GetStrength()
    {
        return rotationStrength;
    }

    //The axis the caught objects rotate around
    public Vector3 GetRotationAxis()
    {
        return rotationAxis;
    }

    //Draw tornado radius circle in Editor
    void OnDrawGizmosSelected()
    {
        Vector3[] positions = new Vector3[30];
        Vector3 centrePos = transform.position;
        for (int pointNum = 0; pointNum < positions.Length; pointNum++)
        {
            // "i" now represents the progress around the circle from 0-1
            // we multiply by 1.0 to ensure we get a fraction as a result.
            float i = (float)(pointNum * 2) / positions.Length;

            // get the angle for this step (in radians, not degrees)
            float angle = i * Mathf.PI * 2;

            // the X & Y position for this angle are calculated using Sin & Cos
            float x = Mathf.Sin(angle) * maxDistance;
            float z = Mathf.Cos(angle) * maxDistance;

            Vector3 pos = new Vector3(x, 0, z) + centrePos;
            positions[pointNum] = pos;
        }

        Gizmos.color = Color.cyan;
        for (int i = 0; i < positions.Length; i++)
        {
            if (i == positions.Length - 1)
            {
                Gizmos.DrawLine(positions[0], positions[positions.Length - 1]);
            }
            else
            {
                Gizmos.DrawLine(positions[i], positions[i + 1]);
            }
        }
    }
}

ステップ 2: トルネードの作成

1. トルネード パーティクルを作成します。

  • 新しいゲームオブジェクトを作成し (GameObject -> Create Empty)、名前を付けます。 "Tornado"
  • 別のゲームオブジェクトを作成して "Particles" という名前を付け、"Tornado" 内に移動して位置を (0, 0, 0) に変更します。
  • ParticleSystem コンポーネントを "Particles" ゲームオブジェクトに追加します
  • パーティクル システムでは、次のモジュールを有効にします: EmissionShapeVelocity over LifetimeColor over LifetimeSize over Lifetime 存続期間にわたる回転外部力レンダラー

2. 各パーティクル システム モジュールに値を割り当てます (下のスクリーンショットを確認してください)。

メイン (パーティクル) モジュール:

発光モジュール:

形状モジュール:

寿命にわたる速度モジュール:

カラーオーバーライフタイムモジュール:

(両端グレー2色、内側ホワイト2色)

ライフタイムモジュールのサイズ:

(ライフタイムにわたるサイズは次のような曲線を使用します):

(サイズは少し小さくなってから大きくなります)

生涯にわたる回転:

外部力モジュール:

このモジュールは変更する必要はなく、デフォルト値のままにしておきます。

レンダラーモジュール:

このモジュールでは、次のマテリアルを割り当てるだけで済みます。

  • 新しいマテリアルを作成して呼び出す "tornado_material"
  • シェーダを次のように変更します。 "Legacy Shaders/Particles/Alpha Blended"
  • 以下のテクスチャをそれに割り当てます (または ここをクリック):

透明な小さな雲のテクスチャ

  • tornado_material を Renderer モジュールに割り当てます。

これで、Tornado パーティクルは次のようになります。

しかし、ご覧のとおり、これはまったく竜巻のようには見えません。これは、追加するコンポーネントがもう 1 つあるためです。それは パーティクル システム フォース フィールド であり、このコンポーネントは円形の風をシミュレートするために必要です。

  • 新しいゲームオブジェクトを作成し、名前を付けます "ForceField"
  • "ForceField" を "Tornado" ゲームオブジェクト内に移動し、その位置を (0, 0, 0) に変更します。

  • パーティクル システム フォース フィールド コンポーネントを "ForceField"
  • Force Field コンポーネントの値を以下のスクリーンショットと同じに変更します。

パーティクル システム フォース フィールド インスペクター ビュー

これで、パーティクルは次のようになります。これははるかに良くなりました。

Unity 3D の竜巻効果

3. トルネード物理学のセットアップ

  • Rigidbody コンポーネントと SC_Tornado コンポーネントを "Tornado" GameObject に追加します

  • 新しいゲームオブジェクトを作成し、名前を付けます "Trigger"
  • "Trigger" を "Tornado" ゲームオブジェクト内に移動し、その位置を (0, 10, 0) に変更し、スケールを (60, 10, 60) に変更します。
  • MeshCollider コンポーネントを "Trigger" GameObject に追加し、Convex および IsTrigger チェックボックスをオンにして、メッシュをデフォルトのシリンダーに変更します。

竜巻の準備は完了です!

これをテストするには、Cube を作成し、Rigidbody コンポーネントを追加して、トリガー領域内に配置するだけです。

Play を押すと、Cube が Tornado によって引き込まれるはずです。

竜巻に引き込まれたキューブ。

ソース
TornadoSystem.unitypackage239.71 KB