概述 如果你正在开发一款赛车游戏,那么其中很重要的一个方面就是检验赛车什么时候冲出了跑道或是装上了别的车。检验上述事件在使用精灵进行编程时是非常容易的事情。精灵提供了4个专门用于处理上述类似事件的方法:
boolean collidesWith(Image image, int x, int y, boolean pixelLevel)
boolean collidesWith(Sprite s, boolean pixelLevel)
boolean collidesWith(TiledLayer t, boolean pixelLevel)
void defineCollisionRectangle(int x, int y, int width, int height)
请注意你能检验的碰撞的类型有很大的灵活性。例如,你不但可以检验精灵之间的碰撞,还能检验精灵与图像、平铺的图层之间的碰撞。 注意:平铺图层(Tiled Layer)是一个由一系列格子组成的可视化元素。这些格子能建立起一个大的卷轴背景而不需要一张大的图画。
碰撞检验 学习碰撞检验的最佳方法就是实际应用它。在这一章,你将创建你的第二个MIDlet。这个MIDlet将向你展示如何检验精灵之间的碰撞。 我们先实际看一看这个MIDlet。我们的目标是:在屏幕上移动小绿人而不碰到其他任何精灵,也就是说不能碰到苹果、立方体、星星和中间的螺旋体。图18展示了MIDlet运行时的3幅屏幕截图,你可以看到小绿人在屏幕上移动但是却没碰到任何其他精灵。
(图18) 图19显示了MIDlet如何相应碰撞。你会注意到,每次发生碰撞时,屏幕的背光会闪烁(屏幕边缘出现浅绿色)
(图19)
创建固定的精灵 第一步,我们先来创建固定不动的精灵(立方体、苹果还有星星)。 在WTK中新建工程和精灵: 1、创建一个名为Collisions的工程; 2、把后面出现的所有代码复制到文本编辑器中; 3、把这些源文件保存到WTK安装目录下的\apps\Collisions\src目录下。 下面是代码:
/**/ /* --------------------------------------------------
* AppleSprite.java
*------------------------------------------------- */
import javax.microedition.lcdui.game. * ;
import javax.microedition.lcdui. * ;
 public class AppleSprite extends Sprite {
 public AppleSprite(Image image) {
// 构造函数
super (image);
// 设置显示在屏幕上的位置
setRefPixelPosition( 146 , 35 );
}
}
/**/ /* --------------------------------------------------
* StarSprite.java
*------------------------------------------------- */
import javax.microedition.lcdui.game. * ;
import javax.microedition.lcdui. * ;

 public class StarSprite extends Sprite {
 public StarSprite(Image image) {
// 构造函数
super (image);
// 设置显示在屏幕上的位置
setRefPixelPosition( 5 , 65 );
}
}
/**/ /* --------------------------------------------------
* CubeSprite.java
*------------------------------------------------- */
import javax.microedition.lcdui.game. * ;
import javax.microedition.lcdui. * ;

 public class CubeSprite extends Sprite {
 public CubeSprite(Image image) {
// 构造函数
super (image);
// 设置显示在屏幕上的位置
setRefPixelPosition( 120 , 116 );
}
}
注意精灵参考像素的位置决定了精灵在画布上的位置。例如,立方体精灵被放置在横坐标=120,纵坐标= 116处。
 创建动画精灵 上一章创建的动画精灵会在这一章使用。 1 、把上一章的代码复制到文本编辑器, 2 、将文件另存为AnimatedSprite.java,保存到WTK安装目录下的\apps\Collisions\src目录 下面是代码:
/**/ /* --------------------------------------------------
* AnimatedSprite.java
*------------------------------------------------- */
import javax.microedition.lcdui.game. * ;
import javax.microedition.lcdui. * ;

 public class AnimatedSprite extends Sprite {
 public AnimatedSprite(Image image, int frameWidth, int frameHeight) {
// 调用Sprite构造函数
super (image, frameWidth, frameHeight);
}
}
创建移动精灵:代码书写 我们将控制移动一个绿色小人图案的精灵。 1、将下面的代码拷贝到文本编辑器。 2、将此源文件保存到WTK安装目录下的\apps\Collisions\src目录
/**/ /* --------------------------------------------------
* ManSprite.java
*
* 这个精灵可以通过调用moveLeft(), moveRight()
* moveUp() and moveDown()方法在屏幕上移动
*------------------------------------------------- */
import javax.microedition.lcdui.game. * ;
import javax.microedition.lcdui. * ;

 public class ManSprite extends Sprite {

private int x = 0 , y = 0 , // 当前的x,y坐标
previous_x, previous_y; // 是一个x,y坐标
private static final int MAN_WIDTH = 25 ; // 精灵宽度
private static final int MAN_HEIGHT = 25 ; // 精灵高度
public ManSprite(Image image) {
// 调用Sprite的构造函数
super (image);
}
public void moveLeft() {
// 如果绿色的小人精灵没有碰到左边缘
if (x > 0 ) {
saveXY();
// 如果离左边界小与3,则置0,
// 否则从当前位置减3
x = (x < 3 ? 0 : x - 3 );
setPosition(x, y);
}
}
public void moveRight( int w) {
// 如果绿色的小人精灵没有碰到右边缘
if ((x + MAN_WIDTH) < w) {
saveXY();
// 如果当前横坐标加上精灵宽度超出了右边界,
// 将当前位置设为最右边. 否则当前位置加3.
x = ((x + MAN_WIDTH > w) ? (w - MAN_WIDTH) : x + 3 );
setPosition(x, y);
}
}
public void moveUp() {
// 如果绿色的小人精灵没有碰到上边缘
if (y > 0 ) {
saveXY();
// 如果离上边界小于3,则置为0
// 否则从当前位置减.
y = (y < 3 ? 0 : y - 3 );
setPosition(x, y);
}
}
public void moveDown( int h) {
// 如果绿色的小人精灵没有碰到下边缘
if ((y + MAN_HEIGHT) < h) {
saveXY();
// 如果当前纵坐标加上精灵高度超出了下边界,
// 将当前位置设为最下边. 否则当前位置加3.
y = ((y + MAN_WIDTH > h) ? (h - MAN_WIDTH) : y + 3 );
setPosition(x, y);
}
}
/**/ /* --------------------------------------------------
* 保存x,y坐标用于碰撞检验
*------------------------------------------------- */
private void saveXY() {
// 将当前坐标缓存到上一坐标
previous_x = x;
previous_y = y;
}
/**/ /* --------------------------------------------------
* 如果检验到碰撞发生,
* 则返回上一坐标
*------------------------------------------------- */
public void restoreXY() {
x = previous_x;
y = previous_y;
setPosition(x, y);
}
} 让我们回过头快速回顾一下上一节代码里的几个关键点。 有4个方法控制精灵的运动。 传递给moveRight() 和 moveDown()方法的参数分别是画布的宽度和高度。这两个参数分别用来判断小人精灵是否到达了屏幕的右边界和下边界。 1. moveLeft() 2. moveRight(int w) 3. moveUp() 4. moveDown(int h) 无论何时,你移动精灵,你首先要将精灵的当前位置保存起来(saveXY()),以便于精灵发生碰撞时使用。一旦碰撞真的发生,你就需要一种方法将精灵恢复到原来位置(restoreXY())
/**/ /* --------------------------------------------------
* 保存x,y坐标用于碰撞检验
*------------------------------------------------- */
private void saveXY() {
// 将当前坐标缓存到上一坐标
previous_x = x;
previous_y = y;
}
/**/ /* --------------------------------------------------
* 如果检验到碰撞发生,
* 则返回上一坐标
*------------------------------------------------- */
public void restoreXY() {
x = previous_x;
y = previous_y;
setPosition(x, y);
}
上面两个方法可以处理任何运动,不管什么方向,都遵循相同的逻辑。 根据精灵的当前位置检验边缘碰撞。例如有一个向左移动的请求,确认当前坐标是否比0大,如果是,则对横坐标进行相应的变换。下面是左移方法的代码:
public void moveLeft() {
// 如果绿色的小人精灵没有碰到左边缘
if (x > 0 ) {
saveXY();
// 如果离左边界小与3,则置0,
// 否则从当前位置减3
x = (x < 3 ? 0 : x - 3 );
setPosition(x, y);
}
}
注意:用3来更新横坐标x值(或者纵坐标y值),这样得到的用户界面反应速度更快。如果每次只移动一个像素,那么在屏幕上移动将是一个漫长的过程。
将精灵添加到画布:代码书写 画布的作用就象是游戏的背景。所有的精灵连同画布一起显示到屏幕上。本节同时处理事件,包括当键(上,下,左,右)按下时屏幕的刷新。更着下面的步骤一步步来: 1、将下面的代码拷贝到文本编辑器。 2、将此源文件保存到WTK安装目录下的\apps\Collisions\src目录,并命名为CollisionCanvas.java。 一旦 所有代码书写完成,就可以返回此代码检验几个关键点。
 /**//*--------------------------------------------------
* CollisionCanvas.java
*-------------------------------------------------*/
import javax.microedition.lcdui.game.*;
import javax.microedition.lcdui.*;

 public class CollisionCanvas extends GameCanvas implements Runnable {
private AnimatedSprite spSpiral; // 动画精灵

private static final int FRAME_WIDTH = 57; // 一帧画面的宽度

private static final int FRAME_HEIGHT = 53; // 一帧画面的高度

private int canvas_width, canvas_height; // 保存画布信息

private ManSprite spMan; // 小人 (可移动)

private AppleSprite spApple; // 苹果 (不可移动)

private CubeSprite spCube; // 立方体 (不可移动)

private StarSprite spStar; // 星星 (不可移动)

private LayerManager lmgr; // 图层管理器

private boolean running = false; // 线程是否运行?

private Collisions midlet; // 主MIDlet

 public CollisionCanvas(Collisions midlet) {
// 构造函数
super(true);
this.midlet = midlet;
 try {
// 非动画精灵
spMan = new ManSprite(Image.createImage("/man.png"));
spApple = new AppleSprite(Image.createImage("/apple.png"));
spCube = new CubeSprite(Image.createImage("/cube.png"));
spStar = new StarSprite(Image.createImage("/star.png"));
// 动画精灵
spSpiral = new AnimatedSprite(Image.createImage("/spiral.png"),
FRAME_WIDTH, FRAME_HEIGHT);
// 将参考像素改到精灵的中心
spSpiral.defineReferencePixel(FRAME_WIDTH / 2, FRAME_HEIGHT / 2);
// 将精灵放置在画布中心
// (精灵中心和画布中心重合)
spSpiral.setRefPixelPosition(getWidth() / 2, getHeight() / 2);
// 创建并添加到图层管理器
lmgr = new LayerManager();
lmgr.append(spSpiral);
lmgr.append(spMan);
lmgr.append(spApple);
lmgr.append(spCube);
lmgr.append(spStar);
 } catch (Exception e) {
System.out.println("Unable to read PNG image");
}
// 保存画布的宽度和高度
canvas_width = getWidth();
canvas_height = getHeight();
}
 /**//*--------------------------------------------------
* 启动线程
*-------------------------------------------------*/
 public void start() {
running = true;
Thread t = new Thread(this);
t.start();
}

 /**//*--------------------------------------------------
* Main game loop
*-------------------------------------------------*/
 public void run() {
Graphics g = getGraphics();
 while (running) {
// 探测有没有键被按下
checkForKeys();
 if (checkForCollision() == false) {
drawDisplay(g);
 } else {
// 背光灯闪烁,设备震动
midlet.display.flashBacklight(500);
midlet.display.vibrate(500);
}
 try {
Thread.sleep(125);
 } catch (InterruptedException ie) {
System.out.println("Thread exception");
}
}
}

 /**//*--------------------------------------------------
* 检测有没有发生碰撞.
*-------------------------------------------------*/
 private boolean checkForCollision() {
if (spMan.collidesWith(spSpiral, true)
|| spMan.collidesWith(spApple, true)
|| spMan.collidesWith(spCube, true)
 || spMan.collidesWith(spStar, true)) {
// 发生碰撞,恢复到上一个位置
spMan.restoreXY();
return true;
} else
return false;
}

 /**//*--------------------------------------------------
* 有键按下,移动精灵
*-------------------------------------------------*/
 private void checkForKeys() {
int keyState = getKeyStates();
![]() |