MIDP 2.0为 移动设备提供了众多新的特性,主要表现在媒体的支持,增强的Ui接口,更多的网络协议,OTA以及安全性等方面。然而最让我们感兴趣的是游戏api的新特性。本文意在通过一些例子来介绍新的游戏API类及其用法。例子调试环境为 J2ME Wireless Toolkit 2.0 Beta.
游戏 API
游戏 API 帮助开发者(游戏开发者或者其他需要更好ui界面的开发者)帮助用户快速开发实用并且节省内存以及存储空间的程序界面。 使用MIDP 1.0 game开发者必须定义自己的一套图像类来获得好的界面及程序性能 ,这必然会增加程序存储方面的开销,使你的jar文件变得更大。新的游戏api可以解决这些问题。
游戏API的基本思想是游戏界面由图层组成。背景可以在一个图层上,而游戏人物可以在另一个图层上。 每一个图层都可以分别被 游戏API控制。 按照经验,游戏的额场景通常会比手机的屏幕大,所以在传统的方法中控制屏幕的滚动是一件很痛苦的事情。 新的游戏API 一个新的观察窗口(view window),通过它可以看到所有的游戏场景,并且它可以很容易的被移动很定位。
游戏API 的路径为javax.microedition.lcdui.game。这五个新的类是: GameCanvas, Layer, LayerManager, Sprite, TiledLayer.
GameCanvas是一个提供了游戏的基本接口的抽象类。这个类与Canvas 类相比有两个优点:1。它拥有屏幕缓冲,2。它可以直接得到设备键盘的物理状态。
Layer 是一个定义了游戏元素的抽象类。 Sprite 和TiledLayer 继承了这个类。 Layer是一个非常常用的类。
LayerManager负责管理Layer对象,并且按照指定的顺序画他们。
Sprite 包含了若干帧图像的Layer。这些帧保存在Image对象中。 通过Sprite类我们可以只使用其中的部分帧,或者通过播放一个帧的序列来创建一个动画。 Sprite类还能检查它是否与其他的Sprite类或者TiledLayers 有重合。
TiledLayer 和 Sprite有点相似,但是它更多的被用来创建背景,比如赛道或者其他更大的区域。TiledLayer包含一个表格(a grid of cells),我们可以用图像或者文字来填充他。所以说一个背景或者一个场景是可以用一系列的小图片来创建的。
处理用户输入
在MIDP 2.0中,传递用户的输入与MIDP 1.0有所不同. 在 1.0 中你需要Canvas的 getGameAction()方法来得到游戏中用户的按键。 在2.0 中你可以调用GameCanvas的getKeyStates() 方法来直接得到键盘的状态。
下面是一个示例代码。 首先我们得到键盘的状态 ,然后通过bit操作判断方向键的状态后作出相应的响应。
protected void keyPressed(int keyCode) { int move = 0; int keyState = getKeyStates(); if ((keyState & LEFT_PRESSED) != 0) { // do something } if ((keyState & RIGHT_PRESSED) != 0) { // do something } if ((keyState & UP_PRESSED) != 0) { // do something } if ((keyState & DOWN_PRESSED) != 0) { // do something } } |
使用屏幕缓存
屏幕缓存(off-screen buffer)使得用户可以很方便的创建无闪烁的动画,并且不需要创建额外的类来实现双缓冲。 对象先被画到缓存中,准备好后在刷新到屏幕。
在下面的代码中GameCanvas的 getGraphics() 用来的到一个显示缓存。在while 循环中, 缓冲用来绘制LayerManager (layers object)的组件. 然后缓存被 flushGraphics() 方法刷新。调用flushGraphics(int x, int y, int width, int height)方法可以只把指定的区域刷新到屏幕上。
public void run() { Graphics g = getGraphics(); while (play) { try { // First draw all the layers layers.paint(g, 0, 0); // If the game is on, flush the graphics if (play) { flushGraphics(); } try { mythread.sleep(sleepTime); } catch (java.lang.InterruptedException e) {} } catch (Exception e) { e.printStackTrace(); } } } |
使用图层
在一个游戏中 (或者其他的图形程序),显示区域内通常包含不同的内容(图像可能是有关联的或者是没有关联的)。比如一只蜜蜂可以在森林,陆地,水面上飞翔,但是在一个迷宫中人却不能穿越围墙。
MIDP 2.0游戏 API为此引进了图层。图层提供了控制屏幕上的对象或者上下文的方法 。图层可以是TiledLayer (比如背景), Sprite (比如飞机), 或者通过继承Layer类自定义的类.
下面代码是对图层使用的示例,本例中最重要的类是TiledLayer, LayerManager, 以及Image. Image 是用来保存具有相同大小的图像或者图象元素的类。 TiledLayer使用这些图像来布置背景
当TiledLayer的实例被创建以时,构造方法要求五个参数,列数,行数,图像 ,图像元素的宽度和高度。这里的例子中背景表格包含40 行, 16 列, 图像时Tiles.png并且长宽都是7。代码开头的一些常量(TILE_GROUND etc.) 表示对图像元素的引用。
当TiledLayer实例被创建后,图像元素的表格可以用 fillCells()方法来填充或者fillCell() 来填充。 在代码的最后 TiledLayer被加入到LayerManager. append() 方法用来把图层加入到观察窗口最下面。使用 insert()可以把图层插入到指定位置。
private TiledLayer tiles; private LayerManager layers; private Image tilesImage; public final int TILE_GROUND = 1; public final int TILE_WATER = 2; public final int TILE_SHORE_LEFT = 3; public final int TILE_FOREST = 4; public final int TILE_AIR = 5; public final int TILE_SHORE_RIGHT = 5; // … // Creating an instance of the TiledLayer layers = new LayerManager(); try { tilesImage = Image.createImage("/Tiles.png"); } catch (IOException e) {} tiles = new TiledLayer(40, 16, tilesImage, 7, 7); // … // Filling the TiledLayer with tiles tiles.fillCells(0, 0, 40, 16, TILE_AIR); tiles.fillCells(14, 12, 12, 4, TILE_WATER); tiles.fillCells(0, 10, 14, 6, TILE_GROUND); // and more tiles like FOREST and the shores… layers.append(tiles); |
使用精灵
正如前面提到的那样, 精灵被定义为屏幕上的一个单独的对象. 这个对象可以是推石头的小人, 一架正在射击的飞机。 Sprite类的工作方式有点类似 TilesLayer ( 实事上他们都是从Layer类继承来的). Sprite 也拥有一个包含几副等大小的图像的Image 对象。但是这些图像与组成背景的图像元素不同, 他们是表现游戏主角的动画的帧。 因此精灵可以拥有动画效果,并且通过更换其中的一些帧就可以轻松改变精灵的形象。
下面代码展示了怎么创建一个Sprite的实例。原理其实和TiledLayer一样.
try { spriteImage = Image.createImage("/Sprite.png"); } catch (IOException e) {} sprite = new Sprite(spriteImage, 7, 7); |
Layer类中的以下两个方法可以轻松的控制精灵的移动:
move(int dx, int dy)
setPositions(int x, int y)
通过 LayerManager来控制精灵的移动,绘制使非常方便的。下面的代码展示了怎样去控制一个精灵的移动。精灵的大小为7×7,并且每次移动的幅度也是7个象素
public static final int UP = 0; public static final int RIGHT = 1; public static final int DOWN = 2; public static final int LEFT = 3; // … switch (direction){ case UP: sprite.move(0, -7); break; case DOWN: sprite.move(0, 7); break; case RIGHT: sprite.move(7, 0); break; case LEFT: sprite.move(-7, 0); break; default: break; } |
游戏编写中还有一个重要的任务就是发现精灵间的碰撞。精灵可能必须呆在某个游戏区域或者指定的迷宫,同时判断精灵间的相互碰撞也是非常重要的。碰撞在有的游戏中意味着转换方向,有时候却意味着game over
Sprite 提供了以下四个方法,使我们可以对精灵的碰撞作出判断:
collidesWith(Image image, int x, int y, boolean pixelLevel) collidesWith(Sprite s, boolean pixelLevel) collidesWith(TiledLayer t, boolean pixelLevel) defineCollisionRectangle(int x, int y, int width, int height) |
当两个Sprite的实例( 也可以时TiledLayer,Sprite ,Image)碰撞或者说重合时,我们可以对此作出响应。
结束语
本文结合几个例子介绍了MIDP 2.0中最常用游戏 API的新特性,有了这些特性,开发者不但可以方便的控制比屏幕大的游戏区域的绘制,而且可以方便的在不同图层上绘制物体。