ParticlePlexus.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. 
  2. // =================================
  3. // Namespaces.
  4. // =================================
  5. using UnityEngine;
  6. using System.Collections.Generic;
  7. using System.Threading;
  8. // =================================
  9. // Define namespace.
  10. // =================================
  11. namespace MirzaBeig
  12. {
  13. namespace Scripting
  14. {
  15. namespace Effects
  16. {
  17. // =================================
  18. // Classes.
  19. // =================================
  20. [RequireComponent(typeof(ParticleSystem))]
  21. [AddComponentMenu("Effects/Particle Plexus")]
  22. public class ParticlePlexus : MonoBehaviour
  23. {
  24. // =================================
  25. // Nested classes and structures.
  26. // =================================
  27. // ...
  28. // =================================
  29. // Variables.
  30. // =================================
  31. // ...
  32. public float maxDistance = 1.0f;
  33. public int maxConnections = 5;
  34. public int maxLineRenderers = 100;
  35. [Range(0.0f, 1.0f)]
  36. public float widthFromParticle = 0.125f;
  37. [Range(0.0f, 1.0f)]
  38. public float colourFromParticle = 1.0f;
  39. [Range(0.0f, 1.0f)]
  40. public float alphaFromParticle = 1.0f;
  41. new ParticleSystem particleSystem;
  42. ParticleSystem.Particle[] particles;
  43. Vector3[] particlePositions;
  44. Color[] particleColours;
  45. float[] particleSizes;
  46. ParticleSystem.MainModule particleSystemMainModule;
  47. public LineRenderer lineRendererTemplate;
  48. List<LineRenderer> lineRenderers = new List<LineRenderer>();
  49. Transform _transform;
  50. [Header("General Performance Settings")]
  51. [Range(0.0f, 1.0f)]
  52. public float delay = 0.0f;
  53. float timer;
  54. public bool alwaysUpdate = false;
  55. bool visible;
  56. // =================================
  57. // Functions.
  58. // =================================
  59. // ...
  60. void Start()
  61. {
  62. particleSystem = GetComponent<ParticleSystem>();
  63. particleSystemMainModule = particleSystem.main;
  64. _transform = transform;
  65. }
  66. // ...
  67. void OnDisable()
  68. {
  69. for (int i = 0; i < lineRenderers.Count; i++)
  70. {
  71. lineRenderers[i].enabled = false;
  72. }
  73. }
  74. // ...
  75. void OnBecameVisible()
  76. {
  77. visible = true;
  78. }
  79. void OnBecameInvisible()
  80. {
  81. visible = false;
  82. }
  83. // ...
  84. void LateUpdate()
  85. {
  86. int lineRenderersCount = lineRenderers.Count;
  87. // In case max line renderers value is changed at runtime -> destroy extra.
  88. if (lineRenderersCount > maxLineRenderers)
  89. {
  90. for (int i = maxLineRenderers; i < lineRenderersCount; i++)
  91. {
  92. Destroy(lineRenderers[i].gameObject);
  93. }
  94. lineRenderers.RemoveRange(maxLineRenderers, lineRenderersCount - maxLineRenderers);
  95. lineRenderersCount -= lineRenderersCount - maxLineRenderers;
  96. }
  97. if (alwaysUpdate || visible)
  98. {
  99. // Prevent constant allocations so long as max particle count doesn't change.
  100. int maxParticles = particleSystemMainModule.maxParticles;
  101. if (particles == null || particles.Length < maxParticles)
  102. {
  103. particles = new ParticleSystem.Particle[maxParticles];
  104. particlePositions = new Vector3[maxParticles];
  105. particleColours = new Color[maxParticles];
  106. particleSizes = new float[maxParticles];
  107. }
  108. timer += Time.deltaTime;
  109. if (timer >= delay)
  110. {
  111. timer = 0.0f;
  112. int lrIndex = 0;
  113. // Only update if drawing/making connections.
  114. if (maxConnections > 0 && maxLineRenderers > 0)
  115. {
  116. particleSystem.GetParticles(particles);
  117. int particleCount = particleSystem.particleCount;
  118. float maxDistanceSqr = maxDistance * maxDistance;
  119. ParticleSystemSimulationSpace simulationSpace = particleSystemMainModule.simulationSpace;
  120. ParticleSystemScalingMode scalingMode = particleSystemMainModule.scalingMode;
  121. Transform customSimulationSpaceTransform = particleSystemMainModule.customSimulationSpace;
  122. Color lineRendererStartColour = lineRendererTemplate.startColor;
  123. Color lineRendererEndColour = lineRendererTemplate.endColor;
  124. float lineRendererStartWidth = lineRendererTemplate.startWidth * lineRendererTemplate.widthMultiplier;
  125. float lineRendererEndWidth = lineRendererTemplate.endWidth * lineRendererTemplate.widthMultiplier;
  126. // Save particle properties in a quick loop (accessing these is expensive and loops significantly more later, so it's better to save them once now).
  127. for (int i = 0; i < particleCount; i++)
  128. {
  129. particlePositions[i] = particles[i].position;
  130. particleColours[i] = particles[i].GetCurrentColor(particleSystem);
  131. particleSizes[i] = particles[i].GetCurrentSize(particleSystem);
  132. }
  133. Vector3 p1p2_difference;
  134. // If in world space, there's no need to do any of the extra calculations... simplify the loop!
  135. if (simulationSpace == ParticleSystemSimulationSpace.World)
  136. {
  137. for (int i = 0; i < particleCount; i++)
  138. {
  139. if (lrIndex == maxLineRenderers)
  140. {
  141. break;
  142. }
  143. Color particleColour = particleColours[i];
  144. Color lineStartColour = Color.LerpUnclamped(lineRendererStartColour, particleColour, colourFromParticle);
  145. lineStartColour.a = Mathf.LerpUnclamped(lineRendererStartColour.a, particleColour.a, alphaFromParticle);
  146. float lineStartWidth = Mathf.LerpUnclamped(lineRendererStartWidth, particleSizes[i], widthFromParticle);
  147. int connections = 0;
  148. for (int j = i + 1; j < particleCount; j++)
  149. {
  150. p1p2_difference.x = particlePositions[i].x - particlePositions[j].x;
  151. p1p2_difference.y = particlePositions[i].y - particlePositions[j].y;
  152. p1p2_difference.z = particlePositions[i].z - particlePositions[j].z;
  153. //float distanceSqr = Vector3.SqrMagnitude(p1p2_difference);
  154. float distanceSqr =
  155. p1p2_difference.x * p1p2_difference.x +
  156. p1p2_difference.y * p1p2_difference.y +
  157. p1p2_difference.z * p1p2_difference.z;
  158. if (distanceSqr <= maxDistanceSqr)
  159. {
  160. LineRenderer lr;
  161. if (lrIndex == lineRenderersCount)
  162. {
  163. lr = Instantiate(lineRendererTemplate, _transform, false);
  164. lineRenderers.Add(lr);
  165. lineRenderersCount++;
  166. }
  167. lr = lineRenderers[lrIndex]; lr.enabled = true;
  168. lr.SetPosition(0, particlePositions[i]);
  169. lr.SetPosition(1, particlePositions[j]);
  170. lr.startColor = lineStartColour;
  171. particleColour = particleColours[j];
  172. Color lineEndColour = Color.LerpUnclamped(lineRendererEndColour, particleColour, colourFromParticle);
  173. lineEndColour.a = Mathf.LerpUnclamped(lineRendererEndColour.a, particleColour.a, alphaFromParticle);
  174. lr.endColor = lineEndColour;
  175. lr.startWidth = lineStartWidth;
  176. lr.endWidth = Mathf.LerpUnclamped(lineRendererEndWidth, particleSizes[j], widthFromParticle);
  177. lrIndex++;
  178. connections++;
  179. if (connections == maxConnections || lrIndex == maxLineRenderers)
  180. {
  181. break;
  182. }
  183. }
  184. }
  185. }
  186. }
  187. else
  188. {
  189. Vector3 position = Vector3.zero;
  190. Quaternion rotation = Quaternion.identity;
  191. Vector3 localScale = Vector3.one;
  192. Transform simulationSpaceTransform = _transform;
  193. switch (simulationSpace)
  194. {
  195. case ParticleSystemSimulationSpace.Local:
  196. {
  197. position = simulationSpaceTransform.position;
  198. rotation = simulationSpaceTransform.rotation;
  199. localScale = simulationSpaceTransform.localScale;
  200. break;
  201. }
  202. case ParticleSystemSimulationSpace.Custom:
  203. {
  204. simulationSpaceTransform = customSimulationSpaceTransform;
  205. position = simulationSpaceTransform.position;
  206. rotation = simulationSpaceTransform.rotation;
  207. localScale = simulationSpaceTransform.localScale;
  208. break;
  209. }
  210. default:
  211. {
  212. throw new System.NotSupportedException(
  213. string.Format("Unsupported scaling mode '{0}'.", simulationSpace));
  214. }
  215. }
  216. // I put these here so I can take out the default exception case.
  217. // Else I'd have a compiler error for potentially unassigned variables.
  218. Vector3 p1_position = Vector3.zero;
  219. Vector3 p2_position = Vector3.zero;
  220. for (int i = 0; i < particleCount; i++)
  221. {
  222. if (lrIndex == maxLineRenderers)
  223. {
  224. break;
  225. }
  226. switch (simulationSpace)
  227. {
  228. case ParticleSystemSimulationSpace.Local:
  229. case ParticleSystemSimulationSpace.Custom:
  230. {
  231. switch (scalingMode)
  232. {
  233. case ParticleSystemScalingMode.Hierarchy:
  234. {
  235. p1_position = simulationSpaceTransform.TransformPoint(particlePositions[i]);
  236. break;
  237. }
  238. case ParticleSystemScalingMode.Local:
  239. {
  240. // Order is important.
  241. //p1_position = Vector3.Scale(particlePositions[i], localScale);
  242. p1_position.x = particlePositions[i].x * localScale.x;
  243. p1_position.y = particlePositions[i].y * localScale.y;
  244. p1_position.z = particlePositions[i].z * localScale.z;
  245. p1_position = rotation * p1_position;
  246. //p1_position += position;
  247. p1_position.x += position.x;
  248. p1_position.y += position.y;
  249. p1_position.z += position.z;
  250. break;
  251. }
  252. case ParticleSystemScalingMode.Shape:
  253. {
  254. // Order is important.
  255. p1_position = rotation * particlePositions[i];
  256. //p1_position += position;
  257. p1_position.x += position.x;
  258. p1_position.y += position.y;
  259. p1_position.z += position.z;
  260. break;
  261. }
  262. default:
  263. {
  264. throw new System.NotSupportedException(
  265. string.Format("Unsupported scaling mode '{0}'.", scalingMode));
  266. }
  267. }
  268. break;
  269. }
  270. }
  271. Color particleColour = particleColours[i];
  272. Color lineStartColour = Color.LerpUnclamped(lineRendererStartColour, particleColour, colourFromParticle);
  273. lineStartColour.a = Mathf.LerpUnclamped(lineRendererStartColour.a, particleColour.a, alphaFromParticle);
  274. float lineStartWidth = Mathf.LerpUnclamped(lineRendererStartWidth, particleSizes[i], widthFromParticle);
  275. int connections = 0;
  276. for (int j = i + 1; j < particleCount; j++)
  277. {
  278. // Note that because particles array is not sorted by distance,
  279. // but rather by spawn time (I think), the connections made are
  280. // not necessarily the closest.
  281. switch (simulationSpace)
  282. {
  283. case ParticleSystemSimulationSpace.Local:
  284. case ParticleSystemSimulationSpace.Custom:
  285. {
  286. switch (scalingMode)
  287. {
  288. case ParticleSystemScalingMode.Hierarchy:
  289. {
  290. p2_position = simulationSpaceTransform.TransformPoint(particlePositions[j]);
  291. break;
  292. }
  293. case ParticleSystemScalingMode.Local:
  294. {
  295. // Order is important.
  296. //p2_position = Vector3.Scale(particlePositions[j], localScale);
  297. p2_position.x = particlePositions[j].x * localScale.x;
  298. p2_position.y = particlePositions[j].y * localScale.y;
  299. p2_position.z = particlePositions[j].z * localScale.z;
  300. p2_position = rotation * p2_position;
  301. //p2_position += position;
  302. p2_position.x += position.x;
  303. p2_position.y += position.y;
  304. p2_position.z += position.z;
  305. break;
  306. }
  307. case ParticleSystemScalingMode.Shape:
  308. {
  309. // Order is important.
  310. p2_position = rotation * particlePositions[j];
  311. //p2_position += position;
  312. p2_position.x += position.x;
  313. p2_position.y += position.y;
  314. p2_position.z += position.z;
  315. break;
  316. }
  317. default:
  318. {
  319. throw new System.NotSupportedException(
  320. string.Format("Unsupported scaling mode '{0}'.", scalingMode));
  321. }
  322. }
  323. break;
  324. }
  325. }
  326. p1p2_difference.x = particlePositions[i].x - particlePositions[j].x;
  327. p1p2_difference.y = particlePositions[i].y - particlePositions[j].y;
  328. p1p2_difference.z = particlePositions[i].z - particlePositions[j].z;
  329. // Note that distance is always calculated in WORLD SPACE.
  330. // Scaling the particle system will stretch the distances
  331. // and may require adjusting the maxDistance value.
  332. // I could also do it in local space (which may actually make more
  333. // sense) by just getting the difference of the positions without
  334. // all the transformations. This also provides opportunity for
  335. // optimization as I can limit the world space transform calculations
  336. // to only happen if a particle is within range.
  337. // Think about: Putting in a bool to switch between the two?
  338. //float distanceSqr = Vector3.SqrMagnitude(p1p2_difference);
  339. float distanceSqr =
  340. p1p2_difference.x * p1p2_difference.x +
  341. p1p2_difference.y * p1p2_difference.y +
  342. p1p2_difference.z * p1p2_difference.z;
  343. // If distance to particle within range, add new vertex position.
  344. // The larger the max distance, the quicker connections will
  345. // reach its max, terminating the loop earlier. So even though more lines have
  346. // to be drawn, it's still faster to have a larger maxDistance value because
  347. // the call to Vector3.Distance() is expensive.
  348. if (distanceSqr <= maxDistanceSqr)
  349. {
  350. LineRenderer lr;
  351. if (lrIndex == lineRenderersCount)
  352. {
  353. lr = Instantiate(lineRendererTemplate, _transform, false);
  354. lineRenderers.Add(lr);
  355. lineRenderersCount++;
  356. }
  357. lr = lineRenderers[lrIndex]; lr.enabled = true;
  358. lr.SetPosition(0, p1_position);
  359. lr.SetPosition(1, p2_position);
  360. lr.startColor = lineStartColour;
  361. particleColour = particleColours[j];
  362. Color lineEndColour = Color.LerpUnclamped(lineRendererEndColour, particleColour, colourFromParticle);
  363. lineEndColour.a = Mathf.LerpUnclamped(lineRendererEndColour.a, particleColour.a, alphaFromParticle);
  364. lr.endColor = lineEndColour;
  365. lr.startWidth = lineStartWidth;
  366. lr.endWidth = Mathf.LerpUnclamped(lineRendererEndWidth, particleSizes[j], widthFromParticle);
  367. lrIndex++;
  368. connections++;
  369. if (connections == maxConnections || lrIndex == maxLineRenderers)
  370. {
  371. break;
  372. }
  373. }
  374. }
  375. }
  376. }
  377. }
  378. // Disable remaining line renderers from the pool that weren't used.
  379. for (int i = lrIndex; i < lineRenderersCount; i++)
  380. {
  381. if (lineRenderers[i].enabled)
  382. {
  383. lineRenderers[i].enabled = false;
  384. }
  385. }
  386. }
  387. }
  388. }
  389. // =================================
  390. // End functions.
  391. // =================================
  392. }
  393. // =================================
  394. // End namespace.
  395. // =================================
  396. }
  397. }
  398. }
  399. // =================================
  400. // --END-- //
  401. // =================================