关闭

从零开始Android游戏编程(第二版) 第十一章 演员(Actor)、视口(ViewWindow),演出开始

3915人阅读 评论(1) 收藏 举报

第十一章 演员(Actor)、视口(ViewWindow),演出开始

本章内容与第七章、第八章关系非常密切,如果对这两章的内容不熟悉请大家先浏览一下七、八章,再回来看本章。

Actor是一个接口,他的作用是统一类的行为(读者可以阅读一下Facede模式相关文章)。我们用一个比喻来说明:演员们有了各自的剧本,导演对所有演员说:做下一个动作!演员们就会各自行动。而不用导演分别告诉每个人,你要这样做,他要那样做。具体到程序中,帧动画、动态图块两种操作会调用完全不同的函数,这样不利于在游戏循环中做出一致的处理。所以我们让他们都实现Actor接口,只要调用接口定义的函数,他们就会做出各自的动作。Actor接口的定义很简单:

public interface Actor {

public void tick();

}

对于实现了Actor接口的任何类型,发出的指令就只有一个:tick。

下面请看实例演示:

我们将创建两个类Tank和Map,分别继承自Sprite和TiledLayer,都实现Actor接口。为tank创建帧动画,为Map创建动态图块,然后将他们显示在SceneMain中。

首先,我们可以复制第十章的例子创建一个新项目,并将第八章中用到的org.yexing.android.games.common包拷贝到项目中。

clip_image002

创建一个Tank类,继承自Sprite,实现Actor接口。

在tick函数中播放帧动画

public void tick() {

// TODO Auto-generated method stub

nextFrame();

}

然后创建一个Map类继承自TiledLayer,实现Actor接口。

在tick函数中播放动态图块

public void tick() {

// TODO Auto-generated method stub

if (getAnimatedTile(-1) == 4) {

setAnimatedTile(-1, 5);

} else {

setAnimatedTile(-1, 4);

}

}

最后在SceneMain中创建这两个类的实例并显示他们

Layer layers[] = new Layer[2];

int mapdate[][] = { { 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0 },

{ 0, 1, 0, 2, 0, 0, 0, 1, 0, 1, 0, 1, 0 },

{ 0, 1, -1, -1, -1, 0, 1, 1, 0, 1, 2, 1, 0 },

{ 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0 },

{ 3, 0, 0, 1, 0, 0, 2, 0, 0, 1, 3, 1, 2 },

{ 3, 3, 0, 0, 0, 1, 0, 0, 2, 0, 3, 0, 0 },

{ 0, 1, 1, 1, 3, 3, 3, 2, 0, 0, 3, 1, 0 },

{ 0, 0, 0, 2, 3, 1, 0, 1, 0, 1, 0, 1, 0 },

{ 2, 1, 0, 2, 0, 1, 0, 1, 0, 0, 0, 1, 0 },

{ 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0 },

{ 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },

{ 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0 },

{ 0, 1, 0, 1, 0, 1, 6, 1, 0, 1, 1, 1, 0 }, };

public SceneMain() {

paint = new Paint();

paint.setColor(Color.WHITE);

paint.setTextAlign(Align.CENTER);

Bitmap bmpTank = BitmapFactory.decodeFile("/sdcard/player1.png");

Bitmap bmpTile = BitmapFactory.decodeFile("/sdcard/tile.png");

Tank tank = new Tank(bmpTank, 32, 32);

tank.setFrameSequence(new int[]{0, 1});

Map map = new Map(13, 13, bmpTile, 32, 32);

map.createAnimatedTile(4);

for(int y=0; y<13; y++) {

for(int x=0; x<13; x++) {

map.setCell(y, x, mapdate[x][y]);

}

}

layers[0] = map;

layers[1] = tank;

}

@Override

public void update(Canvas c) {

// TODO Auto-generated method stub

c.drawARGB(255, 0, 0, 0);

c.drawText("SceneMain", GameView.width/2, GameView.height/2, paint);

for(int i=0; i<2; i++) {

((Actor)layers[i]).tick();

layers[i].paint(c);

}

}

需要读者关注的就是update方法中对tick的调用。运行程序,我们可以看到动画的效果。

还需要注意一点,我们将位图文件放置在了sdcard的根目录。读者可以在项目的res/drawable下找到这两个文件,自行push到sdcard中。

下面再来看ViewWindow。我们可以将它理解成舞台的前台,或者相机的取景器。演员只有走到前台,观众才能看得见。而演员走下台,就从观众视线消失。通常,屏幕就是一个视口,因为无论如何我们也看不到屏幕之外的东西。而有时候,我们需要更小的视口,局部的变化,就需要自行定义视口了。其实,视口的功能我们已经实现了,他就在LayerManager中,只是我们前面没有用到也就没有做介绍。

下面就先让我们来了解一下LayerManager的功能。我们知道Sprite、TiledLayer都是继承自Layer,那么LayerManager,顾名思义,就是用来管理Layer的。具体说是管理Layer的显示的。要使用LayerManager,我们首先调用函数append()或insert()将Layer加入到LayerManager中,就如我们将纸张放入文件夹一样,然后调用paint就可以将所有Layer一次显示出来。Layer有一个属性:z,表示Layer的Z坐标,如图:

clip_image003

Z坐标从屏幕内指向屏幕外,也就是说Z坐标越大离用户越近,前面的Layer会遮住后面的Layer。

我们改写前面的程序,用LayerManager代替Layer数组

public SceneMain() {

super();

……

layerManager.append(map);

layerManager.insert(tank, 100);

……

public void update(Canvas c) {

……

for(int i=0; i

((Actor)layerManager.getLayerAt(i)).tick();

}

layerManager.paint(c, 0, 0);

}

熟悉了LayerManager之后就让我们来学习视口。我们可以将Layer想像成一张张非常大的画布,他们被放在LayerManager这个容器中,视口就是在容器上开一个小窗户,使用户只能看到窗口那一部分,其余的区域都不可见。

让我们看一下LayerManager的构造函数

public LayerManager() {

setViewWindow(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);

}

我们可以看到在这里,已经定义了一个视口。这个视口非常大,可以想象他与Layer一般大,所以我们看不到他的效果。但是如果我们把视口缩小呢?

public void update(Canvas c) {

……

layerManager.setViewWindow(0, 0, 100, 100);

layerManager.paint(c, 0, 0);

}

运行程序,可以看到只有视口内的部分被显示了出来。

clip_image005

ViewWindow的难点在坐标。让我们回头看

layerManager.setViewWindow(x, y, width, height);

layerManager.paint(c, x, y);

这两个函数都用到了坐标,他们有着完全不同的含义。首先看paint,其中的x,y表示视口的左上角相对于屏幕的坐标。我们前面说过,视口可以理解为LayerManager上的窗口,改变这个坐标,整个LayerManager就会跟着一起移动。让我们修改程序,将视口显示在(100,100)的位置

layerManager.paint(c, 100, 100);

clip_image007

可以看到,所有Layer的左上角都移动到了(100,100)的位置。

再来看setViewWindow,其中的x,y表示视口相对于Layer左上角的坐标,让我们修改他们的数值,看看效果。

layerManager.setViewWindow(50, 50, 100, 100);

clip_image009

可以看到,左上角的坦克不见了。

合理的运用setViewWindow可以很方便的实现滚屏效果。

本章示例程序http://u.115.com/file/f198eeda92

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:123859次
    • 积分:1618
    • 等级:
    • 排名:千里之外
    • 原创:18篇
    • 转载:6篇
    • 译文:0篇
    • 评论:191条
    最新评论