void OnGUI() { scaleMode = (ParticleSystemScalingMode)GUI.SelectionGrid(new Rect(25, 25, 300, 30), (int)scaleMode, new GUIContent[] { new GUIContent("Hierarchy"), new GUIContent("Local"), new GUIContent("Shape") }, 3); GUI.Label(new Rect(25, 80, 100, 30), "Scale"); sliderValue = GUI.HorizontalSlider(new Rect(125, 85, 100, 30), sliderValue, 0.0F, 5.0F); GUI.Label(new Rect(25, 120, 100, 30), "Parent Scale"); parentSliderValue = GUI.HorizontalSlider(new Rect(125, 125, 100, 30), parentSliderValue, 0.0F, 5.0F); }
// ... void setScalingModeForAll(ParticleSystemScalingMode mode) { saveUndo(); for (int i = 0; i < particleSystems.Count; i++) { ParticleSystem.MainModule m = particleSystems[i].main; m.scalingMode = mode; } }
protected override void ReadFromImpl(object obj) { base.ReadFromImpl(obj); ParticleSystem.MainModule uo = (ParticleSystem.MainModule)obj; duration = uo.duration; loop = uo.loop; prewarm = uo.prewarm; startDelay = uo.startDelay; startDelayMultiplier = uo.startDelayMultiplier; startLifetime = uo.startLifetime; startLifetimeMultiplier = uo.startLifetimeMultiplier; startSpeed = uo.startSpeed; startSpeedMultiplier = uo.startSpeedMultiplier; startSize3D = uo.startSize3D; startSize = uo.startSize; startSizeMultiplier = uo.startSizeMultiplier; startSizeX = uo.startSizeX; startSizeXMultiplier = uo.startSizeXMultiplier; startSizeY = uo.startSizeY; startSizeYMultiplier = uo.startSizeYMultiplier; startSizeZ = uo.startSizeZ; startSizeZMultiplier = uo.startSizeZMultiplier; startRotation3D = uo.startRotation3D; startRotation = uo.startRotation; startRotationMultiplier = uo.startRotationMultiplier; startRotationX = uo.startRotationX; startRotationXMultiplier = uo.startRotationXMultiplier; startRotationY = uo.startRotationY; startRotationYMultiplier = uo.startRotationYMultiplier; startRotationZ = uo.startRotationZ; startRotationZMultiplier = uo.startRotationZMultiplier; flipRotation = uo.flipRotation; startColor = uo.startColor; gravityModifier = uo.gravityModifier; gravityModifierMultiplier = uo.gravityModifierMultiplier; simulationSpace = uo.simulationSpace; customSimulationSpace = ToID(uo.customSimulationSpace); simulationSpeed = uo.simulationSpeed; useUnscaledTime = uo.useUnscaledTime; scalingMode = uo.scalingMode; playOnAwake = uo.playOnAwake; maxParticles = uo.maxParticles; emitterVelocityMode = uo.emitterVelocityMode; stopAction = uo.stopAction; cullingMode = uo.cullingMode; ringBufferMode = uo.ringBufferMode; ringBufferLoopRange = uo.ringBufferLoopRange; }
static void UpdateScaleMode(ParticleSystemScalingMode mode) { ParticleSystemProcess((particleSystem) => { if (particleSystem.main.scalingMode != mode) { particleSystem.scalingMode = mode; /* * SerializedObject so = new SerializedObject(particleSystem); * * so.FindProperty(" ParticleSystem.MainModule.scalingMode").enumValueIndex = 1; * * so.ApplyModifiedProperties(); * particleSystem.main.scalingMode = mode; */ return(true); } return(false); }); }
// ... void LateUpdate() { if (trianglesMeshFilter) { switch (particleSystemMainModule.simulationSpace) { case ParticleSystemSimulationSpace.World: { // Make sure this is always at origin, or triangle mesh moves out of sync since // the mesh triangle vertices are already set to the world space particle positions. trianglesMeshFilter.transform.position = Vector3.zero; break; } case ParticleSystemSimulationSpace.Local: { // In local space it should follow me. trianglesMeshFilter.transform.position = transform.position; trianglesMeshFilter.transform.rotation = transform.rotation; break; } case ParticleSystemSimulationSpace.Custom: { // In custom space it should follow the custom transform. trianglesMeshFilter.transform.position = particleSystemMainModule.customSimulationSpace.position; trianglesMeshFilter.transform.rotation = particleSystemMainModule.customSimulationSpace.rotation; break; } } } // Filter doesn't exist but mesh is there? That means the filter reference was lost. Clear the mesh. // AKA... If mesh filter is gone (deleted and/or reference nulled), clear the mesh. else if (trianglesMesh) { trianglesMesh.Clear(); } int lineRenderersCount = lineRenderers.Count; // In case max line renderers value is changed at runtime -> destroy extra. if (lineRenderersCount > maxLineRenderers) { for (int i = maxLineRenderers; i < lineRenderersCount; i++) { Destroy(lineRenderers[i].gameObject); } lineRenderers.RemoveRange(maxLineRenderers, lineRenderersCount - maxLineRenderers); //lineRendererData.RemoveRange(maxLineRenderers, lineRenderersCount - maxLineRenderers); lineRenderersCount -= lineRenderersCount - maxLineRenderers; } if (alwaysUpdate || visible) { // Prevent constant allocations so long as max particle count doesn't change. int maxParticles = particleSystemMainModule.maxParticles; if (particles == null || particles.Length < maxParticles) { particles = new ParticleSystem.Particle[maxParticles]; particlePositions = new Vector3[maxParticles]; particleColours = new Color[maxParticles]; particleSizes = new float[maxParticles]; } float deltaTime = Time.deltaTime; timer += deltaTime; if (timer >= delay) { timer = 0.0f; int lrIndex = 0; allConnectedParticles.Clear(); // Only update if drawing/making connections. if (maxConnections > 0 && maxLineRenderers > 0) { particleSystem.GetParticles(particles); //particleSystem.GetCustomParticleData(customParticleData, ParticleSystemCustomData.Custom1); int particleCount = particleSystem.particleCount; float maxDistanceSqr = maxDistance * maxDistance; ParticleSystemSimulationSpace simulationSpace = particleSystemMainModule.simulationSpace; ParticleSystemScalingMode scalingMode = particleSystemMainModule.scalingMode; Transform customSimulationSpaceTransform = particleSystemMainModule.customSimulationSpace; Color lineRendererStartColour = lineRendererTemplate.startColor; Color lineRendererEndColour = lineRendererTemplate.endColor; float lineRendererStartWidth = lineRendererTemplate.startWidth * lineRendererTemplate.widthMultiplier; float lineRendererEndWidth = lineRendererTemplate.endWidth * lineRendererTemplate.widthMultiplier; // Save particle properties in a quick loop (accessing these is expensive and loops significantly more later, so it's better to save them once now). for (int i = 0; i < particleCount; i++) { particlePositions[i] = particles[i].position; particleColours[i] = particles[i].GetCurrentColor(particleSystem); particleSizes[i] = particles[i].GetCurrentSize(particleSystem); // Default is 0.0f, so if default, this is a new particle and I need to assign a custom ID. //if (customParticleData[i].x == 0.0f) //{ // // ++value -> increment first, then return that value. // // That way it won't be zero (default) the first time I use it. // customParticleData[i] = new Vector4(++uniqueParticleID, 0, 0, 0); //} } //particleSystem.SetCustomParticleData(customParticleData, ParticleSystemCustomData.Custom1); Vector3 p1p2_difference; // If in world space, there's no need to do any of the extra calculations... simplify the loop! if (simulationSpace == ParticleSystemSimulationSpace.World) { for (int i = 0; i < particleCount; i++) { if (lrIndex == maxLineRenderers) { break; } Color particleColour = particleColours[i]; Color lineStartColour = Color.LerpUnclamped(lineRendererStartColour, particleColour, colourFromParticle); float lineStartColourOriginalAlpha = Mathf.LerpUnclamped(lineRendererStartColour.a, particleColour.a, alphaFromParticle); lineStartColour.a = lineStartColourOriginalAlpha; float lineStartWidth = Mathf.LerpUnclamped(lineRendererStartWidth, particleSizes[i], widthFromParticle); int connections = 0; int[] connectedParticles = new int[maxConnections + 1]; for (int j = i + 1; j < particleCount; j++) { p1p2_difference.x = particlePositions[i].x - particlePositions[j].x; p1p2_difference.y = particlePositions[i].y - particlePositions[j].y; p1p2_difference.z = particlePositions[i].z - particlePositions[j].z; //float distanceSqr = Vector3.SqrMagnitude(p1p2_difference); float distanceSqr = p1p2_difference.x * p1p2_difference.x + p1p2_difference.y * p1p2_difference.y + p1p2_difference.z * p1p2_difference.z; if (distanceSqr <= maxDistanceSqr) { LineRenderer lr; if (lrIndex == lineRenderersCount) { lr = Instantiate(lineRendererTemplate, _transform, false); lineRenderers.Add(lr); lineRenderersCount++; } lr = lineRenderers[lrIndex]; lr.enabled = true; lr.SetPosition(0, particlePositions[i]); lr.SetPosition(1, particlePositions[j]); float alphaAttenuation = alphaOverNormalizedDistance.Evaluate(distanceSqr / maxDistanceSqr); lineStartColour.a = lineStartColourOriginalAlpha * alphaAttenuation; lr.startColor = lineStartColour; particleColour = particleColours[j]; Color lineEndColour = Color.LerpUnclamped(lineRendererEndColour, particleColour, colourFromParticle); lineEndColour.a = Mathf.LerpUnclamped(lineRendererEndColour.a, particleColour.a, alphaFromParticle); lr.endColor = lineEndColour; lr.startWidth = lineStartWidth; lr.endWidth = Mathf.LerpUnclamped(lineRendererEndWidth, particleSizes[j], widthFromParticle); lrIndex++; connections++; // Intentionally taken AFTER connections++ (because index = 0 is the i'th / line origin particle). connectedParticles[connections] = j; if (connections == maxConnections || lrIndex == maxLineRenderers) { break; } } } if (connections >= 2) { connectedParticles[0] = i; allConnectedParticles.Add(connectedParticles); } } } else { Vector3 position = Vector3.zero; Quaternion rotation = Quaternion.identity; Vector3 localScale = Vector3.one; Transform simulationSpaceTransform = _transform; switch (simulationSpace) { case ParticleSystemSimulationSpace.Local: { position = simulationSpaceTransform.position; rotation = simulationSpaceTransform.rotation; localScale = simulationSpaceTransform.localScale; break; } case ParticleSystemSimulationSpace.Custom: { simulationSpaceTransform = customSimulationSpaceTransform; position = simulationSpaceTransform.position; rotation = simulationSpaceTransform.rotation; localScale = simulationSpaceTransform.localScale; break; } default: { throw new System.NotSupportedException( string.Format("Unsupported scaling mode '{0}'.", simulationSpace)); } } // I put these here so I can take out the default exception case. // Else I'd have a compiler error for potentially unassigned variables. Vector3 p1_position = Vector3.zero; Vector3 p2_position = Vector3.zero; for (int i = 0; i < particleCount; i++) { if (lrIndex == maxLineRenderers) { break; } switch (simulationSpace) { case ParticleSystemSimulationSpace.Local: case ParticleSystemSimulationSpace.Custom: { switch (scalingMode) { case ParticleSystemScalingMode.Hierarchy: { p1_position = simulationSpaceTransform.TransformPoint(particlePositions[i]); break; } case ParticleSystemScalingMode.Local: { // Order is important. //p1_position = Vector3.Scale(particlePositions[i], localScale); p1_position.x = particlePositions[i].x * localScale.x; p1_position.y = particlePositions[i].y * localScale.y; p1_position.z = particlePositions[i].z * localScale.z; p1_position = rotation * p1_position; //p1_position += position; p1_position.x += position.x; p1_position.y += position.y; p1_position.z += position.z; break; } case ParticleSystemScalingMode.Shape: { // Order is important. p1_position = rotation * particlePositions[i]; //p1_position += position; p1_position.x += position.x; p1_position.y += position.y; p1_position.z += position.z; break; } default: { throw new System.NotSupportedException( string.Format("Unsupported scaling mode '{0}'.", scalingMode)); } } break; } } Color particleColour = particleColours[i]; Color lineStartColour = Color.LerpUnclamped(lineRendererStartColour, particleColour, colourFromParticle); float lineStartColourOriginalAlpha = Mathf.LerpUnclamped(lineRendererStartColour.a, particleColour.a, alphaFromParticle); lineStartColour.a = lineStartColourOriginalAlpha; float lineStartWidth = Mathf.LerpUnclamped(lineRendererStartWidth, particleSizes[i], widthFromParticle); int connections = 0; int[] connectedParticles = new int[maxConnections + 1]; for (int j = i + 1; j < particleCount; j++) { // Note that because particles array is not sorted by distance, // but rather by spawn time (I think), the connections made are // not necessarily the closest. switch (simulationSpace) { case ParticleSystemSimulationSpace.Local: case ParticleSystemSimulationSpace.Custom: { switch (scalingMode) { case ParticleSystemScalingMode.Hierarchy: { p2_position = simulationSpaceTransform.TransformPoint(particlePositions[j]); break; } case ParticleSystemScalingMode.Local: { // Order is important. //p2_position = Vector3.Scale(particlePositions[j], localScale); p2_position.x = particlePositions[j].x * localScale.x; p2_position.y = particlePositions[j].y * localScale.y; p2_position.z = particlePositions[j].z * localScale.z; p2_position = rotation * p2_position; //p2_position += position; p2_position.x += position.x; p2_position.y += position.y; p2_position.z += position.z; break; } case ParticleSystemScalingMode.Shape: { // Order is important. p2_position = rotation * particlePositions[j]; //p2_position += position; p2_position.x += position.x; p2_position.y += position.y; p2_position.z += position.z; break; } default: { throw new System.NotSupportedException( string.Format("Unsupported scaling mode '{0}'.", scalingMode)); } } break; } } p1p2_difference.x = particlePositions[i].x - particlePositions[j].x; p1p2_difference.y = particlePositions[i].y - particlePositions[j].y; p1p2_difference.z = particlePositions[i].z - particlePositions[j].z; // Note that distance is always calculated in WORLD SPACE. // Scaling the particle system will stretch the distances // and may require adjusting the maxDistance value. // I could also do it in local space (which may actually make more // sense) by just getting the difference of the positions without // all the transformations. This also provides opportunity for // optimization as I can limit the world space transform calculations // to only happen if a particle is within range. // Think about: Putting in a bool to switch between the two? //float distanceSqr = Vector3.SqrMagnitude(p1p2_difference); float distanceSqr = p1p2_difference.x * p1p2_difference.x + p1p2_difference.y * p1p2_difference.y + p1p2_difference.z * p1p2_difference.z; // If distance to particle within range, add new vertex position. // The larger the max distance, the quicker connections will // reach its max, terminating the loop earlier. So even though more lines have // to be drawn, it's still faster to have a larger maxDistance value because // the call to Vector3.Distance() is expensive. if (distanceSqr <= maxDistanceSqr) { LineRenderer lr; if (lrIndex == lineRenderersCount) { lr = Instantiate(lineRendererTemplate, _transform, false); lineRenderers.Add(lr); lineRenderersCount++; //lineRendererData.Add(new LineRendererData()); } lr = lineRenderers[lrIndex]; //LineRendererData lrd = lineRendererData[lrIndex]; lr.enabled = true; //lrd.previousStartParticleID = lrd.currentStartParticleID; //lrd.previousEndParticleID = lrd.currentEndParticleID; //lrd.currentStartParticleID = customParticleData[i].x; //lrd.currentEndParticleID = customParticleData[j].x; lr.SetPosition(0, p1_position); lr.SetPosition(1, p2_position); //if (lrd.currentStartParticleID != lrd.previousStartParticleID || lrd.currentEndParticleID != lrd.previousEndParticleID) //{ // lrd.timer = 0.0f; //} //if (lrd.timer < 1.0f) //{ // lrd.timer += deltaTime / fadeInTime; //} //if (lrd.timer > 1.0f) //{ // lrd.timer = 1.0f; //} float alphaAttenuation = alphaOverNormalizedDistance.Evaluate(distanceSqr / maxDistanceSqr); //float alphaAttenuation = lrd.timer * alphaOverNormalizedDistance.Evaluate(distanceSqr / maxDistanceSqr); lineStartColour.a = lineStartColourOriginalAlpha * alphaAttenuation; lr.startColor = lineStartColour; particleColour = particleColours[j]; Color lineEndColour = Color.LerpUnclamped(lineRendererEndColour, particleColour, colourFromParticle); lineEndColour.a = Mathf.LerpUnclamped(lineRendererEndColour.a, particleColour.a, alphaFromParticle) * alphaAttenuation; lr.endColor = lineEndColour; lr.startWidth = lineStartWidth; lr.endWidth = Mathf.LerpUnclamped(lineRendererEndWidth, particleSizes[j], widthFromParticle); //lineRendererData[lrIndex] = lrd; lrIndex++; connections++; // Intentionally taken AFTER connections++ (because index = 0 is the i'th / line origin particle). connectedParticles[connections] = j; if (connections == maxConnections || lrIndex == maxLineRenderers) { break; } } } if (connections >= 2) { connectedParticles[0] = i; allConnectedParticles.Add(connectedParticles); } } } } // Disable remaining line renderers from the pool that weren't used. for (int i = lrIndex; i < lineRenderersCount; i++) { if (lineRenderers[i].enabled) { lineRenderers[i].enabled = false; } } // I check against the filter rather than the mesh because the mesh should always exist as long as the filter reference is there. // This way I can stop drawing/updating should the filter reference be lost. if (trianglesMeshFilter) { // Triangles mesh. // For efficiency (and my own general laziness), I only bother taking the first triangle formed. // It doesn't matter all that much since this is an abstract effect anyway. int vertexCount = allConnectedParticles.Count * 3; Vector3[] vertices = new Vector3[vertexCount]; int[] triangles = new int[vertexCount]; Vector2[] uv = new Vector2[vertexCount]; Color[] colours = new Color[vertexCount]; float maxDistanceSqr = (maxDistance * maxDistance) * maxDistanceTriangleBias; for (int i = 0; i < allConnectedParticles.Count; i++) { int[] connectedParticles = allConnectedParticles[i]; float distanceSqr = 0.0f; if (trianglesDistanceCheck) { Vector3 particlePositionA = particlePositions[connectedParticles[1]]; Vector3 particlePositionB = particlePositions[connectedParticles[2]]; //distance = Vector3.Distance(particlePositionA, particlePositionB); Vector3 difference; difference.x = particlePositionA.x - particlePositionB.x; difference.y = particlePositionA.y - particlePositionB.y; difference.z = particlePositionA.z - particlePositionB.z; distanceSqr = difference.x * difference.x + difference.y * difference.y + difference.z * difference.z; } if (distanceSqr < maxDistanceSqr) { int i3 = i * 3; vertices[i3 + 0] = particlePositions[connectedParticles[0]]; vertices[i3 + 1] = particlePositions[connectedParticles[1]]; vertices[i3 + 2] = particlePositions[connectedParticles[2]]; uv[i3 + 0] = new Vector2(0.0f, 0.0f); uv[i3 + 1] = new Vector2(0.0f, 1.0f); uv[i3 + 2] = new Vector2(1.0f, 1.0f); triangles[i3 + 0] = i3 + 0; triangles[i3 + 1] = i3 + 1; triangles[i3 + 2] = i3 + 2; colours[i3 + 0] = particleColours[connectedParticles[0]]; colours[i3 + 1] = particleColours[connectedParticles[1]]; colours[i3 + 2] = particleColours[connectedParticles[2]]; colours[i3 + 0] = Color.LerpUnclamped(Color.white, particleColours[connectedParticles[0]], triangleColourFromParticle); colours[i3 + 1] = Color.LerpUnclamped(Color.white, particleColours[connectedParticles[1]], triangleColourFromParticle); colours[i3 + 2] = Color.LerpUnclamped(Color.white, particleColours[connectedParticles[2]], triangleColourFromParticle); colours[i3 + 0].a = Mathf.LerpUnclamped(1.0f, particleColours[connectedParticles[0]].a, triangleAlphaFromParticle); colours[i3 + 1].a = Mathf.LerpUnclamped(1.0f, particleColours[connectedParticles[1]].a, triangleAlphaFromParticle); colours[i3 + 2].a = Mathf.LerpUnclamped(1.0f, particleColours[connectedParticles[2]].a, triangleAlphaFromParticle); } } trianglesMesh.Clear(); trianglesMesh.vertices = vertices; trianglesMesh.uv = uv; trianglesMesh.triangles = triangles; trianglesMesh.colors = colours; } } } }
// ... void LateUpdate() { int lineRenderersCount = lineRenderers.Count; // In case max line renderers value is changed at runtime -> destroy extra. if (lineRenderersCount > maxLineRenderers) { for (int i = maxLineRenderers; i < lineRenderersCount; i++) { Destroy(lineRenderers[i].gameObject); } lineRenderers.RemoveRange(maxLineRenderers, lineRenderersCount - maxLineRenderers); lineRenderersCount -= lineRenderersCount - maxLineRenderers; } if (alwaysUpdate || visible) { // Prevent constant allocations so long as max particle count doesn't change. int maxParticles = particleSystemMainModule.maxParticles; if (particles == null || particles.Length < maxParticles) { particles = new ParticleSystem.Particle[maxParticles]; particlePositions = new Vector3[maxParticles]; particleColours = new Color[maxParticles]; particleSizes = new float[maxParticles]; } timer += Time.deltaTime; if (timer >= delay) { timer = 0.0f; int lrIndex = 0; // Only update if drawing/making connections. if (maxConnections > 0 && maxLineRenderers > 0) { particleSystem.GetParticles(particles); int particleCount = particleSystem.particleCount; float maxDistanceSqr = maxDistance * maxDistance; ParticleSystemSimulationSpace simulationSpace = particleSystemMainModule.simulationSpace; ParticleSystemScalingMode scalingMode = particleSystemMainModule.scalingMode; Transform customSimulationSpaceTransform = particleSystemMainModule.customSimulationSpace; Color lineRendererStartColour = lineRendererTemplate.startColor; Color lineRendererEndColour = lineRendererTemplate.endColor; float lineRendererStartWidth = lineRendererTemplate.startWidth * lineRendererTemplate.widthMultiplier; float lineRendererEndWidth = lineRendererTemplate.endWidth * lineRendererTemplate.widthMultiplier; // Save particle properties in a quick loop (accessing these is expensive and loops significantly more later, so it's better to save them once now). for (int i = 0; i < particleCount; i++) { particlePositions[i] = particles[i].position; particleColours[i] = particles[i].GetCurrentColor(particleSystem); particleSizes[i] = particles[i].GetCurrentSize(particleSystem); } Vector3 p1p2_difference; // If in world space, there's no need to do any of the extra calculations... simplify the loop! if (simulationSpace == ParticleSystemSimulationSpace.World) { for (int i = 0; i < particleCount; i++) { if (lrIndex == maxLineRenderers) { break; } Color particleColour = particleColours[i]; Color lineStartColour = Color.LerpUnclamped(lineRendererStartColour, particleColour, colourFromParticle); lineStartColour.a = Mathf.LerpUnclamped(lineRendererStartColour.a, particleColour.a, alphaFromParticle); float lineStartWidth = Mathf.LerpUnclamped(lineRendererStartWidth, particleSizes[i], widthFromParticle); int connections = 0; for (int j = i + 1; j < particleCount; j++) { p1p2_difference.x = particlePositions[i].x - particlePositions[j].x; p1p2_difference.y = particlePositions[i].y - particlePositions[j].y; p1p2_difference.z = particlePositions[i].z - particlePositions[j].z; //float distanceSqr = Vector3.SqrMagnitude(p1p2_difference); float distanceSqr = p1p2_difference.x * p1p2_difference.x + p1p2_difference.y * p1p2_difference.y + p1p2_difference.z * p1p2_difference.z; if (distanceSqr <= maxDistanceSqr) { LineRenderer lr; if (lrIndex == lineRenderersCount) { lr = Instantiate(lineRendererTemplate, _transform, false); lineRenderers.Add(lr); lineRenderersCount++; } lr = lineRenderers[lrIndex]; lr.enabled = true; lr.SetPosition(0, particlePositions[i]); lr.SetPosition(1, particlePositions[j]); lr.startColor = lineStartColour; particleColour = particleColours[j]; Color lineEndColour = Color.LerpUnclamped(lineRendererEndColour, particleColour, colourFromParticle); lineEndColour.a = Mathf.LerpUnclamped(lineRendererEndColour.a, particleColour.a, alphaFromParticle); lr.endColor = lineEndColour; lr.startWidth = lineStartWidth; lr.endWidth = Mathf.LerpUnclamped(lineRendererEndWidth, particleSizes[j], widthFromParticle); lrIndex++; connections++; if (connections == maxConnections || lrIndex == maxLineRenderers) { break; } } } } } else { Vector3 position = Vector3.zero; Quaternion rotation = Quaternion.identity; Vector3 localScale = Vector3.one; Transform simulationSpaceTransform = _transform; switch (simulationSpace) { case ParticleSystemSimulationSpace.Local: { position = simulationSpaceTransform.position; rotation = simulationSpaceTransform.rotation; localScale = simulationSpaceTransform.localScale; break; } case ParticleSystemSimulationSpace.Custom: { simulationSpaceTransform = customSimulationSpaceTransform; position = simulationSpaceTransform.position; rotation = simulationSpaceTransform.rotation; localScale = simulationSpaceTransform.localScale; break; } default: { throw new System.NotSupportedException( string.Format("Unsupported scaling mode '{0}'.", simulationSpace)); } } // I put these here so I can take out the default exception case. // Else I'd have a compiler error for potentially unassigned variables. Vector3 p1_position = Vector3.zero; Vector3 p2_position = Vector3.zero; for (int i = 0; i < particleCount; i++) { if (lrIndex == maxLineRenderers) { break; } switch (simulationSpace) { case ParticleSystemSimulationSpace.Local: case ParticleSystemSimulationSpace.Custom: { switch (scalingMode) { case ParticleSystemScalingMode.Hierarchy: { p1_position = simulationSpaceTransform.TransformPoint(particlePositions[i]); break; } case ParticleSystemScalingMode.Local: { // Order is important. //p1_position = Vector3.Scale(particlePositions[i], localScale); p1_position.x = particlePositions[i].x * localScale.x; p1_position.y = particlePositions[i].y * localScale.y; p1_position.z = particlePositions[i].z * localScale.z; p1_position = rotation * p1_position; //p1_position += position; p1_position.x += position.x; p1_position.y += position.y; p1_position.z += position.z; break; } case ParticleSystemScalingMode.Shape: { // Order is important. p1_position = rotation * particlePositions[i]; //p1_position += position; p1_position.x += position.x; p1_position.y += position.y; p1_position.z += position.z; break; } default: { throw new System.NotSupportedException( string.Format("Unsupported scaling mode '{0}'.", scalingMode)); } } break; } } Color particleColour = particleColours[i]; Color lineStartColour = Color.LerpUnclamped(lineRendererStartColour, particleColour, colourFromParticle); lineStartColour.a = Mathf.LerpUnclamped(lineRendererStartColour.a, particleColour.a, alphaFromParticle); float lineStartWidth = Mathf.LerpUnclamped(lineRendererStartWidth, particleSizes[i], widthFromParticle); int connections = 0; for (int j = i + 1; j < particleCount; j++) { // Note that because particles array is not sorted by distance, // but rather by spawn time (I think), the connections made are // not necessarily the closest. switch (simulationSpace) { case ParticleSystemSimulationSpace.Local: case ParticleSystemSimulationSpace.Custom: { switch (scalingMode) { case ParticleSystemScalingMode.Hierarchy: { p2_position = simulationSpaceTransform.TransformPoint(particlePositions[j]); break; } case ParticleSystemScalingMode.Local: { // Order is important. //p2_position = Vector3.Scale(particlePositions[j], localScale); p2_position.x = particlePositions[j].x * localScale.x; p2_position.y = particlePositions[j].y * localScale.y; p2_position.z = particlePositions[j].z * localScale.z; p2_position = rotation * p2_position; //p2_position += position; p2_position.x += position.x; p2_position.y += position.y; p2_position.z += position.z; break; } case ParticleSystemScalingMode.Shape: { // Order is important. p2_position = rotation * particlePositions[j]; //p2_position += position; p2_position.x += position.x; p2_position.y += position.y; p2_position.z += position.z; break; } default: { throw new System.NotSupportedException( string.Format("Unsupported scaling mode '{0}'.", scalingMode)); } } break; } } p1p2_difference.x = particlePositions[i].x - particlePositions[j].x; p1p2_difference.y = particlePositions[i].y - particlePositions[j].y; p1p2_difference.z = particlePositions[i].z - particlePositions[j].z; // Note that distance is always calculated in WORLD SPACE. // Scaling the particle system will stretch the distances // and may require adjusting the maxDistance value. // I could also do it in local space (which may actually make more // sense) by just getting the difference of the positions without // all the transformations. This also provides opportunity for // optimization as I can limit the world space transform calculations // to only happen if a particle is within range. // Think about: Putting in a bool to switch between the two? //float distanceSqr = Vector3.SqrMagnitude(p1p2_difference); float distanceSqr = p1p2_difference.x * p1p2_difference.x + p1p2_difference.y * p1p2_difference.y + p1p2_difference.z * p1p2_difference.z; // If distance to particle within range, add new vertex position. // The larger the max distance, the quicker connections will // reach its max, terminating the loop earlier. So even though more lines have // to be drawn, it's still faster to have a larger maxDistance value because // the call to Vector3.Distance() is expensive. if (distanceSqr <= maxDistanceSqr) { LineRenderer lr; if (lrIndex == lineRenderersCount) { lr = Instantiate(lineRendererTemplate, _transform, false); lineRenderers.Add(lr); lineRenderersCount++; } lr = lineRenderers[lrIndex]; lr.enabled = true; lr.SetPosition(0, p1_position); lr.SetPosition(1, p2_position); lr.startColor = lineStartColour; particleColour = particleColours[j]; Color lineEndColour = Color.LerpUnclamped(lineRendererEndColour, particleColour, colourFromParticle); lineEndColour.a = Mathf.LerpUnclamped(lineRendererEndColour.a, particleColour.a, alphaFromParticle); lr.endColor = lineEndColour; lr.startWidth = lineStartWidth; lr.endWidth = Mathf.LerpUnclamped(lineRendererEndWidth, particleSizes[j], widthFromParticle); lrIndex++; connections++; if (connections == maxConnections || lrIndex == maxLineRenderers) { break; } } } } } } // Disable remaining line renderers from the pool that weren't used. for (int i = lrIndex; i < lineRenderersCount; i++) { if (lineRenderers[i].enabled) { lineRenderers[i].enabled = false; } } } } }
// ... protected virtual void LateUpdate() { _radius = scaledRadius; radiusSqr = _radius * _radius; forceDeltaTime = force * Time.deltaTime; transformPosition = transform.position + offset; // SELECTIVE. // If manually assigned a set of systems, use those no matter what. if (_particleSystems.Count != 0) { // If editor array size changed, clear and add again. if (particleSystems.Count != _particleSystems.Count) { particleSystems.Clear(); particleSystems.AddRange(_particleSystems); } // Else if array size is the same, then re-assign from // the editor array. I do this in case the elements are different // even though the size is the same. else { for (int i = 0; i < _particleSystems.Count; i++) { particleSystems[i] = _particleSystems[i]; } } } // LOCAL. // Else if attached to particle system, use only that. // Obviously, this will only happen if there are no systems specified in the array. else if (particleSystem) { // If just one element, assign as local PS component. if (particleSystems.Count == 1) { particleSystems[0] = particleSystem; } // Else, clear entire array and add only the one. else { particleSystems.Clear(); particleSystems.Add(particleSystem); } } // GLOBAL. // Else, take all the ones from the entire scene. // This is the most expensive since it searches the entire scene // and also requires an allocation for every frame due to not knowing // if the particle systems are all the same from the last frame unless // I had a list to compare to from last frame. In that case, I'm not sure // if the performance would be better or worse. Do a test later? else { particleSystems.Clear(); particleSystems.AddRange(FindObjectsOfType <ParticleSystem>()); } parameters = new GetForceParameters(); particleSystemsCount = particleSystems.Count; // If first frame (array is null) or length is less than the number of systems, initialize size of array. // I never shrink the array. Not sure if that's potentially super bad? I could always throw in a public // bool as an option to allow shrinking since there's a performance benefit for each, but depends on the // implementation case. if (particleSystemParticles == null || particleSystemParticles.Length < particleSystemsCount) { particleSystemParticles = new ParticleSystem.Particle[particleSystemsCount][]; particleSystemMainModules = new ParticleSystem.MainModule[particleSystemsCount]; particleSystemRenderers = new Renderer[particleSystemsCount]; particleSystemExternalForcesMultipliers = new float[particleSystemsCount]; for (int i = 0; i < particleSystemsCount; i++) { particleSystemMainModules[i] = particleSystems[i].main; particleSystemRenderers[i] = particleSystems[i].GetComponent <Renderer>(); particleSystemExternalForcesMultipliers[i] = particleSystems[i].externalForces.multiplier; } } for (int i = 0; i < particleSystemsCount; i++) { if (!particleSystemRenderers[i].isVisible && !alwaysUpdate) { continue; } int maxParticles = particleSystemMainModules[i].maxParticles; if (particleSystemParticles[i] == null || particleSystemParticles[i].Length < maxParticles) { particleSystemParticles[i] = new ParticleSystem.Particle[maxParticles]; } currentParticleSystem = particleSystems[i]; PerParticleSystemSetup(); int particleCount = currentParticleSystem.GetParticles(particleSystemParticles[i]); ParticleSystemSimulationSpace simulationSpace = particleSystemMainModules[i].simulationSpace; ParticleSystemScalingMode scalingMode = particleSystemMainModules[i].scalingMode; // I could also store the transforms in an array similar to what I do with modules. // Or, put all of those together into a struct and make an array out of that since // they'll always be assigned/updated at the same time. Transform currentParticleSystemTransform = currentParticleSystem.transform; Transform customSimulationSpaceTransform = particleSystemMainModules[i].customSimulationSpace; // If in world space, there's no need to do any of the extra calculations... simplify the loop! if (simulationSpace == ParticleSystemSimulationSpace.World) { for (int j = 0; j < particleCount; j++) { parameters.particlePosition = particleSystemParticles[i][j].position; parameters.scaledDirectionToAffectorCenter.x = transformPosition.x - parameters.particlePosition.x; parameters.scaledDirectionToAffectorCenter.y = transformPosition.y - parameters.particlePosition.y; parameters.scaledDirectionToAffectorCenter.z = transformPosition.z - parameters.particlePosition.z; parameters.distanceToAffectorCenterSqr = parameters.scaledDirectionToAffectorCenter.sqrMagnitude; if (parameters.distanceToAffectorCenterSqr < radiusSqr) { float distanceToCenterNormalized = parameters.distanceToAffectorCenterSqr / radiusSqr; float distanceScale = scaleForceByDistance.Evaluate(distanceToCenterNormalized); Vector3 force = GetForce(); float forceScale = (forceDeltaTime * distanceScale) * particleSystemExternalForcesMultipliers[i]; force.x *= forceScale; force.y *= forceScale; force.z *= forceScale; Vector3 particleVelocity = particleSystemParticles[i][j].velocity; particleVelocity.x += force.x; particleVelocity.y += force.y; particleVelocity.z += force.z; particleSystemParticles[i][j].velocity = particleVelocity; } } } else { Vector3 particleSystemPosition = Vector3.zero; Quaternion particleSystemRotation = Quaternion.identity; Vector3 particleSystemLocalScale = Vector3.one; Transform simulationSpaceTransform = currentParticleSystemTransform; switch (simulationSpace) { case ParticleSystemSimulationSpace.Local: { particleSystemPosition = simulationSpaceTransform.position; particleSystemRotation = simulationSpaceTransform.rotation; particleSystemLocalScale = simulationSpaceTransform.localScale; break; } case ParticleSystemSimulationSpace.Custom: { simulationSpaceTransform = customSimulationSpaceTransform; particleSystemPosition = simulationSpaceTransform.position; particleSystemRotation = simulationSpaceTransform.rotation; particleSystemLocalScale = simulationSpaceTransform.localScale; break; } default: { throw new System.NotSupportedException( string.Format("Unsupported scaling mode '{0}'.", simulationSpace)); } } for (int j = 0; j < particleCount; j++) { parameters.particlePosition = particleSystemParticles[i][j].position; switch (simulationSpace) { case ParticleSystemSimulationSpace.Local: case ParticleSystemSimulationSpace.Custom: { switch (scalingMode) { case ParticleSystemScalingMode.Hierarchy: { parameters.particlePosition = simulationSpaceTransform.TransformPoint(particleSystemParticles[i][j].position); break; } case ParticleSystemScalingMode.Local: { // Order is important. parameters.particlePosition = Vector3.Scale(parameters.particlePosition, particleSystemLocalScale); parameters.particlePosition = particleSystemRotation * parameters.particlePosition; parameters.particlePosition = parameters.particlePosition + particleSystemPosition; break; } case ParticleSystemScalingMode.Shape: { parameters.particlePosition = particleSystemRotation * parameters.particlePosition; parameters.particlePosition = parameters.particlePosition + particleSystemPosition; break; } default: { throw new System.NotSupportedException( string.Format("Unsupported scaling mode '{0}'.", scalingMode)); } } break; } } parameters.scaledDirectionToAffectorCenter.x = transformPosition.x - parameters.particlePosition.x; parameters.scaledDirectionToAffectorCenter.y = transformPosition.y - parameters.particlePosition.y; parameters.scaledDirectionToAffectorCenter.z = transformPosition.z - parameters.particlePosition.z; parameters.distanceToAffectorCenterSqr = parameters.scaledDirectionToAffectorCenter.sqrMagnitude; //particleSystemParticles[i][j].velocity += forceDeltaTime * Vector3.Normalize(parameters.scaledDirectionToAffectorCenter); if (parameters.distanceToAffectorCenterSqr < radiusSqr) { // 0.0f -> 0.99...f; float distanceToCenterNormalized = parameters.distanceToAffectorCenterSqr / radiusSqr; // Evaluating a curve within a loop which is very likely to exceed a few thousand // iterations produces a noticeable FPS drop (around minus 2 - 5). Might be a worthwhile // optimization to check outside all loops if the curve is constant (all keyframes same value), // and then run a different block of code if true that uses that value as a stored float without // having to call Evaluate(t). float distanceScale = scaleForceByDistance.Evaluate(distanceToCenterNormalized); // Expanded vector operations for optimization. I think this is already done by // the compiler, but it's nice to have for the editor anyway. Vector3 force = GetForce(); float forceScale = (forceDeltaTime * distanceScale) * particleSystemExternalForcesMultipliers[i]; force.x *= forceScale; force.y *= forceScale; force.z *= forceScale; switch (simulationSpace) { case ParticleSystemSimulationSpace.Local: case ParticleSystemSimulationSpace.Custom: { switch (scalingMode) { case ParticleSystemScalingMode.Hierarchy: { force = simulationSpaceTransform.InverseTransformVector(force); break; } case ParticleSystemScalingMode.Local: { // Order is important. // Notice how rotation and scale orders are reversed. force = Quaternion.Inverse(particleSystemRotation) * force; force = Vector3.Scale(force, new Vector3( 1.0f / particleSystemLocalScale.x, 1.0f / particleSystemLocalScale.y, 1.0f / particleSystemLocalScale.z)); break; } case ParticleSystemScalingMode.Shape: { force = Quaternion.Inverse(particleSystemRotation) * force; break; } // This would technically never execute since it's checked earlier (above). default: { throw new System.NotSupportedException( string.Format("Unsupported scaling mode '{0}'.", scalingMode)); } } break; } } Vector3 particleVelocity = particleSystemParticles[i][j].velocity; particleVelocity.x += force.x; particleVelocity.y += force.y; particleVelocity.z += force.z; particleSystemParticles[i][j].velocity = particleVelocity; } } } currentParticleSystem.SetParticles(particleSystemParticles[i], particleCount); } }
protected override JSONObject ToJSON(WXHierarchyContext context) { ParticleSystemRenderer particleSystemRenderer = particleSys.GetComponent <ParticleSystemRenderer>(); JSONObject json = new JSONObject(JSONObject.Type.OBJECT); JSONObject data = new JSONObject(JSONObject.Type.OBJECT); json.AddField("type", getTypeName()); json.AddField("data", data); JSONObject materials = new JSONObject(JSONObject.Type.ARRAY); data.AddField("materials", materials); Material[] mats = particleSystemRenderer.sharedMaterials; foreach (Material material in mats) { if (material != null) { WXMaterial materialConverter = new WXMaterial(material, particleSystemRenderer); string materialPath = materialConverter.Export(context.preset); materials.Add(materialPath); context.AddResource(materialPath); } } JSONObject modCommon = new JSONObject(JSONObject.Type.OBJECT); JSONObject modCommonData = new JSONObject(JSONObject.Type.OBJECT); data.AddField("common", modCommonData); modCommonData.AddField("startSize3D", particleSys.main.startSize3D); if (particleSys.main.startSize3D) { modCommonData.AddField("startSizeX", ParseMinMaxCurve(particleSys.main.startSizeX)); modCommonData.AddField("startSizeY", ParseMinMaxCurve(particleSys.main.startSizeY)); modCommonData.AddField("startSizeZ", ParseMinMaxCurve(particleSys.main.startSizeZ)); } else { modCommonData.AddField("startSize", ParseMinMaxCurve(particleSys.main.startSize)); } modCommonData.AddField("startColor", ParseMinMaxGradient(particleSys.main.startColor)); modCommonData.AddField("startLifetime", ParseMinMaxCurve(particleSys.main.startLifetime)); modCommonData.AddField("startRotation3D", particleSys.main.startRotation3D); if (particleSys.main.startRotation3D) { modCommonData.AddField("startRotationX", ParseMinMaxCurve(particleSys.main.startRotationX, (float)(180 / Math.PI))); modCommonData.AddField("startRotationY", ParseMinMaxCurve(particleSys.main.startRotationY, (float)(180 / Math.PI))); modCommonData.AddField("startRotationZ", ParseMinMaxCurve(particleSys.main.startRotationZ, (float)(180 / Math.PI))); } else { modCommonData.AddField("startRotationZ", ParseMinMaxCurve(particleSys.main.startRotation, (float)(180 / Math.PI))); } modCommonData.AddField("startSpeed", ParseMinMaxCurve(particleSys.main.startSpeed)); modCommonData.AddField("gravityModifier", ParseMinMaxCurve(particleSys.main.gravityModifier)); #if UNITY_2018_1_OR_NEWER modCommonData.AddField("randomizeRotation", particleSys.main.flipRotation); #endif modCommonData.AddField("randomSeed", particleSys.randomSeed); modCommonData.AddField("autoRandomSeed", particleSys.useAutoRandomSeed); ParticleSystemScalingMode pScalingMode = particleSys.main.scalingMode; int pScalingModeNum = 0; switch (pScalingMode) { case ParticleSystemScalingMode.Hierarchy: pScalingModeNum = 0; break; case ParticleSystemScalingMode.Local: pScalingModeNum = 1; break; case ParticleSystemScalingMode.Shape: pScalingModeNum = 2; break; } modCommonData.AddField("scalingMode", pScalingModeNum); ParticleSystemSimulationSpace simulationSpace = particleSys.main.simulationSpace; int simulationSpaceNum = 0; switch (simulationSpace) { case ParticleSystemSimulationSpace.Local: simulationSpaceNum = 0; break; case ParticleSystemSimulationSpace.World: simulationSpaceNum = 1; break; case ParticleSystemSimulationSpace.Custom: simulationSpaceNum = 2; break; } modCommonData.AddField("simulationSpace", simulationSpaceNum); JSONObject emitter = new JSONObject(JSONObject.Type.OBJECT); JSONObject emitterData = new JSONObject(JSONObject.Type.OBJECT); data.AddField("emitter", emitterData); emitterData.AddField("playOnAwake", particleSys.main.playOnAwake); emitterData.AddField("looping", particleSys.main.loop); emitterData.AddField("duration", particleSys.main.duration); emitterData.AddField("startDelay", ParseMinMaxCurve(particleSys.main.startDelay)); if (particleSys.emission.enabled) { JSONObject burst = new JSONObject(JSONObject.Type.ARRAY); emitterData.AddField("bursts", burst); int count = particleSys.emission.burstCount; ParticleSystem.Burst[] bursts = new ParticleSystem.Burst[count]; particleSys.emission.GetBursts(bursts); for (int i = 0; i < count; i++) { //burst.Add(ParseBurst(particleSys.emission.GetBurst(i))); burst.Add(ParseBurst(bursts[i])); } emitterData.AddField("rateOverTime", ParseMinMaxCurve(particleSys.emission.rateOverTime)); } emitterData.AddField("maxParticles", particleSys.main.maxParticles); if (particleSystemRenderer.enabled) { JSONObject renderer = new JSONObject(JSONObject.Type.OBJECT); JSONObject rendererData = new JSONObject(JSONObject.Type.OBJECT); data.AddField("renderer", rendererData); ParticleSystemRenderMode pRenderMode = particleSystemRenderer.renderMode; int pRenderModeNum = 0; switch (pRenderMode) { case ParticleSystemRenderMode.Billboard: pRenderModeNum = 1; break; case ParticleSystemRenderMode.Stretch: pRenderModeNum = 2; rendererData.AddField("cameraScale", particleSystemRenderer.cameraVelocityScale); rendererData.AddField("speedScale", particleSystemRenderer.velocityScale); rendererData.AddField("lengthScale", particleSystemRenderer.lengthScale); break; case ParticleSystemRenderMode.HorizontalBillboard: pRenderModeNum = 3; break; case ParticleSystemRenderMode.VerticalBillboard: pRenderModeNum = 4; break; case ParticleSystemRenderMode.Mesh: Mesh mesh = particleSystemRenderer.mesh; if (mesh != null) { WXMesh meshConverter = new WXMesh(mesh); string meshPath = meshConverter.Export(context.preset); rendererData.AddField("mesh", meshPath); rendererData.AddField("meshCount", particleSystemRenderer.meshCount); context.AddResource(meshPath); } else { Debug.LogError(string.Format("{0} mesh is null", particleSys.name)); } pRenderModeNum = 5; break; case ParticleSystemRenderMode.None: pRenderModeNum = 0; break; default: pRenderModeNum = 1; break; } rendererData.AddField("renderMode", pRenderModeNum); int mode = 1; switch (particleSystemRenderer.alignment) { case ParticleSystemRenderSpace.View: mode = 1; break; case ParticleSystemRenderSpace.World: mode = 2; break; case ParticleSystemRenderSpace.Local: mode = 3; break; case ParticleSystemRenderSpace.Facing: mode = 4; break; #if UNITY_2017_1_OR_NEWER case ParticleSystemRenderSpace.Velocity: mode = 5; break; #endif default: break; } rendererData.AddField("renderAlignment", mode); mode = 0; switch (particleSystemRenderer.sortMode) { case ParticleSystemSortMode.None: mode = 0; break; case ParticleSystemSortMode.Distance: mode = 1; break; case ParticleSystemSortMode.OldestInFront: mode = 2; break; case ParticleSystemSortMode.YoungestInFront: mode = 3; break; default: break; } rendererData.AddField("sortMode", mode); rendererData.AddField("sortingFudge", particleSystemRenderer.sortingFudge); rendererData.AddField("normalDirection", particleSystemRenderer.normalDirection); rendererData.AddField("minParticleSize", particleSystemRenderer.minParticleSize); rendererData.AddField("maxParticleSize", particleSystemRenderer.maxParticleSize); var flipValue = TryGetContainProperty(particleSystemRenderer, "flip"); if (flipValue != null) { rendererData.AddField("flip", GetVect3((Vector3)flipValue)); } else { renderer.AddField("flip", GetVect3(new Vector3(0, 0, 0))); } //rendererData.AddField("flip", GetVect3(particleSystemRenderer.flip)); rendererData.AddField("pivot", GetVect3(particleSystemRenderer.pivot)); var allowRollData = TryGetContainProperty(particleSystemRenderer, "allowRoll"); if (allowRollData != null) { rendererData.AddField("allowRoll", (bool)allowRollData); } else { rendererData.AddField("allowRoll", false); } } else { String info = "entity: " + particleSys.gameObject.name + " 的粒子组件没有renderer模块,不可导出;请加上renderer模块,或删除该粒子组件"; ErrorUtil.ExportErrorReporter.create() .setGameObject(particleSys.gameObject) .setHierarchyContext(context) .error(0, "粒子组件没有renderer模块,不可导出;请加上renderer模块,或删除该粒子组件"); } if (particleSys.rotationOverLifetime.enabled) { JSONObject rotationByLife = new JSONObject(JSONObject.Type.OBJECT); JSONObject rotationByLifeData = new JSONObject(JSONObject.Type.OBJECT); data.AddField("rotationByLife", rotationByLifeData); rotationByLifeData.AddField("separateAxes", particleSys.rotationOverLifetime.separateAxes); if (particleSys.rotationOverLifetime.separateAxes) { rotationByLifeData.AddField("x", ParseMinMaxCurve(particleSys.rotationOverLifetime.x, (float)(180 / Math.PI))); rotationByLifeData.AddField("y", ParseMinMaxCurve(particleSys.rotationOverLifetime.y, (float)(180 / Math.PI))); rotationByLifeData.AddField("z", ParseMinMaxCurve(particleSys.rotationOverLifetime.z, (float)(180 / Math.PI))); } else { rotationByLifeData.AddField("z", ParseMinMaxCurve(particleSys.rotationOverLifetime.z, (float)(180 / Math.PI))); } } if (particleSys.sizeOverLifetime.enabled) { JSONObject sizeOverLifetime = new JSONObject(JSONObject.Type.OBJECT); JSONObject sizeOverLifetimeData = new JSONObject(JSONObject.Type.OBJECT); data.AddField("sizeByLife", sizeOverLifetimeData); sizeOverLifetimeData.AddField("separateAxes", particleSys.sizeOverLifetime.separateAxes); if (particleSys.sizeOverLifetime.separateAxes) { sizeOverLifetimeData.AddField("x", ParseMinMaxCurve(particleSys.sizeOverLifetime.x)); sizeOverLifetimeData.AddField("y", ParseMinMaxCurve(particleSys.sizeOverLifetime.y)); sizeOverLifetimeData.AddField("z", ParseMinMaxCurve(particleSys.sizeOverLifetime.z)); } else { sizeOverLifetimeData.AddField("x", ParseMinMaxCurve(particleSys.sizeOverLifetime.size)); } } if (particleSys.velocityOverLifetime.enabled) { JSONObject speedByLifeData = new JSONObject(JSONObject.Type.OBJECT); data.AddField("speedByLife", speedByLifeData); switch (particleSys.velocityOverLifetime.space) { case ParticleSystemSimulationSpace.Local: speedByLifeData.AddField("space", 1); break; case ParticleSystemSimulationSpace.World: speedByLifeData.AddField("space", 2); break; case ParticleSystemSimulationSpace.Custom: break; default: break; } speedByLifeData.AddField("x", ParseMinMaxCurve(particleSys.velocityOverLifetime.x)); speedByLifeData.AddField("y", ParseMinMaxCurve(particleSys.velocityOverLifetime.y)); speedByLifeData.AddField("z", ParseMinMaxCurve(particleSys.velocityOverLifetime.z)); #if UNITY_2018_1_OR_NEWER speedByLifeData.AddField("orbitalX", ParseMinMaxCurve(particleSys.velocityOverLifetime.orbitalX)); speedByLifeData.AddField("orbitalY", ParseMinMaxCurve(particleSys.velocityOverLifetime.orbitalY)); speedByLifeData.AddField("orbitalZ", ParseMinMaxCurve(particleSys.velocityOverLifetime.orbitalZ)); speedByLifeData.AddField("orbitalOffsetX", ParseMinMaxCurve(particleSys.velocityOverLifetime.orbitalOffsetX)); speedByLifeData.AddField("orbitalOffsetY", ParseMinMaxCurve(particleSys.velocityOverLifetime.orbitalOffsetY)); speedByLifeData.AddField("orbitalOffsetZ", ParseMinMaxCurve(particleSys.velocityOverLifetime.orbitalOffsetZ)); speedByLifeData.AddField("radial", ParseMinMaxCurve(particleSys.velocityOverLifetime.radial)); #endif #if UNITY_2017_1_OR_NEWER speedByLifeData.AddField("speedScale", ParseMinMaxCurve(particleSys.velocityOverLifetime.speedModifier)); #endif } if (particleSys.limitVelocityOverLifetime.enabled) { JSONObject speedLimitByLifeData = new JSONObject(JSONObject.Type.OBJECT); data.AddField("speedLimitByLife", speedLimitByLifeData); speedLimitByLifeData.AddField("separateAxes", particleSys.limitVelocityOverLifetime.separateAxes); if (particleSys.limitVelocityOverLifetime.separateAxes) { speedLimitByLifeData.AddField("x", ParseMinMaxCurve(particleSys.limitVelocityOverLifetime.limitX, particleSys.limitVelocityOverLifetime.limitXMultiplier)); speedLimitByLifeData.AddField("y", ParseMinMaxCurve(particleSys.limitVelocityOverLifetime.limitY, particleSys.limitVelocityOverLifetime.limitYMultiplier)); speedLimitByLifeData.AddField("z", ParseMinMaxCurve(particleSys.limitVelocityOverLifetime.limitZ, particleSys.limitVelocityOverLifetime.limitZMultiplier)); } else { speedLimitByLifeData.AddField("x", ParseMinMaxCurve(particleSys.limitVelocityOverLifetime.limit, particleSys.limitVelocityOverLifetime.limitMultiplier)); } speedLimitByLifeData.AddField("dampen", particleSys.limitVelocityOverLifetime.dampen); switch (particleSys.limitVelocityOverLifetime.space) { case ParticleSystemSimulationSpace.Local: speedLimitByLifeData.AddField("space", 1); break; case ParticleSystemSimulationSpace.World: speedLimitByLifeData.AddField("space", 2); break; case ParticleSystemSimulationSpace.Custom: break; default: break; } #if UNITY_2017_1_OR_NEWER speedLimitByLifeData.AddField("drag", ParseMinMaxCurve(particleSys.limitVelocityOverLifetime.drag)); speedLimitByLifeData.AddField("dragMultiplyBySize", particleSys.limitVelocityOverLifetime.multiplyDragByParticleSize); speedLimitByLifeData.AddField("dragMultiplyBySpeed", particleSys.limitVelocityOverLifetime.multiplyDragByParticleVelocity); #endif } if (particleSys.colorOverLifetime.enabled) { JSONObject colorOverLifetime = new JSONObject(JSONObject.Type.OBJECT); JSONObject colorOverLifetimeData = new JSONObject(JSONObject.Type.OBJECT); data.AddField("colorByLife", colorOverLifetimeData); colorOverLifetimeData.AddField("gColor", ParseMinMaxGradient(particleSys.colorOverLifetime.color)); } if (particleSys.shape.enabled) { JSONObject shapeData = new JSONObject(JSONObject.Type.OBJECT); var haveShape = true; if (particleSys.shape.shapeType == ParticleSystemShapeType.Cone || particleSys.shape.shapeType == ParticleSystemShapeType.ConeVolume || particleSys.shape.shapeType == ParticleSystemShapeType.ConeVolumeShell || particleSys.shape.shapeType == ParticleSystemShapeType.ConeShell) { shapeData.AddField("shape", ParseConeShape(particleSys.shape)); } else if (particleSys.shape.shapeType == ParticleSystemShapeType.Sphere || particleSys.shape.shapeType == ParticleSystemShapeType.SphereShell) { shapeData.AddField("shape", ParseSphereShape(particleSys.shape)); } else if (particleSys.shape.shapeType == ParticleSystemShapeType.Circle || particleSys.shape.shapeType == ParticleSystemShapeType.CircleEdge) { shapeData.AddField("shape", ParseCircleShape(particleSys.shape)); } else if (particleSys.shape.shapeType == ParticleSystemShapeType.Box || particleSys.shape.shapeType == ParticleSystemShapeType.BoxEdge || particleSys.shape.shapeType == ParticleSystemShapeType.BoxShell) { shapeData.AddField("shape", ParseBox(particleSys.shape)); } else if (particleSys.shape.shapeType == ParticleSystemShapeType.Hemisphere || particleSys.shape.shapeType == ParticleSystemShapeType.HemisphereShell) { shapeData.AddField("shape", ParseHemisphere(particleSys.shape)); } // else if (particleSys.shape.shapeType == ParticleSystemShapeType.SingleSidedEdge) { // shapeData.AddField("shape", ParseSingleSidedEdge(particleSys.shape)); // } else { var parentChain = go.name; var parent = go.transform.parent; while (parent) { parentChain += " -> " + parent.gameObject.name; parent = parent.parent; } Debug.LogError("unSupport shape (" + particleSys.shape.shapeType.ToString() + ") at: " + parentChain); haveShape = false; } if (haveShape) { data.AddField("emitterShape", shapeData); } } if (particleSys.textureSheetAnimation.enabled) { JSONObject textureSheetAnimationData = new JSONObject(JSONObject.Type.OBJECT); data.AddField("textureSheetAnimation", textureSheetAnimationData); int mode = 1; #if UNITY_2017_1_OR_NEWER mode = 1; switch (particleSys.textureSheetAnimation.mode) { case ParticleSystemAnimationMode.Grid: mode = 1; break; case ParticleSystemAnimationMode.Sprites: mode = 2; break; default: break; } textureSheetAnimationData.AddField("mode", mode); #endif JSONObject vec2 = new JSONObject(JSONObject.Type.ARRAY); vec2.Add(particleSys.textureSheetAnimation.numTilesX); vec2.Add(particleSys.textureSheetAnimation.numTilesY); textureSheetAnimationData.AddField("tiles", vec2); mode = 1; switch (particleSys.textureSheetAnimation.animation) { case ParticleSystemAnimationType.WholeSheet: mode = 1; break; case ParticleSystemAnimationType.SingleRow: mode = 2; break; default: break; } textureSheetAnimationData.AddField("animationType", mode); textureSheetAnimationData.AddField("randomRow", particleSys.textureSheetAnimation.useRandomRow); textureSheetAnimationData.AddField("row", particleSys.textureSheetAnimation.rowIndex); //mode = 1; //switch (particleSys.textureSheetAnimation.timeMode) //{ // case ParticleSystemAnimationTimeMode.Lifetime: // mode = 1; // break; // case ParticleSystemAnimationTimeMode.Speed: // mode = 2; // break; // case ParticleSystemAnimationTimeMode.FPS: // mode = 3; // break; // default: // break; //} textureSheetAnimationData.AddField("timeMode", 1); if (mode == 1) { textureSheetAnimationData.AddField("frameOverTime", ParseMinMaxCurve(particleSys.textureSheetAnimation.frameOverTime, particleSys.textureSheetAnimation.numTilesX * particleSys.textureSheetAnimation.numTilesY)); } else { textureSheetAnimationData.AddField("frameOverTime", ParseMinMaxCurve(particleSys.textureSheetAnimation.frameOverTime, particleSys.textureSheetAnimation.numTilesX)); } textureSheetAnimationData.AddField("startFrame", ParseMinMaxCurve(particleSys.textureSheetAnimation.startFrame)); textureSheetAnimationData.AddField("cycles", particleSys.textureSheetAnimation.cycleCount); mode = 0; var a = particleSys.textureSheetAnimation.uvChannelMask; var b = a & UVChannelFlags.UV0; if ((a & UVChannelFlags.UV0) != 0) { mode += 1; } if ((a & UVChannelFlags.UV1) != 0) { mode += 2; } if ((a & UVChannelFlags.UV2) != 0) { mode += 3; } if ((a & UVChannelFlags.UV3) != 0) { mode += 4; } textureSheetAnimationData.AddField("affectedUVChannels", mode); } if (particleSys.subEmitters.enabled) { JSONObject subEmittersData = new JSONObject(JSONObject.Type.ARRAY); data.AddField("subEmitters", subEmittersData); int count = particleSys.subEmitters.subEmittersCount; for (int i = 0; i < count; i++) { ParticleSystemSubEmitterProperties properties = particleSys.subEmitters.GetSubEmitterProperties(i); ParticleSystemSubEmitterType type = particleSys.subEmitters.GetSubEmitterType(i); JSONObject res = new JSONObject(JSONObject.Type.OBJECT); int typeNum = 0; switch (type) { case ParticleSystemSubEmitterType.Birth: typeNum = 0; break; case ParticleSystemSubEmitterType.Collision: typeNum = 1; break; case ParticleSystemSubEmitterType.Death: typeNum = 2; break; default: break; } res.AddField("type", typeNum); int propertiesNum = 0; switch (properties) { case ParticleSystemSubEmitterProperties.InheritNothing: propertiesNum = 0; break; case ParticleSystemSubEmitterProperties.InheritEverything: propertiesNum = 1; break; case ParticleSystemSubEmitterProperties.InheritColor: propertiesNum = 2; break; case ParticleSystemSubEmitterProperties.InheritSize: propertiesNum = 3; break; case ParticleSystemSubEmitterProperties.InheritRotation: propertiesNum = 4; break; default: break; } res.AddField("properties", propertiesNum); subEmittersData.Add(res); } } return(json); }