Performance issues when creating many tiny objects

I have a script that allows me to create an explosion effect on a sprite. It iterates through each pixel of the sprite (24x24 on average) and creates a 1x1 pixel game object for each pixel with the color of the original pixel. It then gives each pixel a random velocity. This creates the effect that the sprite is exploding into it’s individual pixels and looks pretty cool.

My issue is that when using this on larger sprites or on more than 5 sprites at once I run into some performance issues and lag spikes. I can’t really use an object pool for this since the pixels have to be specific to the object’s current sprite during it’s animation.

How could I optimize the code below?

using UnityEngine;
using System.Collections;

public class ExplodeEffect : MonoBehaviour 
{
	public GameObject pixelPrefab;

	public void Explode(Vector3 velocity, Sprite sprite)
	{
		for (int i = 0; i <= sprite.bounds.size.x * 10f; i++)
		{
			for (int j = 0; j <= sprite.bounds.size.y * 10f; j++)
			{
				Vector2 positionOffest = new Vector2(sprite.bounds.extents.x - sprite.bounds.center.x,
													 sprite.bounds.extents.y - sprite.bounds.center.y - 0.05f);

				Vector3 pixelPosition = transform.TransformPoint((i / 10f) - positionOffest.x, 
                                                                 (j / 10f) - positionOffest.y, 0);

				Color pixelColor = sprite.texture.GetPixel((int)sprite.rect.x + i, 
													       (int)sprite.rect.y + j);
				
				if (pixelColor.a != 0f)
				{
					GameObject pixelInstance = Instantiate(pixelPrefab, pixelPosition, Quaternion.identity) as GameObject;
					pixelInstance.GetComponent<SpriteRenderer>().color = pixelColor;
					pixelInstance.rigidbody2D.AddForce(new Vector2(((velocity.x * 10f) + Random.Range(-250, 250)) * 2.5f, 
																   ((velocity.y * 10f) + Random.Range(-250, 300)) * 2.5f));
				}
			}
		}
	}
}

Update:
Thanks to @Bunny83 I got a working particle system implementation.
Below is my modified version of his code.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class SpriteExplosion : MonoBehaviour 
{
	public float pixelsPerUnit = 10f;
	public float lifetime = 4f;
	public string sortingLayer = "Foreground";
	public int sortingOrder = 1;

	public void Explode(Vector3 velocity, Sprite sprite)
	{
		StartCoroutine(DoExplode(velocity, sprite));
	}

	private IEnumerator DoExplode(Vector3 velocity, Sprite sprite)
	{
		ParticleSystem partSystem = GetComponent<ParticleSystem>();
		List<ParticleSystem.Particle> particles = new List<ParticleSystem.Particle>();
		ParticleSystem.Particle currentParticle = new ParticleSystem.Particle();
		partSystem.renderer.sortingLayerName = sortingLayer;
		partSystem.renderer.sortingOrder = sortingOrder;
		currentParticle.size = 1f / pixelsPerUnit;

		for (int i = 0; i < sprite.bounds.size.x * 10f; i++)
		{
			for (int j = 0; j < sprite.bounds.size.y * 10f; j++)
			{
				Vector2 positionOffset = new Vector2(sprite.bounds.extents.x - sprite.bounds.center.x - 0.05f,
													 sprite.bounds.extents.y - sprite.bounds.center.y - 0.05f);

				Vector3 particlePosition = transform.TransformPoint((i / 10f) - positionOffset.x,
																	(j / 10f) - positionOffset.y, 0);

				Color particleColor = sprite.texture.GetPixel((int)sprite.rect.x + i,
															  (int)sprite.rect.y + j);

				if (particleColor.a != 0f)
				{
					currentParticle.position = particlePosition;
					currentParticle.rotation = 0f;
					currentParticle.color = particleColor;
					currentParticle.startLifetime = currentParticle.lifetime = lifetime;
					currentParticle.velocity = new Vector2(velocity.x + Random.Range(-5f, 5f),
														   velocity.y + Random.Range(-5f, 6f));

					particles.Add(currentParticle);
				}
			}
		}

		partSystem.SetParticles(particles.ToArray(), particles.ToArray().Length);
		Destroy(gameObject, lifetime);
		yield return new WaitForSeconds(1f);
	}
}

I would recommend to use a ParticleSystem instead. You just need to setup the grid with particles and move / scale them to match the pixels positions / sizes / colors.

Watch out! There are two different “Particle” structs. The one in the UnityEngine namespace belongs to the old legacy Particle system. The new ParticleSystem has it’s own struct as nested class “ParticleSystem.Particle”. Particle systems are designed to handle several thousands of particles.

edit
Ok here is my little sample webplayer as usual :slight_smile: It also has a link at the bottom to the unitypackage for that example.

Originally i’ve created this example with Unity beta, however i just discovered that you can’t publish beta webbuilds as the Unity webplayer doesn’t have the beta player available automatically. So i migrated to the latest Unity version. Unfortunately Unity beta has a quite useful change on the Sprite class which allows you to read the “pixelsPerUnit” value. I’ve commented that line out and replaced it with a hardcoded value for each sprite. The pixelsPerUnit are important to determine the pixel / particle size in worldspace.

The sample scene contains some sprites with different resolutions and settings. Keep in mind that non-uniform scaled sprites (so with different x and y scale values) can’t be proberly represented with a billboard particle since those are always square. I simply use the area-average at the moment. It would be possible to create a custom mesh on-the-fly and use that mesh as particle, however i guess that’s not worth the trouble.

Finally i’d like to mention that the angularVelocity of the particles simply don’t work in a lot cases. This has been an issue for ages and it still exists. Setting a rotation manually works, however the angularVelacity somehow get reset automatically to 0.

All in all it does work quite well. Even with nearly 80k particles (that’s about 320k vertices) it still runs at 60fps (at least on my PC :))

Keep in mind that a single ParticleSystem can’t have more than 64k vertices, so the max resolution of your sprite isn’t that high. I have used a 32x32 and two 128x128 textures. However i actually let the ParticleSystem discard pixels with an alpha value of 0 by setting the lifetime to a negative value. That way those particles are removed even before they are rendered for the first time. This can of course be optimised to truly discard those particles and use a smaller array. This however requires you to either preprocess the whole image once to count the actual pixels or to use a dynamic List instead of an array. Both include additional overhead.