中国青基会
RSS
热门关键字:  None  linux+moodle安装  mac  rhel5  199
当前位置 : Nixsky>程序设计>Java>列表

J2ME 3D手机游戏开发技术之基本地形渲染

来源:www.javadby.com 作者: 时间:2007-12-29 点击:
  10.1.7 构造游戏场景

  游戏画布的构造方法负责创建场景和启动游戏线程。游戏画布继承了GameCanvas类,并引用了Runnable接口。场景包括摄影机、背景、等高地图和海平面,海平面由多个覆盖纹理的正方形组成。构造场景的代码如下:

  World world = new World(); //创建场景对象

  try{

  heightmap = new HeightMap(); //创建等高地图

  } catch(Exception e){ //捕捉异常

  System.out.println("Heightmap error: "+ e.getMessage());

  e.printStackTrace();

  }

  Mesh[][] map = heightmap.getQuads(); //获取地图数组

  for(int x = 0; x

  {

  for(int y = 0; y

  {

  world.addChild(map[x][y]); //将四边形组添加到场景

  }

  }

  Camera camera = new Camera(); //创建摄影机

  float aspect = (float) getWidth() / (float) getHeight(); //计算屏幕宽高比

  camera.setPerspective(60.0f, aspect, 0.1f, 150.0f); //设置投影参数

  Transform camTrans = new Transform(); //摄影机变换矩阵

  camTrans.postTranslate(0.0f, 5.0f, 0.0f); //摄影机平移到相应位置

  camera.setTransform(camTrans); //移动摄影机

  world.addChild(camera); //将摄影机添加到场景

  world.setActiveCamera(camera); //将摄影机设置为场景的活动摄影机

  addWater(); //添加海平面

  Background back = new Background(); //背景

  back.setColor(0xFF0000FF); //设置背景的颜色

  world.setBackground(back); //将背景添加到场景中

  Thread t = new Thread(this); //创建游戏线程

  t.start(); //启动线程

  10.1.8 绘制场景

  draw方法负责将场景绘制屏幕上,代码如下:

  private void draw(Graphics g)

  {

  try{

  g3d = Graphics3D.getInstance();

  g3d.bindTarget(g, true, Graphics3D.TRUE_COLOR | Graphics3D.DITHER);

  g3d.render(world); //绘制场景

  }catch(Exception e){ //捕捉异常

  System.out.println(e.getMessage());

  System.out.println(e);

  e.printStackTrace();

  } finally{

  g3d.releaseTarget();

  }

  }

  10.1.9 键盘输入

  input方法根据键盘状态对摄影机进行平移和旋转变换,Fire键将摄影机前推,上下键将摄影机上下移动,左右键将摄影机左右旋转,代码如下:

  protected void input()

  {

  int keys = getKeyStates();

  if((keys &GameCanvas.FIRE_PRESSED) != 0) //摄影机前推

  camTrans.postTranslate(0.0f, 0.0f, -1.0f);

  if((keys &GameCanvas.UP_PRESSED) != 0) //摄影机上移

  camTrans.postTranslate(0.0f, 1.0f, 0.0f);

  if((keys &GameCanvas.DOWN_PRESSED) != 0) //摄影机下移

  camTrans.postTranslate(0.0f, -1.0f, 0.0f);

  if((keys &GameCanvas.LEFT_PRESSED) != 0) //摄影机左转

  camTrans.postRotate(5, 0.0f, 1.0f, 0.0f);

  if((keys &GameCanvas.RIGHT_PRESSED) != 0) //摄影机右转

  camTrans.postRotate(-5, 0.0f, 1.0f, 0.0f);

  camera.setTransform(camTrans) ;

  }

  游戏线程中调用场景绘制和键盘输入方法,代码如下:

  public void run() {

  while(true) {

  try {

  input(); //键盘输入

  draw(getGraphics()); //绘制场景

  flushGraphics(); //刷新屏幕

  try{ Thread.sleep(30); } catch(Exception e) {} //休眠30ms

  }catch(Exception e) {

  e.printStackTrace();

  }

  }

  }

  编译、运行程序,其结果如图10-8所示。

  

  

  图10-8 等高图构造场景

  不同的地图能构造不同场景,读者可以自行创建多种有趣的地图,如图10-9所示是各种不同的场景。

  

  

  图10-9 各种不同的场景

  10.1.10 地图纹理的应用

  等高地图创建的场景如果铺上纹理将显得更真实。在HeightMap类中添加createTexture方法来创建纹理,代码如下:

  private static void createTexture(){

  Image2D waterIm = null;

  try {

  waterIm = (Image2D)Loader.load("/texture1.PNG")[0]; //加载纹理图片

  }catch (Exception e){ System.out.println("Cannot make image "); }

  //捕捉异常

  texture = new Texture2D(waterIm); //根据纹理图片创建纹理

  texture.setFiltering(Texture2D.FILTER_NEAREST, Texture2D.FILTER_NEAREST);

  texture.setWrapping(Texture2D.WRAP_CLAMP, Texture2D.WRAP_CLAMP);

  }

  在创建四边形时在顶点缓冲中添加纹理坐标,并且在外观属性中添加纹理,代码如下:

  public static Mesh createQuad(short[] heights)

  {

  …

  short[] TEXCOORDS = {

  0,0, 1,0,

  1,1, 0,1

  };

  TEXCOORD_ARRAY = new VertexArray(TEXCOORDS.length/2, 2, 2); //创建纹理数组

  TEXCOORD_ARRAY.set(0, TEXCOORDS.length/2, TEXCOORDS); //设置纹理数组

  vertexBuffer.setTexCoords(0, TEXCOORD_ARRAY, 1.0f, null);

  //顶点缓冲中添加纹理数组

  createTexture(); //创建纹理

  appearance.setTexture(0, texture); //在外观属性中添加纹理

  …

  }

  不同的纹理使地形呈现不同的地貌,例如沙漠、草地和雪山,编译、运行程序,其结果如图10-10所示。

  

  

  图10-10 不同纹理呈现的不同地图

  10.1.11 摄影机游历场景图

  在高低起伏的地面漫游,为了模拟真实情况,要求视点高度随地形变化而变化,否则在遇到地面高度大于视点高度时,就直接钻入地下了。在漫游时动态改变视点高度,可以形成翻山越岭的真实效果,同样,当往地面场景中加入其他物体时也需要在高度上进行定位。

  在计算某点的高度时,首先计算该点所在下方的四边形的4个顶点的高度,高度可以从地图数组中读取,计算该点的高度需要用到双线性插值算法。

  为了方便理解,先考虑1D情况下的线性插值。对于一个数列c,假设c[a]到c[a+1]之间是线性变化的,那么对于浮点数x(a<=x

  然后把这种插值方式扩展到2D情况:对于一个2D数组c,假设对于任意一个浮点数i,c(a, I)到c(a+1, I)之间是线性变化的,c(I, b)到c(I, b+1)之间也是线性变化的(a, b都是整数),那么对于浮点数的坐标(x, y)满足(a<=x

  c(x,b)= c[a+1][b]*(x-a)+c[a][b]*(1+a-x)

  c(x,b+1)= c[a+1][b+1]*(x-a)+c[a][b+1]*(1+a-x)

  现在已经知道c(x, b)和c(x, b+1)了,而根据假设c(x, b)到c(x, b+1)也是线性变化的,所以:c(x, y)= c(x, b+1)*(y-b)+ c(x, b)*(1+b-y)。

  根据以上算法得出根据某点在XZ平面上的位置计算高度的程序,代码如下:

  float getHeight(float x,float z){

  int Col0 = int(x/MAP_SCALE); //所在x坐标映射到地图数组的列号

  int Row0 = int(z/MAP_SCALE); //所在y坐标映射到地图数组的行号

  nt Col0 = Col0+1; //相邻列

  int Row0 = Row0+1; //相邻行

  if(Col1>MapWidth)Col1=0;

  if(Row1>MapHeight)Row1=0;

  float heights_00 = heightMap[Col0 + Row0 * mapWidth];

  float heights_01 = heightMap[Col1 + Row0 * mapWidth + 1];

  float heights_11 = heightMap[Col1 + Row1 * mapWidth];

  float heights_10 = heightMap[Col0 + Row1 * mapWidth + 1];

  float tx = x/MAP_SCALE - Col0; //块内x偏移

  float tz = z/MAP_SCALE - Row0; //块内z偏移

  float txty = tx * ty; //双线性插值(内插)计算

  float height = heights_00*(1.0f+txty -tx -ty)

  + heights_01*(tx - txty)

  + heights_11*txty

  + heights_10*(tx-txty); //插值计算的结果,为所求的高度

  return height;

  }

  程序中的参量如图10-11所示。

  

  

  图10-11 根据xz位置计算高度

  在求得的平面高度上再增加一个分量,代表人眼距离地面的距离,随着山势的高低,视点的高度也发生变化,这样能够更加真实地模拟草地场景,如图10-12所示。

  

  

最新评论共有 0 位网友发表了评论
发表评论
评论内容:不能超过250字,需审核,请自觉遵守互联网相关政策法规。
用户名: 密码:
匿名?
注册