Unity でトルネードの物理学を作成する
このチュートリアルでは、Unity 内に竜巻シミュレーションを作成します。
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" ゲームオブジェクトに追加します
- パーティクル システムでは、次のモジュールを有効にします: Emission、Shape、Velocity over Lifetime、Color over Lifetime、Size 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 コンポーネントの値を以下のスクリーンショットと同じに変更します。
これで、パーティクルは次のようになります。これははるかに良くなりました。
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 によって引き込まれるはずです。