XWeaponTrail.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. namespace XftWeapon {
  5. public class XWeaponTrail : MonoBehaviour {
  6. public class Element {
  7. public Vector3 PointStart;
  8. public Vector3 PointEnd;
  9. public Vector3 Pos {
  10. get {
  11. return (PointStart + PointEnd) / 2f;
  12. }
  13. }
  14. public Element(Vector3 start, Vector3 end) {
  15. PointStart = start;
  16. PointEnd = end;
  17. }
  18. public Element() {
  19. }
  20. }
  21. public class ElementPool {
  22. private readonly Stack<Element> _stack = new Stack<Element>();
  23. public int CountAll { get; private set; }
  24. public int CountActive { get { return CountAll - CountInactive; } }
  25. public int CountInactive { get { return _stack.Count; } }
  26. public ElementPool(int preCount) {
  27. for (int i = 0; i < preCount; i++) {
  28. Element element = new Element();
  29. _stack.Push(element);
  30. CountAll++;
  31. }
  32. }
  33. public Element Get() {
  34. Element element;
  35. if (_stack.Count == 0) {
  36. element = new Element();
  37. CountAll++;
  38. } else {
  39. element = _stack.Pop();
  40. }
  41. return element;
  42. }
  43. public void Release(Element element) {
  44. if (_stack.Count > 0 && ReferenceEquals(_stack.Peek(), element)) {
  45. Debug.LogError("Internal error. Trying to destroy object that is already released to pool.");
  46. }
  47. _stack.Push(element);
  48. }
  49. }
  50. #region public members
  51. public static string Version = "1.4.3";
  52. public bool UseWith2D = false;
  53. public bool UseWithSRP = false;
  54. public string SortingLayerName;
  55. public int SortingOrder;
  56. public Transform PointStart;
  57. public Transform PointEnd;
  58. public int MaxFrame = 14;
  59. public int Granularity = 60;
  60. public float Fps = 60f;
  61. public Color MyColor = Color.white;
  62. public Material MyMaterial;
  63. #endregion
  64. #region protected members
  65. protected float mTrailWidth = 0f;
  66. protected Element mHeadElem = new Element();
  67. protected List<Element> mSnapshotList = new List<Element>();
  68. protected ElementPool mElemPool;
  69. protected Spline mSpline = new Spline();
  70. protected float mFadeT = 1f;
  71. protected bool mIsFading = false;
  72. protected float mFadeTime = 1f;
  73. protected float mElapsedTime = 0f;
  74. protected float mFadeElapsedime = 0f;
  75. protected GameObject mMeshObj;
  76. protected VertexPool mVertexPool;
  77. protected VertexPool.VertexSegment mVertexSegment;
  78. protected bool mInited = false;
  79. protected bool mActivated = false;
  80. #endregion
  81. #region property
  82. public float UpdateInterval {
  83. get {
  84. return 1f / Fps;
  85. }
  86. }
  87. public Vector3 CurHeadPos {
  88. get { return (PointStart.position + PointEnd.position) / 2f; }
  89. }
  90. public float TrailWidth {
  91. get {
  92. return mTrailWidth;
  93. }
  94. }
  95. protected Camera _myCamera;
  96. public Camera MyCamera {
  97. get {
  98. if (_myCamera == null) {
  99. _myCamera = FindMyCamera();
  100. }
  101. return _myCamera;
  102. }
  103. }
  104. protected Camera FindMyCamera() {
  105. int layerMask = 1 << gameObject.layer;
  106. //Camera[] cameras = GameObject.FindObjectsOfType(typeof(Camera)) as Camera[];
  107. Camera[] cameras = Camera.allCameras;
  108. for (int i = 0, imax = cameras.Length; i < imax; ++i) {
  109. Camera cam = cameras[i];
  110. if ((cam.cullingMask & layerMask) != 0) {
  111. return cam;
  112. }
  113. }
  114. Debug.LogError("can't find proper camera for layer:" + gameObject.layer);
  115. return null;
  116. }
  117. #endregion
  118. #region API
  119. //you may pre-init the trail to save some performance.
  120. public void Init() {
  121. if (mInited)
  122. return;
  123. mElemPool = new ElementPool(MaxFrame);
  124. mTrailWidth = (PointStart.position - PointEnd.position).magnitude;
  125. InitMeshObj();
  126. InitOriginalElements();
  127. InitSpline();
  128. mInited = true;
  129. }
  130. public void Activate() {
  131. if (mActivated) {
  132. return;
  133. }
  134. Init();
  135. mActivated = true;
  136. gameObject.SetActive(true);
  137. //mVertexPool.SetMeshObjectActive(true);
  138. mFadeT = 1f;
  139. mIsFading = false;
  140. mFadeTime = 1f;
  141. mFadeElapsedime = 0f;
  142. mElapsedTime = 0f;
  143. //reset all elemts to head pos.
  144. for (int i = 0; i < mSnapshotList.Count; i++) {
  145. mSnapshotList[i].PointStart = PointStart.position;
  146. mSnapshotList[i].PointEnd = PointEnd.position;
  147. mSpline.ControlPoints[i].Position = mSnapshotList[i].Pos;
  148. mSpline.ControlPoints[i].Normal = mSnapshotList[i].PointEnd - mSnapshotList[i].PointStart;
  149. }
  150. //reset vertex too.
  151. RefreshSpline();
  152. UpdateVertex();
  153. RefreshShader();
  154. }
  155. public void RefreshShader()
  156. {
  157. MyMaterial.shader = Shader.Find(MyMaterial.shader.name);
  158. }
  159. public void Deactivate() {
  160. mActivated = false;
  161. gameObject.SetActive(false);
  162. mVertexPool.SetMeshObjectActive(false);
  163. }
  164. public void StopSmoothly(float fadeTime) {
  165. mIsFading = true;
  166. mFadeTime = fadeTime;
  167. }
  168. #endregion
  169. #region unity methods
  170. void OnEnable() {
  171. Activate();
  172. if (!UseWithSRP) {
  173. Camera.onPostRender += MyPostRender;
  174. Camera.onPreRender += MyPreRender;
  175. }
  176. }
  177. void OnDisable() {
  178. // Deactivate();
  179. if (!UseWithSRP) {
  180. Camera.onPostRender -= MyPostRender;
  181. Camera.onPreRender -= MyPreRender;
  182. }
  183. }
  184. // void Update() {
  185. // if (!UseWithSRP) {
  186. // return;
  187. // }
  188. // MyPreRender(MyCamera);
  189. // }
  190. void LateUpdate() {
  191. if (!UseWithSRP) {
  192. return;
  193. }
  194. MyPreRender(MyCamera);
  195. MyPostRender(MyCamera);
  196. }
  197. public void MyPreRender(Camera cam) {
  198. if (!mInited)
  199. return;
  200. if (cam != MyCamera) {
  201. return;
  202. }
  203. UpdateHeadElem();
  204. mElapsedTime += Time.deltaTime;
  205. if (mElapsedTime > UpdateInterval) {
  206. mElapsedTime = 0f;
  207. RecordCurElem();
  208. }
  209. RefreshSpline();
  210. UpdateFade();
  211. UpdateVertex();
  212. }
  213. public void MyPostRender(Camera cam) {
  214. if (!mInited)
  215. return;
  216. if (cam != MyCamera) {
  217. return;
  218. }
  219. mVertexPool.SetMeshObjectActive(true);
  220. mVertexPool.LateUpdate();
  221. }
  222. void OnDestroy() {
  223. if (!mInited || mVertexPool == null) {
  224. return;
  225. }
  226. mVertexPool.Destroy();
  227. }
  228. void Start() {
  229. mInited = false;
  230. Init();
  231. }
  232. void OnDrawGizmos() {
  233. if (PointEnd == null || PointStart == null) {
  234. return;
  235. }
  236. float dist = (PointStart.position - PointEnd.position).magnitude;
  237. if (dist < Mathf.Epsilon)
  238. return;
  239. Gizmos.color = Color.red;
  240. Gizmos.DrawSphere(PointStart.position, dist * 0.04f);
  241. Gizmos.color = Color.blue;
  242. Gizmos.DrawSphere(PointEnd.position, dist * 0.04f);
  243. }
  244. #endregion
  245. #region local methods
  246. void InitSpline() {
  247. mSpline.Granularity = Granularity;
  248. mSpline.Clear();
  249. for (int i = 0; i < MaxFrame; i++) {
  250. mSpline.AddControlPoint(CurHeadPos, PointStart.position - PointEnd.position);
  251. }
  252. }
  253. void RefreshSpline() {
  254. for (int i = 0; i < mSnapshotList.Count; i++) {
  255. mSpline.ControlPoints[i].Position = mSnapshotList[i].Pos;
  256. mSpline.ControlPoints[i].Normal = mSnapshotList[i].PointEnd - mSnapshotList[i].PointStart;
  257. }
  258. mSpline.RefreshSpline();
  259. }
  260. void UpdateVertex() {
  261. VertexPool pool = mVertexSegment.Pool;
  262. for (int i = 0; i < Granularity; i++) {
  263. int baseIdx = mVertexSegment.VertStart + i * 3;
  264. float uvSegment = (float) i / Granularity;
  265. float fadeT = uvSegment * mFadeT;
  266. Vector2 uvCoord = Vector2.zero;
  267. Vector3 pos = mSpline.InterpolateByLen(fadeT);
  268. //Debug.DrawRay(pos, Vector3.up, Color.red);
  269. Vector3 up = mSpline.InterpolateNormalByLen(fadeT);
  270. Vector3 pos0 = pos + (up.normalized * mTrailWidth * 0.5f);
  271. Vector3 pos1 = pos - (up.normalized * mTrailWidth * 0.5f);
  272. // pos0
  273. pool.Vertices[baseIdx] = pos0;
  274. pool.Colors[baseIdx] = MyColor;
  275. uvCoord.x = 0f;
  276. uvCoord.y = uvSegment;
  277. pool.UVs[baseIdx] = uvCoord;
  278. //pos
  279. pool.Vertices[baseIdx + 1] = pos;
  280. pool.Colors[baseIdx + 1] = MyColor;
  281. uvCoord.x = 0.5f;
  282. uvCoord.y = uvSegment;
  283. pool.UVs[baseIdx + 1] = uvCoord;
  284. //pos1
  285. pool.Vertices[baseIdx + 2] = pos1;
  286. pool.Colors[baseIdx + 2] = MyColor;
  287. uvCoord.x = 1f;
  288. uvCoord.y = uvSegment;
  289. pool.UVs[baseIdx + 2] = uvCoord;
  290. }
  291. mVertexSegment.Pool.UVChanged = true;
  292. mVertexSegment.Pool.VertChanged = true;
  293. mVertexSegment.Pool.ColorChanged = true;
  294. }
  295. void UpdateIndices() {
  296. VertexPool pool = mVertexSegment.Pool;
  297. for (int i = 0; i < Granularity - 1; i++) {
  298. int baseIdx = mVertexSegment.VertStart + i * 3;
  299. int nextBaseIdx = mVertexSegment.VertStart + (i + 1) * 3;
  300. int iidx = mVertexSegment.IndexStart + i * 12;
  301. //triangle left
  302. pool.Indices[iidx + 0] = nextBaseIdx;
  303. pool.Indices[iidx + 1] = nextBaseIdx + 1;
  304. pool.Indices[iidx + 2] = baseIdx;
  305. pool.Indices[iidx + 3] = nextBaseIdx + 1;
  306. pool.Indices[iidx + 4] = baseIdx + 1;
  307. pool.Indices[iidx + 5] = baseIdx;
  308. //triangle right
  309. pool.Indices[iidx + 6] = nextBaseIdx + 1;
  310. pool.Indices[iidx + 7] = nextBaseIdx + 2;
  311. pool.Indices[iidx + 8] = baseIdx + 1;
  312. pool.Indices[iidx + 9] = nextBaseIdx + 2;
  313. pool.Indices[iidx + 10] = baseIdx + 2;
  314. pool.Indices[iidx + 11] = baseIdx + 1;
  315. }
  316. pool.IndiceChanged = true;
  317. }
  318. void UpdateHeadElem() {
  319. mSnapshotList[0].PointStart = PointStart.position;
  320. mSnapshotList[0].PointEnd = PointEnd.position;
  321. }
  322. void UpdateFade() {
  323. if (!mIsFading)
  324. return;
  325. mFadeElapsedime += Time.deltaTime;
  326. float t = mFadeElapsedime / mFadeTime;
  327. mFadeT = 1f - t;
  328. if (mFadeT < 0f) {
  329. Deactivate();
  330. }
  331. }
  332. void RecordCurElem() {
  333. //TODO: use element pool to avoid gc alloc.
  334. //Element elem = new Element(PointStart.position, PointEnd.position);
  335. Element elem = mElemPool.Get();
  336. elem.PointStart = PointStart.position;
  337. elem.PointEnd = PointEnd.position;
  338. if (mSnapshotList.Count < MaxFrame) {
  339. mSnapshotList.Insert(1, elem);
  340. } else {
  341. mElemPool.Release(mSnapshotList[mSnapshotList.Count - 1]);
  342. mSnapshotList.RemoveAt(mSnapshotList.Count - 1);
  343. mSnapshotList.Insert(1, elem);
  344. }
  345. }
  346. void InitOriginalElements() {
  347. mSnapshotList.Clear();
  348. //at least add 2 original elements
  349. mSnapshotList.Add(new Element(PointStart.position, PointEnd.position));
  350. mSnapshotList.Add(new Element(PointStart.position, PointEnd.position));
  351. }
  352. void InitMeshObj() {
  353. //init vertexpool
  354. mVertexPool = new VertexPool(MyMaterial, this);
  355. mVertexSegment = mVertexPool.GetVertices(Granularity * 3, (Granularity - 1) * 12);
  356. UpdateIndices();
  357. }
  358. #endregion
  359. }
  360. }