|
|
当前位置: 化境编程网 >>
J2ME |
|
MIDP中尝试粒子系统 |
[时间: 2006-5-15 1:32:49
作者:favoyang
] |
|
来源:
J2MEDev
粒子系统在图形学上的应用十分广泛。最常见的,是通过控制大量的、具有相似行为的点元素,来描绘自然现象。例如下雨、下雪、火焰、水、雾等等。在家用控制台或PC上的2D、3D游戏中,粒子特效代替了更为传统的精灵绘图,并取得了更灵活多变的视觉效果。但在手机设备上,因为机能和API的诸多限制,对于这一技术的应用并不常见。本文试图尝试一下如何在这些设备上实现简单的例子系统。这仅仅是一次试验,并不能保证一定能用于你的实际开发。
粒子系统简介 粒子系统,即粒子群系统。在自然界中,微粒以各种各样的方式聚集在一起形成,拥有了自身的属性和运动规律,构成了复杂的世界。对这种微粒的研究过于宽泛,我们仅仅关注微粒系统在图形学上应用的一小部分应用。一个常见的微粒系统由两部分组成:微粒群和微粒管理器。微粒包括了一系列的属性:速度、颜色、贴图等和一系列的行为。微粒管理器则负责创建微粒群,调用微粒群的行为、以及将微粒群表现出来(显示)。
JAVA手机网[www.cnjm.net]
JAVA手机网[www.cnjm.net] 例子系统常见的应用有:描述群体(一个子弹群)、描述自然现象(下雨、下雪、火焰、水、雾)。此文关注的是后者。
JAVA手机网[www.cnjm.net] 实现粒子系统的成本 实现通用粒子系统的前提:
JAVA手机网[www.cnjm.net] 1,要有足够的内存空间保存粒子系统本身 2,可以对屏幕上的点进行控制
对于第1条,我的建议是,不要为每一个粒子系统建立类对象,仅仅保存一个多维数组用作描述状态信息。行为可以抽象到粒子管理器去处理。尽管这样,当粒子数目很多时其消耗也是非常惊人的。 对于第2条,MIDP2.0提供了对屏幕上的点进行访问的能力。这一能力是通过Graphics的
JAVA手机网[www.cnjm.net] void drawRGB(int[] rgbData, int offset, int scanlength, int x, int y, int width, int height, boolean processAlpha) Renders a series of device-independent RGB+transparency values in a specified region.
JAVA手机网[www.cnjm.net] 方法实现的。此方法不是直接取得对缓冲区的操作能力,而是通过对一个rgbData进行各种操作,然后将它绘画出来,从而实现点的控制。换句话说,对屏幕的点的控制,转换为了对数组的控制。不开放对缓冲区的直接操作,优点是:不去限制硬件厂商的实现;缺点是:一旦要对整个屏幕进行像素操作,就不得不开辟一个足够大的数组,则对普通的手机来说显得负担承重。以sek700为例,176*221=38896像素,每个像素一个int(占2Byte),则总共需要77792B约76KB的连续空间。正如后面看到的,大量的例子算法需要使用到Blur技术用于提高画面效果。此技术需要上次的屏幕的点信息,因此需要两个76KB的连续空间。这还不算粒子群本身需要的空间。如果仅仅作为屏幕特效,就吃掉两个76KB的连续空间似乎有些得不偿失。另外,标准没有对数组进行操作的绘图指令,因此如何对数组进行简单的画圆、输出字符等操作都很棘手。所以可以预见,因为标准API的限制,我们仅能有限的利用粒子系统。
试验一:模拟瀑布 基本思路:将粒子群的出生地定于一点,随机产生一个小范围的水平速度。让粒子按照自然规律自由落体。在接触地的时候发生碰撞,产生很小的向上动力(能量衰减)。
以下是代码,其中blur是一种简单的模糊算法,它可以上画面更见好看,这里使用的是算法是取一个点为其周围四个点的平均值。这个例子使用了调色板技术实现了瀑布由白向蓝再向黑的过渡(查表法)。通过这个例子,你可了解到例子系统的基本实现、简单的物理模型模拟和调色板的使用技巧。在模拟器上运行很慢,但在Nokia3310上有13fps。 public class Waterfall {
JAVA手机网[www.cnjm.net] public static final int X = 0;
public static final int Y = 1;
public static final int VX = 2;
public static final int VY = 3;
public static final int XACC = 4;
public static final int YACC = 5;
public static final int LIFE = 6;
public static final int DECAY = 7;
int[][] particle;
private int max;
private int last;
JAVA手机网[www.cnjm.net] public Palette pal;
/* * [...][X] :x-coordinates, [...][Y] :y-coordinates, [...][VX] :x-velocity, * [...][VY] :y-velocity, [...][XACC] :x-acceleration, * [...][YACC]:y-acceleration, [...][LIFE] :life cycle, [...][DECAY] :decay */
/** * */ public Waterfall(int max) { this.max = max; particle = new int[max][8]; pal = new Palette(); initPal(); }
private void initPal() {//初始化调色板 for (int i = 0; i < pal.data.length; i++) {//black-->blue-->white pal.setColor(i, i >= 128 ? (i - 128) * 2 : 0, i, i * 2 >= 255 ? 255
JAVA手机网[www.cnjm.net] : i * 2); } // for(int i=0;i<64;i++) // pal.setColor(i,i,i/3,0); // for(int i=64;i<192;i++) // pal.setColor(i,63,21+(i-64)/3,0); // for(int i=192;i<256;i++) // pal.setColor(i,63,63,(i-192)*4); }
public void init(int x, int y, int high) {//初始化 for (int i = 0; i < max; i++) { particle[i][LIFE] = 0;//kill all particle first } refreshState(x, y); last = (y + high) << 10;
JAVA手机网[www.cnjm.net] }
public void refreshState(int x, int y) {//更新 int xx = x << 10;
JAVA手机网[www.cnjm.net] int yy = y << 10; for (int i = 0; i < max; i++) {
JAVA手机网[www.cnjm.net] if (particle[i][LIFE] <= 0) { particle[i][X] = xx; particle[i][Y] = yy; particle[i][VX] = CoreUtilities.rand(500, 500); particle[i][VY] = CoreUtilities.rand(500, 1500); particle[i][XACC] = 0; particle[i][YACC] = 50; particle[i][LIFE] = 1000; particle[i][DECAY] = CoreUtilities.rand(0, 125); } } }
public void move() {//移动 for (int i = 0; i < max; i++) { if (particle[i][LIFE] > 0) { particle[i][X] += particle[i][VX]; particle[i][Y] += particle[i][VY]; // particle[i][VX]+=particle[i][XACC];//not need this time particle[i][VY] += particle[i][YACC]; particle[i][LIFE] -= particle[i][DECAY]; } if (particle[i][Y] > last) { // System.out.println("before: "+particle[i][VY]); // particle[i][VY] = -particle[i][VY]; // particle[i][DECAY]+=50; particle[i][LIFE] -= 500;//模拟能量衰减 // particle[i][VY] += 3500;//manual speed-down particle[i][VY] = CoreUtilities.rand(-1000,400);//模拟能量衰减 // System.out.println("after "+particle[i][VY]); // particle[i][VX] ; // particle[i][LIFE] = 0; // System.out.println("particle[i][LIFE]: "+particle[i][LIFE]);
JAVA手机网[www.cnjm.net] } } }
public void paint(byte[] buffer, int scanlength) {//绘画 int i = 0; int col = 0, row = 0; int lastrow = buffer.length / scanlength - 1; int lastcol = scanlength - 1; try { for (i = 0; i < max; i++) { col = particle[i][X] >> 10; row = particle[i][Y] >> 10; if(row > lastrow||row<0||col<0||col>lastcol) continue; buffer[scanlength * (row > lastrow ? lastrow : row) + (col > lastcol ? lastcol : col)] = 127; } } catch (Exception e) {
JAVA手机网[www.cnjm.net] System.out.println(e); System.out.println("particle[i][X] : " + particle[i][X]); System.out.println("particle[i][Y] : " + particle[i][Y]);
JAVA手机网[www.cnjm.net] System.out .println("particle[i][X]>>10 : " + (particle[i][X] >>10)); System.out .println("particle[i][Y]>>10 : " + (particle[i][Y] >>10)); System.out.println("scanlength * (row > lastrow ? lastrow : row)+ (col > lastcol ? lastcol : col) :" + (scanlength * (row > lastrow ? lastrow : row) + (col > lastcol ? lastcol : col))); } }
public void blur(byte[] inBuffer, byte[] outBuffer, int scanlength) {//模糊算法
int k = scanlength; int backheight = inBuffer.length / scanlength; for (int i = 1; i < backheight - 1; i++) { for (int j = 0; j < scanlength; j++) { outBuffer[k] = (byte) ((inBuffer[k - 1] + inBuffer[k + 1] + inBuffer[k - scanlength] + inBuffer[k + scanlength]) >> 2); // if (outBuffer[k] > -126) // outBuffer[k] -= 2; // else // outBuffer[k] = -128;
JAVA手机网[www.cnjm.net] k++;
JAVA手机网[www.cnjm.net] } } // for (k = 0; k < scanlength; k++) { // outBuffer[k] = inBuffer[k]; // } // for (k = (backheight - 1) * scanlength; k < inBuffer.length; k++) { // outBuffer[k] = inBuffer[k]; // }
JAVA手机网[www.cnjm.net] }
public String toString() {//调试信息 StringBuffer sb = new StringBuffer(); for (int i = 0; i < max; i++) { sb.append(particle[i][X] + " " + particle[i][Y] + " " + particle[i][VX] + " " + particle[i][VY] + " " + particle[i][XACC] + " " + particle[i][YACC] + " " + particle[i][LIFE] + " " + particle[i][DECAY] + "\n"); }
return sb.toString(); } }
JAVA手机网[www.cnjm.net]
JAVA手机网[www.cnjm.net] 试验二:模拟定点火 有了上面的基础,我们这次就驾轻就熟了。只要准备好调色板,确定好粒子的状态:出生位置,选择适当的blur算法就可以了。 火焰这个例子有些特殊的地方是,并不需要很多的粒子属性。仅仅将粒子出生于屏幕底端(火焰的定点位置),再通过一个特殊的blur算法,就可实现了。这个blur算法就是取一点为自身和自身正下方、左下方、右下方四点的平均值。这样一来,火焰就诞生了。这个例子在Nokia3310上有13fps。因为选取的调色板不是很好,所以颜色很黯淡,你也可以使用更高的调色板达到更好的效果。
JAVA手机网[www.cnjm.net] 试验三:模拟爆炸 和火焰大同小异,blur算法换回了标准的算法。主要是物理模型变动,加速度很重要,因为它可以改变物体的运动方向。这个例子在Nokia3310上有13fps。
试验四:模拟水波
JAVA手机网[www.cnjm.net] 算法来自一片经典文章,是某位前辈很早之前贡献出来的,有兴趣可以google一下。为表尊重,图片依旧用的原来的图片。算法大意是通过势能计算物体位移。这个例子在Nokia3310上有12fps。
JAVA手机网[www.cnjm.net]
试验五:模拟星空 最后这个例子并没有用到blur算法,所以速度很快,效果也不错。只要是利用颜色速度区分远近达到视觉上的层次感效果。这个例子在Nokia3310上有36fps。
JAVA手机网[www.cnjm.net]
JAVA手机网[www.cnjm.net]
JAVA手机网[www.cnjm.net] 总结 机能和标准API的双重限制,让我们使用粒子系统遇到了不少的障碍。但在高端手机设备上,内存堆比较大,可以尝试利用粒子系统进行一些特效(菜单使用火焰等),在支持Alpha合成的机器上,还可以引入一个单纯的特性层,来进行特效合成,这对于高档手机的机能是个开发。本文仅仅泛泛而谈,几个实例也很简单,都有优化的余地。感兴趣读者可自行实现。
|
|
|
|