游戏玩法:
这是一个拼图游戏,游戏的开始会在一个完整的图案上留下一个空缺的格子,玩家通过移动这个空缺周围的拼图来最终完成图案。
游戏主要思路:
一、先是将一幅外部的图像导入,然后进行将其进行切分。所谓的切分就是将其代表不同块的区域的复制给一个一个的Sprite。
二、将块打乱顺序
三、然后为每一个Sprite加侦听器响应玩家点击。
四、实时判断是否游戏结束
准备工作:
元件:
只需准备一张外部图像文件名为“slidingimage.jpg”
舞台设置:
一共有三帧。
第一帧:有一个开始按钮,实例名为startButton,帧代码为
stop();
startButton.addEventListener(MouseEvent.CLICK,clickStart);
function clickStart(event:MouseEvent) {
gotoAndStop("play");
}
很容易理解,点击跳到第二帧,即游戏开始帧。
第二帧:帧代码为startSlidingPuzzle();即调用游戏主函数开始游戏。
第三帧:有一个重玩按钮,实例名为playAgainButton,帧代码为
playAgainButton.addEventListener(MouseEvent.CLICK,clickPlayAgain);
function clickPlayAgain(event:MouseEvent) {
gotoAndStop("play");
}
点击重新开始游戏
类文件的编写
此游戏只有一个主类为SlidingPuzzle继承自MovieClip
package {
//导入需要的包
import flash.display.*;
import flash.events.*;
import flash.net.URLRequest;
import flash.geom.*;//这个包有我们后面用到的Point类
import flash.utils.Timer;
public class SlidingPuzzle extends MovieClip {
static const pieceSpace:Number = 2;//定义了块之间的间距
static const horizOffset:Number = 50;//定义了水平的偏移
static const vertOffset:Number = 50;//定义了垂直的偏移
//
static const numPiecesHoriz:int = 4;
static const numPiecesVert:int = 3;
//
static const numShuffle:int = 200;
//
static const slideSteps:int = 10;
static const slideTime:int = 250;
//
private var pieceWidth:Number;
private var pieceHeight:Number;
//
private var puzzleObjects:Array;
//
private var blankPoint:Point;//空白块的坐标
private var slidingPiece:Object;//当前移动的块对象
private var slideDirection:Point;//块移动的点
private var slideAnimation:Timer;//移动动画的计时器
//定义游戏开始函数
public function startSlidingPuzzle() {
// 取整个图片最右下角的块为空白
blankPoint = new Point(numPiecesHoriz-1,numPiecesVert-1);
// 加载图片
loadBitmap("slidingimage.jpg");
}
//
public function loadBitmap(bitmapFile:String) {//参数为字符串类型用来接收图片文件名
var loader:Loader = new Loader();//定义Loader类
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadingDone);//对Load类加侦听器,当加载完成时执行loadingDone函数。
var request:URLRequest = new URLRequest(bitmapFile);//定义URLRequest即图片资源的地址
loader.load(request);//用Loader加载图片
}
//
public function loadingDone(event:Event):void {
// 建立一个新对象来持有图像。此对象为Bitmap类,用他的目的是为了后面好对像素进行操作,来达到复制的目的。
var image:Bitmap = Bitmap(event.target.loader.content);
//用图像高和宽分别除横纵的块数来获取块的大小
pieceWidth = image.width/numPiecesHoriz;
pieceHeight = image.height/numPiecesVert;
// 执行切图函数
makePuzzlePieces(image.bitmapData);
// 执行乱序
shufflePuzzlePieces();
}
//
public function makePuzzlePieces(bitmapData:BitmapData) {//用一个参数来接收图像信息。这里要注意,我们并没有用Bitmap类的变量来作参数,而用BitmapData类型的。这是因为每一个Bitmap类实际上是由自己的BitmapData类来保存信息的,所以我们对其操作也是要用其BitmapData类。
puzzleObjects = new Array();//创建数组
//下面还是经常用的方法:利用双重for循环来排列块
for(var x:uint=0;x<numPiecesHoriz;x++) {
for (var y:uint=0;y<numPiecesVert;y++) {
// 当执行到空白块时,用continue命令跳过此次循环,那么这个位置就没有图案。
if (blankPoint.equals(new Point(x,y))) continue;
var newPuzzlePieceBitmap:Bitmap = new Bitmap(new BitmapData(pieceWidth,pieceHeight));//创建一个块的Bitmap位图
newPuzzlePieceBitmap.bitmapData.copyPixels(bitmapData,new Rectangle(x*pieceWidth,y*pieceHeight,pieceWidth,pieceHeight),new Point(0,0));//将指定的像素拷贝到这个位图
var newPuzzlePiece:Sprite = new Sprite();//创建一个Sprite
newPuzzlePiece.addChild(newPuzzlePieceBitmap);//将刚才创建的位图加入到Sprite中
addChild(newPuzzlePiece);//将Sprite放到舞台上
// 设置块的坐标
newPuzzlePiece.x = x*(pieceWidth+pieceSpace) + horizOffset;
newPuzzlePiece.y = y*(pieceHeight+pieceSpace) + vertOffset;
// 为每个块创建一个Object用来存储每个块的信息,包括当前坐标和目标位置坐标
var newPuzzleObject:Object = new Object();
newPuzzleObject.currentLoc = new Point(x,y);//当前位置坐标
newPuzzleObject.homeLoc = new Point(x,y);//目标位置坐标
newPuzzleObject.piece = newPuzzlePiece;//所持有的块对象的引用
newPuzzlePiece.addEventListener(MouseEvent.CLICK,clickPuzzlePiece);为每个块加侦听
puzzleObjects.push(newPuzzleObject);将此Object加入到数组中
}
}
}
//
public function shufflePuzzlePieces() {
for(var i:int=0;i<numShuffle;i++) {
//执行乱序
shuffleRandom();
}
}
public function shuffleRandom() {
// 循环所有可移动的块,用validMove函数来判断是否可移动,然后用validPuzzleObjects数组来存储所有可移动的块
var validPuzzleObjects:Array = new Array();
for(var i:uint=0;i<puzzleObjects.length;i++) {
if (validMove(puzzleObjects[i]) != "none") {
validPuzzleObjects.push(puzzleObjects[i]);
}
}
// 从validPuzzleObjects数组中所有可以动的块中随机选出一个进行移动
var pick:uint = Math.floor(Math.random()*validPuzzleObjects.length);
movePiece(validPuzzleObjects[pick],false);//调用movePiece移动函数,这个函数有两个参数第一个是要移动块的引用,第二个是一个布尔值,true为执行动画,false为不执行动画。因为我们现在是执行乱序不需要让玩家看到他的过程,所以我们选false。
}
//用来判断是否块可以动的函数,函数有返回值,且返回值为String类型
public function validMove(puzzleObject:Object): String {
// 用来判断空白点是否在此块的上面,如果是返回”up”
if ((puzzleObject.currentLoc.x == blankPoint.x) &&
(puzzleObject.currentLoc.y == blankPoint.y+1)) {
return "up";
}
//用来判断空白点是否在此块的下面,如果是返回”down”
if ((puzzleObject.currentLoc.x == blankPoint.x) &&
(puzzleObject.currentLoc.y == blankPoint.y-1)) {
return "down";
}
//用来判断空白点是否在此块的左面,如果是返回”left”
if ((puzzleObject.currentLoc.y == blankPoint.y) &&
(puzzleObject.currentLoc.x == blankPoint.x+1)) {
return "left";
}
//用来判断空白点是否在此块的右面,如果是返回”right”
if ((puzzleObject.currentLoc.y == blankPoint.y) &&
(puzzleObject.currentLoc.x == blankPoint.x-1)) {
return "right";
}
// 如果此块上下左右都不可移动则返回”none”
return "none";
}
//
public function clickPuzzlePiece(event:MouseEvent) {
// 从puzzleObjects数组里找到当前点击的对象然后执行movePiece函数(块动画函数),并跳出循环。这时我们注意movePiece函数第二个参数为true,说明我们需要让玩家看到块的动画。
for(var i:int=0;i<puzzleObjects.length;i++) {
if (puzzleObjects[i].piece == event.currentTarget) {
movePiece(puzzleObjects[i],true);
break;
}
}
}
// 定义移动函数movePiece
public function movePiece(puzzleObject:Object, slideEffect:Boolean) {
// 通过validMove函数获取空白块与当前块的相对关系并执行movePieceInDirection函数来实现移动,movePieceInDirection函数第二个和第三个参数分别代表相对坐标。可以举个例子,当空白块在当前块的上方,空白y坐标为2,当前应为3,当移动之后块当前变为2,即减一,所以我们传入-1值。
switch (validMove(puzzleObject)) {
case "up":
movePieceInDirection(puzzleObject,0,-1,slideEffect);
break;
case "down":
movePieceInDirection(puzzleObject,0,1,slideEffect);
break;
case "left":
movePieceInDirection(puzzleObject,-1,0,slideEffect);
break;
case "right":
movePieceInDirection(puzzleObject,1,0,slideEffect);
break;
}
}
// 定义movePieceInDirection函数
public function movePieceInDirection(puzzleObject:Object, dx,dy:int, slideEffect:Boolean) {
//将相对坐标加到当前坐标上
puzzleObject.currentLoc.x += dx;
puzzleObject.currentLoc.y += dy;
//空白点则正好相反,减去相对坐标
blankPoint.x -= dx;
blankPoint.y -= dy;
// 用来判断是否开启移动效果
if (slideEffect) {
// 执行动画函数startSlide
startSlide(puzzleObject,dx*(pieceWidth+pieceSpace),dy*(pieceHeight+pieceSpace));
} else {
// 如果不是仅仅是将坐标改变
puzzleObject.piece.x = puzzleObject.currentLoc.x*(pieceWidth+pieceSpace) + horizOffset;
puzzleObject.piece.y = puzzleObject.currentLoc.y*(pieceHeight+pieceSpace) + vertOffset;
}
}
//
public function startSlide(puzzleObject:Object, dx, dy:Number) {
if (slideAnimation != null) slideDone(null);//这句话的意思是当动画还在进行,但是玩家又点击了另外的块,为避免错误,这里强制停止了动画,即执行slideDone函数。
slidingPiece = puzzleObject;
slideDirection = new Point(dx,dy);
slideAnimation = new Timer(slideTime/slideSteps,slideSteps);//定义计时器,执行次数就是动画步数,而每次的间隔时间则是用动画时间除以动画步数
slideAnimation.addEventListener(TimerEvent.TIMER,slidePiece);//为计时器加一个侦听响应每次计时
slideAnimation.addEventListener(TimerEvent.TIMER_COMPLETE,slideDone);//为计时器加一个侦听响应其完成动画时的情况
slideAnimation.start();//启动计时器
}
//每步动画的移动
public function slidePiece(event:Event) {
slidingPiece.piece.x += slideDirection.x/slideSteps;
slidingPiece.piece.y += slideDirection.y/slideSteps;
}
//
public function slideDone(event:Event) {
//这个语句主要应该有两个作用,一个是出现当前块正在移动,而玩家又点击其他块的情况时,我们进行强制停止动画,应该将块一次归位。另一个作用就是对完成移动动画的块位置进行修正,因为之前的动画会设计到浮点运算,会导致最后的位置偏移。
slidingPiece.piece.x = slidingPiece.currentLoc.x*(pieceWidth+pieceSpace) + horizOffset;
slidingPiece.piece.y = slidingPiece.currentLoc.y*(pieceHeight+pieceSpace) + vertOffset;
//停止动画计时器,并将其引用指空。
slideAnimation.stop();
slideAnimation = null;
// 检测是否游戏结束,如果是跳到gameover帧,并清除所有块
if (puzzleComplete()) {
clearPuzzle();
gotoAndStop("gameover");
}
}
//定义检测函数。原理很简单,用for循环puzzleObjects数组的成员,如果出现当前位置不等于目标位置的情况则返回false,如果全都符合则最终返回true
public function puzzleComplete():Boolean {
for(var i:int=0;i<puzzleObjects.length;i++) {
if (!puzzleObjects[i].currentLoc.equals(puzzleObjects[i].homeLoc)) {
return false;
}
}
return true;
}
//
public function clearPuzzle() {
for (var i in puzzleObjects) {
//删除对象和侦听
puzzleObjects[i].piece.removeEventListener(MouseEvent.CLICK,clickPuzzlePiece);
removeChild(puzzleObjects[i].piece);
}
puzzleObjects = null;//将数组置空
}
}
}