- Home /
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.
Looking at the age of this question I'm guessing it deals with Legacy Particles.
The new Shuriken Particle System is very different. $$anonymous$$aybe find a newer post or start a new question.
I believe Shuriken can do this as a subsystem setup.
Answer by kefrens · Jul 13, 2014 at 04:25 PM
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.
Thanks kefrens. Here is a screenshot of how to set this up in the particle system.
Answer by JPLKit · Jan 08, 2014 at 10:41 PM
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
Answer by JonManatee · Jun 16, 2010 at 09:58 PM
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.
Answer by ivanp · Feb 04, 2012 at 09:38 AM
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
Bump on this one, spawning a different particle from a sheet, on new emit, but not random, actually controlling what particle to spawn in script: if value in script changes from x to y, leave existing particles alone and next emit spawn different particle from sheet or sprite, it appears that a trail of pooled instantiate is more suitable, but would love to keep the other particles properties. Tried messing with Start frame, row, frame over time, but as you change the parameter, all existing particles also change, was trying to set up a sub emitter that would spawn the sheet, but my head nearly exploded! Any thoughts>