Unity'de Tornado Fiziği Yapın
Bu eğitimde Unity içinde bir Tornado simülasyonu oluşturacağız.
Unity Bu eğitimde kullanılan sürüm: Unity 2018.3.0f2 (64-bit)
Adım 1: Gerekli tüm komut dosyalarını oluşturun
Bu eğitim 2 komut dosyası gerektirir:
SC_Caught.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]);
}
}
}
}
Adım 2: Kasırga Oluşturma
1. Kasırga parçacıkları oluşturun:
- Yeni bir GameObject oluşturun (GameObject -> Boş Oluştur) ve adlandırın "Tornado"
- Başka bir GameObject oluşturun ve onu "Particles" olarak adlandırın, onu "Tornado" içine taşıyın ve konumunu (0, 0, 0) olarak değiştirin
- "Particles" GameObject'e bir ParticleSystem bileşeni ekleyin
- Parçacık Sisteminde şu modülleri etkinleştirin: Emisyon, Şekil, Ömür Boyu Hız, Ömür Boyu Renk, Ömür Boyu Boyut , Ömür Boyu Dönüş, Dış Kuvvetler, Oluşturucu.
2. Her Parçacık Sistemi modülü için değerleri atayın (Aşağıdaki Ekran Görüntülerini Kontrol Edin):
Ana (Parçacıklar) modülü:
Emisyon modülü:
Şekil modülü:
Ömür Boyu Hız modülü:
Ömür Boyu Renk modülü:
(Her uçta 2 Gri renk ve iç kısımda 2 Beyaz renk)
Ömür Boyu Boyut modülü:
(Ömür Boyu Boyut, şuna benzeyen bir eğri kullanır):
(Boyut biraz azalır, sonra artar)
Ömür Boyu Rotasyon:
Dış Kuvvetler modülü:
Bu modülün herhangi bir değişikliğe ihtiyacı yoktur, sadece varsayılan değerleri bırakın.
Oluşturucu modülü:
Bu modül için yalnızca aşağıdaki materyali atamamız gerekiyor:
- Yeni bir materyal yaratın ve onu çağırın "tornado_material"
- Gölgelendiricisini şu şekilde değiştirin: "Legacy Shaders/Particles/Alpha Blended"
- Aşağıdaki dokuyu ona atayın (veya buraya tıklayın):
- tornado_material'i bir Renderer modülüne atayın:
Şimdi Tornado parçacıkları şöyle görünmeli:
Ancak gördüğünüz gibi hiç de Tornado'ya benzemiyor, çünkü ekleyeceğimiz bir bileşen daha var, o da Parçacık Sistemi Kuvvet Alanı, dairesel rüzgarı simüle etmek için bu bileşene ihtiyaç var:
- Yeni bir GameObject oluşturun ve adlandırın "ForceField"
- "ForceField"'i "Tornado" GameObject'in içine taşıyın ve konumunu (0, 0, 0) olarak değiştirin
- Parçacık Sistemi Kuvvet Alanı bileşenini ekleyin "ForceField"
- Kuvvet Alanı bileşeninin değerlerini aşağıdaki ekran görüntüsündekiyle aynı olacak şekilde değiştirin:
Şimdi parçacıklar şunun gibi görünmeli ki bu çok daha iyi:
3. Tornado Fiziğinin Kurulumu
- "Tornado" GameObject'e Rigidbody ve SC_Tornado bileşenlerini ekleyin
- Yeni bir GameObject oluşturun ve adlandırın "Trigger"
- "Trigger"'ü "Tornado" GameObject'in içine taşıyın ve konumunu (0, 10, 0) olarak değiştirin ve ölçeğini (60, 10, 60) olarak değiştirin
- "Trigger" GameObject'e MeshCollider bileşenini ekleyin, Convex ve IsTrigger onay kutularını işaretleyin ve Mesh'i varsayılan Silindir olarak değiştirin
Kasırga artık hazır!
Bunu test etmek için bir Küp oluşturup bir Rigidbody bileşeni ekleyin ve ardından onu Tetikleyici alanına yerleştirin.
Oynat'a bastığınızda Küp Tornado tarafından çekilmelidir: