Particle System Tutorial on Android


Particle systems are a way to add effects to a game, like an explosion, fire, or water fountain. They usually don’t interact with each other or the world around them, to improve performance. Even a simple system can have 50 particles which are a lot of polygons to do for an effect. First, you’ll want to start a new Android project and paste in the simplest GLSurfaceView application from the Google Android Blog. This is a great starting point for any OpenGL app on Android, I use it all the time when starting a new OpenGL project. It really helped me get up and running quickly so I could start learning more about OpenGL rather than struggle with learning Android and OpenGL at the same time.

First, we need to make a particle object.

package com.bayninestudios.particlesystemdemo;

import java.util.Random;

public class Particle {

    // location
    public float x;
    public float y;
    public float z;

    public Particle(Random gen)
    {
        this.x = gen.nextFloat();
        this.y = gen.nextFloat();
        this.z = gen.nextFloat();
    }
}

Simple particle with an x,y,z coordinate and assigned a random value between 0 and 1. The local variables are public, rather than using getters and setters for performance reasons. Now this particle needs to belong in a system.

package com.bayninestudios.particlesystemdemo;

import com.bayninestudios.particlesystemdemo.Particle;

public class ParticleSystem {
    private Particle[] mParticles;
    private int PARTICLECOUNT = 20;
	
    public ParticleSystem() {
        mParticles = new Particle[PARTICLECOUNT];

        Random gen = new Random(System.currentTimeMillis());
        for (int i=0; i < PARTICLECOUNT; i++) {
            mParticles[i] = new Particle(gen);
        }
    }
}

Simple. Now we have to draw everything. Rather than have a draw routine for each particle, with an assigned texture and vertexes, I have one object that gets allocated by the system and drawn. For each draw, you'll need to do the appropriate translates for the position of the particle. The ParticleSystem class will need a vertex buffer and index buffer to define the triangle to draw. I've also added two helper methods to convert float or short arrays to native order buffers. In previous Android versions I just used FloatBuffer.wrap(float[]) but newer versions require the native order buffers.

    // for use to draw the particle
    private FloatBuffer mVertexBuffer;
    private ShortBuffer mIndexBuffer;
	
    public ParticleSystem() {
        mParticles = new Particle[PARTICLECOUNT];

        Random gen = new Random(System.currentTimeMillis());
        for (int i=0; i < PARTICLECOUNT; i++) {
            mParticles[i] = new Particle(gen);
        }

        float[] coords = {
                -0.1f,0.0f,0.0f,
                0.1f,0.0f,0.0f,
                0.0f,0.0f,0.1f};
        short[] icoords = {0,1,2};

        mVertexBuffer = makeFloatBuffer(coords);
        mIndexBuffer = makeShortBuffer(icoords);
    }

    // use to make native order buffers
    private FloatBuffer makeFloatBuffer(float[] arr) {
        ByteBuffer bb = ByteBuffer.allocateDirect(arr.length*4);
        bb.order(ByteOrder.nativeOrder());
        FloatBuffer fb = bb.asFloatBuffer();
        fb.put(arr);
        fb.position(0);
        return fb;
    }

    // use to make native order buffers
    private ShortBuffer makeShortBuffer(short[] arr) {
        ByteBuffer bb = ByteBuffer.allocateDirect(arr.length*4);
        bb.order(ByteOrder.nativeOrder());
        ShortBuffer ib = bb.asShortBuffer();
        ib.put(arr);
        ib.position(0);
        return ib;
    }

Also ParticleSystem will need a draw routine. This routine setups the vertex buffer to use, the color, translates the draw to the location of the particle, and draws it.

    public void draw(GL10 gl) {
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);
        gl.glColor4f(1f, 1f, 1f, 1f);
        for (int i = 0; i < PARTICLECOUNT; i++) {
            gl.glPushMatrix();
            gl.glTranslatef(mParticles[i].x, mParticles[i].y, mParticles[i].z);
            gl.glDrawElements(GL10.GL_TRIANGLES, 3, GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
            gl.glPopMatrix();
        }
    }

Finally, in the main ParticleSystemDemo class, create a new ParticleSystem, initialize the GL perspective and call the draw routine.

class ClearRenderer implements GLSurfaceView.Renderer {
    private ParticleSystem mParticleSystem;
	
    public ClearRenderer() {
        mParticleSystem = new ParticleSystem();
    }

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLU.gluPerspective(gl, 15.0f, 80.0f/48.0f, 1, 100);
        GLU.gluLookAt(gl, 0f, -10f, 15f, 0.5f, 0.0f, 0f, 0.0f, 1.0f, 1.0f);
    }

    public void onSurfaceChanged(GL10 gl, int w, int h) {
        gl.glViewport(0, 0, w, h);
    }

    public void onDrawFrame(GL10 gl) {
    	gl.glClearColor(0, 0, .5f, 1.0f);
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        mParticleSystem.draw(gl);
    }
}

After all of this you should have a blue background with a bunch of triangle particles in the middle of the screen. They don't move yet but that will be for part two of this. There are other improvements to make, possibly having the system assign the initial location of the particle. Randomizing the color. The particle size is hardcoded, and could be dynamic if needed. Texture mapping the particles. Moving the particle drawing to a different class, that way there can be multiple particle models for the same system.

The most recent code can be found at Bay Nine Studios on Google Code.