Why are all the particles in my particle system disappearing when it's off screen?

I am developing a 2D space game where the stars in the background are generated by multiple shuriken particle systems. I’ll try to explain this as clearly as possible.

The way I do it is that I have multiple “layers” of stars all at different z positions and I use a custom x-y parallax scroll script to move the “layers” of stars at different speeds to give an illusion of depth. These layers are all game objects with a particle system component attached to them.

I use a script to generate all the stars in a layer that works by starting from a centre point and then, using the Random.insideUnitCircle function, I generate all the stars for that layer.

Unfortunately, whenever the centre point of any layer moves off screen, all the particles in that layer completely disappear. When it comes back on screen, they all reappear. It’s incredibly frustrating because it’s a really cool effect when it works. Is there anybody who can help figure out the issue?

I have looked everywhere for an answer and nobody has a solution that works for me. None of the following works:

  1. Setting the simulation space from local to global is not possible because I need it to be local in order to move each layer appropriately
  2. Changing the scale, it’s always been set to 1.
  3. Enabling subemitters

I have done all my work in 4.5 and now the 4.6 beta, where the particles still all disappear. If you need some more specifics, please let me know.

Thanks.

If you don’t get your particle system working, I have an alternate solution for you. From your description, what you need is a way to build a static mesh of quads. That mesh will belong to a game object, so you can move the whole mesh by moving the game object. Below is some code. You attach it to an empty game object, add a material, and then in a separate script set the properties for an array of quads…a maximum of 16380 per mesh. For each quad you can specify:

  • local position
  • 2D (i.e. ‘z’) rotation
  • size
  • spritesheet frame (starting in the lower left corner and working by rows up

using UnityEngine;
using System.Collections;

public class QuadDisplay : MonoBehaviour {
	public Camera camera;                     // Camera to use for rotation
	public Material material;                 // Material for quads
	public float defaultquadsize = 0.2f;
	public int spriteRows = 4;      
	public int spriteCols = 4;

	public struct Quad {
		public Vector3 position;
		public float rotation;
		public float size;
		public int frame;
	}

	private int quadCount = 1000;
	public Quad[] quads;
	private float spriteCellWidth;
	private float spriteCellHeight;
	private int spriteSheetCellCount;
	private const int maxQuads = 16380;
	private Mesh mesh;
	private Transform camTrans;

	void Awake() {
		if (camera == null) 
			camera = Camera.main;

		if (camera != null)
			camTrans = camera.transform;

		MeshFilter mf = GetComponent<MeshFilter>();
		if (mf == null) {
			mf = gameObject.AddComponent<MeshFilter>();
		}
		mesh = new Mesh();
		mf.mesh = mesh;
		mesh.MarkDynamic ();
		Renderer rend = gameObject.AddComponent<MeshRenderer>();
		rend.material = material;

		if (quadCount > maxQuads) {
			Debug.Log ("Maximum of 16380 Quads");
		}

		quadCount = Mathf.Clamp (quadCount, 0, maxQuads);
		spriteCellWidth =  1.0f / spriteCols;
		spriteCellHeight = 1.0f / spriteRows;
		spriteSheetCellCount = spriteRows * spriteCols;

		quads = new Quad[maxQuads];
		for (int i = 0; i < quadCount; i++) {
			quads*.size = defaultquadsize;*
  •  }*
    
  • }*

  • public void Apply(int count) {*

  •  if (count <= 0) return;*
    
  •  quadCount = count;*
    

_ Vector3 vertices = new Vector3[4 * quadCount];_

  •  Vector2[] uvs = new Vector2[vertices.Length];*
    
  •  Vector3 fwd = camTrans.forward;*
    
  •  Quaternion qCam = camTrans.rotation;*
    
  •  for (int j = 0; j < vertices.Length / 4; j++) {*
    
  •  	Quad quad = quads[j];*
    
  •  	float l = quad.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(quad.rotation, fwd) * qCam;_

_ vertices[j * 4] = quad.position + q * v0;_
_ vertices[j * 4 + 1] = quad.position + q * v1;_
_ vertices[j * 4 + 2] = quad.position + q * v2;_
_ vertices[j * 4 + 3] = quad.position + q * v3;_

  •  	int frame = quad.frame % spriteSheetCellCount;*
    

_ float x = quad.frame % spriteCols * spriteCellWidth;_
_ float y = quad.frame / spriteRows * spriteCellHeight;_

_ uvs[j * 4] = new Vector2(x + 0.01f, y + spriteCellWidth - 0.01f);_
_ uvs[j * 4 + 1] = new Vector2(x + 0.01f, y + 0.01f);_
_ uvs[j * 4 + 2] = new Vector2(x + spriteCellWidth - 0.01f, y + 0.01f);_
_ uvs[j * 4 + 3] = new Vector2(x + spriteCellWidth - 0.01f, y + spriteCellHeight - 0.01f);_

  •  }*
    
  •  mesh.triangles = null;*
    
  • mesh.vertices = vertices;*
  • mesh.uv = uvs;*

_ 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;*

  • }*
    }
    Here is a demonstration script on how to drive it. The following script will generate a different star field each time the space bar is pressed:
    using UnityEngine;
    using System.Collections;

public class StarDriver : MonoBehaviour {

  • void Start() {*

  •  BuildStars();*
    
  • }*

  • void Update() {*

  •  if (Input.GetKeyDown (KeyCode.Space)) {*
    
  •  	BuildStars();*
    
  •  }*
    
  • }*

  • void BuildStars () {*

  •  QuadDisplay qd = GetComponent<QuadDisplay>();*
    
  •  int count = Random.Range(3000, 10000);*
    
  •  for (int i = 0; i < count; i++) {*
    

_ qd.quads*.position = new Vector3(Random.Range (-8.0f, 8.0f), Random.Range (-6.0f, 6.0f), 0.0f);_
_ qd.quads.frame = Random.Range (0,16);
qd.quads.size = Random.Range (0.1f, 0.3f);
}
qd.Apply(count);
}
}
Here is the spritesheet I used for my test:
[31649-stars.png*
|31649]*

I made the texture ‘Truecolor’ and used in a material with Unlit/Transparent as the shader. Here is a sample result:
[31651-starfield.png|31651]
Not bad. More work on the spritesheet and perhaps a layout that did more clumping would produce a more realistic result.

_*