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

原创 2011年01月14日 16:47:00

第十一章 演员(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

相关文章推荐

从零开始Android游戏编程(第二版)

没想到重新打开这篇文档已经是一年之后了。去年三月,我停止了这一系列文章的写作。六月,离开了工作了五年的公司。作为公司的创始人和业务主管,我不能容忍它发展的如此缓慢,而合伙人却很享受这种慢节奏的生活。九...
  • yexing
  • yexing
  • 2011年01月14日 17:20
  • 7230

从零开始Android游戏编程(第二版) 前言

前言 没想到重新打开这篇文档已经是一年之后了。 去年三月,我停止了这一系列文章的写作。六月,离开了工作了五年的公司。作为公司的创始人和业务主管,我不能容忍它发展的如此缓慢,而合伙人却很享受这种慢节...
  • yexing
  • yexing
  • 2011年01月14日 16:07
  • 2325

从零开始Android游戏编程(第二版) 第八章 地图的设计和实现

第八章 地图的设计和实现 这本来是第十章,前面计划还有两章的内容,一是跟第四章一样,完成一个Asteroid游戏作为小结,总结一下前面讲过的Sprite的用法,并演示NPC和子弹的处理方法。但是,在...
  • yexing
  • yexing
  • 2011年01月14日 16:29
  • 6094

从零开始Android游戏编程(第二版) 第七章 精灵、帧动画与碰撞检测

第七章 精灵、帧动画与碰撞检测 经过前几章的学习,大家对使用位图、接受用户控制应该已经有了初步的概念,也可以运用这些知识完成简单的小游戏。这一章中,我们会为游戏中最重要的部分——图形处理建立一个基本...
  • yexing
  • yexing
  • 2011年01月14日 16:26
  • 9916

从零开始Android游戏编程(第二版) 第六章 SurfaceView动画

第六章 SurfaceView动画 难度:中等 前面介绍的内容,还是比较简单的,应用这些知识,可以完成一些非实时游戏,比如井字棋等,或者一些画面刷新不是很频繁、实时性不强的游戏,比如我们前面做的扫...
  • yexing
  • yexing
  • 2011年01月14日 16:22
  • 8272

从零开始Android游戏编程(第二版) 第二章 创建第一个程序Hello Tank

第二章 创建第一个程序Hello Tank 难度:容易 现在开始,我们要真正写作Android程序了。虽然前面安装过程那么复杂,但是写起程序来却是非常简单。而且为了让大家有一个直观的认识,本文不会...
  • yexing
  • yexing
  • 2011年01月14日 16:11
  • 7047

从零开始Android游戏编程(第二版) 第四章 响应用户事件

第四章 响应用户事件 上一章介绍了如何显示文字和图片,一般来说,下一步就该讲到动画了。可是我们前面说了,使用View不是最终的选择,要实现动画还需要很多复杂的代码。相对来说,学习如何响应用户事件要简...
  • yexing
  • yexing
  • 2011年01月14日 16:14
  • 6696

从零开始Android游戏编程(第二版) 第九章 游戏程序的生命周期

第九章 游戏程序的生命周期 在讲解游戏程序的生命周期之前,让我们先看看普通Android应用的生命周期。关于生命周期,SDK附带的文档上有详细的解释,让我们打开文档,找到andorid.app->A...
  • yexing
  • yexing
  • 2011年01月14日 16:30
  • 5263

从零开始Android游戏编程_第二版

  • 2013年07月02日 15:12
  • 4.92MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:从零开始Android游戏编程(第二版) 第十一章 演员(Actor)、视口(ViewWindow),演出开始
举报原因:
原因补充:

(最多只允许输入30个字)