Tanoda
UIParticleSystem.cs
Go to the documentation of this file.
1
4
6{
7#if UNITY_5_3_OR_NEWER
8 [ExecuteInEditMode]
9 [RequireComponent(typeof(CanvasRenderer), typeof(ParticleSystem))]
10 [AddComponentMenu("UI/Effects/Extensions/UIParticleSystem")]
11 public class UIParticleSystem : MaskableGraphic
12 {
13 [Tooltip("Having this enabled run the system in LateUpdate rather than in Update making it faster but less precise (more clunky)")]
14 public bool fixedTime = true;
15
16 [Tooltip("Enables 3d rotation for the particles")]
17 public bool use3dRotation = false;
18
19 private Transform _transform;
20 private ParticleSystem pSystem;
21 private ParticleSystem.Particle[] particles;
22 private UIVertex[] _quad = new UIVertex[4];
23 private Vector4 imageUV = Vector4.zero;
24 private ParticleSystem.TextureSheetAnimationModule textureSheetAnimation;
25 private int textureSheetAnimationFrames;
26 private Vector2 textureSheetAnimationFrameSize;
27 private ParticleSystemRenderer pRenderer;
28 private bool isInitialised = false;
29
30 private Material currentMaterial;
31
32 private Texture currentTexture;
33
34#if UNITY_5_5_OR_NEWER
35 private ParticleSystem.MainModule mainModule;
36#endif
37
38 public override Texture mainTexture
39 {
40 get
41 {
42 return currentTexture;
43 }
44 }
45
46 protected bool Initialize()
47 {
48 // initialize members
49 if (_transform == null)
50 {
51 _transform = transform;
52 }
53 if (pSystem == null)
54 {
55 pSystem = GetComponent<ParticleSystem>();
56
57 if (pSystem == null)
58 {
59 return false;
60 }
61
62#if UNITY_5_5_OR_NEWER
63 mainModule = pSystem.main;
64 if (pSystem.main.maxParticles > 14000)
65 {
66 mainModule.maxParticles = 14000;
67 }
68#else
69 if (pSystem.maxParticles > 14000)
70 pSystem.maxParticles = 14000;
71#endif
72
73 pRenderer = pSystem.GetComponent<ParticleSystemRenderer>();
74 if (pRenderer != null)
75 pRenderer.enabled = false;
76
77 if (material == null)
78 {
79 var foundShader = Shader.Find("UI Extensions/Particles/Additive");
80 if (foundShader)
81 {
82 material = new Material(foundShader);
83 }
84 }
85
86 currentMaterial = material;
87 if (currentMaterial && currentMaterial.HasProperty("_MainTex"))
88 {
89 currentTexture = currentMaterial.mainTexture;
90 if (currentTexture == null)
91 currentTexture = Texture2D.whiteTexture;
92 }
93 material = currentMaterial;
94 // automatically set scaling
95#if UNITY_5_5_OR_NEWER
96 mainModule.scalingMode = ParticleSystemScalingMode.Hierarchy;
97#else
98 pSystem.scalingMode = ParticleSystemScalingMode.Hierarchy;
99#endif
100
101 particles = null;
102 }
103#if UNITY_5_5_OR_NEWER
104 if (particles == null)
105 particles = new ParticleSystem.Particle[pSystem.main.maxParticles];
106#else
107 if (particles == null)
108 particles = new ParticleSystem.Particle[pSystem.maxParticles];
109#endif
110
111 imageUV = new Vector4(0, 0, 1, 1);
112
113 // prepare texture sheet animation
114 textureSheetAnimation = pSystem.textureSheetAnimation;
115 textureSheetAnimationFrames = 0;
116 textureSheetAnimationFrameSize = Vector2.zero;
117 if (textureSheetAnimation.enabled)
118 {
119 textureSheetAnimationFrames = textureSheetAnimation.numTilesX * textureSheetAnimation.numTilesY;
120 textureSheetAnimationFrameSize = new Vector2(1f / textureSheetAnimation.numTilesX, 1f / textureSheetAnimation.numTilesY);
121 }
122
123 return true;
124 }
125
126 protected override void Awake()
127 {
128 base.Awake();
129 if (!Initialize())
130 enabled = false;
131 }
132
133
134 protected override void OnPopulateMesh(VertexHelper vh)
135 {
136#if UNITY_EDITOR
137 if (!Application.isPlaying)
138 {
139 if (!Initialize())
140 {
141 return;
142 }
143 }
144#endif
145 // prepare vertices
146 vh.Clear();
147
148 if (!gameObject.activeInHierarchy)
149 {
150 return;
151 }
152
153 if (!isInitialised && !pSystem.main.playOnAwake)
154 {
155 pSystem.Stop(false, ParticleSystemStopBehavior.StopEmittingAndClear);
156 isInitialised = true;
157 }
158
159 Vector2 temp = Vector2.zero;
160 Vector2 corner1 = Vector2.zero;
161 Vector2 corner2 = Vector2.zero;
162 // iterate through current particles
163 int count = pSystem.GetParticles(particles);
164
165 for (int i = 0; i < count; ++i)
166 {
167 ParticleSystem.Particle particle = particles[i];
168
169 // get particle properties
170#if UNITY_5_5_OR_NEWER
171 Vector2 position = (mainModule.simulationSpace == ParticleSystemSimulationSpace.Local ? particle.position : _transform.InverseTransformPoint(particle.position));
172#else
173 Vector2 position = (pSystem.simulationSpace == ParticleSystemSimulationSpace.Local ? particle.position : _transform.InverseTransformPoint(particle.position));
174#endif
175 float rotation = -particle.rotation * Mathf.Deg2Rad;
176 float rotation90 = rotation + Mathf.PI / 2;
177 Color32 color = particle.GetCurrentColor(pSystem);
178 float size = particle.GetCurrentSize(pSystem) * 0.5f;
179
180 // apply scale
181#if UNITY_5_5_OR_NEWER
182 if (mainModule.scalingMode == ParticleSystemScalingMode.Shape)
183 position /= canvas.scaleFactor;
184#else
185 if (pSystem.scalingMode == ParticleSystemScalingMode.Shape)
186 position /= canvas.scaleFactor;
187#endif
188
189 // apply texture sheet animation
190 Vector4 particleUV = imageUV;
191 if (textureSheetAnimation.enabled)
192 {
193#if UNITY_5_5_OR_NEWER
194 float frameProgress = 1 - (particle.remainingLifetime / particle.startLifetime);
195
196 if (textureSheetAnimation.frameOverTime.curveMin != null)
197 {
198 frameProgress = textureSheetAnimation.frameOverTime.curveMin.Evaluate(1 - (particle.remainingLifetime / particle.startLifetime));
199 }
200 else if (textureSheetAnimation.frameOverTime.curve != null)
201 {
202 frameProgress = textureSheetAnimation.frameOverTime.curve.Evaluate(1 - (particle.remainingLifetime / particle.startLifetime));
203 }
204 else if (textureSheetAnimation.frameOverTime.constant > 0)
205 {
206 frameProgress = textureSheetAnimation.frameOverTime.constant - (particle.remainingLifetime / particle.startLifetime);
207 }
208#else
209 float frameProgress = 1 - (particle.lifetime / particle.startLifetime);
210#endif
211
212 frameProgress = Mathf.Repeat(frameProgress * textureSheetAnimation.cycleCount, 1);
213 int frame = 0;
214
215 switch (textureSheetAnimation.animation)
216 {
217
218 case ParticleSystemAnimationType.WholeSheet:
219 frame = Mathf.FloorToInt(frameProgress * textureSheetAnimationFrames);
220 break;
221
222 case ParticleSystemAnimationType.SingleRow:
223 frame = Mathf.FloorToInt(frameProgress * textureSheetAnimation.numTilesX);
224
225 int row = textureSheetAnimation.rowIndex;
226 // if (textureSheetAnimation.useRandomRow) { // FIXME - is this handled internally by rowIndex?
227 // row = Random.Range(0, textureSheetAnimation.numTilesY, using: particle.randomSeed);
228 // }
229 frame += row * textureSheetAnimation.numTilesX;
230 break;
231
232 }
233
234 frame %= textureSheetAnimationFrames;
235
236 particleUV.x = (frame % textureSheetAnimation.numTilesX) * textureSheetAnimationFrameSize.x;
237 particleUV.y = 1.0f - Mathf.FloorToInt(frame / textureSheetAnimation.numTilesX) * textureSheetAnimationFrameSize.y;
238 particleUV.z = particleUV.x + textureSheetAnimationFrameSize.x;
239 particleUV.w = particleUV.y + textureSheetAnimationFrameSize.y;
240 }
241
242 temp.x = particleUV.x;
243 temp.y = particleUV.y;
244
245 _quad[0] = UIVertex.simpleVert;
246 _quad[0].color = color;
247 _quad[0].uv0 = temp;
248
249 temp.x = particleUV.x;
250 temp.y = particleUV.w;
251 _quad[1] = UIVertex.simpleVert;
252 _quad[1].color = color;
253 _quad[1].uv0 = temp;
254
255 temp.x = particleUV.z;
256 temp.y = particleUV.w;
257 _quad[2] = UIVertex.simpleVert;
258 _quad[2].color = color;
259 _quad[2].uv0 = temp;
260
261 temp.x = particleUV.z;
262 temp.y = particleUV.y;
263 _quad[3] = UIVertex.simpleVert;
264 _quad[3].color = color;
265 _quad[3].uv0 = temp;
266
267 if (rotation == 0)
268 {
269 // no rotation
270 corner1.x = position.x - size;
271 corner1.y = position.y - size;
272 corner2.x = position.x + size;
273 corner2.y = position.y + size;
274
275 temp.x = corner1.x;
276 temp.y = corner1.y;
277 _quad[0].position = temp;
278 temp.x = corner1.x;
279 temp.y = corner2.y;
280 _quad[1].position = temp;
281 temp.x = corner2.x;
282 temp.y = corner2.y;
283 _quad[2].position = temp;
284 temp.x = corner2.x;
285 temp.y = corner1.y;
286 _quad[3].position = temp;
287 }
288 else
289 {
290 if (use3dRotation)
291 {
292 // get particle properties
293#if UNITY_5_5_OR_NEWER
294 Vector3 pos3d = (mainModule.simulationSpace == ParticleSystemSimulationSpace.Local ? particle.position : _transform.InverseTransformPoint(particle.position));
295#else
296 Vector3 pos3d = (pSystem.simulationSpace == ParticleSystemSimulationSpace.Local ? particle.position : _transform.InverseTransformPoint(particle.position));
297#endif
298
299 // apply scale
300#if UNITY_5_5_OR_NEWER
301 if (mainModule.scalingMode == ParticleSystemScalingMode.Shape)
302 position /= canvas.scaleFactor;
303#else
304 if (pSystem.scalingMode == ParticleSystemScalingMode.Shape)
305 position /= canvas.scaleFactor;
306#endif
307
308 Vector3[] verts = new Vector3[4]
309 {
310 new Vector3(-size, -size, 0),
311 new Vector3(-size, size, 0),
312 new Vector3(size, size, 0),
313 new Vector3(size, -size, 0)
314 };
315
316 Quaternion particleRotation = Quaternion.Euler(particle.rotation3D);
317
318 _quad[0].position = pos3d + particleRotation * verts[0];
319 _quad[1].position = pos3d + particleRotation * verts[1];
320 _quad[2].position = pos3d + particleRotation * verts[2];
321 _quad[3].position = pos3d + particleRotation * verts[3];
322 }
323 else
324 {
325 // apply rotation
326 Vector2 right = new Vector2(Mathf.Cos(rotation), Mathf.Sin(rotation)) * size;
327 Vector2 up = new Vector2(Mathf.Cos(rotation90), Mathf.Sin(rotation90)) * size;
328
329 _quad[0].position = position - right - up;
330 _quad[1].position = position - right + up;
331 _quad[2].position = position + right + up;
332 _quad[3].position = position + right - up;
333 }
334 }
335
336 vh.AddUIVertexQuad(_quad);
337 }
338 }
339
340 private void Update()
341 {
342 if (!fixedTime && Application.isPlaying)
343 {
344 pSystem.Simulate(Time.unscaledDeltaTime, false, false, true);
345 SetAllDirty();
346
347 if ((currentMaterial != null && currentTexture != currentMaterial.mainTexture) ||
348 (material != null && currentMaterial != null && material.shader != currentMaterial.shader))
349 {
350 pSystem = null;
351 Initialize();
352 }
353 }
354 }
355
356 private void LateUpdate()
357 {
358 if (!Application.isPlaying)
359 {
360 SetAllDirty();
361 }
362 else
363 {
364 if (fixedTime)
365 {
366 pSystem.Simulate(Time.unscaledDeltaTime, false, false, true);
367 SetAllDirty();
368 if ((currentMaterial != null && currentTexture != currentMaterial.mainTexture) ||
369 (material != null && currentMaterial != null && material.shader != currentMaterial.shader))
370 {
371 pSystem = null;
372 Initialize();
373 }
374 }
375 }
376 if (material == currentMaterial)
377 return;
378 pSystem = null;
379 Initialize();
380 }
381
382 protected override void OnDestroy()
383 {
384 currentMaterial = null;
385 currentTexture = null;
386 }
387
388 public void StartParticleEmission()
389 {
390 pSystem.Play();
391 }
392
393 public void StopParticleEmission()
394 {
395 pSystem.Stop(false, ParticleSystemStopBehavior.StopEmittingAndClear);
396 }
397
398 public void PauseParticleEmission()
399 {
400 pSystem.Stop(false, ParticleSystemStopBehavior.StopEmitting);
401 }
402 }
403#endif
404}
Credit Erdener Gonenc - @PixelEnvision.