How to make a particle system with different particles for each emit?

Hello,

I have texture atlas in which there are several frames of animation. I'd like to make a particle emitter that for each emitted particle would pick one frame from this atlas and use that as the particle texture. I do NOT want the particle to animate its appearance over its lifetime (like it would if I used the UV animation properties on the Particle Renderer component), just have a different frame on each emitted particle.

I tried attaching a script and modifying the material texture offset on each call to `Update()` but this of course changes the appearance of all emitted particles, which again is not what I want.

I guess what I'd really want is a way to access the texture offsets of an already emitted particle (so I could just change them on the "newest" particle every frame). I know I can access all the particles that the emitter has emitted via the `particles` array in the `ParticleEmitter` class, but the `Particle` class does not seem to provide a way to access the texture offsets.

Any ideas how I could accomplish this with the standard particle components? I'd really prefer not to write an ad hoc particle emitter just for this.

I'm doing this for the IPhone, so performance is premium.

Thanks in advance.

There is one simple but tricky way:
1 First create bitmap with particle images put in single column.
2 In Particle System enable subsystem “Texture Sheet Animation”.
3 Into it set “Animation” to “SingleRow”.
Now when particle will be spawn the system will randomly choose image for it from column. During each update system will try to animate it by selecting random images across row, but because there is only one image in every row then the image will stay the same.

I recently had to do this. It took a while to figure out, but it allows me to have > 10,000 particles each with a specific texture and color. The performance is reasonable on a macbook pro (i7, intel 4000 integrated graphics) I don’t know how good it would be on a mobile platform.

I can update the position of each particle in the update portion of my game loop by updating the position field of the particles array associated with the emitter.

I have a texture atlas that is a grid of circles each one containing a number:

(1)(2)(3)

(4)(5)(6)

(7)(8)(9)

I want to choose which part of the texture atlas to display based on a value.

First I created the particle system, material and emitter:

`
private void SetupGameObject()
{
this.gameObject = new GameObject(this._id);
this.gameObject.layer = Explorer.Application.LAYER_STAR;

		this.particleRenderer = this.gameObject.AddComponent<ParticleRenderer>();
        this.particleRenderer.maxParticleSize = 1e8f;
        this.particleRenderer.material.shader = Shader.Find("ParticleTextureChooser");

        this.particleRenderer.material.mainTexture = (Texture2D)Resources.Load("Common/Textures/MyTextureName");

		this.emitter = (ParticleEmitter)this.gameObject.AddComponent("EllipsoidParticleEmitter");
		this.emitter.emit = false;
		this.emitter.minSize = 1.0f;
		this.emitter.maxSize = 1.0f;
        this.emitter.minEnergy = 100000.0f;
        this.emitter.maxEnergy = 100000.0f;
		this.emitter.minEmission = 0.0f;
		this.emitter.maxEmission = 1.0f;
		this.emitter.localVelocity = Vector3.zero;
		this.emitter.rndVelocity = Vector3.zero;
		this.emitter.useWorldSpace = false;
	}

`

Using the legacy particle system I was able to set the scale of the texture with:
this.emitter.renderer.material.SetTextureScale(“_MainTex”, new Vector2(1.0f/3.0f, 1.0f/3.0f));

(For a 3x3 grid I set size=1/3, for a 1x3 strip you would use Vector2(1/3, 1))

I though I could change the offset with SetTextureOffset, but it set the offset for the all particles and not on a per particle basis. I ended up writing a custom shader based on the particles/addiditve shader in the unity resources download(filename: Particle Add.shader).
(You can download these from: [The Unity Downloads page][1] select your version and choose “Built-in shaders in the right hand column”)

In order to calculate the offset inside the shader, I needed to pass in the value I wanted displayed. I did this by using the RGBA fields in the color passed into the emit method to hold flags for color and value. The color seems to be the only field that is passed from the particle emitter through to the shader unadulterated.

`
//For each particle

tempcolor.r = (float)valueFlag/255.0f;

tempcolor.g = (float)colorFlag/255.0f;

this.emitter.Emit(

	Vector3.zero,  //position  

	Vector3.zero,  //velocity  

	1.0f,  

	1.0f,  

            tempColor);    //Color passes values through to shader  

`

I then extracted the valueFlag and colorFlag values from the appropriate fields in the color passed into the shader and set the color and uv offset accordingly inside the shader.

This is the Shader Code based on the Particles/Addiitive shader

`
Shader “ParticleTextureChooser” {

Properties {

_TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)  

_MainTex ("Particle Texture", 2D) = "white" {}  

_InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0  

}

Category {

Tags { "Queue"="Transparent" "IgnoreProjector"="True"   

“RenderType”=“Transparent” }
Blend SrcAlpha One

AlphaTest Greater .01  

ColorMask RGB  

Cull Off Lighting Off ZWrite Off Fog { Color (0,0,0,0) }  

BindChannels {  

	Bind "Color", color  

	Bind "Vertex", vertex  

	Bind "TexCoord", texcoord  

}  


// ---- Fragment program cards
SubShader {
	Pass {
	
		CGPROGRAM
		#pragma vertex vert
		#pragma fragment frag
		#pragma fragmentoption ARB_precision_hint_fastest
		#pragma multi_compile_particles

		#include "UnityCG.cginc"

		sampler2D _MainTex;
		fixed4 _TintColor;
		uniform float4 _MainTex_ST;
		
		struct appdata_t {
			float4 vertex : POSITION;
			fixed4 color : COLOR;
			float2 texcoord : TEXCOORD0;
			float2 texcoord1 : TEXCOORD1;
		};

		struct v2f {
			float4 vertex : POSITION;
			fixed4 color : COLOR;
			float2 texcoord : TEXCOORD0;
			#ifdef SOFTPARTICLES_ON
			float4 projPos : TEXCOORD1;
			#endif
		};

		//Custom method to set color based on colorFlag
		float4 setColor(float colorCode)
		{
			float4 outputColor;
			if(colorCode  <= 0.01)
			{
				outputColor = float4(0.0, 1.0, 1.0, 0.6);
			}
			else if (colorCode  <= 1.1) //"O"
			{
				outputColor = float4(0.0/255.0, 0.0/255.0, 255.0/255.0, 0.6);
			}

			                				return outputColor;

			} 

		//Custom method to set uv offset based on valueFlag  

		float2 updateTexCoord(float2 tcIn, float index)
		{
			//out of bounds check
			clamp(index, 0.01, 9.0);

			//This logic is based on a 3x3 texture with 9 circles each containing a number from 1-9
			// (1)(2)(3)
			// (4)(5)(6)
			// (7)(8)(9)
			float colNumber = 2.0-(floor((round(index)-1)/3.0));
			float rowNumber = fmod(round(index)-1, 3.0);	

			return  tcIn + float2((rowNumber), (colNumber) );
		}
		v2f vert (appdata_t v)
		{
			v2f o;
			o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
			#ifdef SOFTPARTICLES_ON
			o.projPos = ComputeScreenPos (o.vertex);
			COMPUTE_EYEDEPTH(o.projPos.z);
			#endif

			//The next 3 lines calculate and set the color and uv offset for the texture atlas
			float textureOffset = updateTexCoord(v.texcoord, v.color.g*255.0); //multiply by 255 to get valueFlag 1-9
			o.color = setColor(v.color.r * 255.0); //multiply by 255 to get colorFlag 1-n
			o.texcoord = TRANSFORM_TEX(textureOffset,_MainTex); //This sets the UV offset choosing the location in the texture atlas
			return o;
		}
		//The rest of the shader is unchanged from particle/additive
		sampler2D _CameraDepthTexture;
		float _InvFade;
		
		fixed4 frag (v2f i) : COLOR
		{
			#ifdef SOFTPARTICLES_ON
			float sceneZ = LinearEyeDepth (UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos))));
			float partZ = i.projPos.z;
			float fade = saturate (_InvFade * (sceneZ-partZ));
			i.color.a *= fade;
			#endif
			
			return 2.0f * i.color * _TintColor * tex2D(_MainTex, i.texcoord);
		}
		ENDCG 
	}
} 	

// ---- Dual texture cards
SubShader {
	Pass {
		SetTexture [_MainTex] {
			constantColor [_TintColor]
			combine constant * primary
		}
		SetTexture [_MainTex] {
			combine texture * previous DOUBLE
		}
	}
}

// ---- Single texture cards (does not do color tint)
SubShader {
	Pass {
		SetTexture [_MainTex] {
			combine texture * primary
		}
	}
}

}
}

`
The key method above is v2f vert (appdata_t v) and the key line in vert to change the vu offset and choose which section of your texture atlas you want to display for that particle is

o.texcoord = TRANSFORM_TEX(textureOffset,_MainTex); //This sets the UV

I set the desired offset and color at the beginning and leave them unchanged, but by getting the array of particles from the emitter and changing the color.r and color.g values you could alter the offset and color from a script.

This post is long and convoluted (much like writing the shader was) so I welcome questions and am happy to add clarification/edits to make it easier to understand.
[1]: http://unity3d.com/unity/download/archive

All the particles in a system will read from the same material. You've probably noticed that objects that share a material will all be affected by the material's properties, including offset. Therefore, you will most likely need to have separate emitters with different materials attached to each one.

A common technique is for each particle to randomly pick a tile in the atlas. In order to achieve this we need to be able to set the uv coordinates of each particle to a random index in the texture atlas.
We don’t need to assign a new material for each particle and we don’t need to modify the material assigned to the particle system either.
Unfortunately I can’t find a way to access the uv index of the particle through the particle class either. This would be good.
Alternatively just having a ‘pick random start frame’ checkbox in the particle renderer would be even easier