Machen Sie Tornado-Physik in Unity

In diesem Tutorial erstellen wir eine Tornado-Simulation in Unity.

Sharp Coder Videoplayer

Unity In diesem Tutorial verwendete Version: Unity 2018.3.0f2 (64-Bit)

Schritt 1: Erstellen Sie alle erforderlichen Skripte

Für dieses Tutorial sind zwei Skripte erforderlich:

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]);
            }
        }
    }
}

Schritt 2: Einen Tornado erstellen

1. Tornado-Partikel erzeugen:

  • Erstellen Sie ein neues GameObject (GameObject -> Create Empty) und benennen Sie es "Tornado"
  • Erstellen Sie ein weiteres GameObject und nennen Sie es "Particles", verschieben Sie es innerhalb von "Tornado" und ändern Sie seine Position in (0, 0, 0).
  • Fügen Sie eine ParticleSystem-Komponente zum "Particles" GameObject hinzu
  • Aktivieren Sie im Partikelsystem diese Module: Emission, Shape, Velocity over Lifetime, Color over Lifetime, Size over Lifetime , Rotation über die Lebensdauer, Externe Kräfte, Renderer.

2. Weisen Sie die Werte für jedes Partikelsystemmodul zu (siehe Screenshots unten):

Hauptmodul (Partikel):

Emissionsmodul:

Formmodul:

Modul „Geschwindigkeit über die Lebensdauer“:

Color over Lifetime-Modul:

(2 graue Farben an jedem Ende und 2 weiße Farben im inneren Teil)

Modul „Größe über Lebensdauer“:

(Größe über Lebensdauer verwendet eine Kurve, die wie folgt aussieht):

(Die Größe nimmt leicht ab und dann zu)

Rotation über die gesamte Lebensdauer:

Modul Externe Kräfte:

Für dieses Modul sind keine Änderungen erforderlich. Behalten Sie einfach die Standardwerte bei.

Renderer-Modul:

Für dieses Modul müssen wir lediglich folgendes Material zuordnen:

  • Erstellen Sie ein neues Material und nennen Sie es "tornado_material"
  • Ändern Sie den Shader in "Legacy Shaders/Particles/Alpha Blended"
  • Weisen Sie ihm die untenstehende Textur zu (oder hier klicken):

Kleine Wolkenstruktur transparent

  • Weisen Sie tornado_material einem Renderer-Modul zu:

Nun sollten Tornado-Partikel etwa so aussehen:

Aber wie Sie sehen, sieht es überhaupt nicht wie ein Tornado aus, denn wir müssen noch eine weitere Komponente hinzufügen, nämlich das Kraftfeld des Partikelsystems. Diese Komponente wird benötigt, um den kreisförmigen Wind zu simulieren:

  • Erstellen Sie ein neues GameObject und benennen Sie es "ForceField"
  • Verschiebe "ForceField" in "Tornado" GameObject und ändere seine Position in (0, 0, 0)

  • Fügen Sie die Komponente „Partikelsystem-Kraftfeld“ hinzu "ForceField"
  • Ändern Sie die Werte der Force Field-Komponente auf die gleichen wie im Screenshot unten:

Ansicht „Partikelsystem-Kraftfeldinspektor“.

Jetzt sollten die Partikel etwa so aussehen, was viel besser ist:

Tornado-Effekt in Unity 3D

3. Einrichten der Tornadophysik

  • Fügen Sie Rigidbody- und SC_Tornado-Komponenten zu "Tornado" GameObject hinzu

  • Erstellen Sie ein neues GameObject und benennen Sie es "Trigger"
  • Verschieben Sie "Trigger" in "Tornado" GameObject und ändern Sie seine Position auf (0, 10, 0) und ändern Sie seine Skalierung auf (60, 10, 60).
  • Fügen Sie die MeshCollider-Komponente zu "Trigger" GameObject hinzu, aktivieren Sie die Kontrollkästchen Convex und IsTrigger und ändern Sie das Mesh in den Standardzylinder

Der Tornado ist jetzt fertig!

Um es zu testen, erstellen Sie einfach einen Cube, fügen Sie eine Rigidbody-Komponente hinzu und platzieren Sie sie dann im Trigger-Bereich.

Sobald Sie „Play“ drücken, sollte der Würfel vom Tornado angezogen werden:

Würfel vom Tornado eingezogen.

Quelle
📁TornadoSystem.unitypackage239.71 KB
Empfohlene Artikel
Implementierung von Physik in Spielen, die in Unity erstellt wurden
Arbeiten mit der Rigidbody-Komponente von Unity
So erkennen Sie Kollisionen mithilfe von Code in Unity
Unity So ziehen Sie einen starren Körper mit dem Mauszeiger
Hinzufügen der Hüpfballphysik in Unity
Erstellen eines physikbasierten Rennspiels in Unity
Implementierung eines 2D-Grappling-Hooks in Unity