Jadikan Fisika Tornado dalam Kesatuan

Dalam tutorial ini, kita akan membuat simulasi Tornado di dalam Unity.

Sharp Coder Pemutar video

Unity versi yang digunakan dalam tutorial ini: Unity 2018.3.0f2 (64-bit)

Langkah 1: Buat semua skrip yang diperlukan

Tutorial ini membutuhkan 2 skrip:

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

Langkah 2: Membuat Tornado

1. Membuat partikel Tornado:

  • Buat GameObject baru (GameObject -> Buat Kosong) dan beri nama "Tornado"
  • Buat GameObject lain dan beri nama "Particles", pindahkan ke dalam "Tornado" dan ubah posisinya menjadi (0, 0, 0)
  • Tambahkan komponen ParticleSystem ke "Particles" GameObject
  • Dalam Sistem Partikel, aktifkan modul berikut: Emission, Shape, Velocity over Lifetime, Color over Lifetime, Size over Lifetime , Rotasi Seumur Hidup, Kekuatan Eksternal, Renderer.

2. Tetapkan nilai untuk setiap modul Sistem Partikel (Periksa Tangkapan Layar di bawah):

Modul Utama (Partikel):

Modul emisi:

Modul bentuk:

Modul Kecepatan selama Seumur Hidup:

Modul Warna di atas Seumur Hidup:

(2 warna abu-abu di setiap ujung dan 2 warna putih di bagian dalam)

Ukuran melebihi modul Seumur Hidup:

(Size over Lifetime menggunakan kurva yang terlihat seperti ini):

(Ukuran sedikit turun lalu naik)

Rotasi sepanjang Seumur Hidup:

Modul Kekuatan Eksternal:

Modul ini tidak memerlukan perubahan apa pun, biarkan saja nilai defaultnya.

Modul penyaji:

Untuk modul ini kita hanya perlu menugaskan materi sebagai berikut:

  • Buat materi baru dan beri nama "tornado_material"
  • Ubah Shadernya menjadi "Legacy Shaders/Particles/Alpha Blended"
  • Tetapkan tekstur di bawah ini (atau klik di sini):

Tekstur Awan Kecil Transparan

  • Tetapkan tornado_material ke modul Renderer:

Sekarang partikel Tornado akan terlihat seperti ini:

Namun seperti yang Anda lihat, tidak terlihat seperti Tornado sama sekali, itu karena kita memiliki satu komponen lagi yang perlu ditambahkan, yaitu Medan Gaya Sistem Partikel, komponen ini diperlukan untuk mensimulasikan angin melingkar:

  • Buat GameObject baru dan beri nama "ForceField"
  • Pindahkan "ForceField" ke dalam "Tornado" GameObject dan ubah posisinya menjadi (0, 0, 0)

  • Tambahkan komponen Medan Gaya Sistem Partikel ke "ForceField"
  • Ubah nilai komponen Force Field menjadi sama seperti pada gambar di bawah:

Tampilan Inspektur Lapangan Kekuatan Sistem Partikel

Sekarang partikelnya akan terlihat seperti ini, yang jauh lebih baik:

Efek Tornado di Unity 3D

3. Menyiapkan Fisika Tornado

  • Tambahkan komponen Rigidbody dan SC_Tornado ke "Tornado" GameObject

  • Buat GameObject baru dan beri nama "Trigger"
  • Pindahkan "Trigger" ke dalam "Tornado" GameObject dan ubah posisinya menjadi (0, 10, 0) dan ubah skalanya menjadi (60, 10, 60)
  • Tambahkan komponen MeshCollider ke "Trigger" GameObject, centang Convex dan IsTrigger, dan ubah Mesh-nya menjadi Cylinder default

Tornado sekarang sudah siap!

Untuk mengujinya cukup buat sebuah Cube dan tambahkan komponen Rigidbody, lalu letakkan di dalam area Trigger.

Setelah Anda menekan Mainkan, Kubus akan ditarik oleh Tornado:

Kubus Ditarik oleh Tornado.

Sumber
📁TornadoSystem.unitypackage239.71 KB
Artikel yang Disarankan
Penerapan Fisika pada Game Buatan Unity
Bekerja dengan Komponen Rigidbody Unity
Membuat Simulasi Bendera dalam Unity
Fisika di Balik Raycasting dalam Unity
Membuat Game Balapan Berbasis Fisika di Unity
Menerapkan Grappling Hook 2D di Unity
Menerapkan Mekanika Penambangan di Game Unity