جعل فيزياء تورنادو في الوحدة

في هذا البرنامج التعليمي، سنقوم بإنشاء محاكاة للإعصار داخل Unity.

Sharp Coder مشغل فديوهات

Unity الإصدار المستخدم في هذا البرنامج التعليمي: Unity 2018.3.0f2 (64 بت)

الخطوة 1: إنشاء كافة البرامج النصية اللازمة

يتطلب هذا البرنامج التعليمي نصين:

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

الخطوة 2: إنشاء إعصار

1. إنشاء جزيئات تورنادو:

  • قم بإنشاء GameObject جديد (GameObject -> إنشاء فارغ) وقم بتسميته "Tornado"
  • قم بإنشاء كائن GameObject آخر وقم بتسميته "Particles"، ثم انقله إلى داخل "Tornado" وقم بتغيير موضعه إلى (0، 0، 0)
  • قم بإضافة مكون ParticleSystem إلى "Particles" GameObject
  • في نظام الجسيمات، قم بتمكين هذه الوحدات: الانبعاث، الشكل، السرعة على مدى العمر، اللون على مدى العمر، الحجم على مدى العمر ، الدوران على مدار العمر، القوى الخارجية، العارض.

2. قم بتعيين القيم لكل وحدة نظام الجسيمات (راجع لقطات الشاشة أدناه):

الوحدة الرئيسية (الجزيئات):

وحدة الانبعاثات:

وحدة الشكل:

السرعة على مدى الحياة الوحدة:

وحدة اللون على مدى الحياة:

(لونان رماديان في كل طرف ولونين أبيضان في الجزء الداخلي)

الحجم على مدى الحياة الوحدة:

(يستخدم الحجم على مدار العمر منحنى يشبه هذا):

(الحجم ينخفض ​​قليلا ثم يرتفع)

التناوب على مدى الحياة:

وحدة القوى الخارجية:

هذه الوحدة لا تحتاج إلى أي تغييرات، فقط اترك القيم الافتراضية.

وحدة العارض:

بالنسبة لهذه الوحدة، نحتاج فقط إلى تعيين المواد التالية:

  • قم بإنشاء مادة جديدة وقم بتسميتها "tornado_material"
  • قم بتغيير التظليل الخاص به إلى "Legacy Shaders/Particles/Alpha Blended"
  • قم بتعيين المادة أدناه (أو انقر هنا):

سحابة صغيرة الملمس شفافة

  • قم بتعيين مادة tornado_material إلى وحدة العارض:

الآن يجب أن تبدو جسيمات الإعصار كما يلي:

ولكن كما ترون، فهو لا يبدو مثل الإعصار على الإطلاق، وذلك لأن لدينا مكونًا آخر لنضيفه، وهو مجال قوة نظام الجسيمات، وهذا المكون ضروري لمحاكاة الرياح الدائرية:

  • قم بإنشاء GameObject جديد وقم بتسميته "ForceField"
  • انقل "ForceField" داخل "Tornado" GameObject وقم بتغيير موضعه إلى (0, 0, 0)

  • أضف مكون حقل قوة نظام الجسيمات إلى "ForceField"
  • قم بتغيير قيم مكون Force Field إلى نفسها كما في لقطة الشاشة أدناه:

عرض المفتش الميداني لقوة نظام الجسيمات

الآن يجب أن تبدو الجزيئات بهذا الشكل، وهو أفضل بكثير:

تأثير الإعصار في Unity 3D

3. إعداد فيزياء تورنادو

  • أضف مكونات Rigidbody وSC_Tornado إلى "Tornado" GameObject

  • قم بإنشاء GameObject جديد وقم بتسميته "Trigger"
  • انقل "Trigger" داخل "Tornado" GameObject وغير موضعه إلى (0, 10, 0) وقم بتغيير مقياسه إلى (60, 10, 60)
  • قم بإضافة مكون MeshCollider إلى "Trigger" GameObject، وحدد مربعي الاختيار Convex وIsTrigger، وقم بتغيير الشبكة الخاصة بها إلى الأسطوانة الافتراضية

الإعصار جاهز الآن!

لاختباره، قم ببساطة بإنشاء مكعب وإضافة مكون Rigidbody، ثم ضعه داخل منطقة Trigger.

بمجرد الضغط على Play، يجب أن يتم سحب المكعب بواسطة Tornado:

المكعب الذي تم سحبه بواسطة الإعصار.