下面将构造一个简单的焰火粒子系统。整个系统由3个类组成:Particle、FireworksEffect和ParticleSystem.
Particle类存储了每个粒子的基本属性,包括生命,衰减,速度,位置和颜色,对于复杂的粒子还包含其他更多的
属性。Particle类代码如下:
public class Particle{ //生命属性 private float life = 1.0f; // 衰减属性 private float degradation = 0.1f; // 速度属性 private float[] vel = {0.0f, 0.0f, 0.0f}; // 位置属性 private float[] pos = {0.0f, 0.0f, 0.0f}; //颜色属性 private int color = 0xffffff; /** 空构造函数 */ public Particle() { } //设置速度,初始位置,初始颜色的方法 public Particle(float[] velocity, float[] position, int color) { setVel(velocity); //设置初始速度 setPos(position); //设置初始位置 this.setColor(color); //设置初始颜色 }
//设置生命方法 void setLife(float life) { this.life = life; }
//获取生命 float getLife() { return life; }
//设置速度 void setVel(float[] tvel) { System.arraycopy(tvel, 0, vel, 0, vel.length); }
//获取速度 float[] getVel() { return vel; }
//设置位置 void setPos(float[] tpos) { System.arraycopy(tpos, 0, pos, 0, pos.length); }
//获取位置 float[] getPos() { return pos; }
//设置颜色 void setColor(int color) { this.color = color; }
//获取颜色 int getColor() { return color; }
//设置衰减 public void setDegradation(float degradation) { this.degradation = degradation; }
//获取衰减 public float getDegradation() { return degradation; }}
Particle类提供了粒子的数据结构,还需要有粒子实体。FireworksEffect类提供了init方法对粒子参数进行初始化,并且创建四边形Mesh对象。Mesh对象包含了顶点缓冲,索引缓冲和外观属性。FireworksEffect类的createAlphaPlane方法创建四边形,该四边形只有位置数组和纹理坐标数组,设置外观属性只显示正面,并且纹理和色彩进行颜色融合
FireworksEffect类代码如下:
public class FireworksEffect { // 发射角度 private int angle = 90; // 三角函数 private float[] trig = {1.0f, 0.0f}; // 喷发源 private float[] pos = {0.0f, 0.0f, 0.0f}; // 随机数 Random rand = null; Mesh mesh = null; // 矩阵转换 Transform trans = new Transform(); // 缩放值 float scale = 1.0f; //构造函数 public FireworksEffect(int angle) { // 设置发射角度 setAngle(angle); // 创建四边形 mesh = createAlphaPlane("/particle.png"); // 缩放值 this.scale = 0.1f; // 获得随机数 rand = new Random(); }
//对粒子的参数进行初始化 public void init(Particle p) { // 设置生命 p.setLife(1.0f); // 设置位置 p.setPos(pos); // 速度 float[] vel = new float[3]; // rand.nextFloat()随机产生0-1的数 float xyvel = rand.nextFloat() * 0.8f + 0.2f; // 设置衰减速度 p.setDegradation(xyvel / 18); // 设置x,y方向速度 vel[0] = xyvel * trig[1] + rand.nextFloat() * 0.125f - 0.0625f; vel[1] = xyvel * trig[0] + rand.nextFloat() * 0.125f - 0.0625f; // z方向速度 vel[2] = 0.0f; // 设置粒子的速度数组 p.setVel(vel); int r = (int)(120 * rand.nextFloat()) + 120; // 随机生成Red颜色 int g = (int)(120 * rand.nextFloat()) + 120; //随机生成Green颜色 int b = (int)(120 * rand.nextFloat()) + 120; //随机生成Blue颜色 int col = (r << 16) | (g << 8) | b; //融合RGB p.setColor(col); //设置粒子的颜色 }
//已经消失的粒子则重新初始化 public void update(Particle p) { float[] ppos = p.getPos(); //获取当前位置 float[] vel = p.getVel(); //获取速度 ppos[0] += vel[0]; //x方向上移动 ppos[1] += vel[1]; //y方向上移动 ppos[2] += vel[2]; //z方向上不移动 // 更新生命值 p.setLife(p.getLife() - p.getDegradation()); // 判断粒子是否存活 if(p.getLife() < -0.001f) { init(p); } }
//设置发射角度 public void setAngle(int angle) { this.angle = angle; //设置发射角度 trig[0] = (float)Math.sin(Math.toRadians(angle)); //正弦 trig[1] = (float)Math.cos(Math.toRadians(angle)); //余弦 }
//获取发射角度 public int getAngle() { return angle; }
//缩放粒子并平移 public void render(Particle p, Graphics3D g3d) { // Alpha值 int alpha = (int)(255 * p.getLife()); // RGBA颜色 int color = p.getColor() | (alpha << 24); // 设置四边形的颜色 mesh.getVertexBuffer().setDefaultColor(color); trans.setIdentity(); trans.postScale(scale, scale, scale); float[] pos = p.getPos(); trans.postTranslate(pos[0], pos[1], pos[2]); //平移 // 根据变换矩阵绘制四边形 g3d.render(mesh, trans); } //创建四边形的方法 private Mesh createAlphaPlane(String texFilename) { // 顶点缓冲 short POINTS[] = new short[] {-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0}; // 定点数组 short TEXCOORDS[] = new short[] {0, 255, 255, 255, 255, 0, 0, 0}; // 位置数组 VertexArray POSITION_ARRAY = new VertexArray(POINTS.length/3, 3, 2); POSITION_ARRAY.set(0, POINTS.length/3, POINTS); //纹理数组 VertexArray TEXCOORD_ARRAY = new VertexArray(TEXCOORDS.length / 2, 2, 2); TEXCOORD_ARRAY.set(0, TEXCOORDS.length / 2, TEXCOORDS); // 顶点缓冲 VertexBuffer vertexBuffer = new VertexBuffer(); vertexBuffer.setPositions(POSITION_ARRAY, 1.0f, null); //设置位置数组 vertexBuffer.setTexCoords(0, TEXCOORD_ARRAY, 1.0f/255.0f, null); //设置纹理数组 vertexBuffer.setDefaultColor(0xffffffff); //设置默认颜色 // 索引缓冲 int INDICES[] = new int[] {0, 1, 3, 2}; int[] LENGTHS = new int[] {4}; // 索引缓冲 IndexBuffer indexBuffer = new TriangleStripArray(INDICES, LENGTHS);
// 外观属性 Appearance appearance = new Appearance(); PolygonMode polygonmode = new PolygonMode(); polygonmode.setCulling(PolygonMode.CULL_BACK); //剔除背面 appearance.setPolygonMode(polygonmode); CompositingMode compositingmode = new CompositingMode(); compositingmode.setBlending(CompositingMode.ALPHA); //透明融合 appearance.setCompositingMode(compositingmode); try { //加载纹理图片 Image texImage = Image.createImage(texFilename); Texture2D texture = new Texture2D(new Image2D(Image2D.RGBA, texImage)); // 支持透明颜色 texture.setBlending(Texture2D.FUNC_REPLACE); texture.setWrapping(Texture2D.WRAP_CLAMP, Texture2D.WRAP_CLAMP); texture.setFiltering(Texture2D.FILTER_BASE_LEVEL, Texture2D.FILTER_NEAREST); texture.setBlending(Texture2D.FUNC_BLEND); appearance.setTexture(0, texture);
} catch(Exception e) { // 捕捉异常 System.out.println("Failed to create texture"); System.out.println(e); } // 创建四边形对象 Mesh mesh = new Mesh(vertexBuffer, indexBuffer, appearance);
return mesh; }}
Particle类保存了粒子的方位速度等抽象信息,FireworksEffect类用来创建,更新粒子,ParticleSystem类
将两者连接并进行管理。代码如下:
public class ParticleSystem{ //粒子效果 private FireworksEffect effect = null; // 粒子数组 Particle[] parts = null;
public ParticleSystem(FireworksEffect effect, int num) { // 设置粒子效果 setEffect(effect); // 根据数量创建粒子数组 parts = new Particle[num]; for(int i = 0; i < num; i++) { parts[i] = new Particle(); effect.init(parts[i]); //根据粒子效果的内容初始化粒子数组,部分数据采用随机生成 } } public void emit(Graphics3D g3d) { for(int i = 0; i < parts.length; i++) { getEffect().update(parts[i]); //更新粒子信息 getEffect().render(parts[i], g3d); //绘制粒子 } }
public void setEffect(FireworksEffect effect) { this.effect = effect; }
public FireworksEffect getEffect() { return effect; }}
下面是游戏画布类M3GCanvas类的代码:
public class M3GCanvas extends GameCanvas implements Runnable {
boolean[] key = new boolean[5]; //获取按键改变发射角度 public static final int FIRE = 0; public static final int UP = FIRE + 1; public static final int DOWN = UP + 1; public static final int LEFT = DOWN + 1; public static final int RIGHT = LEFT + 1; // 矩阵 Transform identity = new Transform(); //3D画笔对象 Graphics3D g3d = null; //背景 Background back = null; // 场景摄像机 Camera camera = null; // 粒子系统 ParticleSystem ps = null; FireworksEffect effect = null; //初始化 public M3GCanvas() { super(true); // 设置全屏 setFullScreenMode(true); // 加载摄像机 camera = new Camera(); // 创建背景 back = new Background(); back.setColor(0);
// 获取实例 g3d = Graphics3D.getInstance(); Light light = new Light(); light.setMode(Light.AMBIENT); light.setIntensity(1.0f); // 增加灯光 g3d.addLight(light, identity); Thread t = new Thread(this);
t.start(); }
//绘制屏幕 private void draw(Graphics g) { try { g3d.bindTarget(g, true, Graphics3D.ANTIALIAS | Graphics3D.TRUE_COLOR | Graphics3D.DITHER); //清屏 g3d.clear(back); g3d.setCamera(camera, identity); // 初始化粒子 if(ps == null) { effect = new FireworksEffect(90); ps = new ParticleSystem(effect, 30); } // 发射粒子 ps.emit(g3d); // 获取角度 if(key[LEFT]) effect.setAngle(effect.getAngle() + 5); if(key[RIGHT]) effect.setAngle(effect.getAngle() - 5);
} catch(Exception e) {
e.printStackTrace(); } finally { // 记得释放 g3d.releaseTarget(); } }
//线程 public void run() { while(true) { try { // 获取键盘输入 process(); // 画 draw(getGraphics()); flushGraphics(); //等待时间 try{ Thread.sleep(30); } catch(Exception e) {} } catch(Exception e) {
e.printStackTrace(); } }
}
//获取键盘输入方法 protected void process() { int keys = getKeyStates();
if((keys & GameCanvas.LEFT_PRESSED) != 0) key[LEFT] = true; else key[LEFT] = false; if((keys & GameCanvas.RIGHT_PRESSED) != 0) key[RIGHT] = true; else key[RIGHT] = false; } }
下面是M3GMIDlet类的代码:
public class M3GMIDlet extends MIDlet implements CommandListener{
private Command exitCommand = new Command("Exit", Command.EXIT, 1); public void startApp() { M3GCanvas mCanvas = new M3GCanvas(); mCanvas.addCommand(exitCommand); mCanvas.setCommandListener(this); Display.getDisplay(this).setCurrent(mCanvas); } public void pauseApp() { }
public void destroyApp(boolean unconditional) { } public void commandAction(Command command, Displayable displayable) { if (command == exitCommand) { destroyApp(true); notifyDestroyed(); } }
下面是该例子程序的演示效果以及用例用到的图片: