cocos2d-js版本A*算法

来源:互联网 发布:黄海波惹了谁 知乎 编辑:程序博客网 时间:2024/05/17 03:37

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, 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))},});

源码地址

0 0
原创粉丝点击