游戏玩法:

这是一个拼图游戏,游戏的开始会在一个完整的图案上留下一个空缺的格子,玩家通过移动这个空缺周围的拼图来最终完成图案。

游戏主要思路:

一、先是将一幅外部的图像导入,然后进行将其进行切分。所谓的切分就是将其代表不同块的区域的复制给一个一个的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;//将数组置空
              }
       }
}