GJM : 用Unity模仿CSGO里的火焰效果 [转载]

  • 感谢您的阅读。喜欢的、有用的就请大哥大嫂们高抬贵手“推荐一下”吧!你的精神支持是博主强大的写作动力以及转载收藏动力。欢迎转载!
  • 版权声明:本文原创发表于 【请点击连接前往】 ,未经作者同意必须保留此段声明!如有问题请联系我,侵立删,谢谢!
  • 我的博客:http://www.cnblogs.com/GJM6/  -  传送门:【点击前往

本文章由天天不在编写,转载请注明出处。 所有权利保留。 
文章链接:http://www.cnblogs.com/zhouxin/p/5910257.html
作者:天天不在


  CSGO里的火焰效果和真实的情况比较像,能沿着遮挡物前进,如下是模仿效果。

 


  思路比较简单,开始想的是一圈一圈发出去,但是前圈与后圈的联系不好做,换种思路,每个方向发射一条线,这样根据上一个位置的方位先向前进,如果前面有遮挡,则计算好新的位置与方向,反之前面没有遮挡,选择合适的位置,并从这个方向的上面向下检测,检测这个方向的垂直位置有没遮挡,如果有遮挡,计算新的方向与位置,没有,则表明延展不下去。如下图所示.


  相关主要代码:  

[AppleScript] 纯文本查看 复制代码
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//根据一条线的上一个节点,确定这个节点如何定位
    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;
    }


  如上代码每句都加上了注释,我们添加了一个radius参数,用来表示每次前进的步子大小,radius越小,则越密集,然后添加一个height参数用来表示垂直方向升降能力,可以看下图。
  确定一条线上显示后,我们只要考虑沿着周围全部扩展就行,在这我们用level表示扩展多少层,层数越多,我们每条线的相隔的角度也应该越小,如下效果。


 


  相关代码:  

[AppleScript] 纯文本查看 复制代码
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//分成多条线向外扩散
    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;
        }
    }


  这些蓝线确定了火焰沿线步局,但是现在最中心会比较密集,我们需要有选择的是否生成,如下图:

 


  相关代码:  

[AppleScript] 纯文本查看 复制代码
 
1
2
3
4
5
6
7
8
9
//确定当前层上的某个位置是否需要生成
    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;
    }


  这样就能简单在每层生成合适的个数,其中最上面有个prefabHeight,用来控制模型中心与下边的高度,用来控制生成的模型贴着遮挡物,如图:


 


  如下是完整代码:  

[AppleScript] 纯文本查看 复制代码
 
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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;
    }
}


  直接放到一个物体上就可以了,其中radius设为相应的火焰半径大小就可,如果要密一些,降低一些就行,相应offset参数用来设定水平与垂直上的偏移,免的给人太规律了,代码上都有详细注释,可以自己改动。

原文地址:https://www.cnblogs.com/TDou/p/6098811.html