用Unity模仿CSGO裏的火焰效果

  CSGO裏的火焰效果和真實的狀況比較像,能沿着遮擋物前進,以下是模仿效果。dom

  

  思路比較簡單,開始想的是一圈一圈發出去,可是前圈與後圈的聯繫很差作,換種思路,每一個方向發射一條線,這樣根據上一個位置的方位先向前進,若是前面有遮擋,則計算好新的位置與方向,反以前面沒有遮擋,選擇合適的位置,並從這個方向的上面向下檢測,檢測這個方向的垂直位置有沒遮擋,若是有遮擋,計算新的方向與位置,沒有,則代表延展不下去。以下圖所示.ide

  

  相關主要代碼:  this

    //根據一條線的上一個節點,肯定這個節點如何定位
    bool forward(ref Vector3 pos, ref Quaternion quat)
    {
        var forward = quat * Vector3.forward;
        var up = quat * Vector3.up;

        var nextPos = pos + forward * radius;
        RaycastHit hit;
        //前面有沒擋住
        //Debug.DrawRay(nextPos, forward, Color.red, 100);
        var hitForward = Physics.Raycast(nextPos, forward, out hit, radius * 2);
        var hitDown = false;
        //若是前面沒有擋住,檢查垂直位置
        if (!hitForward)
        {
            //height用來控制垂直擴展的高度
            var upPos = pos + 2 * forward * radius + up * height * 2;
            //Debug.DrawRay(upPos, -up, Color.green, 100);
            hitDown = Physics.Raycast(upPos, -up, out hit, height * 4);
        }
        if (hitForward || hitDown)
        {
            //新的位置
            pos = hit.point + hit.normal * prefabHeight;
            //Debug.DrawRay(pos, hit.normal, Color.blue, 100);
            quat = Quaternion.FromToRotation(up, hit.normal) * quat;
            return true;
        }
        return false;
    }
forward

  如上代碼每句都加上了註釋,咱們添加了一個radius參數,用來表示每次前進的步子大小,radius越小,則越密集,而後添加一個height參數用來表示垂直方向升降能力,能夠看下圖。spa

  肯定一條線上顯示後,咱們只要考慮沿着周圍所有擴展就行,在這咱們用level表示擴展多少層,層數越多,咱們每條線的相隔的角度也應該越小,以下效果。3d

  

  相關代碼:  code

    //分紅多條線向外擴散
    public void extend()
    {
        //分紅多條線
        count = (int)(2 * level * Mathf.PI);
        //每條偏移角度
        angle = 360.0f / count;
        var up = transform.rotation * Vector3.up;
        for (int i = 0; i < count; i++)
        {
            var curAngle = angle * i;
            var curQuat = Quaternion.AngleAxis(curAngle, up);
            var quat = curQuat * transform.rotation;
            //forward(transform.position, quat, level, i);
            StartCoroutine(forward(transform.position, quat, level, i));
        }
    }

    //每條線向前擴展,自動翻越遮擋
    public IEnumerator forward(Vector3 pos, Quaternion quat, int level, int place)
    {
        var npos = pos;
        var nquat = quat;
        int curLevel = 1;

        var prePos = npos;
        while (curLevel <= level && forward(ref npos, ref nquat))
        {
            //Debug.DrawLine(prePos, npos, Color.blue, 100f, true);
            if (bCreate(curLevel, place))
            {
                var offset = offsetPos(nquat, 1);
                var obj = Instantiate(getPrefab(curLevel), npos + offset, nquat);
                //Debug.DrawLine(prePos, npos, Color.blue, 100f, true);
                var duration = getDuration(curLevel);
                Destroy(obj, duration);
            }
            yield return new WaitForSeconds(deltaTime);
            curLevel++;
            prePos = npos;
        }
    }
extend

  這些藍線肯定了火焰沿線步局,可是如今最中心會比較密集,咱們須要有選擇的是否生成,以下圖:orm

  

  相關代碼:  blog

    //肯定當前層上的某個位置是否須要生成
    bool bCreate(int level, int place)
    {
        //當前須要顯示的個數
        var levelCount = (int)(2 * level * Mathf.PI);
        var levelLength = Mathf.RoundToInt((float)count / levelCount);
        //每層起點偏移一點位置
        var offset = place + level;
        return offset % levelLength == 0;
    }
bCreate

  這樣就能簡單在每層生成合適的個數,其中最上面有個prefabHeight,用來控制模型中心與下邊的高度,用來控制生成的模型貼着遮擋物,如圖:生命週期

   

  以下是完整代碼:  ip

using UnityEngine;
using System.Collections;

public class FireMove : MonoBehaviour
{
    public GameObject[] particlePrefabs;
    [Tooltip("半徑越小,模型越密集")]
    public float radius = 0.5f;
    [Tooltip("高度越大,模型能越過更高的遮擋物")]
    public float height = 0.5f;
    [Tooltip("層級越多,向外擴展的導數越多")]
    public int level = 10;
    [Tooltip("每層間隔生成時間")]
    public float deltaTime = 0.1f;
    [Tooltip("控制火焰顯示時間")]
    public float duration = 5.0f;
    [Tooltip("水平偏移")]
    public float offsetHeight = 0.1f;
    [Tooltip("高度偏移")]
    public float offsetPanel = 0.2f;
    [Tooltip("模型與地面的距離")]
    public float prefabHeight = 0.01f;

    private int count;
    private float angle;

#if UNITY_EDITOR       
    public void Update()
    {
        if (Input.GetKeyDown(KeyCode.K))
        {
            extend();
        }
    }
#endif

    public void attack(int level)
    {
        this.level = level;
        extend();
    }

    //分紅多條線向外擴散
    public void extend()
    {
        //分紅多條線
        count = (int)(2 * level * Mathf.PI);
        //每條偏移角度
        angle = 360.0f / count;
        var up = transform.rotation * Vector3.up;
        for (int i = 0; i < count; i++)
        {
            var curAngle = angle * i;
            var curQuat = Quaternion.AngleAxis(curAngle, up);
            var quat = curQuat * transform.rotation;
            //forward(transform.position, quat, level, i);
            StartCoroutine(forward(transform.position, quat, level, i));
        }
    }

    //每條線向前擴展,自動翻越遮擋
    public IEnumerator forward(Vector3 pos, Quaternion quat, int level, int place)
    {
        var npos = pos;
        var nquat = quat;
        int curLevel = 1;

        var prePos = npos;
        while (curLevel <= level && forward(ref npos, ref nquat))
        {
            //Debug.DrawLine(prePos, npos, Color.blue, 100f, true);
            if (bCreate(curLevel, place))
            {
                var offset = offsetPos(nquat, 1);
                var obj = Instantiate(getPrefab(curLevel), npos + offset, nquat);
                //Debug.DrawLine(prePos, npos, Color.blue, 100f, true);
                var duration = getDuration(curLevel);
                Destroy(obj, duration);
            }
            yield return new WaitForSeconds(deltaTime);
            curLevel++;
            prePos = npos;
        }
    }

    //肯定當前層上的某個位置是否須要生成
    bool bCreate(int level, int place)
    {
        //當前須要顯示的個數
        var levelCount = (int)(2 * level * Mathf.PI);
        var levelLength = Mathf.RoundToInt((float)count / levelCount);
        //每層起點偏移一點位置
        var offset = place + level;
        return offset % levelLength == 0;
    }
    public GameObject getPrefab(int curLevel)
    {
        var prfabCount = particlePrefabs.Length;
        //var t = Mathf.RoundToInt(prfabCount * (curLevel - 1) / level);
        var index = Random.Range(0, prfabCount);
        return particlePrefabs[index];
    }

    //加上偏移
    public Vector3 offsetPos(Quaternion quat, int level)
    {
        var xAxis = quat * Vector3.right * Random.Range(-offsetPanel, offsetPanel) * level;
        var zAxis = quat * Vector3.forward * Random.Range(-offsetPanel, offsetPanel) * level;
        var yAxis = quat * Vector3.up * Random.Range(-offsetHeight, offsetHeight) * level;
        return xAxis + zAxis + yAxis;
    }

    //每層的生命週期
    float getDuration(int level)
    {
        var levelDuration = duration - 2 * level * deltaTime;
        return levelDuration;
    }

    //根據一條線的上一個節點,肯定這個節點如何定位
    bool forward(ref Vector3 pos, ref Quaternion quat)
    {
        var forward = quat * Vector3.forward;
        var up = quat * Vector3.up;

        var nextPos = pos + forward * radius;
        RaycastHit hit;
        //前面有沒擋住
        //Debug.DrawRay(nextPos, forward, Color.red, 100);
        var hitForward = Physics.Raycast(nextPos, forward, out hit, radius * 2);
        var hitDown = false;
        //若是前面沒有擋住,檢查垂直位置
        if (!hitForward)
        {
            //height用來控制垂直擴展的高度
            var upPos = pos + 2 * forward * radius + up * height * 2;
            //Debug.DrawRay(upPos, -up, Color.green, 100);
            hitDown = Physics.Raycast(upPos, -up, out hit, height * 4);
        }
        if (hitForward || hitDown)
        {
            //新的位置
            pos = hit.point + hit.normal * prefabHeight;
            //Debug.DrawRay(pos, hit.normal, Color.blue, 100);
            quat = Quaternion.FromToRotation(up, hit.normal) * quat;
            return true;
        }
        return false;
    }
}
FireMove

  直接放到一個物體上就能夠了,其中radius設爲相應的火焰半徑大小就可,若是要密一些,下降一些就行,相應offset參數用來設定水平與垂直上的偏移,免的給人太規律了,代碼上都有詳細註釋,能夠本身改動。

相關文章
相關標籤/搜索