cocos2d-js版本A*算法

A*算法的东西网上讲了很多~但还是不可避免的要去研究一下,cocos官网上有一个cocos2dx版本的A星算法(cocos2d-x A星算法),正好拿来改造一下,顺便踩踩cocos2d-js的坑

原理和伪代码部分可以参考这个(A*算法伪代码)废话不多说,直接上正题.

主要有2个封装原型类:

1、GameLayer:负责加载地图、保持地图跟随、坐标转换、地图上物品判断、接受触控事件

var GameLayer = cc.Layer.extend({
    _tileMap:null,_bgLayer:null,_objectLayer:null,_batchNode:null,_cat:null,bonesCount:null,ctor:function () {
        // ////////////////////////////
        // 1. super init first
	    this._super();
	    // 加载地图
	    this._tileMap = new cc.TMXTiledMap(res.CatMaze_tmx);
	    this._bgLayer = this._tileMap.getLayer("Background");
	    this._objectLayer = this._tileMap.getLayer("Objects");
	    this.addChild(this._tileMap);
	    
	    // 25*25 tiles
	    cc.log("map size width:"+this._tileMap.getMapSize().width+"		height:	"+this._tileMap.getMapSize().height);
	    
	    // tile size: 32*32
	    cc.log("tile size width:"+this._tileMap.getTileSize().width+"		height:	"+this._tileMap.getTileSize().height);
	    
	    // tile坐标
	    var spawnTileCoord = cc.p(24,0);
	    var spawnPos = this.positionForTileCoord(spawnTileCoord);
	    
	    // 视角跟随
	    this.setViewpointCenter(spawnPos);
	    // 背景音乐
	    cc.audioEngine.playMusic(res.SuddenDefeat_mp3,true);

	    cc.spriteFrameCache.addSpriteFrames(res.CatMaze_plist);
	    
	    //存放在地图上面~则会跟随地图
	    this._batchNode = new cc.SpriteBatchNode(res.CatMaze_png);
	    this._tileMap.addChild(this._batchNode);
	    
	    this._cat = new CatSprite(this);
	    this._cat.setPosition(spawnPos);
	    this._batchNode.addChild(this._cat);
	    
	    // bmfont 字体
	    this.bonesCount = new cc.LabelBMFont("Bones: 0",res.Arial_fnt);
	    this.bonesCount.setPosition(400,30);
	    this.addChild(this.bonesCount);
	    
	    cc.eventManager.addListener({
	    		event: cc.EventListener.TOUCH_ONE_BY_ONE,swallowTouches: true,onTouchBegan:function (touch,event) {
	    			// 猫移动向该点
	    			var point = event.getCurrentTarget()._tileMap.convertTouchToNodeSpace(touch);
	    			cc.log("touch point x:	"+point.x+"y:	"+point.y);
	    			event.getCurrentTarget()._cat.moveToward(point);
	    			return true;
	    		}
	    },this);
	    
	    this.scheduleUpdate();
        return true;
    },// 坐标转换为tile
    positionForTileCoord:function(p){
    	var x = (p.x * this._tileMap.getTileSize().width) + this._tileMap.getTileSize().width / 2;
    	var y = (this._tileMap.getMapSize().height *this. _tileMap.getTileSize().height) -
    	(p.y *this._tileMap.getTileSize().height) - this._tileMap.getTileSize().height / 2;
    	return cc.p(x,y);
    },// 地图跟随
    setViewpointCenter:function(position){
    	var size = cc.director.getWinSize();
    	var x = Math.max(position.x,size.width / 2);
    	var y = Math.max(position.y,size.height / 2);
    	x = Math.min(x,(this._tileMap.getMapSize().width * this._tileMap.getTileSize().width) - size.width / 2);
    	y = Math.min(y,(this._tileMap.getMapSize().height * this._tileMap.getTileSize().height) - size.height / 2);
    	var p = cc.p(x,y);
    	var center = cc.p(size.width/2,size.height/2);
    	var viewPoint = cc.pSub(center,p);

    	this._tileMap.setPosition(viewPoint);
    },update:function(){
    	this.setViewpointCenter(this._cat.getPosition());
    },tileCoordForPosition:function(position){
    	var x = parseInt( position.x / this._tileMap.getTileSize().width);
    	var y = parseInt(((this._tileMap.getMapSize().height *this._tileMap.getTileSize().height) - position.y) / this._tileMap.getTileSize().height);
    	return cc.p(x,//是否是墙壁
    isWallAtTileCoord:function(tileCoord){
    	return this.isPropAtTileCoordForLayer("Wall",tileCoord,this._bgLayer);
    },//显示骨头
    showNumBones:function(numBones)
    {
    	this.bonesCount.setString("Bones: "+ numBones);
    },isValidTileCoord:function(tileCoord){
    	if (tileCoord.x < 0 || tileCoord.y < 0 ||
    			tileCoord.x >= this._tileMap.getMapSize().width ||
    			tileCoord.y >= this._tileMap.getMapSize().height)
    	{
    		return false;
    	}
    	else
    	{
    		return true;
    	}
    },//是否有骨头
    isBoneAtTilecoord:function(tileCoord)
    {
    	//bone 存放在_objectLayer上
    	return this.isPropAtTileCoordForLayer("Bone",this._objectLayer);
    },//是否有狗
    isDogAtTilecoord:function(tileCoord) 
    {
    	return this.isPropAtTileCoordForLayer("Dog",//是否为出口
    isExitAtTilecoord:function(tileCoord) 
    {
    	return this.isPropAtTileCoordForLayer("Exit",//判断tile上存放了什么
    isPropAtTileCoordForLayer:function(prop,layer)
    {
    	if (!this.isValidTileCoord(tileCoord))
    	{
    		return false;
    	}
    	
    	//获得tile对应id
    	var gid = layer.getTileGIDAt(tileCoord);
    	
    	//这里返回的是dict类型
    	var properties = this._tileMap.getPropertiesForGID(gid);
    	if (properties==null)
    	{
    		return false;
    	}
    	return properties[prop]==1;
    },//移除tiles
    removeObjectAtTileCoord:function(tileCoord){
    	this._objectLayer.removeTileAt(tileCoord);
    },winGame:function(){
    	cc.log("win");
    	this.endScene();
    },loseGame:function(){
    	cc.log("lose");
    	this.endScene();
    },endScene:function()
    {
    	var self = this;
    	this._cat.runAction(cc.sequence( cc.scaleBy(0.5,3.0),cc.delayTime(1.0),cc.scaleTo(0.5,0.0),cc.callFunc(self.showRestartMenu,self)
    			));
    	this._cat.runAction(cc.repeatForever(cc.rotateBy(0.5,360.0)));
    },showRestartMenu:function(){
    	cc.log("showRestartMenu");
    },//是否可以通过
    walkableAdjacentTilesCoordForTileCoord:function(tileCoord){
    		var tmp = [];
    		// 上
    		var p1=cc.p(tileCoord.x,tileCoord.y - 1);
    		if (this.isValidTileCoord(p1) && !this.isWallAtTileCoord(p1)){
    			tmp.push(p1);
    		}
    		// 左
    		var p2=cc.p(tileCoord.x - 1,tileCoord.y);    		
    		if (this.isValidTileCoord(p2) && !this.isWallAtTileCoord(p2)){
    			tmp.push(p2);
    		}
    		// 下
    		var p3=cc.p(tileCoord.x,tileCoord.y + 1);    
    		if (this.isValidTileCoord(p3) && !this.isWallAtTileCoord(p3)){
    			tmp.push(p3);
    		}
    		// 右
    		var p4=cc.p(tileCoord.x + 1,tileCoord.y);    
    		if (this.isValidTileCoord(p4) && !this.isWallAtTileCoord(p4)){
    			tmp.push(p4);
    		}
    		cc.log("tileCoord: "+tileCoord.x+" "+tileCoord.y);
    		for(var i = 0;i<tmp.length;i++){
    			cc.log("tmp "+i+":	"+tmp[i].x+	"  "+tmp[i].y);
    		}
    		return tmp;
    }
});
2、CatSprite:负责A*算法实现、猫展示动画
var CatSprite = cc.Sprite.extend({
	_gameLayer:null,_facingForwardAnimation:null,_facingBackAnimation:null,_facingLeftAnimation:null,_facingRightAnimation:null,_bonenum:0,_shortestPath:[],//最短路径
	_spOpenSteps:[],//开放列表
	_spClosedSteps:[],//关闭列表
	_tempShortestPath:[],ctor:function(gameLayer){
		this._super("#cat_forward_1.png");
		this._gameLayer = gameLayer;

		this._facingForwardAnimation = this.createCatAnimation("forward");
		
		this._facingBackAnimation = this.createCatAnimation("back");
	
		this._facingLeftAnimation = this.createCatAnimation("left");
		
		this._facingRightAnimation = this.createCatAnimation("right");
			
		return true;
	},moveToward:function(target){
		cc.log("moveToward");
		var fromTileCoord = this._gameLayer.tileCoordForPosition(this.getPosition());
		var toTileCoord = this._gameLayer.tileCoordForPosition(target);
		
		if(toTileCoord.x == fromTileCoord.x&&toTileCoord.y==fromTileCoord.y){
			cc.log("You're already there! :P");
			return;
		}
		if(!this._gameLayer.isValidTileCoord(toTileCoord) ||this._gameLayer.isWallAtTileCoord(toTileCoord)){
			cc.audioEngine.playEffect(res.hitWall_wav);
			return;
		}
		cc.log("From:	" + fromTileCoord.x + "	"+ fromTileCoord.y);
		cc.log("To:	" + toTileCoord.x + "	"+ toTileCoord.y);
		
		this._spOpenSteps = [];
		this._spClosedSteps = [];
		// 首先,添加猫的方块坐标到open列表
		this.insertInOpenSteps(new ShortestPathStep(fromTileCoord));
		do{
			//这里要当心死循环		
			var currentStep = this._spOpenSteps[0];
			currentStep.retain();		
			cc.log("currentStep:"+currentStep.getPosition().x+"  "+currentStep.getPosition().y);
			// 添加当前步骤到closed列表
			this._spClosedSteps.push(currentStep);
			// 将它从open列表里面移除
			this._spOpenSteps.splice(0,1);
			// 如果当前步骤是目标方块坐标,那么就完成了
			if (toTileCoord.x == currentStep.x&&toTileCoord.y==currentStep.y){
				cc.log("path found");
				this.constructPathAndStartAnimationFromStep(currentStep);
				this._spOpenSteps = [];
				this._spClosedSteps = [];
				break;		
			}
			//this.printPath(currentStep);
			var adjSteps = this._gameLayer.walkableAdjacentTilesCoordForTileCoord(currentStep.getPosition());
			for (var i = 0; i < adjSteps.length; ++i){
				var step = new ShortestPathStep(adjSteps[i]);
				if (this.indexOf(this._spClosedSteps,step)!=-1){
			
					continue;
				}
				var moveCost = this.costToMoveFromStepToAdjacentStep(currentStep,step);
				var index = this.indexOf(this._spOpenSteps,step);
				if (index == -1){
					step.setParent(currentStep);
					step.setGScore(currentStep.getGScore() + moveCost);
					step.setHScore(this.computeHScoreFromCoordToCoord(step.getPosition(),toTileCoord));
					this.insertInOpenSteps(step);
				}else{
					step = this._spOpenSteps[index];
					if ((currentStep.getGScore() + moveCost) < step.getGScore()){
						step.setGScore(currentStep.getGScore() + moveCost);
						// 因为G值改变了,F值也会跟着改变
						// 所以为了保持open列表有序,需要将此步骤移除,再重新按序插入
						// 在移除之前,需要先保持引用
//						step.retain();
						// 现在可以放心移除,不用担心被释放
						this._spOpenSteps.splice(index,1);
//						// 重新按序插入
						this.insertInOpenSteps(step);
//						// 现在可以释放它了,因为open列表应该持有它
//						step.release();
					}
				}
			}
		}while (this._spOpenSteps.length > 0);

		for (var i = 0;i<this._shortestPath.length;i++){
			cc.log("Description:",this._shortestPath[i].getDescription());
		}		

	},//序列帧动画
	createCatAnimation:function(animType)
	{
		var animation = new cc.Animation();
		for (var i = 1; i <= 2; ++i)
		{
			animation.addSpriteFrame(cc.spriteFrameCache.getSpriteFrame("cat_"+animType+"_"+i+".png"));
		}
		animation.setDelayPerUnit(0.2);
		animation.retain()
		return animation;
	},//用来判断step所在位置
	indexOf:function(array,step){
		if(array.length>0){
			for(var i = 0;i<array.length;i++){
				if(array[i].isEqual(step)){
					return i;
				}
			}
		}
		return -1;
	},//插入一个step  维护一个有序列表
	insertInOpenSteps:function(step)
	{
		var stepFScore = step.getFScore();
		var count = this._spOpenSteps.length;
		var i ;
		for (i = 0; i < count; ++i)
		{
			if (stepFScore <= this._spOpenSteps[i].getFScore())
			{
				break;
			}
		}

		this._spOpenSteps.splice(i,step);
	},//计算H值
	computeHScoreFromCoordToCoord:function(fromCoord,toCoord)
	{
		// 这里使用曼哈顿方法,计算从当前步骤到达目标步骤,在水平和垂直方向总的步数
		// 忽略了可能在路上的各种障碍
		return Math.abs(toCoord.x - fromCoord.x) +  Math.abs(toCoord.y - fromCoord.y);
	},//这里可以扩展~
	costToMoveFromStepToAdjacentStep:function(fromStep,toStep){
		//return ((fromStep->getPosition().x != toStep->getPosition().x)
		//	&& (fromStep->getPosition().y != toStep->getPosition().y)) ? 14 : 10;
		return 1;
	},//构造最短路径
	constructPathAndStartAnimationFromStep:function(step){
		this._shortestPath=[];
		do{
			// 起始位置不要进行添加
			if (step.getParent())
			{
				// 总是插入到索引0的位置,以便反转路径
				this._shortestPath.splice(0,step); 
			}
			step = step.getParent();   // 倒退
		} while (step);                 // 直到没有上一步
		for (var i = 0;i<this._shortestPath.length;i++){
			cc.log("Description:",this._shortestPath[i].getDescription());
		}
		this.popStepAndAnimate();
	},//打印路径
	printPath:function(step){
		this._tempShortestPath=[];
		do{
			// 起始位置不要进行添加
			if (step.getParent())
			{
				// 总是插入到索引0的位置,以便反转路径
				this._tempShortestPath.splice(0,step); 
			}
			step = step.getParent();   // 倒退
		} while (step);                 // 直到没有上一步
		for (var i = 0;i<this._tempShortestPath.length;i++){
			cc.log("Description:",this._tempShortestPath[i].getDescription());
		}
		cc.log("---------------------------------------------------------------------");
	},//展示运动
	popStepAndAnimate:function(){
		var currentPosition = this._gameLayer.tileCoordForPosition(this.getPosition());
		if(this._gameLayer.isBoneAtTilecoord(currentPosition)){
			this._bonenum++;
			this._gameLayer.showNumBones(this._bonenum);
			this._gameLayer.removeObjectAtTileCoord(currentPosition);
		}else if(this._gameLayer.isDogAtTilecoord(currentPosition)){
			if (this._bonenum <= 0){
				this._gameLayer.loseGame();
				return;
			}else{
				this._bonenum--;
				this._gameLayer.showNumBones(this._bonenum);
				this._gameLayer.removeObjectAtTileCoord(currentPosition);
			}
		}else if (this._gameLayer.isExitAtTilecoord(currentPosition)){
			this._gameLayer.winGame();
		}
		
		var  s = this._shortestPath[0];
		
		if(s==undefined){
			return;
		}
		s.retain();
		var futurePosition = s.getPosition();
	
		var diff = cc.pSub(futurePosition,currentPosition);
		if (Math.abs(diff.x) > Math.abs(diff.y)){
			if (diff.x > 0){
				this.runAction(cc.animate(this._facingRightAnimation));
			}else{
				this.runAction(cc.animate(this._facingLeftAnimation));
			}
		}else{
			if (diff.y > 0){
				this.runAction(cc.animate(this._facingForwardAnimation));
			}else{
				this.runAction(cc.animate(this._facingBackAnimation));
			}
		}
			
		var moveAction = cc.moveTo(0.4,this._gameLayer.positionForTileCoord(s.getPosition()));
		
		this._shortestPath[0].retain();
		this._shortestPath.splice(0,1);
		var moveCallback = cc.callFunc(this.popStepAndAnimate,this);
		this.runAction(cc.sequence(moveAction,moveCallback))	
	},});

源码地址

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...