您的位置:首页 > 移动开发 > Cocos引擎

cocos2d-js版本A*算法

2015-01-23 13:07 387 查看
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.
4000
_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, y);
},
//是否是墙壁
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", tileCoord, this._objectLayer);
},
//是否有狗
isDogAtTilecoord:function(tileCoord)
{
return this.isPropAtTileCoordForLayer("Dog", tileCoord, this._objectLayer);
},

//是否为出口
isExitAtTilecoord:function(tileCoord)
{
return this.isPropAtTileCoordForLayer("Exit", tileCoord, this._objectLayer);
},

//判断tile上存放了什么
isPropAtTileCoordForLayer:function(prop,tileCoord, 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, 0, 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,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,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))
},
});
源码地址
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息