Gör tornadofysik i enhet
I den här handledningen kommer vi att skapa en Tornado-simulering inuti Unity.
Unity version som används i denna handledning: Unity 2018.3.0f2 (64-bitars)
Steg 1: Skapa alla nödvändiga skript
Denna handledning kräver 2 skript:
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]);
}
}
}
}
Steg 2: Skapa en tornado
1. Skapa tornado-partiklar:
- Skapa ett nytt GameObject (GameObject -> Create Empty) och namnge det "Tornado"
- Skapa ett annat GameObject och namnge det "Particles", flytta det inuti "Tornado" och ändra dess position till (0, 0, 0)
- Lägg till en ParticleSystem-komponent till "Particles" GameObject
- Aktivera dessa moduler i partikelsystemet: Emission, Form, Hastighet över livslängd, Färg över livslängd, Storlek över livstid , Rotation över livslängd, Externa krafter, Renderer.
2. Tilldela värdena för varje partikelsystemmodul (kolla skärmdumparna nedan):
Huvudmodul (partiklar):
Emissionsmodul:
Formmodul:
Hastighet över livslängd-modul:
Färg över livslängd-modul:
(2 grå färger i varje ände och 2 vita färger i den inre delen)
Modulstorlek över livslängd:
(Size over Lifetime använder en kurva som ser ut så här):
(Storleken går ner något och går sedan upp)
Rotation över livstid:
Externa Forces-modul:
Denna modul behöver inga ändringar, lämna bara standardvärdena.
Renderermodul:
För denna modul behöver vi bara tilldela följande material:
- Skapa ett nytt material och kalla det "tornado_material"
- Ändra dess Shader till "Legacy Shaders/Particles/Alpha Blended"
- Tilldela texturen nedan till den (eller klicka här):
- Tilldela tornado_material till en Renderer-modul:
Nu borde Tornado-partiklar se ut ungefär så här:
Men som du kan se ser det inte alls ut som en tornado, det beror på att vi har ytterligare en komponent att lägga till, som är Partikelsystemkraftfältet, denna komponent behövs för att simulera den cirkulära vinden:
- Skapa ett nytt GameObject och namnge det "ForceField"
- Flytta "ForceField" inuti "Tornado" GameObject och ändra dess position till (0, 0, 0)
- Lägg till partikelsystemet Force Field-komponent till "ForceField"
- Ändra värdena för Force Field-komponenten till samma som i skärmdumpen nedan:
Nu borde partiklarna se ut ungefär så här, vilket är mycket bättre:
3. Ställa in Tornado Physics
- Lägg till komponenterna Rigidbody och SC_Tornado till "Tornado" GameObject
- Skapa ett nytt GameObject och namnge det "Trigger"
- Flytta "Trigger" inuti "Tornado" GameObject och ändra dess position till (0, 10, 0) och ändra dess skala till (60, 10, 60)
- Lägg till MeshCollider-komponenten till "Trigger" GameObject, markera kryssrutorna Convex och IsTrigger och ändra dess Mesh till standardcylinder
Tromben är nu klar!
För att testa den skapar du bara en kub och lägger till en Rigidbody-komponent och placerar den sedan i triggerområdet.
När du trycker på Spela ska kuben dras in av tornadon: