书名顾名思义,可以理解AS3.0游戏编程大学
这个书,从常用的游戏基础知识讲起,然后外加7-8个游戏范本,从无到有,从简单到复杂,循序渐进的方式教学,很适合第一次以AS3.0开发游戏的人员学习。我个人认为此书比较系统的讲解了每个基础点的知识,让人学后就会灵活使用的好书。
如果大家有英语基础知识的话,可以下载英文原版看看。
今天我翻译的一个游戏教程是:配对游戏
游戏虽然简单,但是翻译的工作量比较大,因为自己明白了不是真正的明白,要把明白的东西写出来让大家明白才是真的明白,所以转载的话,麻烦大家不要编辑掉我的博客地址,因为我会在陆续的时间里,接着翻译书本剩下的游戏 ![]()
http://www.gotoandstop.cn
或者这个地址:
http://www.minicg.com/blog/2010/01/04/actionscript-3-game-programming-university.html
(1)Placing Interactive Elements 定义配对的卡片
(2)Game Play 开始游戏
(3)Encapsulating the Game 游戏的整合封装
(4)Adding Scoring and a Clock 添加游戏积分和时间的控制
(5)Adding Game Effects 添加游戏的效果
(6)Modifying the Game 游戏的修改
(1)定义配对的卡片
创建配对图片的方法:
一.Multiple-Symbol Method 创建多个元件
创建多个元件配对的方式,就是一个影片元件代表一个卡片,假如我们这个游戏需要36个卡片,因为是配对游戏,所以实际所需的卡片数位36/2=18个,这样我们就需要18个独立的元件。这样就给我们带来一些问题:
1.我们需要复制17次相同的元件,因为每个卡片都有自己共同的特点,就是边框和背景色。当然你可能会想到,解决这个方法,就是弄一个背景色的图形元件解决此类问题。
2.同样一个问题,就是如果将来我们需要改变卡片的话,例如,我们需要重设这个卡片的尺寸的话,我们需要同时修改其他的卡片18次。
假如你只是一个程序员的话,因为界面图形都需要其他设计师设计的话,设计师要更换图片符号的话,需要一定的时间,这样效率不是最好的。
二.Single-Symbol Method 一个简单的元件
第二个方法就是设置一个单独的影片剪辑了,我们只需要一个影片剪辑,但是里面是多帧的,也就是说,每帧里有不同的图片符号内容。这样我们就可以在单独的一层里放置这个背景和图形,这样每个帧里都可以用上。
这样的好处在于以后更新内容尺寸的话,会非常的方便,我们很容易的找到我们需要修改的内容。同时我们也会很方便的从其他设计师手里得到这个更新的影片剪辑
[题外话]最方便的还是利用xml+外部pic来实现,这样只需要在外部修改图片,就可以更改游戏内容了,因为所翻译的游戏暂时没有提到,我这里就略过了,等整个游戏结束后,抽空在把这部分加上。
Setting Up the Flash Movie
flash里的影片剪辑设置
我们在库里至少需要一个影片剪辑,这个影片剪辑包括所有的我们需要的配对内容。当然,我们这个影片剪辑第一帧就是默认的卡片背部,就是没有点击时卡片的显示状态。
现在我们需要在这个影片剪辑里创建19个关键帧,第一帧是默认的初始状态,后面的就是单个关键帧对应一个图形。状态如下图:
在库里找到这个影片剪辑,然后右键链接里进行一些参数的设置。如下图:
Base Class默认为MovieClip,Class类名,我们设置为Card。
好了,现在基本工作做好了,我们的主场景里啥也没有,只是在库里有一个影片剪辑,现在我们需要的工作就是开始我们的代码设计了。
Creating the Basic ActionScript Class
现在我们需要创建一个文档类了,在flash里新建一个AS文件。
package { import flash.display.*; }
我们开始我们这个类文件的书写了。我们可以看到这部分代码,package就是我们通知flash引擎,我们的类文件所在的路径。import flash.display.*;这部分,就是告诉引擎,我们需要导入这个显示类包,这样我们可以就可以导入这个包下的所有的子类文件了。这样我们才能创建和控制想卡片这样的影片剪辑。
接下来我们需要申明我们的类文件了:
public class MatchingGame1 extends MovieClip
请注意,此时的类名和我们的这个类文件名要统一
接下来就是定义一些类属性了,我们当前要做的就是在屏幕生成36个卡片,所以我们暂时不需要定义属性,等有需要的时候在加上。:)
现在要做的就是申明一个构造函数了,它也是和类名,文件名同名的。
public function MatchingGame1():void {
现在我们开始构造我们的那36个卡片吧,我们期望的是6×6型的排列方式,因此我们需要2个循环函数
for(var x:uint=0;x<6;x++) { for(var y:uint=0;y<6;y++) {
第一个循环函数,是对x坐标进行控制的,后一个循环就是对y坐标进行控制的。
接下来我们要进行一些属性的设置,来控制卡片的具体位置和显示的方式。
var thisCard:Card = new Card(); //从库里复制一个元件 thisCard.stop(); //让其停止在第一帧,就是初始时的显示 thisCard.x = x*52+120; //120是卡片的x坐标偏移位置,52,就是卡片的间距 thisCard.y = y*52+45; //45就是y坐标的偏移位置,52也是间距 addChild(thisCard); //这个很关键,所有的显示的物件,都必须addChild后才能在显示列表里显示出来,否则我们是看不到的。
好了,基本工作做好了,我们开始进行检测的测试下吧,这个新建的文档类,我们可以直接在属性面板里设定,如下图:
设置好了,发布试试看,有什么结果。
Using Constants for Better Coding
在我们进行下一步的操作前,我们开始看看,我们现在需要解决的手头问题是什么
假如你不需要6×6,而需要4×4,或者需要6×5排列方式的话,我们只需要在上面所提到的循环体里修改合适的参数就可以实现了,这里就留给大家自己去测试吧。。。偷懒了。。
大家经过几次测试后,会感觉到这个循环体的特点了,现在我们把那几个以后会改动的参数拿出来,以类属性的方式来定义他们,以后修改的话,就不需要在代码里去找了,直接修改变量值就可以轻松实现了。
Horizontal Rows = 6 Vertical Rows = 6 Horizontal Spacing = 52 Vertical Spacing = 52 Horizontal Screen Offset = 120 Vertical Screen Offset = 45 public class MatchingGame2 extends MovieClip { // game constants private static const boardWidth:uint = 6; private static const boardHeight:uint = 6; private static const cardHorizontalSpacing:Number = 52; private static const cardVerticalSpacing:Number = 52; private static const boardOffsetX:Number = 120; private static const boardOffsetY:Number = 45;
private 是私有的意思,就是只有这个类里可以访问到的 static静态的话,就是说以后假如还会扩展这个类的话,他们这个属性是共有的,是这个类所有的共性。 Const常量,就是说这个值不可以修改。
现在有了变量的话,我们要做的工作就是把之前循环体里的数据,替换为我们定义的变量了
var thisCard:Card = new Card(); thisCard.stop(); thisCard.x = x*cardHorizontalSpacing+boardOffsetX; thisCard.y = y*cardVerticalSpacing+boardOffsetY; addChild(thisCard);
好了,现在的这个类设置好了,我们可以测试了,我们尝试的修改变量值,看会有什么结果。
Shuffling and Assigning Cards
现在的卡片已经在舞台上显示了,我们现在需要的是这些卡片中的图片能随机的出现在每个位置。我们当前是拥有36个卡片在舞台上,因为是配对游戏,我们只需要18组不同的卡片在随机的位置。
为了解决这个问题,我们需要一个数组来保存我们这些卡片,然后随机的从数组里抽取卡片。这个数组有36个,即18组卡片,每次从数组里抽取一个卡片后,我们就从这个数组里删去所抽取的卡片,同时在舞台上让其显示出来。
var cardlist:Array = new Array(); for(var i:uint=0;icardlist.push(i); cardlist.push(i); }
数组添加后的结果就是:
0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,14,14,15,15,16,16,17,17
现在我们已经得到一个拥有36个,18组的卡片信息了,现在要做的就是随机的从数组里抽取卡片了。
for(var x:uint=0;xfor(var y:uint=0;yvar c:Card = new Card(); // copy the movie clip c.stop(); // stop on first frame c.x = x*cardHorizontalSpacing+boardOffsetX; // set position c.y = y*cardVerticalSpacing+boardOffsetY; var r:uint = Math.floor(Math.random()*cardlist.length); // get a random face c.cardface = cardlist[r]; // assign face to card cardlist.splice(r,1); // remove face from list c.gotoAndStop(c.cardface+2); addChild(c); // show the card } }
var r:uint = Math.floor(Math.random()*cardlist.length);
这段代码就是从数组列表里从0-元素个数之间得到一个随机数。即0-35.9999,因为元素的索引值是整数的,所以我们需要用到取整函数Math.floor,即向下取整,获得的最大值就是35了,如果想上取整的话,会发生错误的。
cardlist.splice(r,1); // remove face from list
就是将获得的索引的元素从数组里删去,不然的话,会发生重复调用的情况,因为已经使用过了,不需要了,删去。
c.gotoAndStop(c.cardface+2);
因为c.carface获得的值为0-17,而我们的Card这个影片剪辑的关键帧是从1开始到19的,因为第一帧我们用做了背景。
测试下,我们会看到什么结果?
现在离我们的目标越来越近了。。
Game Play
现在的游戏基本的思路已经弄好了,现在我们需要做的就是让用户点击卡片之后,去匹配我们的选择结果。我们需要一个记录卡片信息的值,这样我们才能知道第一次点击后第二次点击的卡片是否符合要求。
Adding Keyboard Listeners
上面提到了用户点击的话,我们就需要添加鼠标点击相关的类文件了,还有事件侦听函数了。addEventListener,这个函数有两个参数为必须的,一个是侦听的事件类型,一个是回调函数
我们现在需要的是:
c.addEventListener(MouseEvent.CLICK,clickCard);
我们需要添加另外一个事件类了
Import flash.events.*;
回调函数为:
public function clickCard(event:MouseEvent) { var thisCard:Card = (event.currentTarget as Card); // what card? trace(thisCard.cardface); }
在这个实例里,event参数是很关键的,因为我们要利用它来获取用户当前点击的卡片是36个图片中的哪一个。事件目标就是我们所获得的重要的信息,确切的说当前事件的目标。就这样,我们新建一个thisCard物件来指向我们所点击的卡片,我们可以从这个物件里获得cardface属性。我们可以利用trace来调试我们所获得的这些信息。MatchingGame4.as
Setting Up Game Logic
的开始设计游戏的逻辑了
当用户点击了卡片后,我们需要确定到底属于游戏中的哪种情况,我们现在有3中情况需要处理:
1. 开始没有卡片被选取的情况下,用户选择了第一张卡片。
2. 已有一个卡片选取了,玩家选取第二个卡片,现在要处理的是所选取的这两个卡片是否匹配。
3. 2个卡片选取了,但是并不匹配,需要做的是让这两个卡片恢复到初始状态。
为了方便记录我们所选的卡片记录,我们需要用定义两个变量。firstCard和secondCard。
Private var firstCard; Private var secondCard;
虽然定义了两个变量,但是我们并没有给他附加任何具体的值。因为在稍后的操作里,我们会用这个null的值的变量去记录用户所选的卡片的具体状态。
好,回到先前的逻辑,不过用代买来解释了
1. 开始没有卡片被选取的情况下,用户选择了第一张卡片。
Firstcard
2.已有一个卡片选取了,玩家没有选取第二个卡片。
具体代码:
public function clickCard(event:MouseEvent) { var thisCard:Card = (event.target as Card); // what card? if (firstCard == null) { // first card in a pair firstCard = thisCard; // note it firstCard.gotoAndStop(thisCard.cardface+2); // turn it over } else if (firstCard == thisCard) { // clicked first card again firstCard.gotoAndStop(1); // turn back over firstCard = null; } else if (secondCard == null) { // second card in a pair secondCard = thisCard; // note it secondCard.gotoAndStop(thisCard.cardface+2); // turn it over // compare two cards if (firstCard.cardface == secondCard.cardface) {
如果两个卡片匹配成功的话,我们需要移除这两个卡片同时重设fisrtcardhe secondcard这两个变量。虽然从显示列表里移除了,但是他们实际上仍然储存在变量里,所以我们需要把变量的值重设为null,这样才会从flash播放器里得到处理,真正的移除掉引用。
// remove a match removeChild(firstCard); removeChild(secondCard); // reset selection firstCard = null; secondCard = null; }
当然如果不匹配的话,我们需要重设这两个卡片
} else { // starting to pick another pair // reset previous pair firstCard.gotoAndStop(1); secondCard.gotoAndStop(1); secondCard = null; // select first card in next pair firstCard = thisCard; firstCard.gotoAndStop(thisCard.cardface+2); } }
在MatchingGame5.as
Checking for Game Over
检查游戏的结束
游戏结束的时候,需要在屏幕上出现相应的提示信息告诉玩家,你已完成游戏,所有的卡片都匹配完毕了。
解决的方法有:
弄一个变量记录当前的卡片匹配个数,如果玩家每找到一组的话,就相应的加1,然后再检查这个变量是否与总数是否相等。
另一个方法就是检查卡片的个数,因为每匹配一组的话,就会从显示列表移除一组,总数是36个,当所有的都移除的话,显示列表的个数numchildren就为0了,也就是说游戏结束了。
有这个方法的话,会发生的问题就是,如果当前的显示列表里有其他的显示元素,例如背景图片,标题等等的时候,显示的个数也会被算进去的。
所以我们还是采用第一种方案
新建变量
private var cardsLeft:uint; cardsLeft = 0; for(var x:uint=0;xfor(var y:uint=0;yvar c:Card = new Card(); // copy the movie clip c.stop(); // stop on first frame c.x = x*cardHorizontalSpacing+boardOffsetX; // set position c.y = y*cardVerticalSpacing+boardOffsetY; var r:uint = Math.floor(Math.random()*cardlist.length); // get a random face c.cardface = cardlist[r]; // assign face to card cardlist.splice(r,1); // remove face from list c.addEventListener(MouseEvent.CLICK,clickCard); // have it listen for clicks addChild(c); // show the card cardsLeft++; }
在原来匹配的代码里添加
cardsLeft -= 2; if (cardsLeft == 0) { gotoAndStop(“gameover”); }
当剩余卡片数为0的时候,就跳转到gameover标签的关键帧
此时,我们需要移除所有用代码创建的元素,但是,因为之前我们创建的36个卡片,因为我们每匹配一次就移除了一次,所有当匹配完所有的后,没有卡片剩余在屏幕上。所以我们可以直接跳转到gameover
在gameover这个关键帧里,我们可以放入简单的文字元件,或者其他的图形元件,当然你也可以放入动画等
好了,接下来的工作就是在这个关键帧里加入一个重玩游戏的按键
Encapsulating the Game
封装游戏
现在我们可以正常运行我们的游戏了,当我们的flash影片运行是,游戏就初始化并开始了,影片就是游戏,游戏就是影片了
此时的游戏只是个简单的类型,但现实情况是我们需要游戏说明,游戏结束是的画面,加载画面等等。你甚至希望加入新的不同的内容用于不用的游戏版本。Flash是个很好的工具,swf其实就是一个影片剪辑,你可以在一个影片剪辑里嵌套一个影片剪辑,所以说一个游戏可以是一个影片剪辑,或者说一个游戏可以作为一个影片剪辑嵌套在另一个影片剪辑里。
为什么我们需要这样做呢?很简单,就是为了方便的更新新的内容,因此我们可以在第一帧作为游戏说明的界面,第二帧为游戏,第三帧为游戏结束画面。第二帧里的所拥有的影片,我们取名为MatchingGameObject7,我们将它捆包上MatchingGameObject7.as
Creating the Game Movie Clip
在这个MatchingGameObject7.fla我们有3个关键帧,第二帧里只有一个简单的影片剪辑,它是一个空的影片剪辑,设置它和MatchingGameObject7.as类绑定,这样我们的游戏,实际上是由这个空的影片剪辑掌管了。
这样我们就将游戏分为了游戏前和游戏后,而关键的游戏部分代码就在第二帧里的这个空影片剪辑所绑定的类文件。
Adding an Introduction Screen
添加游戏介绍的界面
我们不希望玩家一开始就直接玩游戏,他们也需要一些简单的游戏说明,在这个界面,我们要加入少量的AS,首先我们必须让游戏一开始运行的时候,就停止在这个关键帧上,然后加入几个按钮给用户选择。
playButton.addEventListener(MouseEvent.CLICK,startGame); function startGame(event:MouseEvent) { gotoAndStop(“playgame”); } stop();
第二帧的标签名为playgame
第二帧里,需要放入之前我们绑定的空的影片剪辑,因为类文件名修改了,所以我们需要把之前的类文件重命名MatchingGameObject7.as。
我们要修改一处的代码
MovieClip(root).gotoAndStop(“gameover”);
之前直接可以gotoAndStop,因为现在和影片剪辑绑定了,而gameover是root里的标签,所以需要修改此处。
同时把属性面板绑定的文档类给删除掉。
Adding a Play Again Button
添加重玩按钮
在最后一帧,我们将要添加一个重玩按钮。
playAgainButton.addEventListener(MouseEvent.CLICK,playAgain); function playAgain(event:MouseEvent) { gotoAndStop(“playgame”); }
Adding Scoring and a Clock
稍后补上,读者可以自己先弄弄 :cool:





Comments