游戏开发论坛

 找回密码
 立即注册
搜索
查看: 1322|回复: 0

MIDP中尝试粒子系统 wxh zt

[复制链接]

1367

主题

1993

帖子

2118

积分

金牌会员

Rank: 6Rank: 6

积分
2118
发表于 2005-10-17 20:35:00 | 显示全部楼层 |阅读模式
作者:favoyang
粒子系统在图形学上的应用十分广泛。最常见的,是通过控制大量的、具有相似行为的点元素,来描绘自然现象。例如下雨、下雪、火焰、水、雾等等。在家用控制台或PC上的2D、3D游戏中,粒子特效代替了更为传统的精灵绘图,并取得了更灵活多变的视觉效果。但在手机设备上,因为机能和API的诸多限制,对于这一技术的应用并不常见。本文试图尝试一下如何在这些设备上实现简单的例子系统。这仅仅是一次试验,并不能保证一定能用于你的实际开发。

粒子系统简介
粒子系统,即粒子群系统。在自然界中,微粒以各种各样的方式聚集在一起形成,拥有了自身的属性和运动规律,构成了复杂的世界。对这种微粒的研究过于宽泛,我们仅仅关注微粒系统在图形学上应用的一小部分应用。一个常见的微粒系统由两部分组成:微粒群和微粒管理器。微粒包括了一系列的属性:速度、颜色、贴图等和一系列的行为。微粒管理器则负责创建微粒群,调用微粒群的行为、以及将微粒群表现出来(显示)。





例子系统常见的应用有:描述群体(一个子弹群)、描述自然现象(下雨、下雪、火焰、水、雾)。此文关注的是后者。

实现粒子系统的成本
实现通用粒子系统的前提:
1,要有足够的内存空间保存粒子系统本身
2,可以对屏幕上的点进行控制

对于第1条,我的建议是,不要为每一个粒子系统建立类对象,仅仅保存一个多维数组用作描述状态信息。行为可以抽象到粒子管理器去处理。尽管这样,当粒子数目很多时其消耗也是非常惊人的。
对于第2条,MIDP2.0提供了对屏幕上的点进行访问的能力。这一能力是通过Graphics的

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.

方法实现的。此方法不是直接取得对缓冲区的操作能力,而是通过对一个rgbData进行各种操作,然后将它绘画出来,从而实现点的控制。换句话说,对屏幕的点的控制,转换为了对数组的控制。不开放对缓冲区的直接操作,优点是:不去限制硬件厂商的实现;缺点是:一旦要对整个屏幕进行像素操作,就不得不开辟一个足够大的数组,则对普通的手机来说显得负担承重。以sek700为例,176*221=38896像素,每个像素一个int(占2Byte),则总共需要77792B约76KB的连续空间。正如后面看到的,大量的例子算法需要使用到Blur技术用于提高画面效果。此技术需要上次的屏幕的点信息,因此需要两个76KB的连续空间。这还不算粒子群本身需要的空间。如果仅仅作为屏幕特效,就吃掉两个76KB的连续空间似乎有些得不偿失。另外,标准没有对数组进行操作的绘图指令,因此如何对数组进行简单的画圆、输出字符等操作都很棘手。所以可以预见,因为标准API的限制,我们仅能有限的利用粒子系统。

试验一:模拟瀑布
基本思路:将粒子群的出生地定于一点,随机产生一个小范围的水平速度。让粒子按照自然规律自由落体。在接触地的时候发生碰撞,产生很小的向上动力(能量衰减)。




      
以下是代码,其中blur是一种简单的模糊算法,它可以上画面更见好看,这里使用的是算法是取一个点为其周围四个点的平均值。这个例子使用了调色板技术实现了瀑布由白向蓝再向黑的过渡(查表法)。通过这个例子,你可了解到例子系统的基本实现、简单的物理模型模拟和调色板的使用技巧。在模拟器上运行很慢,但在Nokia3310上有13fps。
public class Waterfall {

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;

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
     : 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[LIFE] = 0;//kill all particle first
  }
  refreshState(x, y);
  last = (y + high) << 10;
}

public void refreshState(int x, int y) {//更新
  int xx = x << 10;
  int yy = y << 10;
  for (int i = 0; i < max; i++) {
   if (particle[LIFE] <= 0) {
    particle[X] = xx;
    particle[Y] = yy;
    particle[VX] = CoreUtilities.rand(500, 500);
    particle[VY] = CoreUtilities.rand(500, 1500);
    particle[XACC] = 0;
    particle[YACC] = 50;
    particle[LIFE] = 1000;
    particle[DECAY] = CoreUtilities.rand(0, 125);
   }
  }
}

public void move() {//移动
  for (int i = 0; i < max; i++) {
   if (particle[LIFE] > 0) {
    particle[X] += particle[VX];
    particle[Y] += particle[VY];
    //    particle[VX]+=particle[XACC];//not need this time
    particle[VY] += particle[YACC];
    particle[LIFE] -= particle[DECAY];
   }
   if (particle[Y] > last) {
//    System.out.println("before: "+particle[VY]);
//    particle[VY] = -particle[VY];
    //        particle[DECAY]+=50;
    particle[LIFE] -= 500;//模拟能量衰减
//    particle[VY] += 3500;//manual speed-down
    particle[VY] = CoreUtilities.rand(-1000,400);//模拟能量衰减
//    System.out.println("after "+particle[VY]);
//    particle[VX] ;
    //    particle[LIFE] = 0;
//    System.out.println("particle[LIFE]: "+particle[LIFE]);
   }
  }
}

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[X] >> 10;
    row = particle[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) {
   System.out.println(e);
   System.out.println("particle[X] : " + particle[X]);
   System.out.println("particle[Y] : " + particle[Y]);
   System.out
     .println("particle[X]>>10 : " + (particle[X] >>10));
   System.out
     .println("particle[Y]>>10 : " + (particle[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;
    k++;
   }
  }
  //  for (k = 0; k < scanlength; k++) {
  //   outBuffer[k] = inBuffer[k];
  //  }
  //  for (k = (backheight - 1) * scanlength; k < inBuffer.length; k++) {
  //   outBuffer[k] = inBuffer[k];
  //  }
}

public String toString() {//调试信息
  StringBuffer sb = new StringBuffer();
  for (int i = 0; i < max; i++) {
   sb.append(particle[X] + " " + particle[Y] + " "
     + particle[VX] + " " + particle[VY] + " "
     + particle[XACC] + " " + particle[YACC] + " "
     + particle[LIFE] + " " + particle[DECAY] + "\n");
  }

  return sb.toString();
}
}


试验二:模拟定点火
有了上面的基础,我们这次就驾轻就熟了。只要准备好调色板,确定好粒子的状态:出生位置,选择适当的blur算法就可以了。
火焰这个例子有些特殊的地方是,并不需要很多的粒子属性。仅仅将粒子出生于屏幕底端(火焰的定点位置),再通过一个特殊的blur算法,就可实现了。这个blur算法就是取一点为自身和自身正下方、左下方、右下方四点的平均值。这样一来,火焰就诞生了。这个例子在Nokia3310上有13fps。因为选取的调色板不是很好,所以颜色很黯淡,你也可以使用更高的调色板达到更好的效果。





     
试验三:模拟爆炸
和火焰大同小异,blur算法换回了标准的算法。主要是物理模型变动,加速度很重要,因为它可以改变物体的运动方向。这个例子在Nokia3310上有13fps。




   

试验四:模拟水波
算法来自一片经典文章,是某位前辈很早之前贡献出来的,有兴趣可以google一下。为表尊重,图片依旧用的原来的图片。算法大意是通过势能计算物体位移。这个例子在Nokia3310上有12fps。






试验五:模拟星空
最后这个例子并没有用到blur算法,所以速度很快,效果也不错。只要是利用颜色速度区分远近达到视觉上的层次感效果。这个例子在Nokia3310上有36fps。




   

总结
机能和标准API的双重限制,让我们使用粒子系统遇到了不少的障碍。但在高端手机设备上,内存堆比较大,可以尝试利用粒子系统进行一些特效(菜单使用火焰等),在支持Alpha合成的机器上,还可以引入一个单纯的特性层,来进行特效合成,这对于高档手机的机能是个开发。本文仅仅泛泛而谈,几个实例也很简单,都有优化的余地。感兴趣读者可自行实现。



您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2026-1-22 08:56

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表