Particles Tutorial: Motion

Because I have learned quite a bit since I started this tutorial series, I have found that I really should have explained some theory about creating effects because we ever started writing code as we may have made some different design choices. This next part will focus on some heavy particle effect theory on how to get convincing effects using what I call senses of motion.

I also, would like to give a brief rundown of what is to come. Part 4 will be revamping what was done in parts 1 and 2. Part 4 and 5 will focus on management of particle systems/emitters. Then 6 will show how to port what we have done to run with opengl. Finally, anything else will be showing how to use the system to do different effects such as trails.

Requirements:

  • Finished parts 1-2.

What you get:

  • A solid foundation on how particles create an effect.
  • A better understanding on how you would go about coding a particle library.

I absolutely love games with awesome effects. However, even with AAA titles I still find effects that look like crap. Ones that I know I could make look better without any additional art resources or increasing budgets. Indie games also have huge problems with effects looking very bad. Spawning 500, 1000, or more particles with a smoke texture does not make billowing smoke. Using any particle system to create believable effects is an art form. An art that is actually easy to pick up.

Creating believable effects is all about using motion. Pick your favorite game or games. Look at some of the coolest effects in the game. Seriously pause reading this and go look at them. How were they? Cool, right. Now think of how they were done. Why they looked real or cool. I bet no effect was a single just sitting there in the scene. The key to good effects is using motion. There are many forms of motion. I am going to mention a several common ones.

Movement:

This is the most common. A static image just is not convincing. Moving adds to the illusion of an effect.

Rotation:

Rotation helps create the illusion. Make sure to have particles not all rotate clockwise or counter-clockwise but both.

Scaling:

Scaling is another common sense of motion. Yet this is not simply growing or shrinking there are many forms. You can grow/shrink each x,y, or z axis independently.

Color:

Changing the color over the life of a particle is a nice effect and one that is very important to have.

Transparency: 

Fading particles in, out, and back again. This is one some would bundle into color but it is a completely different sense of motion and one that I think is very important.

Emittion Shape:

The shape or way the particle are emitted into the scene. Circle, box, cone, angle, and any other from that changes how the particles enter the world.

Compositing Particles:

One particle moving across another or on top of another is integral to creating an illusion that all the particles or one entity. This sense of motion is key to keeping the viewer from noticing that most particles are a single image.

Multiple Images

Instead of using one smoke image, use 4 or 10. This adds a variety to the effect. Particles will no longer all have the same image making it hard to tell they are not the effect trying to be produced.

Animated Image:

One step above multiple images. This is having an animation playing for each particle independently. This can add a huge sense of realism and motion but cost more memory.

Complex Forces:

This is where we add things like gravity, forces, wind, and other physics type of effects to the particles to enhance their basic movement.

Complex Composition:

Additive blending, Multiplicative blending, and various alpha composition. This is where we using different and advanced forms of image composites when rendering the particles to help produce an effect. Google additive blending.

Multiple System Composition:

Compositing many particle effects together to make an effect. A system to launch dirt into the air, spawn smoke, spawn little bits of debris, spawn spars, delayed black smoke, delayed embers, shock wave, spawn huge billowing fire, spawn shrapnel with trails, and others are just a few systems that would make up a nice explosion.

Shaders:

Can add many effects to the way particles are rendered and a must in any advanced particle system.

These are some of the most common senses of motion. Sure particles can also have some physics for world collision and maybe some other things but these really are core.

With that being said. Go look at that game/s effects again and think of these sense of motion. I bet you dissected the effect for more effectively now that you knew some things to look for. It is like listening to music, the more instruments you know and the longer you have studied them, the easy it is to pick a song apart.

So why look at other games effects? To learn. I strongly recommend pausing gameplay and just looking around and see if you can figure out how they developers did things or even look at the actual textures used.

Lets look at a few examples of bad or lacking effects.

Mass Effect 3 was a great game for me. The effects looked great when you did not look at them. If you look at most effects such as smoke or explotions. you quickly see that there are only a few images that creates the effect. Some only have one. One particular one that you see all the time is the Centurion smoke grenade.

Smoke

It looks fancy but when you move around you can tell it is a single image billboarded (google this term) to face the camera. There is a shader effect on it to make it “wobble”. This is not a bad effect but one that could use work.

I recently started tinkering with unity and was browsing their asset store and came across “Ultra Realistic Fire and Some” or something. Look at the demo here The effects just look quite bad. The fire seems kinda “real” as it is using some realistic textures but they do not move enough at all. The smoke is horrible. It is like 1000 particles. The 1000 isn’t the problem but that you can tell it is 1000.

We could go on and on looking at games or apps but now we will go over effectively using some of these senses of motion.

Here are a few particle editors out for you to play with.

Unity3D: Their editor is easy to get into and has a very nice UI. Play with it.

Libgdx: Their particle editor is similar to Unity’s as they both like curves instead of numbers.

SystemX: This is my custom editor. It is a little wonky and is missing some features but I think it is the easiest for loading images/sprite sheets/animations for particles. It also has some textures for you to use.

Here, here, and here are some additional free particle animations to try out.

Play with composting particle on one another with motion and rotation. While particles move across one another you get a very nice illusion of an effect. Also, composite multiple emitters/particle systems on each other for a similar effect. Try to use as few particles as possible. With the unity and libgdx editors have the particles fade in and fade out to eliminate popping. Popping is where a particle just “pops” into the world or “pops” out of the world. This never happens in real life. Also, try some animate, multiple textures, and single texture particle effects to see how much better/worse the effect is.

Next time we will refactor old code and write some new stuff to make things better.

Here is a screen using my editor and one of the provided textures.

exasdasd

Advertisements

SpriteBatching Super Easy Optimization.

Sprite Batching is a really easy to do optimization for rendering sprites. It is so easy, in fact, that any “engine” that does not support it, isn’t worth crap. (Exaggerating a bit there)

I made this tutorial a while ago and never posted it here. At the end of it I will give you a link to another tutorial by a very talented coder on how to do a much more modern and robust sprite batcher.

So after spend countless hours on the inter webs and pushing my googlfu skillz to their limit I got a simpe but fast SriteBatcher working. I am going to explain the process of making a SpriteBatcher and then give you the code if you would just like to modify it for your own needs.

Note: I hate it when mathematics books give you the simplest example problem and then tell you do to all sorts of tricky problems. So I will be showing you something a little more complicated than need be so you will know what to do if you want to change something.

Lets get started

What you need: Computer, EDI/Notepad, have gotten something more then glBegin/glEnd working, have at least done some basic things with Vertex Arrays/VBO in LWJGL before.
Tip: http://www.java-gaming.org/topics/introduction-to-vertex-arrays-and-vertex-buffer-objects-opengl/24272/view.html
A big plus is knowing what a Texture Atlas is.

We need to understand what a SpriteBatcher is. A huge slow down in programing shtuffs in openGL is draw calls. By lowering draw calls you reduce the load on the CPU. By batching as many sprites as we can into one draw call, we reduce the CPU load thus improving performance. We can do this with Vertex Arrays. Why not VBO? Because sprites are very dynamic little buggers and can change possible every frame. (and then some) VBOs can be faster when the data is not so dynamic which is not the case for a spritebatcher. Now that I have blabbered on for w while lets look at some actual code.

Quick Note: This is my batcher and I use a class called TexRegion which is what it sounds like, a texture region. This is to show you how to set it up for working with Texture Atlases.

public class SpriteBatcher {
   private static float[] empty = new float[8];
   private static Vector4f empty1 = new Vector4f(0,0,0,0);
   
   private float[] vertArray;
   private byte[] colorArray;
   private float[] texArray;
   private int draws;
   private int maxDraws = 1000;
   private int vertIndex;
   private int colIndex;
   private int texIndex;
   private int currentTex;
   private FloatBuffer vertBuff, texBuff;
   private ByteBuffer colBuff;
   
   static{
      empty[0] = 0;
      empty[1] = 0;
      empty[2] = 1;
      empty[3] = 0;
      empty[4] = 1;
      empty[5] = 1;
      empty[6] = 0;
      empty[7] = 1;
   }

So what is all this jazz? The two static fields are for when you may want to have the sprite batcher draw something with out specifying a color or texture region. It is better to not create these things every time we need them via new Vector4f or new float[].

We have 3 float arrays, one for vertex coords, one for tex coords, and one for color coords. You can guess that the three ints are what we will be using to keep track of where we are in filling up the batcher. Then we also have an int to keep track of what texture we are working with.

We have 2 float buffers for vertex and texture coords and one byte buffer for color. Why a byte buffer? Since we want to be able to do sprites that can change transparency every frame we will need RGBA. If we used floats this would be 4*4*4 bytes. Bye reducing the bytes we send to the gpu, we can increase performance slightly. If you would like the more actuate float, simply drop the byte buffer and add another float buffer.

Last we have max draw calls and current draw calls. Why do we have these? There is an optimal size for VBOs and vertex arrays. That is to say, you want to give things to the GPU in byte sized chunks. The most optimal I have found for this batcher is between 1000-1500 sprites at a given time. So lets make a constructor for this class.

   public SpriteBatcher()
   {
      this(1000);
   }
   
   public SpriteBatcher(int size)
   {
      vertArray = new float[size*2*4];
      vertBuff = BufferUtils.createFloatBuffer(vertArray.length);
      colorArray = new byte[size*4*4];
      colBuff = BufferUtils.createByteBuffer(colorArray.length);
      texArray = new float[size*2*4];
      texBuff = BufferUtils.createFloatBuffer(texArray.length);
      vertIndex = 0;
      colIndex = 0;
      texIndex = 0;
      maxDraws = size;
      draws = 0; 
   }

The default constructor calls sets the size to 1000 but we also will let people choose what they want the size to be.
Most things here are straight forward. vertArray needs to have the size * 2 (vertices at each corner) * 4 (number of corners). The vertBuff will have the vertArrays length. You could also put size*2*4. The same goes for the other arrays. Only thing to note is that the byte array will have a multiplier of 4 because we are using RGBA. Set all indexes to 0, draws to 0, and maxDraws to size.

Lets keep things in openGL style and create two methods that will be used to start and end rendering with the batcher, begin() and end().

   public void begin()
   {
      glEnableClientState(GL11.GL_VERTEX_ARRAY);
      glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
      glEnableClientState(GL11.GL_COLOR_ARRAY);
   }
   
   public void end()
   {
      render();
           
      glDisableClientState(GL11.GL_VERTEX_ARRAY);
      glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
      glDisableClientState(GL11.GL_COLOR_ARRAY);
   }

Very simple. Enable the client states and then render() and disable client states in the end(). Now lets look at the render().

   private void render()
   {
      glBindTexture(GL11.GL_TEXTURE_2D, currentTex);
      vertBuff.put(vertArray);
      vertBuff.flip();
      colBuff.put(colorArray);
      colBuff.flip();
      texBuff.put(texArray);
      texBuff.flip();
      glVertexPointer(2, 0, vertBuff);
      glColorPointer(4,true, 0, colBuff);
      glTexCoordPointer(2, 0, texBuff);
      glDrawArrays(GL_QUADS, 0, draws*4);
      vertBuff.clear();
      colBuff.clear();
      texBuff.clear();
      vertIndex = 0;
      colIndex = 0;
      texIndex = 0;
      draws = 0; 
   }

Still very simple. Bind what ever texture is being used, fill the buffers, flip the buffers, (never forget that) and specify the pointers. Note the color pointer. We are using bytes and saying that they are unsigned. Then we draw using draws*4 because there are 4 indices for each sprites. Why are we not using the whole indices buffer trick and drawElements or drawRangeElements? Due too sprites dynamic nature, they will rarely share triangles so you will lose 1-2 fps by adding in an indices buffer. If you do not know what I mean when I say indices buffer, do not fret! Use the googlefu! or just ignore it and continue on.

We will finally clear the buffers, set the indexes back to 0, and set the draws to 0. Woh! really simple! well….no comes the complex part…actually filling the arrays with useful information such as where are sprites is, what size it is, what texture it is using, what color is it if any at all, and yes…if it is rotated at all.

So here is the scariest looking method in the whole class. draw(blah blah blah sprite stuff)

   public void draw(int texID, float[] region, float x, float y, float width, float height, float rotation, Vector4f col )
   {
      if(texID != currentTex)
      {
         render();
         currentTex = texID; 
      }
      if(draws == maxDraws)
      {
         render();
      }

      final float p1x = -width/2;
      final float p1y = -height/2;
      final float p2x = width/2;
      final float p2y = -height/2;
      final float p3x = width/2;
      final float p3y = height/2;
      final float p4x = -width/2;
      final float p4y = height/2;

      float x1;
      float y1;
      float x2;
      float y2;
      float x3;
      float y3;
      float x4;
      float y4;

      // rotate
      if (rotation != 0) {
      final float cos = (float) FastMath.cosDeg(rotation);
      final float sin = (float) FastMath.sinDeg(rotation);

      x1 = cos * p1x - sin * p1y;
      y1 = sin * p1x + cos * p1y;

      x2 = cos * p2x - sin * p2y;
      y2 = sin * p2x + cos * p2y;

      x3 = cos * p3x - sin * p3y;
      y3 = sin * p3x + cos * p3y;

      x4 = cos * p4x - sin * p4y;
      y4 = sin * p4x + cos * p4y;
      } else {
      x1 = p1x;
      y1 = p1y;

      x2 = p2x;
      y2 = p2y;

      x3 = p3x;
      y3 = p3y;

      x4 = p4x;
      y4 = p4y;
      }
      x1+=x;
      x2+=x;
      x3+=x;
      x4+=x;
      y1+=y;
      y2+=y;
      y3+=y;
      y4+=y;
      
      vertArray[vertIndex]    = x1;
      texArray[texIndex]       = region[0];
      vertArray[vertIndex+1]    = y1;
      texArray[texIndex+1]    = region[1];
      
      vertArray[vertIndex+2]    = x2;
      texArray[texIndex+2]    = region[2];
      vertArray[vertIndex+3]    = y2;
      texArray[texIndex+3]    = region[3];
      
      vertArray[vertIndex+4]    = x3;
      texArray[texIndex+4]    = region[4];
      vertArray[vertIndex+5]    = y3;
      texArray[texIndex+5]    = region[5];
      
      vertArray[vertIndex+6]    = x4;
      texArray[texIndex+6]    = region[6];
      vertArray[vertIndex+7]    = y4;
      texArray[texIndex+7]    = region[7];
      
      colorArray[colIndex]     = getColor(col.x);
      colorArray[colIndex+1]    = getColor(col.y);
      colorArray[colIndex+2]    = getColor(col.z);
      colorArray[colIndex+3]    = getColor(col.w);
      
      colorArray[colIndex+4]    =  getColor(col.x);
      colorArray[colIndex+5]    =  getColor(col.y);
      colorArray[colIndex+6]    =  getColor(col.z);
      colorArray[colIndex+7]    =  getColor(col.w);
      
      colorArray[colIndex+8]    =  getColor(col.x);
      colorArray[colIndex+9]    =  getColor(col.y);
      colorArray[colIndex+10] =  getColor(col.z);
      colorArray[colIndex+11] =  getColor(col.w);
      
      colorArray[colIndex+12] =  getColor(col.x);
      colorArray[colIndex+13] =  getColor(col.y);
      colorArray[colIndex+14] =  getColor(col.z);
      colorArray[colIndex+15] =  getColor(col.w);
      
      
      vertIndex+=8;
      texIndex+=8;
      colIndex += 16;
      draws++; 
   }

Woh! Lots of stuff happening here. Lets explain. First we check if the texture is different from the one we are using, if it is, we render() and then set that as our texture. Then we will check to see if we have hit the max draw calls and again, if we have, render().

Now comes the fun part, rotation. If you would like you can skip this but I think you should read on.

We are going to render the quads with the center of the quad at the x and y given. This means that we need to divide the width/height by 2 and minus or subtract it depending on what corner of the quad we are specifying. We could just draw like we would in Java2D by using the x and y as the top left corner point but by making it the center we greatly simplify the stuff the user has to manage. Why are we not using x and y here? The coordinates are not in screen space because we are going to rotate them at the origin which we assume is (0,0). Now we will set up vars for our final coordinates after we rotate and translate into screen space. But WAIT!! what if we don’t need to rotate? Well we have the IF statement to check to see if we need to rotate, if not, we will just set the final coords to the p1x/p1y stuff and translate them into screen space by adding either x or y. Now for the rotation.

We will store the Sin and Cos of the degree so we only have to calculate these once. After that, we will use them as a rotation matrix.
R = Cos(degree) -Sin(degree)
Sin(degree) Cos(degree)
To see where I get the sin and cos multiplication and addition go here.
http://en.wikipedia.org/wiki/Rotation_matrix#In_two_dimensions Pointing

We have rotated coordinates so we can translate them into screen space by adding x to x coords and y to y coords.

Now that we have all the information we need and can fill up the arrays with the new data. We will use the index then add 1 to it for each subsequent placement into the array. The texture array is getting the tex coordinates from a float[] that is given in the method call. This is so we can specify only partial regions of a texture. (IE: texture atlas ). The we add the the same color for each corner of our quad. This is the method the getColor().

   private byte getColor(float f)
   {
      return (byte) (f*255);
   }

Now that everything is filled up, we increase the indexes and add 1 to the draw count.

Here are some convenience methods for rendering if you don’t specify a color or float[].

   public void draw(int texID, float x, float y, float sizex, float sizey )
   {
      draw(texID, empty, x, y, sizex, sizey, 0, empty1);
   }

   public void draw(int texID, float x, float y, float sizex, float sizey,float rotation, Vector4f col )
   {
      draw(texID, empty, x, y, sizex, sizey, rotation, col);
   }

And here is the whole class.

http://pastebin.java-gaming.org/f5814300d3e

This will work on just about every system out there because it only uses GL11. (even on mobile devices…although I don’t know why you would EVER not use libgdx)

Now if you want to improve even more for those many proud owners of a graphics card supporting opengl 3.0 or better, you can use geometry shaders which will take off even more stress from the cpu. I will add this some time in the future in such a way that you do not have to change any way you call stuff to be render from the SpriteBatcher. (Just plug it in and it works)

Quick performance specs:
On an integrate chip, you will get fillrate limited before you will come even close to cpu.
On a dedicated gpu, you will hit a cpu bottle neck first but is still much faster then fixed-function.

On my 6 year old computer: quad core @2.6ghz, GeForce 250 1Gig V-ram, 4 Gig ram (3 gig effective) can do 50k sprites at 60fps no problem.
On my 2 year old laptop: i5 2.8ghz, GeForce 420m Pointing is never used, 4 Gig Ram can do 50k at 30fps on integrated chip Pointing fillrate limited.

Have a nice day,
Stumpy.

Now this was a while ago and I have learned that you really do not need to drop the colors to a byte. The performance gain is almost no existent. This is also a very primitive batcher. It is fast but primitive. It works really well for just throwing gobs and gobs of sprites and particles without much thought.

Here is an infinitely better tutorial. More complex but more robust.

https://github.com/mattdesl/lwjgl-basics/wiki/Sprite-Batching

And Finally here is a cool image of a game that uses this sprite batcher in action.

Particles Tutorial: Foundation

Greetings!

So this will be part 2 of my particle tutorial series. We will be changing the particle class and adding a new class to make things cleaner. We will talk about the many different attributes a particle can have and the many reasons behind these attributes and why we may or may not need them. Let’s just get started.

Requirements:

  • Finished and understood part 1
  • A few minutes to read this tutorial

What you get:

  • A much more complex paritlce
  • A better example to play with

Now before we write code or change anything we need to talk about particles. Creating effects with particles is very much an art form. You use particles or images that when rendered together, (on top of or next to each other) create some type of effect.  Fire. Smoke. Explosions. The problem is that the more particles you have the more processing time it takes to render the effect. Therefor, the goal is to get the best looking effects with the least amount of particles or processing time.  Now we need to think of how we want to do this. I like the idea of a fat particle. A big, really fluffy, complex particle that can do a whole lot of stuff. I like this because, you don’t need as many fat particles to create the same effect that would take double or triple the amount of lightweight particles and you don’t need to worry about creating a complex component or inheritance system to make sure you are using the right particle for the job. This is not the best way as there is no “best” way of doing anything really.

Before we change are particle we are going to create a new class. This class will basically be holding two doubles and have a few operations for these two doubles. Sounds very unnecessary right now but it will make everything work a whole lot cleaner as we add more attributes to the particles. The class will be named Vector2d. No, we will not be doing any 3d programming and no we will not do any hard math. We call it Vector2d because it is a vector that holds 2 doubles. Why doubles? Well we want to have more accuracy when it comes to our particles. We want to be able to move them by less than 1. So why not floats? This is because java be default converts everything to doubles when it does math operation. There is no performance boost but it saves us a little time so we don’t have to write that “f” at the end of all our numbers 1f , .045f , 2.5f. If you really want to change it, all it takes is a few Ctrl+fs.

Note: All major 3D languages (like opengl) use floats and not doubles when you need floating point precision. Doubles use a lot more memory and are really not necessary.

Here is the class

public class Vector2d {

	public double x;
	public double y;

	public Vector2d(double num1, double num2){
		this.x = num1;
		this.y = num2;
	}

	public void add(double num){
		x += num;
		y += num;
	}

	public void sub(double num){
		x -= num;
		y -= num;
	}

	public void div(double num){
		x /= num;
		y /= num;
	}

	public void mult(double num){
		x *= num;
		y *= num;
	}

	public void add(Vector2d other){
		x += other.x;
		y += other.y;
	}

	public void sub(Vector2d other){
		x -= other.x;
		y -= other.y;
	}

	public void div(Vector2d other){
		x /= other.x;
		y /= other.y;
	}

	public void mult(Vector2d other){
		x *= other.x;
		y *= other.y;
	}
}

We have our 2 doubles x and y. We named them that because x refers to width and y height. We can use them for things other than width and height.

We make the 2 doubles public so we can access them faster (not in terms of speed but typing). If you want, you can make them private and have a getter.

We have the basic fundamental arithmetic operations. We can pass a number to be applied to the 2 numbers in our Vector2d or by giving an other Vector2d in which case we use that Vectors x for the x value and the y for the y value.

So lets use this in our particle class and see what it looks like.

public class Particle {

	private Vector2d loc;
	private Vector2d vel;
	private Vector2d size;
	private Vector2d life;
	private Color color;

X and y are now one variable called loc which is short for location, dx and dy are now vel which is short for velocity, (getting physicsy there), size is still named size but now we have a width and height, and life is life but we can have a max life and current life.

Let’s add some more stuff to our particle. I want acceleration. Wind/gravity. I want it so our particle can grow or shrink and I want to have a max size our particle can be.

public class Particle {

	private Vector2d loc;
	private Vector2d vel;
	private Vector2d acc;
	private Vector2d size;
	private Vector2d maxSize;
	private Vector2d growth;
	private Vector2d life;
	private Color color;

Nice. Lets change the update and render methods. First, the hard one, updating. Now we need to know what happens when our particles do certain things. What happens when they reach maxSize? What happens when their size is 0?  When the max size is hit we could stop growing, start shrinking, or die. Or…we could do all of them. Yeah, lets do all of them. We will have 2 booleans,  ultSize and default. If default is true, we simply kill particles if they hit max size or 0. If ultSize is true and we reach max size, we stop growing and if we hit 0, stop shrinking. If false and we reach max size, we will reverse our growth and shrink and if we reach 0, we reverse and grow. Simple. Now lets actually implement all this.
And here is the new update()

	public boolean update(){
		vel.add(acc);
		loc.add(vel);
		size.add(growth);
		life.x--;

		if(life.x <= 0)
			return true;

		if(defaultSize){
			if(size.x >= maxSize.x){
				if(size.y >= maxSize.y)
					return true;
				else
					size.x = maxSize.x;
			}
			if(size.y >= maxSize.y) //Note: we already checked if both x and y are bigger.
				size.y = maxSize.y;
			if(size.x <= 0)
				if(size.y <= 0)
					return true;
				else
					size.x = 1;
			if(size.y <= 0)
				size.y = 1;
			return false; // we are done
		}

		if(ultSize){ // We will shrink and grow back and forth
			if(size.x > maxSize.x){
				size.x = maxSize.x;
				growth.x *= -1;
			}
			if(size.y > maxSize.y){
				size.y = maxSize.y;
				growth.y *= -1;
			}
			if(size.x <= 0){
				size.x = 1;
				growth.x *= -1;
			}
			if(size.y <= 0){
				size.y = 1;
				growth.y *= -1;
			}
		}
		else{ //We stop growing or shrinking.
			if(size.x > maxSize.x)
				size.x = maxSize.x;
			if(size.y > maxSize.y)
				size.y = maxSize.y;
			if(size.x <= 0)
				size.x = 1;
			if(size.y <= 0)
				size.y = 1;
		}
		return false;
	}

Finally, lets change the render code. We just need to make it use the new Vector2d size and cast to ints.

Here is render()

	public void render(Graphics g){
		Graphics2D g2d = (Graphics2D) g.create();

		g2d.setColor(color);
		g2d.fillRect((int)(loc.x-(size.x/2)), (int)(loc.y-(size.y/2)), (int)size.x, (int)size.y);

		g2d.dispose();
	}

Not bad. Now lets add some setters and getters for our particle variables.

Here is our new and improved Particle.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;

public class Particle {

	private Vector2d loc;
	private Vector2d vel;
	private Vector2d acc;
	private Vector2d size;
	private Vector2d maxSize;
	private Vector2d growth;
	private Vector2d life;
	private Color color;

	private boolean ultSize = false;
	private boolean defaultSize = false;

	public Particle(double x, double y, double dx, double dy, double size, double life, Color c){
		this.loc = new Vector2d(x,y);
		this.vel = new Vector2d(dx,dy);
		this.acc = new Vector2d(0,0);
		this.life = new Vector2d(life,life);
		this.size = new Vector2d(size,size);
		this.growth = new Vector2d(0,0);
		this.maxSize = new Vector2d(0,0);
		this.color = c;
	}

	public boolean update(){
		vel.add(acc);
		loc.add(vel);
		size.add(growth);
		life.x--;

		if(life.x <= 0)
			return true;

		if(defaultSize){
			if(size.x >= maxSize.x){
				if(size.y >= maxSize.y)
					return true;
				else
					size.x = maxSize.x;
			}
			if(size.y >= maxSize.y) //Note: we already checked if both x and y are bigger.
				size.y = maxSize.y;
			if(size.x <= 0)
				if(size.y <= 0)
					return true;
				else
					size.x = 1;
			if(size.y <= 0)
				size.y = 1;
			return false; // we are done
		}

		if(ultSize){ // We will shrink and grow back and forth
			if(size.x > maxSize.x){
				size.x = maxSize.x;
				growth.x *= -1;
			}
			if(size.y > maxSize.y){
				size.y = maxSize.y;
				growth.y *= -1;
			}
			if(size.x <= 0){
				size.x = 1;
				growth.x *= -1;
			}
			if(size.y <= 0){
				size.y = 1;
				growth.y *= -1;
			}
		}
		else{ //We stop growing or shrinking.
			if(size.x > maxSize.x)
				size.x = maxSize.x;
			if(size.y > maxSize.y)
				size.y = maxSize.y;
			if(size.x <= 0)
				size.x = 1;
			if(size.y <= 0)
				size.y = 1;
		}
		return false;
	}

	public void render(Graphics g){
		Graphics2D g2d = (Graphics2D) g.create();

		g2d.setColor(color);
		g2d.fillRect((int)(loc.x-(size.x/2)), (int)(loc.y-(size.y/2)), (int)size.x, (int)size.y);

		g2d.dispose();
	}

	public void setLoc(double x,  double y){
		loc.x = x;
		loc.y = y;
	}

	public void setVel(double x,  double y){
		vel.x = x;
		vel.y = y;
	}

	public void setAcc(double x,  double y){
		acc.x = x;
		acc.y = y;
	}

	public void setSize(double x,  double y){
		size.x = x;
		size.y = y;
	}

	public void setMaxSize(double x,  double y){
		maxSize.x = x;
		maxSize.y = y;
	}

	public void setGrowth(double x,  double y){
		growth.x = x;
		growth.y = y;
	}

	public void setLife(double num){
		life.x = num;
		life.y = num;
	}

	public void setSizeDeault(boolean c){
		defaultSize = c;
	}

	public void setUltSize(boolean c){
		defaultSize = false;
		ultSize = c;
	}

	public Vector2d getLoc(){
		return loc;
	}

	public Vector2d getVel(){
		return vel;
	}

}

Me like. So now it is time to test it. I have edited the test program a little. I have added some random methods. One returns a positive or negative number and the other returns just a positive. I have added a Color, c, so we can change the color of the spawned particles. I have added a boolean so we can pause the test. I have added a KeyListener that we can change stuff with. Pressing “p” pauses and pressing space will change the color. I made the window realizable and finally made it so the particle will bounce off the sizes of the window. Look at it a few times and then run it. After that play with it and the particles. Try changing the way they grow or shrink. Mess around. And think about some other things you want a particle to do and how you would go about making emitters to emit the particles.

The test program


import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.JFrame;

public class Window extends JFrame {

	private ArrayList<Particle> particles = new ArrayList<Particle>(500);

	private int x = 0;
	private int y = 0;
	private BufferStrategy bufferstrat = null;
	private Canvas render;

	private Random rnd = new Random(); //used to generate random numbers
	private Color c = Color.blue; // the default particle color
	private boolean running = true; // should we update?

	public static void main(String[] args)
	{
		Window window = new Window(450, 280, "Particles: ");
		window.pollInput();
		window.loop();
	}

	public Window( int width, int height, String title){
		super();
		setTitle(title);
		setIgnoreRepaint(true);
		setResizable(true);

		render = new Canvas();
		render.setIgnoreRepaint(true);
		int nHeight = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
        int nWidth = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
        nHeight /= 2;
        nWidth /= 2;

        setBounds(nWidth-(width/2), nHeight-(height/2), width, height);
		render.setBounds(nWidth-(width/2), nHeight-(height/2), width, height);

		add(render);
		pack();
		setVisible(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		render.createBufferStrategy(2);
		bufferstrat = render.getBufferStrategy();
	}

	public void pollInput()
	{
		render.addMouseListener(new MouseListener(){

			public void mouseClicked(MouseEvent e) {
				addParticle();addParticle();addParticle();
				addParticle();addParticle();addParticle();
			}

			public void addParticle(){//play with this method
				Particle p = new Particle(x,y,0,0,0,0,c);
				p.setVel(random(7),random(7));
				p.setAcc(random(.02),random(.02));
				p.setLife(randomPlus(150)+150);
				p.setSize(randomPlus(25)+25, randomPlus(25)+25);
				p.setMaxSize(50,50);
				p.setGrowth(random(2), random(2));
				p.setUltSize(true);
				particles.add(p);
			}

			public void mouseEntered(MouseEvent e) {

			}

			public void mouseExited(MouseEvent e) {

			}

			public void mousePressed(MouseEvent e) {

			}

			public void mouseReleased(MouseEvent e) {

			}

		});

		render.addKeyListener(new KeyListener(){//keylistener

			public void keyPressed(KeyEvent e) {
				int code = e.getKeyCode();
				if(code == 'P'){
					if(running)
						running = false;
					else
						running = true;
				}
				if(code == ' '){//new random color
					c = new Color((int)randomPlus(255),(int)randomPlus(255),(int) randomPlus(255));
				}
			}

			public void keyReleased(KeyEvent e) {

			}

			public void keyTyped(KeyEvent e) {

			}

		});
	}

	public double random( double num ){//random method may not be the best
    	return (num * 2)  * rnd.nextDouble() - num;
    }

	public double randomPlus( double num ){//return only a positive number
		double temp = ((num * 2)  * rnd.nextDouble()) - num;
    	if( temp < 0 )
    		return temp * -1;
    	else
    		return temp;
    }

	//This is a bad game loop example but it is quick to write and easy to understand
	//If you want to know how to do a good one use the all knowing google.
	public void loop(){
		while(true){
			if(running)
				update();
			render();

			try {
				Thread.sleep(1000/60);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	public void update(){
		Point p = render.getMousePosition();
		if(p !=null ){
			x = p.x;
			y = p.y;
		}
		for(int i = 0; i <= particles.size() - 1;i++){
			Particle part = particles.get(i);
			if(part.update())
				particles.remove(i);
			if(part != null){
				if(part.getLoc().x <= 0){
					part.getLoc().x = 0;
					part.getVel().x *= -1;
				}
				if(part.getLoc().x >= render.getWidth()){
					part.getLoc().x = render.getWidth();
					part.getVel().x *= -1;
				}
				if(part.getLoc().y <= 0){
					part.getLoc().y = 0;
					part.getVel().y *= -1;
				}
				if(part.getLoc().y >= render.getHeight()){
					part.getLoc().y = render.getHeight();
					part.getVel().y *= -1;
				}
			}
		}
	}

	public void render(){
		do{
			do{
				Graphics2D g2d = (Graphics2D) bufferstrat.getDrawGraphics();
	            g2d.fillRect(0, 0, render.getWidth(), render.getHeight());

	            renderParticles(g2d);

				g2d.dispose();
	         }while(bufferstrat.contentsRestored());
	          bufferstrat.show();
		}while(bufferstrat.contentsLost());
	}

	public void renderParticles(Graphics2D g2d){
		for(int i = 0; i <= particles.size() - 1;i++){
			particles.get(i).render(g2d);
		}
	}
}

Here are some screens of these particles in action.

Here are a few fun settings for your particles. Just copy it over the addParticle method.

public void addParticle(){//play with this method
				Particle p = new Particle(x,y,0,0,0,0,c);
				p.setVel(random(4),random(4));
				p.setAcc(0,randomPlus(.2)+.1);
				p.setLife(randomPlus(150)+150);
				p.setSize(25, 25);
				p.setMaxSize(25,25);
				p.setGrowth(-randomPlus(.2)-.5, -randomPlus(.2)-.5);
				p.setSizeDeault(true);
				//p.setUltSize(false);
				particles.add(p);
			}

 

	public void addParticle(){//play with this method
				Particle p = new Particle(x+random(32),y+random(32),0,0,0,0,c);
				p.setVel(random(1),random(1));
				p.setAcc(0,-randomPlus(.04)-.02);
				p.setLife(randomPlus(150)+550);
				p.setSize(16, 16);
				p.setMaxSize(25,25);
				p.setGrowth(-.1, -.1);
				p.setSizeDeault(true);
				//p.setUltSize(false);
				particles.add(p);
			}

Greetings!

Are you looking for a way to make your games explosions look like explosions? or maybe add some pizzazz to your boring Data-Base application? Well you’re in luck as I happen to know a whole lot about pizzazz. We are going to learn about particles in java. So we are going to make 2D particles as last time I checked java does not do 3D.

Note: If you are already know how to make a basic particle system but want to learn how to make particles look good and perform well, wait for later parts in the tutorial as I will cover more than moving dots across the screen.

So let’s get started!

Requirements:

  • basic programming skills
  • basic understanding of the java2D api (know what the Graphics object is)
  • basic understanding of swing/awt
  • An IDE such as Eclipse or some way of writing and compiling code
  • + know something about game loops

What you get:

  • A working example of particles in action
  • Better understanding of what particles are and how they work
  • Better understanding of the swing/awt api
  • Better understanding of the java2D api

So as you might be able to tell this will be geared towards games and not Data-Base applications. Before we write any code lets start with looking at what particles are and no I am not talking about physics. Particles can be just about anything but for the most part, they are used to make effects like fire and smoke in CGI. You can go about creating these effects with particles in many different ways. For example there are Animated particles, Volumetric particles, Image based particles, primitive particles, etc etc etc. We will be starting with primitive particles…squares and circles.

We will make a particle class but before we do that lets decide what is going into it first and maybe what methods it will need. I think we will want to know the particles location on the screen, the speed it is moving at, the size of the particle, the life of the particle (as nothing lives for ever) and finally the color of the particle because particles are a little like humans, they come in all shapes, sizes and colors. We will want an update method to you know…update the particle. The method will return a boolean signifying whether or not the particle is still alive. And a render method that will take a Graphics object as an argument so we can render our particle.

Now let’s make that particle class and add all that stuff I just talked about above.

import java.awt.Color;
import java.awt.Graphics;

public class Particle {

	private int x;
	private int y;
	private int dx;
	private int dy;
	private int size;
	private int life;
	private Color color;

	public boolean update(){

	}

	public void render(Graphics g){

	}

}

So the x and y variables are obviously the x and y coordinate locations of the particle. Now the dx and dy variables are the speeds of the x and y coordinates. So why did we not use some variation of speedX and speedY? This is because of the way calculus defines the change in some variable over time. Do not worry we will not be doing any derivatives or integrals. Basically dx is the change in x and dy is the change in y.The rest is very self-explanatory except the update and render methods.

So how is our logic going to work? What is our update and render methods going to look like? Well, let’s think about it.
Every time we update the particle we will add the dx to x and dy to y. Then we will subtract 1 from the life of the particle. If the life is less than or equal to 0, it is dead and we will return true, else, return false.

So here is our new update method

public boolean update(){
		x += dx;
		y += dy;
		life--;
		if(life <= 0)
			return true;
		return false;
	}

Nice I know! But what about rendering the particle? First we need to make a new Graphics2D object to work with and then we will set our color.
The actual rendering is a little bit tricky because we want the center point of the particle to be our x and y coordinates.

Lets look at the method first.


public void render(Graphics g){
		Graphics2D g2d = (Graphics2D) g.create();

		g2d.setColor(color);
		g2d.fillRect(x-(size/2), y-(size/2), size, size);

		g2d.dispose();
	}

By subtracting size/2 from both the x and y and then finishing with size, we will render a square with the center point at our x and y coordinates. Simple.

Now how about a constructor? Well, our construct should be really simply.


public Particle(int x, int y, int dx, int dy, int size, int life, Color c){
		this.x = x;
		this.y = y;
		this.dx = dx;
		this.dy = dy;
		this.size = size;
		this.life = life;
		this.color = c;
	}

So now that are particle is done, what about all that “working example on screen” stuff?

Here is the Window class I have for our little test app. I will explain everything after and do not be discouraged if you don’t understand everything that is going on.

import java.awt.Canvas;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;

import javax.swing.JFrame;

public class Window extends JFrame {

	private ArrayList<Particle> particles = new ArrayList<Particle>(500);

	private int x = 0;
	private int y = 0;
	private BufferStrategy bufferstrat = null;
	private Canvas render;

	public static void main(String[] args)
	{
		Window window = new Window(450, 280, "Particles: ");
		window.pollInput();
		window.loop();
	}

	public void pollInput()
	{
		render.addMouseListener(new MouseListener(){

			public void mouseClicked(MouseEvent e) {

			}

			public void mouseEntered(MouseEvent e) {

			}

			public void mouseExited(MouseEvent e) {

			}

			public void mousePressed(MouseEvent e) {

			}

			public void mouseReleased(MouseEvent e) {

			}

		});
	}

	public Window( int width, int height, String title){
		super();
		setTitle(title);
		setIgnoreRepaint(true);
		setResizable(false);

		render = new Canvas();
		render.setIgnoreRepaint(true);
		int nHeight = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
        int nWidth = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
        nHeight /= 2;
        nWidth /= 2;

        setBounds(nWidth-(width/2), nHeight-(height/2), width, height);
		render.setBounds(nWidth-(width/2), nHeight-(height/2), width, height);

		add(render);
		pack();
		setVisible(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		render.createBufferStrategy(2);
		bufferstrat = render.getBufferStrategy();
	}

	//This is a bad game loop example but it is quick to write and easy to understand
	//If you want to know how to do a good one use the all knowing google.
	public void loop(){
		while(true){

			update();
			render();

			try {
				Thread.sleep(1000/60);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	public void update(){
		Point p = render.getMousePosition();
		if(p !=null ){
			x = p.x;
			y = p.y;
		}
		for(int i = 0; i <= particles.size() - 1;i++){
			if(particles.get(i).update())
				particles.remove(i);
		}
	}

	public void render(){
		do{
			do{
				Graphics2D g2d = (Graphics2D) bufferstrat.getDrawGraphics();
	            g2d.fillRect(0, 0, render.getWidth(), render.getHeight());

	            renderParticles(g2d);

				g2d.dispose();
	         }while(bufferstrat.contentsRestored());
	          bufferstrat.show();
		}while(bufferstrat.contentsLost());
	}

	public void renderParticles(Graphics2D g2d){
		for(int i = 0; i <= particles.size() - 1;i++){
			particles.get(i).render(g2d);
		}
	}
}

Let’s talk about the above. The Window class extends JFrame. We have an ArrayList to keep track of our particles, x and y int variables to keep track of where the mouse is, a BufferStrategy for double buffering, and finally a Canvas that we get our BufferStrategy from. The constructor simply sets up a window with the canvas in it so we can draw stuff to the screen. The pollInput() method gives us access to the mouse. The loop method sets up a infinite loop that will update() and render() the particles then try sleeping for a bit. This is NOT a good loop for a game or any more robust application. We are just suing it to test particles. If you want a more accurate game loop use google.

 

The update method gets a point from the canvas we have named render and sets our x and y mouse location. It then updates the particles by looping through the ArrayList. The render method seems complicated but is not. We have the 2 nested do-while loops setup because I believe it makes sure that the contents of Volatile Images are not lost. Also, the java documents say that this is the best way of double buffering. In our loops we get the draw graphics from the BufferStrategy, then we clear the screen by filling a rectangle the size of our screen, draw the particles by, again, looping through the ArrayList, and finally we dispose of the graphics and tell our BufferedStrategy to blit or show what we just drew.

 

Now lets add a few particles every time you click by calling a method that create different particles by a boolean argument. Lets also randomize them a bit by calling Math.random()*”our random number”

 

Here is our method and the code to click for particles.

 

public void mouseClicked(MouseEvent e) {
				addParticle(true);addParticle(false);addParticle(true);
				addParticle(false);addParticle(true);addParticle(false);
			}

			public void addParticle(boolean bool){
				int dx,dy;
				if(bool){
					dx = (int) (Math.random()*5);
					dy = (int) (Math.random()*5);
				}
				else{
					dx = (int) (Math.random()*-5);
					dy = (int) (Math.random()*-5);
				}
				int size = (int) (Math.random()*12);
				int life = (int) Math.random()*(120)+380;
				particles.add(new Particle(x,y,dx,dy,size,life,Color.blue));
			}

Click around and watch the particles go. This is the end of this tutorial. Now this is very basic. We want those special effects like fire and smoke but we have to take it one step at a time.
In the next tutorial I am going to show you a much more object oriented approach to particles and we will build the foundations for a much more complex particle. See you then.

Particles Tutorial Part 1: Get Something on the Screen