Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 14 Next capture
2021 2022 2023
2 captures
12 Jun 22 - 14 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 r-dotfunky · Aug 24, 2015 at 02:42 PM · particlesystemshurikenplacement

Manually Placing Particles

Hi all,

I have some questions around the proper methodology to manually place particles at specific locations in world space. The intention is that the particles are generated once and persist.

In my particle system I have everything disabled except for "Renderer" and "Emission". Part of my questions below relate to the "Shape" module.

Here's some code. I've renamed/removed unrelated code:

     public class BuildPositions : MonoBehaviour {
         public bool showDebugMessages;
         public ParticleSystem theParticleSystem;
         public Transform objectPrefab;
         public float fDistanceModifier = 1f;
         private ParticleSystem.Particle[] arrParticles;
         private List<systems> listObjects;
         private float farDistance = 0f;
         public bool isPositioned;
 
         // Use this for initialization
         void Start () {
             // Generate the empty game objects and place them
             GenerateObjects ();
         }
         
         void Update () {
         
         }
 
         private void LateUpdate()
         {
             // If not positioned
             if (!isPositioned) 
             {
                 // Start positioning
                 isPositioned = true;
                 StartCoroutine(setPositions());
             }
         }
 
         IEnumerator setPositions()
         {
             // Get the # particles
             int numParticlesAlive = theParticleSystem.GetParticles (arrParticles);
             
             // Set the scale of the system
             theParticleSystem.gameObject.transform.localScale = new Vector3 (farDistance, farDistance, farDistance);
 
             Debug.Log (numParticlesAlive.ToString ());
 
             // Change only the particles that are alive
             for (int i = 0; i < numParticlesAlive; i++) {
                 arrParticles [i].position = new Vector3 (listObjects [i].x, listObjects [i].y, listObjects [i].z);
                 yield return null;
             }
             
             // Apply the particle changes to the particle system
             theParticleSystem.SetParticles (arrParticles, numParticlesAlive);
             // Enable the particle system - play on awake is checked
             theParticleSystem.gameObject.SetActive (true);
         }
 
         void GenerateObjects() {
             
             //listObjects = <A LIST GENERATED FROM SQL QUERY UP TO 100,000 rows)
             //  Each of these objects has an x,y,z coordinate - see myObj class below
             
             if(showDebugMessages)
                 Debug.Log (listObjects.Count.ToString ());
 
             int i = 0;
             
             // Clear anything existing
             theParticleSystem.Clear ();
             // Set the count
             theParticleSystem.maxParticles = listObjects.Count;
             // Create the array to hold the particle instances
             arrParticles = new ParticleSystem.Particle[listObjects.Count];
             
             Vector3 home = new Vector3 (0f, 0f, 0f);
             
             
             foreach (myObj obj in listObjects) 
             {
                 float x = obj.x * fDistanceModifier;
                 float y = obj.y * fDistanceModifier;
                 float z = obj.z * fDistanceModifier;
                 Vector3 pos = new Vector3 (x,y,z);
                 //Debug.Log(pos.ToString());
 
                 if(Vector3.Distance(pos, home) > farDistance)
                 {
                     farDistance = Vector3.Distance(pos, home);
                 }
 
                 // Creating an empty game object here and placing it so I can easily jump to where I expect a particle to be
                 Transform clone = (Transform)Instantiate(objectPrefab, pos, Quaternion.identity);
                 clone.name = obj.name;
                 clone.SetParent(gameObject.transform);
             
                 i++;
             }
             //  Multiply by 2 to get a full radius
             farDistance *= 2f;
         }
     }
     
     // Instance class
     class myObj {
         public float x;
         public float y;
         public float z;
         public string name;
     }

Here are my questions:

  1. In my particle system am I REQUIRED to set a "Shape" in order to place particles? If I do not the particles all smoosh together around world origin. I have code above (farDistance) which should get me the full radius of a sphere where all these coordinates should live.

  2. Am I getting/setting the particles correctly? I've seen a number of posts and followed the basic methodology but I am just not getting the particles to place. They still "randomize" locations each time I start the game in the editor. I've even tried setting just 1 particle to 0,0,0 and no matter what that particle seems to go where it wants.

I know I'm missing something here... Can anyone help me out with some insight?

Comment
Add comment · Show 1
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 r-dotfunky · Aug 25, 2015 at 02:28 AM 0
Share

Ok so changing my setPositions function to this DOES place the particles correctly... just one at a time. It takes about 10 $$anonymous$$utes to render them all out. Brutal.

Like so:

             for (int i = 0; i < listObjects.Count; i++) {
                 Vector3 pos = new Vector3(listObjects [i].x * fDistance$$anonymous$$odifier, listObjects [i].y * fDistance$$anonymous$$odifier, listObjects [i].z * fDistance$$anonymous$$odifier);
                 theParticleSystem.Emit(pos,Vector3.zero,10f,900f,new Color32(255,255,0,255));
                 yield return null;
             }


I also tried it like this thinking that if I set each particle manually and then fired the emit it would work. This did not work.

         IEnumerator setPositions()
         {
             theParticleSystem.gameObject.transform.localScale = new Vector3 (farDistance, farDistance, farDistance);
 
             // Change only the particles that are alive
             for (int i = 0; i < listSystems.Count; i++) {
                 arrParticles [i].position = new Vector3 (listObjects[i].x * fDistance$$anonymous$$odifier, listObjects[i].y * fDistance$$anonymous$$odifier, listObjects[i].z * fDistance$$anonymous$$odifier);
                 arrParticles [i].velocity = Vector3.zero;
                 arrParticles [i].size = 10f;
                 arrParticles [i].lifetime = 900f;
                 arrParticles [i].color = new Color32(255,255,0,255);
 
                 yield return null;
             }
             
             theParticleSystem.SetParticles (arrParticles, arrParticles.Length);
             theParticleSystem.Emit (arrParticles.Length);
         }

Is there no other way to do this other than emit them one at a time?

2 Replies

· Add your reply
  • Sort: 
avatar image
5

Answer by r-dotfunky · Aug 26, 2015 at 08:22 AM

Ok so I do believe I finally got this nailed down. I'm running around ~36k particles right now and it starts in less than 2 seconds.

To answer my own questions:

  1. No - shape is NOT required. In my example below I have disabled everything in the particle system except for the renderer. Getting a possible radius of the entire space is cool but not required.

  2. I was missing some critical steps which I've marked with "Critical Step!" below. a. After telling the code how many particles to create... you need to "GetParticles" and pass in the array to be populated with the base generic length particles. b. When modifying a particle it is not enough to just set the values on the particle. One must set the particle values and then ASSIGN that particle back into the array in its proper location. You'll see that where I have 'arrParticles [i] = par;'. c. It seems that any manipulation of particles should be done in LateUpdate. I tried Update and just didn't work. d. Coroutines do not work here. I tried one in LateUpdate and it did not work. e. Unity Editor will fool you. You may try to navigate to where you expect a particle to be and see nothing. Mess around with the position of the camera in the area of the particle and eventually it (and the rest) will show up. Not sure if this is a Unity bug or not. Main Camera seems unaffected.

Here's the code.

     public class testParticlePlacer : MonoBehaviour {
         public bool showDebugMessages;
         public ParticleSystem theParticleSystem;
         public Transform objPrefab;
         public float fDistanceModifier = 1f;
         private Vector3 mainCamPos;
         private ParticleSystem.Particle[] arrParticles;
         private List<myObj> listObjects;
         private float farDistance = 0f;
         public bool isPositioned;
 
         /*
         USAGE NOTES
          1. In the particle system disable EVERYTHING except for Renderer.
          
         */
 
         // Use this for initialization
         void Start () {
             // Setup the database connection
             _dbManager = EDExplorationCompanion.instance.dbManagerObject;
             GenerateObjects ();
 
         }
         
         // Update is called once per frame
         void Update () {
             // Keep the position of the main camera
             mainCamPos = Camera.main.transform.position;
         }
 
 
         private void LateUpdate()
         {
             if (!isPositioned) 
             {
                 // This whole block should only fire once when isPositioned is false. Set to true to prevent firing on the next frame
                 isPositioned = true;
                 
                 // Create a new array to hold the particles we expect to display from the particle system. In this case use the listObjects count
                 //  to tell how many particles are needed
                 arrParticles = new ParticleSystem.Particle[listObjects.Count];
                 
                 // Critical step! You MUST fire GetParticles and pass the array you just created into GetParticles
                 //  This gets a generic set of particles to modify and set BACK to the system once completed
                 theParticleSystem.GetParticles(arrParticles);
                 
                 // Run a loop through the number of particles.. i is our counter value
                 for (int i = 0; i < arrParticles.Length; i++) {
                     // For ease of use assign a ParticleSystem.particle for the current object
                     ParticleSystem.Particle par = arrParticles [i];
                     
                     // Now assign the values you wish to change via 'par'.
                     par.position = new Vector3 (listObjects [i].x * fDistanceModifier, listObjects [i].y * fDistanceModifier, listObjects [i].z * fDistanceModifier);
                     par.velocity = Vector3.zero;
                     par.size = 10f;
                     par.lifetime = 900f;
                     par.color = new Color32(255,255,0,255);
                     
                     // Critical step! You MUST assign the modified particle BACK into the current position of the particles
                     //  This seems to be how the data is saved to the particle.
                     arrParticles [i] = par;
                 }
                 
                 // Apply the particle changes to the particle system
                 //  Note there is no need for Emit or Play here. Execute SetParticles and it works.
                 theParticleSystem.SetParticles (arrParticles, arrParticles.Length);
             }
         }
 
         void GenerateObjects() {
 
             //listObjects should be populated by whatever means you need - DB, array, text file et
             listObjects = SOMETHING YOU SPECIFY
             int i = 0;
             
             theParticleSystem.Clear ();
             theParticleSystem.maxParticles = listObjects.Count;
 
             foreach (myObj obj in listObjects) 
             {
                 // Get the X Y and Z coordinates for the object
                 float x = obj.x * fDistanceModifier;
                 float y = obj.y * fDistanceModifier;
                 float z = obj.z * fDistanceModifier;
                 
                 // Set the position to a vector3 
                 Vector3 objPos = new Vector3 (x,y,z);
                 
                 if(showDebugMessages)
                     Debug.Log("Position of obj: " + objPos.ToString());
                 
                 // Create the game objects that sit at the positions in space where objs exist
                 //  Set the position and parent the object accordingly
                 Transform clone = (Transform)Instantiate(objPrefab, objPos, Quaternion.identity);
                 clone.name = obj.name;
                 clone.SetParent(gameObject.transform);
                 
                 i++;
             }
             
             if(showDebugMessages)
                 Debug.Log("Generate GenerateObjects has finished.");
         }
 
     } // end class


Comment
Add comment · 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
4

Answer by Artifact-Jesse · Nov 21, 2018 at 07:59 AM

@r-dotfunky thanks for your answer, it helped me arrive at this one.

For those stumbling across this while using Shuriken in Unity 2018.x, the solution I finally hunted down was to use the overloaded ParticleSystem.Emit(EmitParams emitParams, int count) particles, rather than trying to create them from data like above. This seems much more convenient, compact, and has the added benefit of not completely breaking the normal operations of particles in the system, which for some reason happened between @r-dotfunky's answer and mine; so now you can, like, change particle velocities in LateUpdate() and stuff again (not shown in this example, but I can whip one up if needed).

Disclaimer: I haven't tested to see how performant this solution is since I'm only emitting one particle every few frames, but I expect it's pretty good. There's no frame-by-frame overhead beyond that of a normal particle system and you don't have to regenerate the entire array of particles every frame, plus lots of stuff is cached.

Here's my complete, functional example of a fairly generic particle pool:

 using UnityEngine;
 
 [RequireComponent(typeof(ParticleSystem))]
 public class ParticlePool : MonoBehaviour
 {
 
     private int lastParticleIndex = 0;  // keeps track of our oldest particle (for deletion)
 
     // these will all be inited in Initialize() on Start()
     private ParticleSystem particleSys;             // our object's particle system
     private ParticleSystem.Particle[] particles;    // our reusable array of particles
     private ParticleSystem.EmitParams emitParams;   // reusable emitparams
     private int maxParticles = 0;       // total number of particles in our scene before re-using
 
 
     private void Awake()
     {
         Initialize(); // initialize all of our member variables
     }
 
 
 
     public void CreateParticle(Vector3 position, float size, Vector3 velocity, float angularVelocity)
     {
         // if we're at our particle count limit, kill our oldest particle.
 
         int activeParticles = particleSys.GetParticles(particles);
         /// this thing sucks. Read the Unity docs VERY carefully to understand...
         /// basically the parameter (particles) is an out parameter which will
         /// write out the existing particles in the particle system to our
         /// reusable array. After that, we can directly modify the particles
         /// and then when we're done, write the particles back into the
         /// particle system with ParticleSystem.SetParticles( particles, count )
         
         if (activeParticles >= maxParticles)
         {
             // set lifetime to -1 to kill the particle
             particles[lastParticleIndex].remainingLifetime = -1;
             // we need to reset start lifetime to a normal value, too or the particle will still have infinite lifetime
             particles[lastParticleIndex].startLifetime = 1;
 
             lastParticleIndex++;    // keep track of oldest particle
             if (lastParticleIndex >= maxParticles) lastParticleIndex = 0;
 
             // have to re-write
             particleSys.SetParticles(particles, particles.Length);  // write those pesky particles back into our ParticleSystem
         }
 
 
         // set up params for this particle, you can use whatever you want (see unity docs for EmitParams for what's available)
         emitParams.angularVelocity = angularVelocity;
         emitParams.position = position;
         emitParams.startSize = size;
         emitParams.velocity = velocity;
         emitParams.startLifetime = float.MaxValue; // float.MaxValue makes the particle's lifetime infinite
 
         particleSys.Emit(emitParams, 1);
         particleSys.Play();
     }
 
 
     void Initialize()
     {
         if (particleSys == null)
             particleSys = GetComponent<ParticleSystem>();
 
         maxParticles = particleSys.main.maxParticles;
 
         if (particles == null || particles.Length < particleSys.main.maxParticles)
             particles = new ParticleSystem.Particle[particleSys.main.maxParticles];
     }
 }



Edit: I should mention, the particle system in this example is just bare mininum with a renderer. Not looping, no start on awake, simulation space set to World. MaxParticles is respected by the above script.

Hope that helps someone.

Comment
Add comment · 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

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

4 People are following this question.

avatar image avatar image avatar image avatar image

Related Questions

how to change the size of a particle 0 Answers

[Shuriken] Direct particles to a point 1 Answer

How to control particle lifetime when emitting programmatically? 1 Answer

ParticleSystem GetParticles returns 0 0 Answers

Unity particle collision detection 0 Answers


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