[转载]Nokia UI学习范例FishTank研究

Nokia UI学习范例FishTank研究
由于MIDP1.0中提供的API比较有限,因此Nokia实现了自己的类库来提供补充。其中包括声音的支持、屏幕的背景光和机器震动、图像的翻转、Alpha通道以及对像素的操作支持。学习Nokia UI的使用的最好例子莫过于Nokia官方提供的FishTank了。本文讲主要介绍一下这个范例,其中的一些思路和技巧是值得我们学习的。

2005111192445491.gifFishTank的界面比较简单,只有一个屏幕。池塘里的鱼儿不停的游动,水底的水草也不停的漂浮。这个界面是分层设计的,因此引入了一个变量z,这样就和水平参数x和垂直参数y构成了三维的空间。其中水草处于中间层。鱼的游动是无规律的,其中的算法我们可以参考,当鱼触及到边缘的时候,它会反向游动。通过Nokia UI中提供的水平翻转可以实现。 dg.drawImage(img, x, y, (Graphics.LEFT | Graphics.TOP),DirectGraphics.FLIP_HORIZONTAL);水草的游动也是随机的

下面我们看看整个应用的结构
2005111193332558.gif

程序总共有四个类组成,其中我们主要关注的是FishTankCanvas、Fish和Weeds。其中Fish和Weeds比较相似,他们代表了两个对象鱼儿和水草。而FishTankCanvas是FullCanvas的子类并且实现了Runnable接口,它是一个独立的线程。每隔一段时间他会更新一下Fish和Weeds的状态,然后重新绘制屏幕。run()方法中的代码如下
public void run()
{
Thread currentThread = Thread.currentThread();

try
{
// This ends when animationThread is set to null, or when
// it is subsequently set to a new thread; either way, the
// current thread should terminate
while (currentThread == animationThread)
{
long startTime = System.currentTimeMillis();

// Only animate when the canvas is visible.
if (isShown())
{
tick();

// Repaint everything above the sand, the fish
// never swim at h > waterHeight.
repaint(0, 0, waterWidth, waterHeight);
serviceRepaints();
}

long timeTaken = System.currentTimeMillis() - startTime;
if (timeTaken < MILLIS_PER_TICK)
{
synchronized (this)
{
wait(MILLIS_PER_TICK - timeTaken);
}
} else
{
currentThread.yield();
}
}
} catch (InterruptedException e)
{
}
}
由于在屏幕的下方有一部分是泥土,因此在这里调用了repaint(0,0,waterWidth,waterLength)。其实FishTankCanvas同时也是一个容器类,它是Fish和Weeds的容器。他去更新Fish和Weeds的状态然后调用各自的draw()方法来重新绘制。下面我们看看FishTankCanvas是如何绘制的
private synchronized void drawFishTank(Graphics g)
{
// Draw the water
g.setColor(0, 255, 255);
g.fillRect(0, 0, waterWidth, waterHeight);

// Draw the sand
g.setColor(255, 128, 64);
g.fillRect(0, waterHeight, waterWidth, getHeight());

// Draw the weeds and fishes
for (int plane = 0; plane < NUM_PLANES; plane++)
{
if (plane == WEEDS_PLANE)
{
weeds.draw(g);
}

for (int i = 0; i < fishes.size(); i++)
{
Fish fish = (Fish) (fishes.elementAt(i));
if (fish.getZ() == plane)
{
fish.draw(g);
}
}
}
}

接下来我们研究一下Fish和Weeds的代码,它们就是有很多图片构成的。实现的结果和MIDP2.0中的Sprite差不多。Fish和Weeds的图片都是有类型之分的,每个类型又有几个桢。这样才可以构造出不同的对象,并且相同的对象在不同的时刻也有不同的状态。
private static final int TYPE_NUM = 3;

private static final int FRAME_NUM = 2;

private static final Image[][] fishImage;
Fish和Weeds都是采用了静态初始化块的方式来初始化相应的二维数组。例如
static
{
fishImage = new Image[TYPE_NUM][FRAME_NUM];

int i = 0;
int k = 0;
try
{
for (i = 0; i < TYPE_NUM; i++)
{
for (k = 0; k < FRAME_NUM; k++)
{
fishImage[i][k] = Image.createImage("/fish" + i + k
+ ".png");
}
}
} catch (java.io.IOException e)
{
fishImage[i][k] = null;
}

// This MIDlet implicitly assumes that all the Images
// are the same width and height.

fishWidth = fishImage[0][0].getWidth();
fishHeight = fishImage[0][0].getHeight();
}

为了让鱼儿或者水草具有更好的随机性,代码是通过如下的算法来控制他们的方向的,下面以方向x为例说明
// mostly continue as we are, but sometimes randomly change
if ((ticksSinceLastChangeX > 20) && (rand(20) == 0))
{
vx = rand(2) * 2 - 1; // -1 or 1
ticksSinceLastChangeX = 0;
}

// if moving would take us off the left or right of the water,
// reverse
if (((vx < 0) && (x + vx < 0))
|| ((vx > 0) && (x + fishWidth + vx > FishTankCanvas.waterWidth)))
{
vx = -vx;
ticksSinceLastChangeX = 0;
}
当鱼接触到水的边缘的时候,它的速度方向会改变。在此前的代码是为了增加他的随机性。Fish的重新绘制也比较简单,这里用到了Nokia UI中的水平翻转的扩展功能
public void draw(Graphics g)
{
DirectGraphics dg = DirectUtils.getDirectGraphics(g);

Image img = fishImage[type][frame];
if (img != null)
{
if (vx < 0)
{
dg.drawImage(img, x, y, (Graphics.LEFT | Graphics.TOP),
DirectGraphics.FLIP_HORIZONTAL);
} else
{
dg.drawImage(img, x, y, (Graphics.LEFT | Graphics.TOP), 0);
}
}

frame = (frame + rand(2)) % FRAME_NUM;
}

我们可以看到每次绘画结束后都会把frame的编号随机设置一下,这样可以得到鱼儿本身运动的效果。

MIDlet的代码比较简单,直接给出
package example.fishtank;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class FishTankMIDlet extends MIDlet
{
private final FishTankCanvas canvas;

public FishTankMIDlet()
{
canvas = new FishTankCanvas(this);
}

public void startApp()
{
Display.getDisplay(this).setCurrent(canvas);
canvas.start();
}

public void pauseApp()
{
canvas.stop();
}

public void destroyApp(boolean unconditional)
{
canvas.stop();
}

/*
* On the exit command, cleanup and notify that the MIDlet has been
* destroyed.
*/
void exitRequested()
{
destroyApp(false);
notifyDestroyed();
}
}

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/374079/viewspace-131764/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/374079/viewspace-131764/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值