I think your current particle system issue can be figured out. But I was giving some though to how this would be done in a mesh (vs individual game object), and decided to write some test code. What I came up with is a class called ParticleDisplay. It has most of the standard particle properties:
- color;
- lifetime;
- position;
- rotation;
- size;
- velocity;
- angularVelocity
There’s no emitter…just access to the array so the properties can be modified dynamically. Performance is around 10x faster than using game objects and roughly 1/2 the performance of a particle system with all particles displayed. I’m sure there are general performance improvements to be had in this code, but given your hardware, the best thing would be to rework it to use threads.
using UnityEngine;
using System.Collections;
public class ParticleDisplay : MonoBehaviour {
public Camera camera; // Camera to use for rotation
public int maxParticles = 1000;
public bool dynamicColor = false; // Use color defined in particle
public Material material; // Material for particles
public float defaultParticleSize = 0.1f;
public Color defaultParticleColor = Color.white;
public struct Particle {
public float angularVelocity;
public Color color;
public float lifetime;
public Vector3 position;
public float rotation;
public float size;
public Vector3 velocity;
}
public Particle[] particles;
private const int quadsPerMesh = 16380;
private GameObject[] goMeshes;
private Mesh[] meshes;
private bool isRunning = false;
private Transform camTrans;
void Awake() {
if (camera == null)
camera = Camera.main;
if (camera != null)
camTrans = camera.transform;
CreateMeshes();
}
void CreateMeshes() {
if (maxParticles <= 0) return;
particles = new Particle[maxParticles];
for (int i = 0; i < particles.Length; i++) {
particles*.size = defaultParticleSize;*
_ particles*.color = defaultParticleColor;_
_ }*_
* int meshCount = maxParticles / quadsPerMesh + 1;*
* int quadsLastMesh = maxParticles % quadsPerMesh;*
* goMeshes = new GameObject[meshCount];*
* meshes = new Mesh[meshCount];*
* for (int i = 0; i < meshCount; i++) {*
* GameObject go = new GameObject();*
* go.transform.parent = transform;*
* MeshFilter mf = go.AddComponent();*
* Mesh mesh = new Mesh();*
* mesh.MarkDynamic ();*
* mf.mesh = mesh;*
* Renderer rend = go.AddComponent();*
* rend.material = material;*
* Vector3[] vertices;*
* if (i != meshCount - 1) {*
_ vertices = new Vector3[4 * quadsPerMesh];
* }*
* else {*
vertices = new Vector3[4 * quadsLastMesh];_
* }*
* mesh.vertices = vertices;*
_ int triangles = new int[mesh.vertices.Length / 2 * 3];_
* for (int j = 0; j < vertices.Length / 4; j++) {*
triangles[j * 6 + 0] = j * 4 + 0; // 0_ 3 0 ___ 3
_ triangles[j * 6 + 1] = j * 4 + 3; // | / | /|_
triangles[j * 6 + 2] = j * 4 + 1; // 1|/ 1|/__|2
_ triangles[j * 6 + 3] = j * 4 + 3; // 3_
_ triangles[j * 6 + 4] = j * 4 + 2; // /|
triangles[j * 6 + 5] = j * 4 + 1; // 1/|2
* }*
* mesh.triangles = triangles;*
* Color[] colors = new Color[mesh.vertices.Length];*
* for (int j = 0; j < colors.Length; j++) {*
* colors[j] = defaultParticleColor;*
* }*
* mesh.colors = colors;*
* Vector2[] uvs = new Vector2[mesh.vertices.Length];*
* for (int j = 0; j < vertices.Length / 4; j++) {*
_ uvs[j * 4 + 0] = new Vector2(0,1);
uvs[j * 4 + 1] = new Vector2(0,0);
uvs[j * 4 + 2] = new Vector2(1,0);
uvs[j * 4 + 3] = new Vector2(1,1);_
* }*
* mesh.uv = uvs;*
_ goMeshes = go;
meshes = mesh;
* }
}*_
* public void Clear () {*
* for (int i = 0; i < particles.Length; i++)*
_ particles*.lifetime = -1.0f;
}*_
* public void Play() {*
* isRunning = true;*
* foreach (GameObject go in goMeshes) go.renderer.enabled = true;*
* }*
* public void Stop () {*
* isRunning = false;*
* foreach (GameObject go in goMeshes) go.renderer.enabled = false;*
* }*
* void LateUpdate() {*
* if (!isRunning) return;*
* Vector3 fwd = camTrans.forward;*
* Quaternion qCam = camTrans.rotation;*
* for (int i = 0; i < meshes.Length; i++) {*
_ Mesh mesh = meshes*;
Vector3 vertices = mesh.vertices;
Color colors = null;
if (dynamicColor) {
colors = mesh.colors;
}
for (int j = 0; j < vertices.Length / 4; j++) {*
Particle particle = particles[i * quadsPerMesh + j];_
* if (particle.lifetime < 0) {*
_ vertices[j * 4] = Vector3.zero;
vertices[j * 4 + 1] = Vector3.zero;
vertices[j * 4 + 2] = Vector3.zero;
vertices[j * 4 + 3] = Vector3.zero;
* }
else {
float l = particle.size / 2.0f;
Vector3 v0 = new Vector3(-l, l, 0);
Vector3 v1 = new Vector3(-l,-l, 0);
Vector3 v2 = new Vector3( l,-l, 0);
Vector3 v3 = new Vector3( l, l, 0);*_
_ Quaternion q = Quaternion.AngleAxis(particle.rotation, fwd) * qCam;_
_ vertices[j * 4] = particle.position + q * v0;
vertices[j * 4 + 1] = particle.position + q * v1;
vertices[j * 4 + 2] = particle.position + q * v2;
vertices[j * 4 + 3] = particle.position + q * v3;_
* if (dynamicColor) {*
_ colors[j * 4] = particle.color;
colors[j * 4 + 1] = particle.color;
colors[j * 4 + 2] = particle.color;
colors[j * 4 + 3] = particle.color;
* }*_
* particle.lifetime -= Time.deltaTime;*
_ particle.rotation += particle.angularVelocity * Time.deltaTime;
particle.position += particle.velocity * Time.deltaTime;_
_ particles[i * quadsPerMesh + j] = particle;
* }
}
mesh.vertices = vertices;*_
* if (dynamicColor) {*
* mesh.colors = colors;*
* }*
* }*
* }*
}
And here is simple class as an example of how to drive the ParticleDisplay class:
using UnityEngine;
using System.Collections;
public class ParticleDisplayTest : MonoBehaviour {
* private ParticleDisplay pd;*
* private ParticleDisplay.Particle[] pdp;*
* void Start () {*
* pd = GetComponent();*
* pdp = pd.particles;*
* for (int i = 0; i < pdp.Length; i++) {*
* Particle p;*
_ pdp*.velocity = Random.insideUnitSphere;
pdp.angularVelocity = Random.Range (0.0f, 180.0f);
pdp.lifetime = Mathf.Infinity;
pdp.color = new Color(Random.Range (0.0f, 1.0f), Random.Range (0.0f, 1.0f), Random.Range (0.0f, 1.0f));
}*_
* pd.Play();*
* }*
* void Update () {*
* for (int i = 0; i < pdp.Length; i++) {*
_ if (pdp*.position.magnitude > 8.0f) {
pdp.velocity = -pdp.velocity;
pdp.position = pdp.position.normalized * 7.999f;
pdp.color.r = (pdp.color.r + Time.deltaTime * 0.5f) % 1.0f;
}
}
}
}*
There’s no default material, so you’ll need to create one. I used Particles/Additive as the shader, and a snowflake with transparency for the test image._