Faceți Tornado Physics în Unity

În acest tutorial, vom crea o simulare Tornado în Unity.

Sharp Coder Video player

Unity versiune folosită în acest tutorial: Unity 2018.3.0f2 (64 de biți)

Pasul 1: Creați toate scripturile necesare

Acest tutorial necesită 2 scripturi:

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

Pasul 2: Crearea unei tornade

1. Creați particule Tornado:

  • Creați un nou GameObject (GameObject -> Create Empty) și denumiți-l "Tornado"
  • Creați un alt GameObject și numiți-l "Particles", mutați-l în "Tornado" și schimbați-i poziția în (0, 0, 0)
  • Adăugați o componentă ParticleSystem la "Particles" GameObject
  • În Particle System, activați aceste module: Emisia, Shape, Velocity over Lifetime, Color over Lifetime, Size over Life , Rotație pe durata vieții, Forțe externe, Renderer.

2. Atribuiți valorile pentru fiecare modul de sistem de particule (verificați capturile de ecran de mai jos):

Modulul principal (particule):

Modul de emisie:

Modul de formă:

Modulul viteză pe durata vieții:

Modul Color over Lifetime:

(2 culori gri la fiecare capăt și 2 culori albe în partea interioară)

Modul Dimensiune pe durata vieții:

(Dimensiunea pe durata vieții folosește o curbă care arată astfel):

(Mărimea scade ușor, apoi crește)

Rotație de-a lungul vieții:

Modulul Forțe externe:

Acest modul nu are nevoie de modificări, doar lăsați valorile implicite.

Modul de redare:

Pentru acest modul trebuie doar să atribuim următorul material:

  • Creați un material nou și numiți-l "tornado_material"
  • Schimbați Shader-ul în "Legacy Shaders/Particles/Alpha Blended"
  • Atribuiți-i textura de mai jos (sau faceți clic aici):

Textura de nor mic Transparent

  • Atribuiți tornado_material unui modul Renderer:

Acum particulele Tornado ar trebui să arate cam așa:

Dar, după cum puteți vedea, nu arată deloc ca o Tornado, asta pentru că mai avem o componentă de adăugat, care este Particle System Force Field, această componentă este necesară pentru a simula vântul circular:

  • Creați un nou GameObject și denumiți-l "ForceField"
  • Mutați "ForceField" în interiorul "Tornado" GameObject și schimbați-i poziția în (0, 0, 0)

  • Adăugați componenta Particle System Force Field la "ForceField"
  • Schimbați valorile componentei câmp de forță la aceleași ca în captura de ecran de mai jos:

Vizualizarea inspectorului câmpului de forță al sistemului de particule

Acum particulele ar trebui să arate cam așa, ceea ce este mult mai bine:

Efect de tornadă în Unity 3D

3. Configurarea fizicii tornadelor

  • Adăugați componentele Rigidbody și SC_Tornado la "Tornado" GameObject

  • Creați un nou GameObject și denumiți-l "Trigger"
  • Mutați "Trigger" în interiorul "Tornado" GameObject și schimbați-i poziția la (0, 10, 0) și schimbați-i scala la (60, 10, 60)
  • Adăugați componenta MeshCollider la "Trigger" GameObject, bifați casetele de selectare Convex și IsTrigger și schimbați-i Mesh la Cilindru implicit

Tornada este acum gata!

Pentru a-l testa, pur și simplu creați un Cub și adăugați o componentă Rigidbody, apoi plasați-o în zona de declanșare.

Odată ce apăsați pe Play, cubul ar trebui să fie atras de Tornado:

Cubul atras de tornadă.

Sursă
📁TornadoSystem.unitypackage239.71 KB
Articole sugerate
Implementarea fizicii în jocurile create în Unity
Lucrul cu componenta Rigidbody a Unity
Adăugarea Bouncing Ball Physics în Unity
Crearea unui joc de curse bazat pe fizică în Unity
Implementarea unui grappling 2D în Unity
Crearea unei simulări de steag în Unity
Implementarea mecanicii miniere în Unity Game