Search Unity

Particle Playground

Discussion in 'Assets and Asset Store' started by save, Dec 4, 2013.

  1. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Sorry for being vague, i was referring to a mesh procedurally drawn as a line. Unity has the Trail Renderer and Line Renderer at your service. The beam could also be done by a static mesh with an animated UV. As long as you don't need high interactivity/flexibility for drawing trail shaped effects, particles will always be the slowest and possibly most difficult method to control. Playground has functionality for it, but in your particular example I would have tried using a mesh with an animated texture. Then let Playground rather add visuals on top of the trail having particles move along the mesh, where you could try approaching the examples posted above.
     
  2. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
  3. Boingbloed

    Boingbloed

    Joined:
    Oct 17, 2012
    Posts:
    6
    Guess a trail is made from particles (geometry, shaded particles, sprite images or sprite animations)
     
  4. Jiraiya

    Jiraiya

    Joined:
    May 16, 2013
    Posts:
    12
    Hi,
    I am trying to adjust the scale of some of the playground particles presets with a particle scale script (it works fine on a vanilla shuriken particle system), but I get a warning about if the scale is not 1,1,1m shuriken wont render properly. What is the best way to adjust the scale of a playground particle system?
     
  5. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Hi!
    The reason why you get a warning is because of this issue with the Shuriken component. Playground has a bit different approach when it comes to scaling as the source is relying on how its source transform is configured.
    Source positions of State, World Object, Skinned World Object, Paint and Projection can be scaled through their assigned transform(s) using its localScale. But that will not affect how forces or lifetime positioning calculates.
    As it is now, the best approach is to scale the source transform, then use the individual scaling methods available for Particle Scale, Lifetime Positioning Scale and unfortunately set scaled forces by hand where the Vector3AnimationCurves has a scale multiplier. It would be difficult to implement a source + all other settings (affected would be velocity, overflow offset, source scatter, particle size and lifetime positioning) as the desired end result would vary from each user case.

    A good rule is that if you want to scale source positions, then always use another transform than your particle system - as it otherwise may trigger the Shuriken bug.

    I'll look into adding an overall velocity scale for next update which would be the most common operation. Then you could have all force settings and particle scale follow along depending on how you've scaled your source's transform. I'll add a script in the Simple Scripts library for overall scaling too.
    Any thoughts and suggestions is welcome of course!
     
  6. ferretnt

    ferretnt

    Joined:
    Apr 10, 2012
    Posts:
    412
    I'm trying to use particle playground to play a particle system that "swarms" an arbitrary mesh, rather like the mesh target manipulator demo, but assigning the mesh to the particle system at runtime.

    My scene setup demonstrably works if assigning the mesh in the inspector. However, my attempts to modify the mesh at runtime don't seem to work. ObjectToSwarm has a MeshFilter and MeshRenderer attached to it.

    A first hacky attempt would look like this:

    Transform targetTransform = objectToSwarm.transform;

    targetPartices.manipulators[0].property.meshTarget.transform = targetTransform;
    targetPartices.manipulators[0].transform.SetFromTransform(targetTransform);
    //targetPartices.manipulators[0].property.meshTarget.Initialize();
    targetPartices.Emit(true);

    However, this doesn't seem to work. With the line shown commented out, it looks like I get my original mesh relative to the new transform space (possibly...) and with that line commented in I get nothing. How should I modify the mesh target at runtime? (Incidentally, the mesh is also procedurally generated at runtime, about one frame before this is called, so it's not getting set up any earlier!)
     
  7. ferretnt

    ferretnt

    Joined:
    Apr 10, 2012
    Posts:
    412
    Sorry, one more question. I want to interpolate to positions on the mesh, not to mesh verticies (i.e. rather than pick a random vtx, more like pick a random tri, randomly interpolate those 3 vertices, and go to that position.) My target meshes are fairly coarse, and my swarming objects are small. There are many times more particles than there are verts in the target.

    Answers of the form "begin hacking code at this line" are cool. But it looks like there's a lot of caching going on so I didn't want to try without checking with you first.
     
  8. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Hey ferretnt!

    What you basically need is to assign the GameObject of your mesh target then update the manipulator's cached id whenever your mesh changes. It's not intuitive as I haven't focused on procedural mesh targets yet for the framework, but this is something it already handles inside the calculation loop.

    The two simple things to do is to first assign the GameObject of your mesh target (which has the Mesh Filter):
    Code (csharp):
    1. manipulator.property.meshTarget.gameObject = gameObject;
    Then whenever your mesh may update, set a new cached id to trigger a refresh of the mesh cache and the target sortings:
    Code (csharp):
    1. manipulator.property.meshTarget.cachedId = 0;
    To get more vertices to target or alter them in any way, your best option is to have a duplicate of your visual mesh and use that duplicate as the Manipulator's Mesh Target. How you interpolate between vertices would be how you structure your duplicated mesh, having a look into barycentric coordinate in your particular case would probably help (I'm unfortunately not great at working with procedural meshes so that is as far as my knowledge reaches). You can set the duplicate parented to your visual mesh's GameObject so it will follow its position and rotation. The Mesh Target won't render in your scene, but rather just attract particles.

    I put together an example as I wanted to double check that everything runs as expected, what this does is to create a procedural mesh of an isosphere and a Manipulator of property type Mesh Target. Then you can subdivide that mesh a couple of steps to get more target points, whenever a subdivision occurs I reset the cached id. The script might seem like a lot, but the most of it is just for demonstration purposes of creating a procedural mesh, where the meat of targeting particles towards a procedural mesh is the two lines posted above.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using ParticlePlayground;
    5.  
    6. /// <summary>
    7. /// Particle Playground mesh target of a procedural mesh.
    8. /// Assign this script to an empty GameObject in your scene. A Manipulator of your assigned Particle Playground system (particles) will be created.
    9. ///
    10. /// References:
    11. /// http://wiki.unity3d.com/index.php/ProceduralPrimitives#C.23_-_IsoSphere
    12. /// http://answers.unity3d.com/questions/259127/does-anyone-have-any-code-to-subdivide-a-mesh-and.html
    13. /// </summary>
    14. public class PlaygroundProceduralMeshTarget : MonoBehaviour {
    15.  
    16.     /// <summary>
    17.     /// Assign the particles in Inspector.
    18.     /// </summary>
    19.     public PlaygroundParticlesC particles;
    20.     public float manipulatorSize = 100f;
    21.     public float manipulatorStrength = 20f;
    22.     public float proceduralMeshSize = 3f;
    23.     public float rotationSpeed = 10f;
    24.  
    25.     /// <summary>
    26.     /// The cached reference to your Manipulator.
    27.     /// </summary>
    28.     ManipulatorObjectC manipulator;
    29.     int subdivisionFactor;
    30.  
    31.     void Start () {
    32.  
    33.         // Create an isosphere on this GameObject (could be any type of mesh configuration)
    34.         IsoSphere.CreateMesh(gameObject, 1, proceduralMeshSize, false);
    35.  
    36.         // Create a Manipulator on the particle system and set it up to work as a mesh target
    37.         if (particles!=null) {
    38.  
    39.             // Create the new manipulator
    40.             manipulator = PlaygroundC.ManipulatorObject(gameObject.transform, particles);
    41.  
    42.             // Set the manipulator to Property: Mesh Target and make it transition its particles
    43.             manipulator.type = MANIPULATORTYPEC.Property;
    44.             manipulator.property.type = MANIPULATORPROPERTYTYPEC.MeshTarget;
    45.             manipulator.property.meshTarget.gameObject = gameObject;
    46.             manipulator.property.transition = MANIPULATORPROPERTYTRANSITIONC.Linear;
    47.  
    48.         } else Debug.Log ("You must assign a Particle Playground system to this script.", gameObject);
    49.     }
    50.  
    51.     void Update () {
    52.  
    53.         // Make particle system follow the mouse position
    54.         particles.particleSystemTransform.position = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10f));
    55.  
    56.         // Set size & strength of manipulator from this script
    57.         manipulator.size = manipulatorSize;
    58.         manipulator.strength = manipulatorStrength;
    59.  
    60.         // Rotate mesh transform
    61.         transform.RotateAround(Vector3.zero, Vector3.up, -rotationSpeed * Time.deltaTime);
    62.  
    63.     }
    64.  
    65.     void OnGUI () {
    66.  
    67.         // Manipulator values
    68.         GUI.Box (new Rect(0,0,220,125), "Manipulator");
    69.         GUI.Label (new Rect(10, 30, 200, 30), "Strength: "+manipulatorStrength.ToString("f1"));
    70.         manipulatorStrength = GUI.HorizontalSlider(new Rect(10, 55, 200, 30), manipulatorStrength, 0f, 30f);
    71.         if (GUI.Button (new Rect(10, 85, 200, 30), "Transition: "+manipulator.property.transition.ToString())) {
    72.             if (manipulator.property.transition == MANIPULATORPROPERTYTRANSITIONC.Linear)
    73.                 manipulator.property.transition = MANIPULATORPROPERTYTRANSITIONC.Lerp;
    74.             else
    75.                 manipulator.property.transition = MANIPULATORPROPERTYTRANSITIONC.Linear;
    76.         }
    77.  
    78.         // Turbulence values
    79.         GUI.Box (new Rect(240,0,220,125), "Turbulence");
    80.         GUI.Label (new Rect(250, 30, 200, 30), "Strength: "+particles.turbulenceStrength.ToString("f1"));
    81.         particles.turbulenceStrength = GUI.HorizontalSlider(new Rect(250, 55, 200, 30), particles.turbulenceStrength, 0f, 50f);
    82.         if (GUI.Button (new Rect(250, 85, 200, 30), "Type: "+particles.turbulenceType.ToString())) {
    83.             if (particles.turbulenceType == TURBULENCETYPE.Perlin)
    84.                 particles.turbulenceType = TURBULENCETYPE.Simplex;
    85.             else
    86.                 particles.turbulenceType = TURBULENCETYPE.Perlin;
    87.         }
    88.  
    89.         // Mesh values
    90.         GUI.Box (new Rect(Screen.width-220,0,220,125), "Mesh");
    91.         GUI.Label (new Rect(Screen.width-210, 30, 200, 30), "Rotation Speed: "+rotationSpeed.ToString("f1"));
    92.         rotationSpeed = GUI.HorizontalSlider(new Rect(Screen.width-210, 55, 200, 30), rotationSpeed, -100f, 100f);
    93.         if (GUI.Button (new Rect(Screen.width-210, 85, 200, 30), "Subdivide x"+subdivisionFactor.ToString())) {
    94.             subdivisionFactor++; subdivisionFactor=subdivisionFactor%4;
    95.  
    96.             // Subdivide or recreate the original mesh depending on subdivision factor
    97.             if (subdivisionFactor==0)
    98.                 IsoSphere.CreateMesh(gameObject, 1, proceduralMeshSize, false);
    99.             else
    100.                 SubdivideMesh.Subdivide(gameObject.GetComponent<MeshFilter>().mesh);
    101.  
    102.             UpdateTargetManipulator();
    103.         }
    104.     }
    105.  
    106.     /// <summary>
    107.     /// All you need to do when changing your mesh is to set a new cached id for your mesh target.
    108.     /// Particles will get new targets and target sorting inside the calculation loop.
    109.     /// </summary>
    110.     void UpdateTargetManipulator () {
    111.         manipulator.property.meshTarget.cachedId = 0;
    112.     }
    113. }
    114.  
    115. /****************************************************************************************************
    116.     Everything below is for working with the procedural mesh.
    117. ****************************************************************************************************/
    118.  
    119. /// <summary>
    120. /// Class for generating an isosphere.
    121. /// This is for demonstration purpose only - use any type of mesh configuration to target particles.
    122. /// Courtesy of Bérenger http://wiki.unity3d.com/index.php/ProceduralPrimitives#C.23_-_IsoSphere
    123. /// </summary>
    124. public class IsoSphere {
    125.     private struct TriangleIndices {
    126.         public int v1;
    127.         public int v2;
    128.         public int v3;
    129.    
    130.         public TriangleIndices(int v1, int v2, int v3) {
    131.             this.v1 = v1;
    132.             this.v2 = v2;
    133.             this.v3 = v3;
    134.         }
    135.     }
    136.  
    137.     // return index of point in the middle of p1 and p2
    138.     private static int getMiddlePoint(int p1, int p2, ref List<Vector3> vertices, ref Dictionary<long, int> cache, float radius) {
    139.         // first check if we have it already
    140.         bool firstIsSmaller = p1 < p2;
    141.         long smallerIndex = firstIsSmaller ? p1 : p2;
    142.         long greaterIndex = firstIsSmaller ? p2 : p1;
    143.         long key = (smallerIndex << 32) + greaterIndex;
    144.    
    145.         int ret;
    146.         if (cache.TryGetValue(key, out ret)) {
    147.             return ret;
    148.         }
    149.    
    150.         // not in cache, calculate it
    151.         Vector3 point1 = vertices[p1];
    152.         Vector3 point2 = vertices[p2];
    153.         Vector3 middle = new Vector3
    154.             (
    155.                 (point1.x + point2.x) / 2f,
    156.                 (point1.y + point2.y) / 2f,
    157.                 (point1.z + point2.z) / 2f
    158.                 );
    159.    
    160.         // add vertex makes sure point is on unit sphere
    161.         int i = vertices.Count;
    162.         vertices.Add( middle.normalized * radius );
    163.    
    164.         // store it, return index
    165.         cache.Add(key, i);
    166.    
    167.         return i;
    168.     }
    169.  
    170.     /// <summary>
    171.     /// Creates the isosphere mesh and all necessary components to render it. The mesh will be attached to the passed in GameObject.
    172.     /// </summary>
    173.     /// <param name="gameObject">GameObject the isosphere should be created on.</param>
    174.     /// <param name="recursionLevel">Recursion level (triangle resolution).</param>
    175.     /// <param name="radius">Radius.</param>
    176.     /// <param name="visible">Should the renderer be visible from start?</param>
    177.     public static void CreateMesh(GameObject gameObject, int recursionLevel, float radius, bool visible) {
    178.         MeshRenderer renderer = gameObject.GetComponent<MeshRenderer>();
    179.         if (renderer==null)
    180.             renderer = gameObject.AddComponent<MeshRenderer>();
    181.         renderer.enabled = visible;
    182.         MeshFilter filter = gameObject.GetComponent<MeshFilter>();
    183.         if (filter==null)
    184.             filter = gameObject.AddComponent<MeshFilter>();
    185.         Mesh mesh = filter.mesh;
    186.         mesh.Clear();
    187.    
    188.         List<Vector3> vertList = new List<Vector3>();
    189.         Dictionary<long, int> middlePointIndexCache = new Dictionary<long, int>();
    190.    
    191.         // create 12 vertices of a icosahedron
    192.         float t = (1f + Mathf.Sqrt(5f)) / 2f;
    193.    
    194.         vertList.Add(new Vector3(-1f,  t,  0f).normalized * radius);
    195.         vertList.Add(new Vector3( 1f,  t,  0f).normalized * radius);
    196.         vertList.Add(new Vector3(-1f, -t,  0f).normalized * radius);
    197.         vertList.Add(new Vector3( 1f, -t,  0f).normalized * radius);
    198.    
    199.         vertList.Add(new Vector3( 0f, -1f,  t).normalized * radius);
    200.         vertList.Add(new Vector3( 0f,  1f,  t).normalized * radius);
    201.         vertList.Add(new Vector3( 0f, -1f, -t).normalized * radius);
    202.         vertList.Add(new Vector3( 0f,  1f, -t).normalized * radius);
    203.    
    204.         vertList.Add(new Vector3( t,  0f, -1f).normalized * radius);
    205.         vertList.Add(new Vector3( t,  0f,  1f).normalized * radius);
    206.         vertList.Add(new Vector3(-t,  0f, -1f).normalized * radius);
    207.         vertList.Add(new Vector3(-t,  0f,  1f).normalized * radius);
    208.    
    209.    
    210.         // create 20 triangles of the icosahedron
    211.         List<TriangleIndices> faces = new List<TriangleIndices>();
    212.    
    213.         // 5 faces around point 0
    214.         faces.Add(new TriangleIndices(0, 11, 5));
    215.         faces.Add(new TriangleIndices(0, 5, 1));
    216.         faces.Add(new TriangleIndices(0, 1, 7));
    217.         faces.Add(new TriangleIndices(0, 7, 10));
    218.         faces.Add(new TriangleIndices(0, 10, 11));
    219.    
    220.         // 5 adjacent faces
    221.         faces.Add(new TriangleIndices(1, 5, 9));
    222.         faces.Add(new TriangleIndices(5, 11, 4));
    223.         faces.Add(new TriangleIndices(11, 10, 2));
    224.         faces.Add(new TriangleIndices(10, 7, 6));
    225.         faces.Add(new TriangleIndices(7, 1, 8));
    226.    
    227.         // 5 faces around point 3
    228.         faces.Add(new TriangleIndices(3, 9, 4));
    229.         faces.Add(new TriangleIndices(3, 4, 2));
    230.         faces.Add(new TriangleIndices(3, 2, 6));
    231.         faces.Add(new TriangleIndices(3, 6, 8));
    232.         faces.Add(new TriangleIndices(3, 8, 9));
    233.    
    234.         // 5 adjacent faces
    235.         faces.Add(new TriangleIndices(4, 9, 5));
    236.         faces.Add(new TriangleIndices(2, 4, 11));
    237.         faces.Add(new TriangleIndices(6, 2, 10));
    238.         faces.Add(new TriangleIndices(8, 6, 7));
    239.         faces.Add(new TriangleIndices(9, 8, 1));
    240.    
    241.    
    242.         // refine triangles
    243.         for (int i = 0; i < recursionLevel; i++) {
    244.             List<TriangleIndices> faces2 = new List<TriangleIndices>();
    245.             foreach (var tri in faces) {
    246.                 // replace triangle by 4 triangles
    247.                 int a = getMiddlePoint(tri.v1, tri.v2, ref vertList, ref middlePointIndexCache, radius);
    248.                 int b = getMiddlePoint(tri.v2, tri.v3, ref vertList, ref middlePointIndexCache, radius);
    249.                 int c = getMiddlePoint(tri.v3, tri.v1, ref vertList, ref middlePointIndexCache, radius);
    250.            
    251.                 faces2.Add(new TriangleIndices(tri.v1, a, c));
    252.                 faces2.Add(new TriangleIndices(tri.v2, b, a));
    253.                 faces2.Add(new TriangleIndices(tri.v3, c, b));
    254.                 faces2.Add(new TriangleIndices(a, b, c));
    255.             }
    256.             faces = faces2;
    257.         }
    258.    
    259.         mesh.vertices = vertList.ToArray();
    260.    
    261.         List< int > triList = new List<int>();
    262.         for( int i = 0; i < faces.Count; i++ ) {
    263.             triList.Add( faces[i].v1 );
    264.             triList.Add( faces[i].v2 );
    265.             triList.Add( faces[i].v3 );
    266.         }  
    267.         mesh.triangles = triList.ToArray();
    268.         mesh.uv = new Vector2[ mesh.vertices.Length ];
    269.    
    270.         Vector3[] normales = new Vector3[ vertList.Count];
    271.         for( int i = 0; i < normales.Length; i++ )
    272.             normales[i] = vertList[i].normalized;
    273.  
    274.         mesh.normals = normales;
    275.    
    276.         mesh.RecalculateBounds();
    277.         mesh.Optimize();
    278.     }
    279. }
    280.  
    281. /// <summary>
    282. /// Class for subdividing a mesh vertices, normals and triangles.
    283. /// This is for demonstration purpose only if you want more points for your particles to target. In this particular example you could as well use a higher recursion level for your isosphere.
    284. /// Courtesy of Bunny83 http://answers.unity3d.com/questions/259127/does-anyone-have-any-code-to-subdivide-a-mesh-and.html
    285. /// </summary>
    286. public class SubdivideMesh {
    287.     static List<Vector3> vertices;
    288.     static List<Vector3> normals;
    289.  
    290.     static List<int> indices;
    291.     static Dictionary<uint,int> newVectices;
    292.  
    293.     static int GetNewVertex(int i1, int i2) {
    294.         // We have to test both directions since the edge
    295.         // could be reversed in another triangle
    296.         uint t1 = ((uint)i1 << 16) | (uint)i2;
    297.         uint t2 = ((uint)i2 << 16) | (uint)i1;
    298.         if (newVectices.ContainsKey(t2))
    299.             return newVectices[t2];
    300.         if (newVectices.ContainsKey(t1))
    301.             return newVectices[t1];
    302.         // generate vertex:
    303.         int newIndex = vertices.Count;
    304.         newVectices.Add(t1,newIndex);
    305.    
    306.         // calculate new vertex
    307.         vertices.Add((vertices[i1] + vertices[i2]) * 0.5f);
    308.         normals.Add((normals[i1] + normals[i2]).normalized);
    309.  
    310.         return newIndex;
    311.     }
    312.  
    313.  
    314.     public static void Subdivide(Mesh mesh) {
    315.         newVectices = new Dictionary<uint,int>();
    316.    
    317.         vertices = new List<Vector3>(mesh.vertices);
    318.         normals = new List<Vector3>(mesh.normals);
    319.         indices = new List<int>();
    320.    
    321.         int[] triangles = mesh.triangles;
    322.         for (int i = 0; i < triangles.Length; i += 3) {
    323.             int i1 = triangles[i + 0];
    324.             int i2 = triangles[i + 1];
    325.             int i3 = triangles[i + 2];
    326.        
    327.             int a = GetNewVertex(i1, i2);
    328.             int b = GetNewVertex(i2, i3);
    329.             int c = GetNewVertex(i3, i1);
    330.             indices.Add(i1);   indices.Add(a);   indices.Add(c);
    331.             indices.Add(i2);   indices.Add(b);   indices.Add(a);
    332.             indices.Add(i3);   indices.Add(c);   indices.Add(b);
    333.             indices.Add(a );   indices.Add(b);   indices.Add(c); // center triangle
    334.         }
    335.         mesh.vertices = vertices.ToArray();
    336.         mesh.normals = normals.ToArray();
    337.         mesh.triangles = indices.ToArray();
    338.    
    339.         // since this is a static function and it uses static variables
    340.         // we should erase the arrays to free them:
    341.         newVectices = null;
    342.         vertices = null;
    343.         normals = null;
    344.  
    345.         indices = null;
    346.     }
    347. }
    348.  
    And the outcome would look something like this:


    Try it out here: http://www.polyfied.com/products/de...eshtarget/playgroundproceduralmeshtarget.html

    Remember that you have a turbulence manipulator in 2.1, which could come in handy if your particles should swarm only when entering a certain space. Turbulence time, scale and lifetime strength will have a great affect on how they behave (which isn't included in the example).
     
    Last edited: Oct 3, 2014
    Exeneva and ferretnt like this.
  9. ferretnt

    ferretnt

    Joined:
    Apr 10, 2012
    Posts:
    412
    Perfect reply - thanks for the professionalism.
     
  10. ferretnt

    ferretnt

    Joined:
    Apr 10, 2012
    Posts:
    412
    I'm getting a lot of editor exceptions, and it looks like it's because the PlayGroundInspector thinks playgroundLanguage is null. I'm guessing that it's not getting set up correctly (it's a static so not serialized?)

    This is mildly annoying, because it means I can't leave "Error Pause" enabled because the editor then stops any time I'm editing a PlaygroundParticle. Any suggestions? I've thought about replacing that member with a property that fetches all the time as an attempt to fix it, and I've also tried switching languages and switching back in the Playground window.

    NullReferenceException: Object reference not set to an instance of an object
    PlaygroundInspectorC.ManipulatorTypeName (MANIPULATORTYPEC mType) (at Assets/Particle Playground/Scripts/Editor/PlaygroundInspectorC.cs:774)
    PlaygroundParticleSystemInspectorC.OnInspectorGUI () (at Assets/Particle Playground/Scripts/Editor/PlaygroundParticleSystemInspectorC.cs:1710)
    UnityEditor.InspectorWindow.DrawEditor (UnityEditor.Editor editor, Int32 editorIndex, Boolean forceDirty, System.Boolean& showImportedObjectBarNext, UnityEngine.Rect& importedObjectBarRect, Boolean eyeDropperDirty)
    UnityEditor.DockArea:OnGUI()
     
  11. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Could you walk me through the steps to reproduce this?
    It sounds like the Playground Inspector doesn't get initialized before the PlaygroundParticles Inspector, which is an unexpected behavior as the PlaygroundParticles Inspector will initialize needed values of the Playground Inspector upon load. What you could try for a quick fix before we're able to dig deeper into it is to add:
    Code (csharp):
    1.  
    2. if (playgroundParticlesScriptReference.manipulators.Count>0) {
    3.     if (PlaygroundInspectorC.playgroundSettings==null)
    4.         PlaygroundInspectorC.playgroundSettings = PlaygroundSettingsC.GetReference();
    5.     if (PlaygroundInspectorC.playgroundLanguage==null)
    6.         PlaygroundInspectorC.playgroundLanguage = PlaygroundSettingsC.GetLanguage();
    7.  
    on line 1693 in PlaygroundParticleSystemInspector.cs.

    Small update with fixes, velocity scaling and the example scene for procedural mesh targets will be published during the weekend.

    Edit: Found an issue of adding manipulators through script while viewing the Manipulator list in Inspector, could it be related to that? (fix coming!)
     
    Last edited: Oct 3, 2014
  12. ferretnt

    ferretnt

    Joined:
    Apr 10, 2012
    Posts:
    412
    I can't confirm how I caused it originally, although I can confirm that it's essentially any time I reopen my project now. I suspect it's *either* because I added manipulators via script with the inspector open (as you suggest) or because I created a bunch of prefabs from your demo scene then dragged them into my main scene, and I believe at one point this may have meant there were two PlaygroundManagers in the scene.

    I can confirm that I haven't managed to reproduce the problem since adding your fix above.

    Can I ask, given that it's editor code and only executed on desktop for a few widgets each frame, is the easiest fix just to make playgroundLanguage a property that checks if it is null, and otherwise fetches the language and caches the result internally?

    Alex
     
  13. ZJP

    ZJP

    Joined:
    Jan 22, 2010
    Posts:
    2,649
    Oh yeah... Thank for this example. :cool:
     
  14. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Good thing we were able to nail it down immediately. I'm not sure yet why PlaygroundInspector would loose its value (has to be static for other members to reach it - but that might just be the issue as well when called externally). All editor windows will call for a cached reference of the selected language in PlaygroundSettingsC.cs by the GetLanguage() function. If no language is found (possibly due to user deleted them) an instance of the serialized english will be cached instead, which then is reused for the editor windows. Therefore whenever a playgroundLanguage in an editor window ends up null, I believe the best solution is to check at an Inspector's initialization (or at any external calls towards another - which was your particular case) if the variable is null, otherwise assign the reference.

    Further if you're interested in implementing this yourself, the method of using ScriptableObject and localization can be used quite easily for your applications. Having an XML import/export will make it easy to work with your languages externally and by making use of Unity's SerializedObject and SerializedProperty you'll have an easy way to iterate through your values. Then in your final build run everything from the ScriptableObject for performance (rather than reading from the original XML). If you're interested you can have a look in PlaygroundSettingsC.cs and the ImportLanguage() / ExportLanguage() functions. Another nice feature this gives is immediate language changes without any importing going on, where you just assign a different version of your ScriptableObject.

    My pleasure! :)
     
  15. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    There we go, 2.11 available taking care of the recent issues and adding overall velocity scaling support. You'll find a particle system scaler in the Simple Scripts library and a new example scene called Procedural Mesh Target.
     
  16. Dave_Voyles

    Dave_Voyles

    Joined:
    Feb 7, 2014
    Posts:
    32
    Since I updated to the latest version today, I started getting this issue on one of my Particle Playground prefabs.

    DirectoryNotFoundException: Directory 'C:/Users/DaveVoyles/SkyDrive/Development/unity/Sidescrolling-Shooter/Assets/Particle Playground/Playground Assets/Brushes' not found.

    System.IO.Directory.GetFileSystemEntries (System.String path, System.String searchPattern, FileAttributes mask, FileAttributes attrs) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.IO/Directory.cs:514)

    What could I be missing here? Is there some aspect of the Matrix Cube that uses a brush?

    I only see this error when I select the prefab. I'm using the Playground Matrix Cube.
     
  17. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    I take it you updated from a version prior to 2? In that case it would make sense as I've rewritten all static paths (to be able to move the Particle Playground folder to wherever you'd like in your project).
    The issue should stem from the PlaygroundParticleSystemInspector.cs (line 395) trying to initialize the brushes. This is done with System.IO and it looks like you have a valid project path, but could it be access permission issues? (A bit curious about how SkyDrive is handled)

    If that's not the case, have a look at the Window > Particle Playground > Settings > Paths, does it add up with where you have your Brushes folder? (Should be in your Particle Playground/Playground Assets/ folder)


    Edit: Should you continue to experience issues with this, uncomment the line 345 in PlaygroundParticleSystemInspector.cs:
    Code (csharp):
    1.  
    2. //LoadBrushes();
    3.  
    However I'd really want to do a followup with you, where we could find another way of loading brushes from the project folder. Please e-mail support@polyfied.com if you keep experiencing issues.
     
    Last edited: Oct 4, 2014
  18. Dave_Voyles

    Dave_Voyles

    Joined:
    Feb 7, 2014
    Posts:
    32
    Thank you for the thorough response! Yes you are correct -- I was using a version prior to 2.0, then updated.

    I realized that I couldn't find the Settings button, I decided to delete the Particle Playground folder from Unity, and reset. Afterwards, I went back to the Asset Store and saw that another update was available. I updated, and imported the last version. Problem solved!

    But..... another problem arises:

    NullReferenceException: Object reference not set to an instance of an object
    ParticlePlayground.AnimationCurveExtensions.Reset1 (UnityEngine.AnimationCurve animationCurve) (at Assets/Particle Playground/Scripts/PlaygroundC.cs:4028)
    PlaygroundParticleSystemInspectorC.SetMissingKeys () (at Assets/Particle Playground/Scripts/Editor/PlaygroundParticleSystemInspectorC.cs:368)
    PlaygroundParticleSystemInspectorC.OnEnable () (at Assets/Particle Playground/Scripts/Editor/PlaygroundParticleSystemInspectorC.cs:361)

    I only see this when I click on a a prefab in the presets folder. The odd thing though, is that I can drag it into the scene, and it displays fine. I haven't had any issues with it, but I figured I'd bring it to your attention.
     
  19. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Thanks for getting back to me! The previous problem was possibly some Unity serialization issue in the project (beats me!). There's definitely some kind of bogey here (the animation curves should have value from beginning). Which version of Unity are you using?
    To patch you up, add this on line 4014 and on line 4026 in PlaygroundC.cs at the top of both functions:
    Code (csharp):
    1.  
    2. if (animationCurve==null)
    3.     animationCurve = new AnimationCurve();
    4.  
    Sorry Dave, really interesting bugs lately I must say! Regarding your previous issue I'm changing initialization of brushes so they'll only load when unfolding in paint mode, it's a bit mad they initialize upon OnEnable.
     
  20. Ennothan

    Ennothan

    Joined:
    Oct 17, 2013
    Posts:
    51
    Well, I don't know if this was already answered, but my question is: there is an automatic way to convert a shuriken particle into a playground one?

    Thanks
     
  21. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Sorry there isn't, at least not an overall solution. I've been down that road a couple of times to iterate through a Shuriken component through SerializedProperty (that is possible but a real time consuming task even finding values). However the biggest issue is that most techniques available in Playground isn't reversible into a Shuriken, they're simply too far apart (for both better and worse - in this case).
    If you know specifically what values you want to transfer over, it gets (a bit) easier though. Here's an example to find all names and values in a Shuriken component:
    Code (CSharp):
    1. ParticleSystem shurikenSource;
    2. SerializedObject shuriken = new SerializedObject(shurikenSource);
    3. SerializedProperty shurikenProperty = shuriken.GetIterator();
    4. while (shurikenProperty.Next (true))
    5.     Debug.Log ("Name: "+shurikenProperty.propertyPath+" Type: "+shurikenProperty.propertyType);
    Then you would use those values to apply into a PlaygroundParticlesC object. The scripting reference should come in handy.

    Here's some more info: http://answers.unity3d.com/questions/483831/how-to-modify-particlesystem-from-editor-script.html
    Note that this can only be done through the Unity Editor and not in a build. I do not recommend going down this road unless you really need to. It may even be faster to replicate the particle systems through dragging the sliders in Inspector, even if you have hundreds of them. It's really unfortunate Shuriken doesn't have more public members.
     
  22. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Realized Playground has been a scripting class for a year so let's give this a go, tell your friends!

     
    Last edited: Oct 6, 2014
  23. Ennothan

    Ennothan

    Joined:
    Oct 17, 2013
    Posts:
    51
    Thanks Save, with all that work, replicate through the inspector is faster as you said.

    But regardless of the convertion work, should I convert then all to playground (I don't have a lot yet)? The playground is more efficient than shuriken?
     
  24. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    It really depends on what kind of effects those are and what you're looking to do with them. Performance-wise they tend to run about equally (except you'll have multithreading of the calculations in Playground). Keep in mind that there's always the process of putting particles back into the Shuriken component of a Particle Playground system, which is a necessity when working procedurally with particle systems in Unity.
    Convert them if you're looking to change those particles dynamically in your scene at any point or let them take part of the other gadgets inside the Playground. Otherwise I'd say you could keep running them au naturel. :)
     
  25. ferretnt

    ferretnt

    Joined:
    Apr 10, 2012
    Posts:
    412
    Thanks for the updated package over the weekend, including the example I asked for, rather fantastic turnaround! Thanks!

    Couple of quick questions:

    1. Particle scale won't produce a good looking downscale with Turbulence. From a quick glance, it looks like multiplying turbulenceScale by 1/scale gets close (corrects the position-based part of simplex), but I didn't trace back to see what the velocityScale-based part of this would need to have done to it. Can you confirm?

    2. The "manipulator" in the ink example isn't actually connected to anything. Was that always the case?

    Thanks once again for Particle Playground as a whole. Having a great time using it, and the rendering via native Shuriken at the lowest level is very nice.
     
  26. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Splendid!

    1. Ah, thanks yes you're correct. The turbulence resolution scale should be divided instead of multiplied. I'll add this to the fix-list for next update.
    2. It should be connected, have a look in the Light Points particle system > Manipulators > 0 Repellent. It's a transform controlled from PlygroundInk.cs which pushes them away from the ink particles.

    I'm glad you enjoy it, even though the required minor tweaks needed here and there. It's great you report in. :)
     
  27. ferretnt

    ferretnt

    Joined:
    Apr 10, 2012
    Posts:
    412
    Is there a way to reset a particle system, i.e. at runtime on a given frame I want to kill all currently emitted particles from that system, so that I can begin spawning again?

    I tried calling PlaygroundC.Clear(), but that sets the playgroundParticleCache to null, which then breaks if you call Emit() immediately afterwards.
     
    Last edited: Oct 8, 2014
  28. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    You can disable the GameObject or the PlaygroundParticlesC component, then when you want it to start emit just enable it again.
     
  29. ferretnt

    ferretnt

    Joined:
    Apr 10, 2012
    Posts:
    412
    I have a particle system with an event set on particle death. The system is set up to do a one-time emit, i.e. it does not loop, and has "disable on done" set. It emits N particles, once, and all of them then die exactly once.

    Performance is a bit of a disaster because my deathEvent seems to be getting called for *all* dead particles, every frame, not just particles that have just died that frame.

    In other words, if I count the number of death events (and cut my particle system to 75 particles, so it's easier to debug), I see that on the final frame, I get 75 death events, as the last particle dies, and in fact you get over 3000 total death events broadcast. Most of these appear to have the particle position set to (0, 1000000, 0) so it is possible to ignore them, however with a very large particle system the actual overhead of the call is becoming a problem as the total number of calls explodes exponentially. In the worse case (a slow spawn rate), it looks like you get 1 + 2 + 3 + 4 +... + 75 calls!

    Is there any way just to get a single death event for each particle as it dies?
     
  30. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    I could reproduce it from your description and that's one serious bug, I'll jump to fixing that ASAP. I'll be in touch when the solution is ready.
     
  31. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    You have a pm with a fix!

    I'm currently working through a lot of performance fixes and smashing a couple of bugs on the way. As soon as all testing is done a neat update will be available.
     
    Last edited: Oct 12, 2014
  32. JVaughan

    JVaughan

    Joined:
    Feb 6, 2013
    Posts:
    23
    This is probably a simple question, but, how do you scale up FX? I need to make an effect about 100 times larger and I can't seem to find the right solution for this. Thanks in advance!
     
  33. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Hey!
    From version 2.11 you'll find a script called ParticleSystemScaler.cs in the Simple Scripts folder. You can drag that script onto any Particle Playground system and adjust Scale, in your case set it to 100. As a precaution it is set to not affect the particle system in edit mode, just uncomment that line if you want to scale it outside of play mode. Note that version 2.12 soon available will have a turbulence field fix for the overall scaling.
    Code (CSharp):
    1. /// <summary>
    2. /// Scale a Particle Playground system. Assign this script to the Particle Playground system's GameObject.
    3. /// Uncomment [ExecuteInEditMode()] below to apply the effect in Edit Mode.
    4. /// </summary>
    5. [ExecuteInEditMode()]
    6. public class ParticleSystemScaler : MonoBehaviour {
    Should you notice any limitations in available max values you can edit them in Window > Particle Playground > Editor Limits. The limits are simply there to guide the average user but will be too narrow when using pixel measure instead of units for instance.
     
  34. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    primus88 and gurayg like this.
  35. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Good, no... great news!
    Wanted to share an update on what you can expect from the reworked multithreading system. So I implemented a thread aggregator which can act differently depending on your setup, figured this is good to be aware about before the update has landed in your projects. You'll be able to determine how the overall particle systems should calculate through the Playground Manager, here's the options:

    Code (CSharp):
    1.     /// <summary>
    2.     /// Multithreading method. This determines how particle systems calculate over the CPU. Keep in mind each thread will generate memory garbage which will be collected at some point.
    3.     /// Selecting ThreadMethod.NoThreads will make particle systems calculate on the main-thread.
    4.     /// ThreadMethod.OnePerSystem will create one thread per particle system each frame.
    5.     /// ThreadMethod.OneForAll will bundle all calculations into one single thread.
    6.     /// ThreadMethod.Automatic will distribute all particle systems evenly bundled along available CPUs/cores.
    7.     /// </summary>
    8.     public enum ThreadMethod {
    9.         /// <summary>
    10.         /// No calculation threads will be created. This will in most cases have a negative impact on performance as Particle Playground will calculate along all other logic on the main-thread.
    11.         /// Use this for debug purposes or if you know there's no multi- or hyperthreading possibilities on your target platform.
    12.         /// </summary>
    13.         NoThreads,
    14.         /// <summary>
    15.         /// One calculation thread per particle system will be created. Use this when having heavy particle systems in your scene. Note that this method will never bundle calculation calls.
    16.         /// </summary>
    17.         OnePerSystem,
    18.         /// <summary>
    19.         /// One calculation thread for all particle systems will be created. Use this if you have other multithreaded logic which has higher performance priority than Particle Playground or your project demands strict use of garbage collection.
    20.         /// Consider using ThreadMethod.Automatic for best performance.
    21.         /// </summary>
    22.         OneForAll,
    23.         /// <summary>
    24.         /// Let calculation threads distribute evenly for all particle systems in your scene. This will bundle calculation calls to match the platform's SystemInfo.processorCount.
    25.         /// This is the recommended and overall fastest method to calculate particle systems.
    26.         /// Having fewer particle systems than processing units will create one thread per particle system. Having more particle systems than processing units will initiate thread bundling.
    27.         /// </summary>
    28.         Automatic
    29.     }
    Most of you will probably want to run the automatic solution, it is structured to evenly distribute particle systems over the available threads reported by SystemInfo.processorCount. You can also set how many threads that is allowed to be created each calculation loop if you should find the need. The results in chosen method will differ a bit depending on your scene setup, but overall the difference in performance is, well remarkable to say the least. :)


    (sorry about the ugly)

    I'm almost done but I want to add independent settings for advanced user cases where you may want to send a special particle system to its own calculated thread (where the rest is calculated on the same or a couple of other threads). I could imagine that being a helpful scenario for tweaking performance if you should want a massive fx at some point during your game which needs extra attention.
    The good thing with this layout I've seen so far is that if you have an insane amount of demanding particle systems, your main-thread won't lag but rather just the particle simulation.

    I'm trying to keep the solution very open for tweaking, but with focus on that you should not have to dig into this part in the first place, so you don't have to know what a thread is to make some explosions. :)
    Any thoughts and suggestions are welcome of course!
     
    RavenMikal, red2blue and hopeful like this.
  36. chiapet1021

    chiapet1021

    Joined:
    Jun 5, 2013
    Posts:
    605
    Those optimization comparisons look amazing!
     
  37. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Should come in handy for low-end devices, or just particle happy scene setups. This combined with the internal active check really boosted performance quite the bit. :)

    To all: Don't miss out on the 50% off deal, about to come to an end. If you need several licenses, this is the time. All future updates included.
     
  38. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Another interesting result with bundled thread calls after todays testing. Here are all particle systems bundled into one, while skinned meshes calculate on their individual.

     
  39. ZJP

    ZJP

    Joined:
    Jan 22, 2010
    Posts:
    2,649
    Oh.. Please, add this example in the next update. :cool:
     
  40. RavenMikal

    RavenMikal

    Joined:
    Oct 18, 2014
    Posts:
    144
    Save,

    First, I love the upgrade to the particle system as a whole, it was overdue, and as a whole, is amazing.

    Second, many of the questions I've been struggling with could be answered by just showing the assets to the demo.
    For three nights I've been trying to get the snapshot to work, for instance. Its in a demo, and if I had a copy of the unbuilt unity version, it would be done. So rather then ask 100 questions your demo's would answer by themselves, is there any way to get a copy of the assets to those demos? =)

    Thank you for your consideration in this...

    P.S. - Did appreciate the broken down procedural mesh demo =)
     
    Last edited: Oct 21, 2014
  41. Jonathan-Bro

    Jonathan-Bro

    Joined:
    Apr 11, 2013
    Posts:
    35
    Hi there,

    I created a particle preset that transitions from transparent to opaque (alpha 0-->255). But, when I instantiate the particle preset in the scene, the particle flashes (fully opaque) for a moment before doing the transition as I set earlier. Would you have any ideas on why the particle is behaving like this?

    Code (CSharp):
    1. particle = ParticlePlayground.PlaygroundC.InstantiatePreset("preset name");
    2. particle.transform.position = spawnPos;
    3.  
     
  42. RavenMikal

    RavenMikal

    Joined:
    Oct 18, 2014
    Posts:
    144
    honestly, I'm not sure how often this is replied to, but I know from going through this thread that it sounds like the issue they were bringing up with presets. If no one's replied in a few hours I'll find the exact post, I *think* there was a work around. Regardless, best of luck =)
     
  43. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Sure! :)

    Thanks, I'm glad you enjoy it.
    You'll find all demos in Particle Playground/Examples/Example Scenes/ and subfolder Interactive/. All components they use is available in the package.
    Tip: Transition over to a snapshot you like and duplicate that particle system, remove all snapshots on the duplicate, tweak it for your needs and drag it over to your Project View to make a prefab.

    Hi!
    How are you doing the transition?
    You could use the Particle Settings > Particle Mask with Mask Time to do the transition from fully transparent to opaque. Particle Mask will be the amount of particles that should be masked where Mask Time will determine how long the fade is in seconds. I tested this on version 2.12 which is just about to be released, however 2.11 should act similarly:

    Code (CSharp):
    1. IEnumerator Start () {
    2.     PlaygroundParticlesC particles = PlaygroundC.InstantiatePreset ("preset name");
    3.     particles.particleMask = particles.particleCount;
    4.     yield return null;
    5.     particles.particleMask = 0;
    6. }
    Let me know how it works out!
     
    Last edited: Oct 22, 2014
  44. RavenMikal

    RavenMikal

    Joined:
    Oct 18, 2014
    Posts:
    144
    lol...figures >.<

    Thanks, Save! =)
     
  45. Jonathan-Bro

    Jonathan-Bro

    Joined:
    Apr 11, 2013
    Posts:
    35
    Thank you! It appears correctly, but the time between when the particle is spawned and the effect appears on the screen is very long for some reason.

    Please see my GIF http://imgur.com/duvuOrg

    The last half of the animation shows my problem. You can see the particle being spawned correctly in the top scene view window, but the actual effect doesn't appear in the game view window until up to a full second later in some cases.
     
  46. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Thanks for the gif! I'll do some testing, this might already be fixed in the upcoming version. Your systems are set to Lifetime: Burst right? If you continue to experience issues after the update it would be great if you could send the particle system over to support@polyfied.com.
     
  47. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Confirming it should be solved, but should you experience issues with your particle system it would be good to see its setup. Update should be ready by tomorrow if all testing goes according to plan. You'll also be able to destroy and not just inactivate a particle system after loop has finished (as in the animation).
     
  48. mriegger

    mriegger

    Joined:
    Mar 28, 2009
    Posts:
    7
    Hello,

    Are there any known side effects with Particle Playground and Occlusion culling?

    One of our artists recently purchased Particle Playground and it very much enjoying it. He is having a very unusual side effect though; when a Playground effect is dragged into the level, the shadows on our main directional light start turning off/on at certain camera angles.

    The problem goes away when we disable Occlusion Culling on the camera. The sunlight still has shadow mapping enabled in the Inspector, but it just isn't being applied at some view angles.

    I am really quite stumped with this one. Do you have any idea?
     
  49. RavenMikal

    RavenMikal

    Joined:
    Oct 18, 2014
    Posts:
    144
    Save,

    are their any issues with snapshots in regards to using them with skinned mesh objects? lol....I only ask because I notice all of their sources for the other snapshots is the snapshot system, which is normally where I pick skinned mesh object...I'm likely missing something >.<

    effectively what I've done is, as a test, taken an animated horse model and particalized it, over a semi transparent model, so I'm trying to go from a swirling smoke to that, I know I'm going to have to animate the models transparency, but as far as the particles, it should work, right?
     
    Last edited: Oct 24, 2014
  50. save

    save

    Joined:
    Nov 21, 2008
    Posts:
    744
    Hi!
    That's a new one, I'm stumped as well. I'm trying to reproduce the issue on Unity 4.3.4 but I can't see it happening. As the Playground uses the Shuriken component to render the origin of the problem may be a bug on their side. If possible, please send a sample scene to support@polyfied.com and let me know which version of Unity you're using.

    You can transition a snapshot towards a skinned mesh, however it will transition over to a fixed initial state of when you took the snapshot. Therefore it's not ideal when working with animated skinned meshes as the vertices have a high probability of being in another position when the transition occur.
    Another and perhaps better solution in your case would be to use a skinned target manipulator which will move the particles over to the skinned mesh vertices in realtime, much like the procedural target example. You could work with the particle mask and fade time before you disable them if you'd like your particles to fade out as your mesh fades in. Did that make sense?

    To all:
    The 2.12 update has been pushed ahead a couple of days as I really want to make sure we get a stable release. Really soon available though!