Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
2
Question by lolzrofl · Oct 09, 2014 at 07:07 AM · c#instantiateperformance

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);
     }
 }
 
Comment
Add comment · Show 7
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Andres-Fernandez · Oct 09, 2014 at 08:19 AM 2
Share

Just think about what you are doing. 5 sprites 24x24 size: 5x24x24 = 2880. You have 2880 pairs of Instantiate+GetComponent calls (just with 5 average size sprites). That's a lot for a function that is supposed to execute in a single frame. Besides, Unity still has to deal with the rest of objects in scene. You'll get a performance spike for sure.

Besides the fact that you actually can use pooling (maybe you don't see it now but you will see it, believe me), if you want to stick to your instantiate approach maybe you should consider the use of a coroutine to distribute the prefab instantiation over a few frames.

avatar image dmg0600 · Oct 09, 2014 at 09:15 AM 0
Share

As Andres has pointed out, you can use pooling. Just take one object from the pool (which will be the same as if you had an instiated prefab) and change its values as you do right now.

It will improve your performance because Instantiate is a really heavy function in performance terms.

avatar image KayelGee · Oct 10, 2014 at 06:47 AM 0
Share

You could try to make one GameObject which manages all the pixel colors, positions and velocitys. Then you draw each pixel onto one big texture.

And as stated above you should consider pooling.

avatar image lolzrofl · Oct 10, 2014 at 08:54 AM 0
Share

@Andres How big should I make the object pool? I won't know how many object's I'll need until they are needed. For now I'll give your co-routine idea a shot.

avatar image Kiwasi · Oct 12, 2014 at 11:45 PM 1
Share

Particle system is the answer, as indicated by @Bunny83. A particle system is optimised specifically for this type of work.

But to complete the discussion on object pooling there are several options to make a pool work without knowing the total size. In no particular order:

  • Calculate a maximum theoretical size of the pool

  • Allow the pool to grow

  • Build a pool size to match your performance budget. Drop objects once your budget is spent.

Show more comments

1 Reply

· Add your reply
  • Sort: 
avatar image
4
Best Answer

Answer by Bunny83 · Oct 10, 2014 at 08:46 AM

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 :) 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.

Comment
Add comment · Show 9 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image lolzrofl · Oct 10, 2014 at 08:52 AM 0
Share

I originally tried this approach however I couldn't figure out if I was supposed to have a ParticleSystem already on the object or if it was all already done from code. If you could provide a more complete solution that would be much appreciated.

avatar image Bunny83 · Oct 10, 2014 at 09:00 AM 0
Share

$$anonymous$$aybe later, need some sleep :)

avatar image Bunny83 · Oct 10, 2014 at 07:02 PM 0
Share

@lolzrofl: I've updated my answer with a sample project

avatar image lolzrofl · Oct 12, 2014 at 10:46 PM 0
Share

@Bunny83 Just now saw your update! I'll try it out and let you know the results.

avatar image Kiwasi · Oct 13, 2014 at 06:56 AM 0
Share

I'll be taking that with me. Look forward to pixel by pixel explosions in my game soon.

Works great for me.

Show more comments

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

9 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Distribute terrain in zones 3 Answers

Multiple Cars not working 1 Answer

Activating GameObject(s) is slow 2 Answers

Is there a way to optimize Loading.PersistentManager? 2 Answers

How to create objects that fill a specific area? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges