UIParticle.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. using LuaInterface;
  2. using System;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEngine.Profiling;
  6. using UnityEngine.UI;
  7. using ShaderPropertyType = UIExtensions.UIParticle.AnimatableProperty.ShaderPropertyType;
  8. namespace UIExtensions
  9. {
  10. [ExecuteInEditMode]
  11. public class UIParticle : MaskableGraphic
  12. {
  13. //################################
  14. // Constant or Readonly Static Members.
  15. //################################
  16. static readonly int s_IdMainTex = Shader.PropertyToID("_MainTex");
  17. static readonly List<Vector3> s_Vertices = new List<Vector3>();
  18. static readonly List<Color32> s_Colors = new List<Color32>();
  19. static readonly List<UIParticle> s_TempRelatables = new List<UIParticle>();
  20. static readonly List<UIParticle> s_ActiveParticles = new List<UIParticle>();
  21. //################################
  22. // Serialize Members.
  23. //################################
  24. [Tooltip("The ParticleSystem rendered by CanvasRenderer")]
  25. [SerializeField] ParticleSystem m_ParticleSystem;
  26. [Tooltip("The UIParticle to render trail effect")]
  27. [SerializeField] UIParticle m_TrailParticle;
  28. [HideInInspector] [SerializeField] bool m_IsTrail = false;
  29. [Tooltip("Particle effect scale")]
  30. [SerializeField] float m_Scale = 1;
  31. [Tooltip("Ignore parent scale")]
  32. [SerializeField] bool m_IgnoreParent = false;
  33. [Tooltip("Animatable material properties. AnimationでParticleSystemのマテリアルプロパティを変更する場合、有効にしてください。")]
  34. [SerializeField] AnimatableProperty[] m_AnimatableProperties = new AnimatableProperty[0];
  35. static MaterialPropertyBlock s_Mpb;
  36. [System.Serializable]
  37. public class AnimatableProperty : ISerializationCallbackReceiver
  38. {
  39. public enum ShaderPropertyType
  40. {
  41. Color,
  42. Vector,
  43. Float,
  44. Range,
  45. Texture,
  46. };
  47. [SerializeField]
  48. string m_Name = "";
  49. [SerializeField]
  50. ShaderPropertyType m_Type = ShaderPropertyType.Vector;
  51. public int id { get; private set; }
  52. public ShaderPropertyType type { get { return m_Type; } }
  53. public void OnBeforeSerialize()
  54. {
  55. }
  56. public void OnAfterDeserialize()
  57. {
  58. id = Shader.PropertyToID(m_Name);
  59. }
  60. }
  61. //################################
  62. // Public/Protected Members.
  63. //################################
  64. public override Texture mainTexture
  65. {
  66. get
  67. {
  68. Texture tex = null;
  69. if (!m_IsTrail && cachedParticleSystem)
  70. {
  71. Profiler.BeginSample("Check TextureSheetAnimation module");
  72. var textureSheet = cachedParticleSystem.textureSheetAnimation;
  73. if (textureSheet.enabled && textureSheet.mode == ParticleSystemAnimationMode.Sprites && 0 < textureSheet.spriteCount)
  74. {
  75. tex = textureSheet.GetSprite(0).texture;
  76. }
  77. Profiler.EndSample();
  78. }
  79. if (!tex && _renderer)
  80. {
  81. Profiler.BeginSample("Check material");
  82. var mat = material;
  83. if (mat && mat.HasProperty(s_IdMainTex))
  84. {
  85. tex = mat.mainTexture;
  86. }
  87. Profiler.EndSample();
  88. }
  89. return tex ?? s_WhiteTexture;
  90. }
  91. }
  92. public override Material material
  93. {
  94. get
  95. {
  96. return _renderer
  97. ? m_IsTrail
  98. ? _renderer.trailMaterial
  99. : _renderer.sharedMaterial
  100. : null;
  101. }
  102. set
  103. {
  104. if (!_renderer)
  105. {
  106. }
  107. else if (m_IsTrail && _renderer.trailMaterial != value)
  108. {
  109. _renderer.trailMaterial = value;
  110. SetMaterialDirty();
  111. }
  112. else if (!m_IsTrail && _renderer.sharedMaterial != value)
  113. {
  114. _renderer.sharedMaterial = value;
  115. SetMaterialDirty();
  116. }
  117. }
  118. }
  119. /// <summary>
  120. /// Particle effect scale.
  121. /// </summary>
  122. public float scale { get { return _parent ? _parent.scale : m_Scale; } set { m_Scale = value; } }
  123. /// <summary>
  124. /// Should the soft mask ignore parent soft masks?
  125. /// </summary>
  126. /// <value>If set to true the soft mask will ignore any parent soft mask settings.</value>
  127. public bool ignoreParent
  128. {
  129. get { return m_IgnoreParent; }
  130. set
  131. {
  132. if (m_IgnoreParent != value)
  133. {
  134. m_IgnoreParent = value;
  135. OnTransformParentChanged();
  136. }
  137. }
  138. }
  139. /// <summary>
  140. /// Is this the root UIParticle?
  141. /// </summary>
  142. public bool isRoot
  143. {
  144. get { return !_parent; }
  145. }
  146. /// <summary>
  147. /// Should this graphic be considered a target for raycasting?
  148. /// </summary>
  149. public override bool raycastTarget { get { return false; } set { base.raycastTarget = value; } }
  150. /// <summary>
  151. /// ParticleSystem.
  152. /// </summary>
  153. public ParticleSystem cachedParticleSystem {
  154. get { return m_ParticleSystem ? m_ParticleSystem : (m_ParticleSystem = GetComponent<ParticleSystem>()); }
  155. set { m_ParticleSystem = value; }
  156. }
  157. public string EffectName { get; set; }
  158. public void PlayCachedParticalSystem(bool result)
  159. {
  160. cachedParticleSystem.gameObject.SetActive(result);
  161. if (result)
  162. {
  163. cachedParticleSystem.Simulate(0.0f, true, true);
  164. cachedParticleSystem.Play(true);
  165. }
  166. }
  167. /// <summary>
  168. /// Perform material modification in this function.
  169. /// </summary>
  170. /// <returns>Modified material.</returns>
  171. /// <param name="baseMaterial">Configured Material.</param>
  172. public override Material GetModifiedMaterial(Material baseMaterial)
  173. {
  174. Material mat = null;
  175. if (!_renderer)
  176. mat = baseMaterial;
  177. else if (m_AnimatableProperties.Length == 0)
  178. mat = _renderer.sharedMaterial;
  179. else
  180. mat = new Material(material);
  181. return base.GetModifiedMaterial(mat);
  182. }
  183. /// <summary>
  184. /// This function is called when the object becomes enabled and active.
  185. /// </summary>
  186. protected override void OnEnable()
  187. {
  188. // Register.
  189. if (s_ActiveParticles.Count == 0)
  190. {
  191. Canvas.willRenderCanvases += UpdateMeshes;
  192. s_Mpb = new MaterialPropertyBlock();
  193. }
  194. s_ActiveParticles.Add(this);
  195. // Reset the parent-child relation.
  196. GetComponentsInChildren<UIParticle>(false, s_TempRelatables);
  197. for (int i = s_TempRelatables.Count - 1; 0 <= i; i--)
  198. {
  199. s_TempRelatables[i].OnTransformParentChanged();
  200. }
  201. s_TempRelatables.Clear();
  202. _renderer = cachedParticleSystem ? cachedParticleSystem.GetComponent<ParticleSystemRenderer>() : null;
  203. if (_renderer && Application.isPlaying)
  204. {
  205. _renderer.enabled = false;
  206. }
  207. // Create objects.
  208. _mesh = new Mesh();
  209. _mesh.MarkDynamic();
  210. CheckTrail();
  211. if (cachedParticleSystem)
  212. {
  213. _oldPos = cachedParticleSystem.main.scalingMode == ParticleSystemScalingMode.Local
  214. ? rectTransform.localPosition
  215. : rectTransform.position;
  216. }
  217. base.OnEnable();
  218. }
  219. /// <summary>
  220. /// This function is called when the behaviour becomes disabled.
  221. /// </summary>
  222. protected override void OnDisable()
  223. {
  224. // Unregister.
  225. s_ActiveParticles.Remove(this);
  226. if (s_ActiveParticles.Count == 0)
  227. {
  228. Canvas.willRenderCanvases -= UpdateMeshes;
  229. }
  230. // Reset the parent-child relation.
  231. for (int i = _children.Count - 1; 0 <= i; i--)
  232. {
  233. _children[i].SetParent(_parent);
  234. }
  235. _children.Clear();
  236. SetParent(null);
  237. // Destroy objects.
  238. DestroyImmediate(_mesh);
  239. _mesh = null;
  240. CheckTrail();
  241. base.OnDisable();
  242. }
  243. #if UNITY_EDITOR
  244. /// <summary>
  245. /// Reset to default values.
  246. /// </summary>
  247. protected override void Reset()
  248. {
  249. // Disable ParticleSystemRenderer on reset.
  250. if (cachedParticleSystem)
  251. {
  252. cachedParticleSystem.GetComponent<ParticleSystemRenderer>().enabled = false;
  253. }
  254. base.Reset();
  255. }
  256. #endif
  257. /// <summary>
  258. /// Call to update the geometry of the Graphic onto the CanvasRenderer.
  259. /// </summary>
  260. protected override void UpdateGeometry()
  261. {
  262. }
  263. /// <summary>
  264. /// This function is called when the parent property of the transform of the GameObject has changed.
  265. /// </summary>
  266. protected override void OnTransformParentChanged()
  267. {
  268. UIParticle newParent = null;
  269. if (isActiveAndEnabled && !m_IgnoreParent)
  270. {
  271. var parentTransform = transform.parent;
  272. while (parentTransform && (!newParent || !newParent.enabled))
  273. {
  274. newParent = parentTransform.GetComponent<UIParticle>();
  275. parentTransform = parentTransform.parent;
  276. }
  277. }
  278. SetParent(newParent);
  279. base.OnTransformParentChanged();
  280. }
  281. /// <summary>
  282. /// Callback for when properties have been changed by animation.
  283. /// </summary>
  284. protected override void OnDidApplyAnimationProperties()
  285. {
  286. }
  287. #if UNITY_EDITOR
  288. /// <summary>
  289. /// This function is called when the script is loaded or a value is changed in the inspector(Called in the editor only).
  290. /// </summary>
  291. protected override void OnValidate()
  292. {
  293. OnTransformParentChanged();
  294. base.OnValidate();
  295. }
  296. #endif
  297. //################################
  298. // Private Members.
  299. //################################
  300. Mesh _mesh;
  301. protected ParticleSystemRenderer _renderer;
  302. UIParticle _parent;
  303. List<UIParticle> _children = new List<UIParticle>();
  304. Matrix4x4 scaleaMatrix = default(Matrix4x4);
  305. Vector3 _oldPos;
  306. static readonly Vector3 minimumVec3 = new Vector3(0.0000001f, 0.0000001f, 0.0000001f);
  307. static ParticleSystem.Particle[] s_Particles = new ParticleSystem.Particle[4096];
  308. /// <summary>
  309. /// Update meshes.
  310. /// </summary>
  311. static void UpdateMeshes()
  312. {
  313. for (int i = 0; i < s_ActiveParticles.Count; i++)
  314. {
  315. if (s_ActiveParticles[i])
  316. {
  317. s_ActiveParticles[i].UpdateMesh();
  318. }
  319. }
  320. }
  321. /// <summary>
  322. /// Update meshe.
  323. /// </summary>
  324. void UpdateMesh()
  325. {
  326. try
  327. {
  328. Profiler.BeginSample("CheckTrail");
  329. CheckTrail();
  330. Profiler.EndSample();
  331. if (m_ParticleSystem && canvas)
  332. {
  333. // I do not know why, but it worked fine when setting `transform.localPosition.z` to `0.01`. (#34, #39)
  334. {
  335. Vector3 pos = rectTransform.localPosition;
  336. if (Mathf.Abs(pos.z) < 0.01f)
  337. {
  338. pos.z = 0.01f;
  339. rectTransform.localPosition = pos;
  340. }
  341. }
  342. var rootCanvas = canvas.rootCanvas;
  343. Profiler.BeginSample("Disable ParticleSystemRenderer");
  344. if (Application.isPlaying)
  345. {
  346. _renderer.enabled = false;
  347. }
  348. Profiler.EndSample();
  349. // #69: Editor crashes when mesh is set to null when ParticleSystem.RenderMode=Mesh
  350. if (_renderer.renderMode == ParticleSystemRenderMode.Mesh && !_renderer.mesh)
  351. return;
  352. // #61: When ParticleSystem.RenderMode=None, an error occurs
  353. if (_renderer.renderMode == ParticleSystemRenderMode.None)
  354. return;
  355. Profiler.BeginSample("Make Matrix");
  356. ParticleSystem.MainModule main = m_ParticleSystem.main;
  357. scaleaMatrix = main.scalingMode == ParticleSystemScalingMode.Hierarchy
  358. ? Matrix4x4.Scale(scale * Vector3.one)
  359. : Matrix4x4.Scale(scale * rootCanvas.transform.localScale);
  360. Matrix4x4 matrix = default(Matrix4x4);
  361. switch (main.simulationSpace)
  362. {
  363. case ParticleSystemSimulationSpace.Local:
  364. matrix =
  365. scaleaMatrix
  366. * Matrix4x4.Rotate(rectTransform.rotation).inverse
  367. * Matrix4x4.Scale(rectTransform.lossyScale + minimumVec3).inverse;
  368. break;
  369. case ParticleSystemSimulationSpace.World:
  370. matrix =
  371. scaleaMatrix
  372. * rectTransform.worldToLocalMatrix;
  373. bool isLocalScaling = main.scalingMode == ParticleSystemScalingMode.Local;
  374. Vector3 newPos = rectTransform.position;
  375. Vector3 delta = (newPos - _oldPos);
  376. _oldPos = newPos;
  377. if (!Mathf.Approximately(scale, 0) && 0 < delta.sqrMagnitude)
  378. {
  379. if (isLocalScaling)
  380. {
  381. var s = rootCanvas.transform.localScale * scale;
  382. delta.x *= 1f - 1f / s.x;
  383. delta.y *= 1f - 1f / s.y;
  384. delta.z *= 1f - 1f / s.z;
  385. }
  386. else
  387. {
  388. delta = delta * (1 - 1 / scale);
  389. }
  390. int count = m_ParticleSystem.particleCount;
  391. if (s_Particles.Length < count)
  392. {
  393. s_Particles = new ParticleSystem.Particle[s_Particles.Length * 2];
  394. }
  395. m_ParticleSystem.GetParticles(s_Particles);
  396. for (int i = 0; i < count; i++)
  397. {
  398. var p = s_Particles[i];
  399. p.position = p.position + delta;
  400. s_Particles[i] = p;
  401. }
  402. m_ParticleSystem.SetParticles(s_Particles, count);
  403. }
  404. break;
  405. case ParticleSystemSimulationSpace.Custom:
  406. break;
  407. }
  408. Profiler.EndSample();
  409. _mesh.Clear();
  410. if (0 < m_ParticleSystem.particleCount)
  411. {
  412. Profiler.BeginSample("Bake Mesh");
  413. var cam = rootCanvas.renderMode == RenderMode.ScreenSpaceOverlay
  414. ? UIParticleOverlayCamera.GetCameraForOvrelay(rootCanvas)
  415. : canvas.worldCamera ?? Camera.main;
  416. if (!cam)
  417. {
  418. return;
  419. }
  420. if (m_IsTrail)
  421. {
  422. _renderer.BakeTrailsMesh(_mesh, cam, true);
  423. }
  424. else
  425. {
  426. _renderer.BakeMesh(_mesh, cam, true);
  427. }
  428. Profiler.EndSample();
  429. // Apply matrix.
  430. Profiler.BeginSample("Apply matrix to position");
  431. if (QualitySettings.activeColorSpace == ColorSpace.Linear)
  432. {
  433. _mesh.GetColors(s_Colors);
  434. var count_c = s_Colors.Count;
  435. for (int i = 0; i < count_c; i++)
  436. {
  437. s_Colors[i] = ((Color)s_Colors[i]).gamma;
  438. }
  439. _mesh.SetColors(s_Colors);
  440. }
  441. _mesh.GetVertices(s_Vertices);
  442. var count = s_Vertices.Count;
  443. for (int i = 0; i < count; i++)
  444. {
  445. s_Vertices[i] = matrix.MultiplyPoint3x4(s_Vertices[i]);
  446. }
  447. _mesh.SetVertices(s_Vertices);
  448. _mesh.RecalculateBounds();
  449. s_Vertices.Clear();
  450. s_Colors.Clear();
  451. Profiler.EndSample();
  452. }
  453. // Set mesh to CanvasRenderer.
  454. Profiler.BeginSample("Set mesh and texture to CanvasRenderer");
  455. canvasRenderer.SetMesh(_mesh);
  456. canvasRenderer.SetTexture(mainTexture);
  457. // Copy the value from MaterialPropertyBlock to CanvasRenderer (#41)
  458. UpdateAnimatableMaterialProperties();
  459. Profiler.EndSample();
  460. }
  461. }
  462. catch (System.Exception e)
  463. {
  464. Debug.LogException(e);
  465. }
  466. }
  467. /// <summary>
  468. /// Checks the trail.
  469. /// </summary>
  470. void CheckTrail()
  471. {
  472. if (isActiveAndEnabled && !m_IsTrail && m_ParticleSystem && m_ParticleSystem.trails.enabled)
  473. {
  474. if (!m_TrailParticle)
  475. {
  476. m_TrailParticle = new GameObject("[UIParticle] Trail").AddComponent<UIParticle>();
  477. var trans = m_TrailParticle.transform;
  478. trans.SetParent(transform);
  479. trans.localPosition = Vector3.zero;
  480. trans.localRotation = Quaternion.identity;
  481. trans.localScale = Vector3.one;
  482. m_TrailParticle._renderer = GetComponent<ParticleSystemRenderer>();
  483. m_TrailParticle.m_ParticleSystem = GetComponent<ParticleSystem>();
  484. m_TrailParticle.m_IsTrail = true;
  485. }
  486. m_TrailParticle.enabled = true;
  487. }
  488. else if (m_TrailParticle)
  489. {
  490. m_TrailParticle.enabled = false;
  491. }
  492. }
  493. /// <summary>
  494. /// Set the parent of the soft mask.
  495. /// </summary>
  496. /// <param name="newParent">The parent soft mask to use.</param>
  497. void SetParent(UIParticle newParent)
  498. {
  499. if (_parent != newParent && this != newParent)
  500. {
  501. if (_parent && _parent._children.Contains(this))
  502. {
  503. _parent._children.Remove(this);
  504. _parent._children.RemoveAll(x => x == null);
  505. }
  506. _parent = newParent;
  507. }
  508. if (_parent && !_parent._children.Contains(this))
  509. {
  510. _parent._children.Add(this);
  511. }
  512. }
  513. /// <summary>
  514. /// Copy the value from MaterialPropertyBlock to CanvasRenderer (#41)
  515. /// </summary>
  516. void UpdateAnimatableMaterialProperties()
  517. {
  518. #if UNITY_EDITOR
  519. if (!Application.isPlaying)
  520. return;
  521. #endif
  522. if (0 == m_AnimatableProperties.Length)
  523. return;
  524. _renderer.GetPropertyBlock(s_Mpb);
  525. for (int i = 0; i < canvasRenderer.materialCount; i++)
  526. {
  527. var mat = canvasRenderer.GetMaterial(i);
  528. foreach (var ap in m_AnimatableProperties)
  529. {
  530. switch (ap.type)
  531. {
  532. case ShaderPropertyType.Color:
  533. mat.SetColor(ap.id, s_Mpb.GetColor(ap.id));
  534. break;
  535. case ShaderPropertyType.Vector:
  536. mat.SetVector(ap.id, s_Mpb.GetVector(ap.id));
  537. break;
  538. case ShaderPropertyType.Float:
  539. case ShaderPropertyType.Range:
  540. mat.SetFloat(ap.id, s_Mpb.GetFloat(ap.id));
  541. break;
  542. case ShaderPropertyType.Texture:
  543. mat.SetTexture(ap.id, s_Mpb.GetTexture(ap.id));
  544. break;
  545. }
  546. }
  547. }
  548. }
  549. }
  550. }