android 游戏开发之粒子系统应用

       上一节说的是小球运动,而这节介绍的是粒子系统,从名字上就可以得出数量之极多.简单说:粒子系统就是有好多好多小球在一起欲动,那么这就叫做粒子系统,考虑的是整体感觉,而不是单个小球的轨迹.这让我联想到了墨迹和新浪推出的天气动态背景,是不是也是这样实现的呢.总而言之,如下雨,下雨,瀑布等都可以做到,但是做起来很复杂,估计我想远跑偏了,肯定有更好的办法实现动态背景.

我们还是先看一个案例,实现起来和前面讲解小球一样.

首先粒子对象Particle.java

package com.jj.partical;

/***
 * 粒子对象
 * 
 * @author zhangjia
 * 
 */
public class Particle {
	int color; // 颜色
	int r;// 半径
	double vertical_v;// 垂直速度
	double horizontal_v;// 水平速度
	int startX;
	int startY;
	int x;
	int y;
	double startTime;// 起始时间

	public Particle(int color, int r, double vertical_v, double horizontal_v,
			int x, int y, double startTime) {
		super();
		this.color = color;
		this.r = r;
		this.vertical_v = vertical_v;
		this.horizontal_v = horizontal_v;
		this.startX = x;
		this.startY = y;
		this.x = x;
		this.y = y;
		this.startTime = startTime;
	}

}
就是一些属性,下面我们创建一个存放粒子的几个ParticleSet.java其实二者可以整个一个里面,总而言之,达到效果就OK.

package com.jj.partical;

import java.util.ArrayList;

import android.graphics.Color;

/***
 * 存放粒子对象集合
 * 
 * @author zhangjia
 * 
 */
public class ParticleSet {
	ArrayList<Particle> particals;

	public ParticleSet() {
		super();
		particals = new ArrayList<Particle>();
	}

	/***
	 * 获取相关颜色
	 * 
	 * @return
	 */
	public int getColor(int i) {
		int color = Color.RED;
		switch (i % 4) {
		case 0:
			color = Color.RED;
			break;
		case 1:
			color = Color.GREEN;
			break;
		case 2:
			color = Color.YELLOW;
			break;
		case 3:
			color = Color.WHITE;
			break;
		default:
			break;
		}
		return color;
	}

	/***
	 * 添加粒子
	 * 
	 * @param count
	 *            个数
	 * @param startTime
	 *            起始时间
	 */
	public void addPartical(int count, double startTime, int window_Width) {
		for (int i = 0; i < count; i++) {
			int color = getColor(i);
			int r = 1;
			double vertical_v = -30 + 10 * (Math.random());// 垂直速度
			double horizontal_v = 10 - 20 * (Math.random());// 水平速度
			int startX = window_Width / 2;
			int startY = 100;

			Particle partical = new Particle(color, r, vertical_v,
					horizontal_v, startX, startY, startTime);
			particals.add(partical);
		}

	}
}
接下来介绍:粒子物理引擎ParticleThread.java

package com.jj.partical;

import java.util.ArrayList;

/***
 * 粒子物理引擎
 * 
 * @author zhangjia
 * 
 */
public class ParticleThread extends Thread {
	boolean flag;// 线程标识
	ParticleView particalView;
	int sleepSpan = 80;
	double time = 0;
	double span = 0.15;
	private int window_Width;

	public ParticleThread(ParticleView particleView, int window_Width) {
		this.particalView = particleView;
		this.flag = true; // 设置线程执行标志位为true
		this.window_Width = window_Width;
	}

	@Override
	public void run() {
		while (flag) {
			particalView.particleSet.addPartical(5, time, window_Width);// 每次添加5粒
			ArrayList<Particle> particals = particalView.particleSet.particals;// 获取粒子集合
			int count = particals.size();
			for (int i = 0; i < count; i++) {
				Particle partical = particals.get(i);
				double timeSpan = time - partical.startTime;// 计算程序开始到现在的时间差
				int tempX = (int) (partical.startX + partical.horizontal_v
						* timeSpan);// 计算此时粒子的X值
				// 4.9 * timeSpan * timeSpan=1/2*g*t*t;//物理公式,g=9.8m/s;
				int tempY = (int) (partical.startY + 4.9 * timeSpan * timeSpan);// 计算此时粒子的Y值
				if (tempY > ParticleView.DIE_OUT_LINE) {
					particals.remove(partical);// 删除该粒子
					count = particals.size();// 重新获取个数
				}
				partical.x = tempX;
				partical.y = tempY;
			}
			time += span;
			try {
				Thread.sleep(sleepSpan);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

ParticleView.java

package com.jj.partical;

import java.util.ArrayList;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

//继承自SurfaceView并实现SurfaceHolder.Callback接口的类
public class ParticleView extends SurfaceView implements SurfaceHolder.Callback {
	public static int DIE_OUT_LINE = 420;// 粒子的Y坐标超过该值会从粒子集合移除
	DrawThread drawThread; // 后台刷新屏幕线程
	ParticleSet particleSet; // ParticleSet对象引用
	ParticleThread particleThread; // ParticleThread对象引用
	String fps = "FPS:N/A"; // 声明帧速率字符串

	// 构造器,初始化主要成员变量
	public ParticleView(Context context, int window_Width, int window_Height) {
		super(context); // 调用父类构造器
		DIE_OUT_LINE = window_Height;
		this.getHolder().addCallback(this); // 添加Callback接口
		drawThread = new DrawThread(this, getHolder()); // 创建DrawThread对象
		particleSet = new ParticleSet(); // 创建ParticleSet对象
		particleThread = new ParticleThread(this, window_Width); // 创建ParticleThread对象
	}

	// 方法:绘制屏幕
	public void doDraw(Canvas canvas) {
		canvas.drawColor(Color.BLACK); // 清屏
		ArrayList<Particle> particleSet = this.particleSet.particals; // 获得ParticleSet对象中的粒子集合对象
		Paint paint = new Paint(); // 创建画笔对象
		for (int i = 0; i < particleSet.size(); i++) { // 遍历粒子集合,绘制每个粒子
			Particle p = particleSet.get(i);
			paint.setColor(p.color); // 设置画笔颜色为粒子颜色
			int tempX = p.x; // 获得粒子X坐标
			int tempY = p.y; // 获得粒子Y坐标
			int tempRadius = p.r; // 获得粒子半径
			RectF oval = new RectF(tempX, tempY, tempX + 2 * tempRadius, tempY
					+ 2 * tempRadius);
			// canvas.drawOval(oval, paint); // 绘制椭圆粒子
			canvas.drawCircle(tempX, tempY, tempRadius, paint);
		}
		paint.setColor(Color.WHITE); // 设置画笔颜色
		paint.setTextSize(18); // 设置文字大小
		paint.setAntiAlias(true); // 设置抗锯齿
		canvas.drawText(fps, 15, 15, paint);// 画出帧速率字符串
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {// 重写surfaceChanged方法
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {// 从写surfaceCreated方法
		if (!drawThread.isAlive()) { // 如果DrawThread没有启动,就启动这个线程
			drawThread.start();
		}
		if (!particleThread.isAlive()) { // 如果ParticleThread没有启动,就启动这个线程
			particleThread.start();
		}
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {// 重写surfaceDestroyed方法
		drawThread.flag = false; // 停止线程的执行
		drawThread = null; // 将dt指向的对象声明为垃圾
	}
}

用于重绘屏幕和计算帧率DrawThread.java

package com.jj.partical;

import android.graphics.Canvas;
import android.view.SurfaceHolder;

/***
 * 用于重绘屏幕和计算帧率
 * 
 * @author zhangjia
 * 
 */
public class DrawThread extends Thread {
	ParticleView particalView;// 自定义View
	SurfaceHolder surfaceHolder;
	boolean flag = false;// 线程标识
	int sleepSpan = 15;// 线程休眠
	long start = System.nanoTime();// 其实时间,用于计算帧速率
	int count = 0;// 计算帧率

	public DrawThread(ParticleView particalView, SurfaceHolder surfaceHolder) {
		super();
		this.particalView = particalView;
		this.surfaceHolder = surfaceHolder;
		this.flag = true;
	}

	@Override
	public void run() {
		Canvas canvas = null;
		while (flag) {
			try {
				canvas = surfaceHolder.lockCanvas();// 获取canvas.
				synchronized (surfaceHolder) {
					particalView.doDraw(canvas);// 进行绘制ballView.

				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				if (canvas != null) {
					surfaceHolder.unlockCanvasAndPost(canvas);// 解锁
				}
			}
			this.count++;
			if (count == 20) { // 如果计满20帧
				count = 0; // 清空计数器
				long tempStamp = System.nanoTime();// 获取当前时间
				long span = tempStamp - start; // 获取时间间隔
				start = tempStamp; // 为start重新赋值
				double fps = Math.round(100000000000.0 / span * 20) / 100.0;// 计算帧速率
				particalView.fps = "FPS:" + fps;// 将计算出的帧速率设置到BallView的相应字符串对象中
			}
			try {
				Thread.sleep(sleepSpan); // 线程休眠一段时间
			} catch (Exception e) {
				e.printStackTrace(); // 捕获并打印异常
			}
		}
	}

}
接下来调用展示View,就不写了,相信大家都明白如何操作,

现在我简单讲解一下原理:首先我们创建DrawThread线程,目的是相隔sleepSpan进行绘制doDraw方法.其实就好比就是刷屏操作.而我们的ParticleThread物理引擎则每隔sleepSpan都要添加5个粒子进去.然后在通过DrawThread线程进行不停的更新UI从而实现粒子系统.

烟火粒子示例
      

其实我们稍微整理下逻辑就可以变为瀑布效果.

	// double horizontal_v = 10 - 20 * (Math.random());// 水平速度(火焰效果)
			double horizontal_v = 10 + 20 * (Math.random());// 水平速度(瀑布效果)
瀑布效果图:



关于粒子系统就说到这里,有不足的地方,请指示.

这个相比物理小球运动简单一点,不涉及太多的逻辑判断.总而言之:这套实现的步骤和方案值得参考.





  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值