Lav tornadofysik i enhed
I denne tutorial vil vi lave en Tornado-simulering inde i Unity.
Unity version brugt i denne øvelse: Unity 2018.3.0f2 (64-bit)
Trin 1: Opret alle de nødvendige scripts
Denne tutorial kræver 2 scripts:
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]);
}
}
}
}
Trin 2: Oprettelse af en tornado
1. Opret Tornado partikler:
- Opret et nyt GameObject (GameObject -> Create Empty) og navngiv det "Tornado"
- Opret et andet GameObject og navngiv det "Particles", flyt det ind i "Tornado" og skift dets position til (0, 0, 0)
- Tilføj en ParticleSystem-komponent til "Particles" GameObject
- Aktiver disse moduler i partikelsystemet: Emission, Form, Hastighed over levetid, Farve over levetid, Størrelse over levetid , Rotation over levetid, Eksterne kræfter, Gengiver.
2. Tildel værdierne for hvert partikelsystemmodul (tjek skærmbilleder nedenfor):
Hovedmodul (partikler):
Emissionsmodul:
Formmodul:
Hastighed over levetid modul:
Farve over levetid modul:
(2 grå farver i hver ende og 2 hvide farver i den indre del)
Størrelse over levetid modul:
(Størrelse over levetid bruger en kurve, der ser sådan ud):
(Størrelsen går lidt ned og går op)
Rotation over levetid:
Eksterne styrker modul:
Dette modul behøver ingen ændringer, lad blot standardværdierne stå.
Renderer modul:
Til dette modul skal vi kun tildele følgende materiale:
- Opret et nyt materiale og kald det "tornado_material"
- Skift dens Shader til "Legacy Shaders/Particles/Alpha Blended"
- Tildel teksturen nedenfor til den (eller klik her):
- Tildel tornado_materialet til et Renderer-modul:
Nu skulle Tornado-partikler se sådan ud:
Men som du kan se, ligner det slet ikke en tornado, det er fordi vi har endnu en komponent at tilføje, som er Partikel System Force Field, denne komponent er nødvendig for at simulere den cirkulære vind:
- Opret et nyt GameObject og navngiv det "ForceField"
- Flyt "ForceField" inde i "Tornado" GameObject og skift dets position til (0, 0, 0)
- Tilføj Particle System Force Field-komponent til "ForceField"
- Skift værdierne for Force Field-komponenten til de samme som på skærmbilledet nedenfor:
Nu skulle partiklerne se sådan her ud, hvilket er meget bedre:
3. Opsætning af Tornado Physics
- Tilføj Rigidbody- og SC_Tornado-komponenter til "Tornado" GameObject
- Opret et nyt GameObject og navngiv det "Trigger"
- Flyt "Trigger" inde i "Tornado" GameObject og skift dets position til (0, 10, 0) og skift dets skala til (60, 10, 60)
- Tilføj MeshCollider-komponent til "Trigger" GameObject, marker afkrydsningsfelterne Convex og IsTrigger, og skift dens Mesh til standardcylinder
Tornadoen er nu klar!
For at teste det skal du blot oprette en terning og tilføje en Rigidbody-komponent, og derefter placere den inde i Trigger-området.
Når du trykker på Spil, skal terningen trækkes ind af tornadoen: