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; }
如上代碼每句都加上了註釋,咱們添加了一個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; } }
這些藍線肯定了火焰沿線步局,可是如今最中心會比較密集,咱們須要有選擇的是否生成,以下圖: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; }
這樣就能簡單在每層生成合適的個數,其中最上面有個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; } }
直接放到一個物體上就能夠了,其中radius設爲相應的火焰半徑大小就可,若是要密一些,下降一些就行,相應offset參數用來設定水平與垂直上的偏移,免的給人太規律了,代碼上都有詳細註釋,能夠本身改動。