设计sfc上的一款战棋游戏戏需要那些知识?

《游戏脚本的设计与开发》-(战棋部分)2.3 战场上的寻路和移动 - CSDN博客
《游戏脚本的设计与开发》-(战棋部分)2.3 战场上的寻路和移动
上次已经让我军,友军和敌军都出现在了战场上,本章来说说如何让一个部队在战场上进行移动。在战棋游戏中,我军回合行动的时候,点击我军的某一个部队,会出现选择列表,选择【部队移动】一项后,会出现该部队可能移动的范围,然后点击范围内的某一位置,则部队就会向着这个位置移动。在这一过程中涉及到两个算法,一个是部队移动范围的搜索,另一个就是部队移动时的寻路算法。复杂指数来说,寻路算法相对复杂一些,之前研究AS3的时候,曾经写过一篇A*寻路的分析文章,有兴趣的朋友可以看一下。javascript中的A*算法其实A*寻路,主要应用在RPG或即时战略等游戏中,用于快速寻找最短路径,战棋游戏中在这方面要求并不高,所以广度优先搜索和深度优先搜索等算法都是无所谓的,不过由于我已经有了之前对AS3版本的A*算法的研究,就直接移植过来了。原理,我就不多说了,想了解的可以直接看我的一文,下面我直接贴出完整代码,有需要的朋友可以直接拿去用。function LStarQuery(){
var self =
self._map = [];//地图
self._w = 0;//地图的宽
self._h = 0;//地图的高
self._open = [];//开放列表
self._starPoint =//起点
self._endPoint =//目标点
self._path = [];//计算出的路径
self.queryType = 0;//寻路方式[0:八方向,1:上下四方向,2:斜角四方向]
LStarQuery.prototype = {
drawPath:function(node){
var self =
var pathNode =
//倒过来得到路径
while (pathNode != self._starPoint) {
self._path.unshift(pathNode);
pathNode = pathNode.
setStart:function(){
var self =
for (var y=0; y&self._h; y++) {
for (var x=0; x&self._w; x++) {
self._map[y][x].init();
self._open = [];
/*计算每个节点*/
count:function(neighboringNode,centerNode,eight){
var self =
//是否已经检测过
if (neighboringNode.isChecked)
var g = eight ? centerNode.value_g + 14:centerNode.value_g + 10;
//不在关闭列表里才开始判断
if (neighboringNode.open) {
//如果该节点已经在开放列表里
if (neighboringNode.value_g &= g) {
//如果新G值小于或者等于旧值,则表明该路更优,更新其值
neighboringNode.value_g =
self.ghf(neighboringNode);
neighboringNode.nodeparent = centerN
self.setOpen(neighboringNode);
//如果该节点未在开放列表里
//计算GHF值
neighboringNode.value_g =
self.ghf(neighboringNode);
neighboringNode.nodeparent = centerN
//添加至列表
self.setOpen(neighboringNode,true);
/*计算ghf各值*/
ghf:function(node){
var self =
var dx = Math.abs(node.x - self._endPoint.x);
var dy = Math.abs(node.y - self._endPoint.y);
node.value_h = 10*(dx+dy);
node.value_f = node.value_g + node.value_h;
/*加入开放列表*/
setOpen:function(newNode,newFlg){
var self =
if (newFlg) {
newNode.open =
var new_f = newNode.value_f;
self._open.push(newNode);
new_index = self._open.length - 1;
new_index = newNode.
while (true) {
//找到父节点
var f_note_index = new_index * 0.5 &&& 0;
if (f_note_index &= 0)
//如果父节点的F值较大,则与父节点交换
if (self._open[new_index].value_f &= self._open[f_note_index].value_f)
var obj_note = self._open[f_note_index];
self._open[f_note_index] = self._open[new_index];
self._open[new_index] = obj_
self._open[f_note_index].index = f_note_
self._open[new_index].index = new_
new_index = f_note_
/*取开放列表里的最小值*/
getOpen:function(){
var self =
var change_
//将第一个节点,即F值最小的节点取出,最后返回
var obj_note = self._open[1];
self._open[1] = self._open[self._open.length - 1];
self._open[1].index = 1;
self._open.pop();
var this_index = 1;
while (true) {
var left_index = this_index * 2;
var right_index = this_index * 2 + 1;
if (left_index &= self._open.length)
if (left_index == self._open.length - 1) {
//当二叉树只存在左节点时,比较左节点和父节点的F值,若父节点较大,则交换
if (self._open[this_index].value_f &= self._open[left_index].value_f)
change_note = self._open[left_index];
self._open[left_index] = self._open[this_index];
self._open[this_index] = change_
self._open[left_index].index = left_
self._open[this_index].index = this_
this_index = left_
} else if (right_index & self._open.length) {
//找到左节点和右节点中的较小者
if (self._open[left_index].value_f &= self._open[right_index].value_f) {
//比较左节点和父节点的F值,若父节点较大,则交换
if (self._open[this_index].value_f &= self._open[left_index].value_f)
change_note = self._open[left_index];
self._open[left_index] = self._open[this_index];
self._open[this_index] = change_
self._open[left_index].index = left_
self._open[this_index].index = this_
this_index = left_
//比较右节点和父节点的F值,若父节点较大,则交换
if (self._open[this_index].value_f &= self._open[right_index].value_f)
change_note = self._open[right_index];
self._open[right_index] = self._open[this_index];
self._open[this_index] = change_
self._open[right_index].index = right_
self._open[this_index].index = this_
this_index = right_
return obj_
/*开始寻路*/
queryPath:function (star,end){
var self =
self._path = [];
if(end.x &= self._map[0].length)end.x = self._map[0].length - 2;
if(end.y &= self._map.length)end.y = self._map.length - 2;
if (star.x == end.x && star.y == end.y) return self._
self.setStart();
self._starPoint = self._map[star.y][star.x];
self._endPoint = self._map[end.y][end.x];
self._open = [];
self._open.push(null);
var isOver =
var thisPoint = self._starP
var firstCheck =
while (!isOver) {
thisPoint.isChecked =
var checkList = [];
if(self.queryType == 0 || self.queryType == 2){
if (thisPoint.x & 0 && thisPoint.y & 0) {
checkList.push(self._map[(thisPoint.y-1)][thisPoint.x - 1]);
if (thisPoint.x & self._w - 1 && thisPoint.y & self._h - 1) {
checkList.push(self._map[thisPoint.y + 1][(thisPoint.x+1)]);
if (thisPoint.x & 0 && thisPoint.y & self._h - 1) {
checkList.push(self._map[(thisPoint.y+1)][thisPoint.x - 1]);
if (thisPoint.x & self._w - 1 && thisPoint.y & 0) {
checkList.push(self._map[(thisPoint.y-1)][thisPoint.x + 1]);
if(self.queryType == 0 || self.queryType == 1){
if (thisPoint.y & 0) {
checkList.push(self._map[(thisPoint.y-1)][thisPoint.x]);
if (thisPoint.x & 0) {
checkList.push(self._map[thisPoint.y][(thisPoint.x-1)]);
if (thisPoint.x & self._w - 1) {
checkList.push(self._map[thisPoint.y][(thisPoint.x+1)]);
if (thisPoint.y & self._h - 1) {
checkList.push(self._map[(thisPoint.y+1)][thisPoint.x]);
//检测开始
var startIndex = checkList.
for (var i = 0; i&startI i++) {
//周围的每一个节点
var checkPoint = checkList[i];
if (self.isWay(checkPoint,thisPoint)) {
//如果坐标可以通过,则首先检查是不是目标点
if (checkPoint == self._endPoint) {
//如果搜索到目标点,则结束搜索
checkPoint.nodeparent = thisP
self.count(checkPoint, thisPoint);
if (! isOver) {
//如果未到达指定地点则取出f值最小的点作为循环点
if (self._open.length & 1) {
thisPoint = self.getOpen();
//开发列表为空,寻路失败
return [];
//路径做成
self.drawPath(self._endPoint);
return self._
/*判断是否可通过*/
isWay:function(checkPoint,thisPoint){
var self =
if(self.queryType == 0){
if (self._map[checkPoint.y][thisPoint.x].value == 0 && self._map[thisPoint.y][checkPoint.x].value == 0 && self._map[checkPoint.y][checkPoint.x].value == 0)
}else if(self.queryType == 1){
if (self._map[checkPoint.y][checkPoint.x].value == 0)
}else if(self.queryType == 2){
if (self._map[checkPoint.y][checkPoint.x].value == 0)
function LNode(_x,_y,_v){
var self =
self.x = _x;
self.y = _y;
self.value = _v?_v:0;
self.isChecked =
self.value_g = 0;
self.value_h = 0;
self.value_f = 0;
self.nodeparent =
self.index = 0;
self.open =
LNode.prototype = {
init:function(){
var self =
self.open =
self.isChecked =
self.value_g = 0;
self.value_h = 0;
self.value_f = 0;
self.nodeparent =
self.index = -1;
};这个寻路类通过设置queryType属性的值,可以实现【上下四方向】,【斜角四方向】和【八方向】三种寻路,下面我主要说说,这个类的用法。LNode类是地图中的坐标节点,使用LStarQuery类来搜索路径的话,必须先给它指定地图,就是LStarQuery类中的_map属性,这个_map是一个地图数组,它内部的地图节点是由LNode或者LNode的子类来组成的。比如说我随机生成了一个地图,0表示可通过,1表示障碍物。 var map = [];
//随机地图
for(var i=0;i&40;i++){
var childData = [];
for(var j=0;j&80;j++){
childData.push(Math.random() & 0.2 ? 0:1);
map.push(childData);
}这个地图显然跟LNode没有任何关系,自然也就无法直接在LStarQuery中使用,需要进行下面的变换。 //初始化寻路类
query = new LStarQuery();
query._map = [];
query._w = map[0].
query._h = map.
//初始化寻路类的地图
for (var y=0; y&query._h; y++) {
query._map.push([]);
for (var x=0; x&query._w; x++) {
query._map[y].push(new LNode(x,y,map[y][x]));
}经过上面的变换,就可以直接使用LStarQuery类的queryPath(star,end)来搜索路径了。参数star和end是两个坐标点,你可以使用lufylegend.js引擎中的LPoint类,也可以直接使用Object对象,只要带有x,y属性就可以了,其中star是起始点,end是目标点。比如var returnList = query.queryPath(new LPoint(2,3),new LPoint(20,16));就是搜索,坐标(2,3)到坐标(20,16)之间的最短路径。我写了一个测试的小demo,连接如下想看效果的朋友可以点上面的连接自己试验一下,障碍物是随机生成的,如果一开始刚好没有可走的路径的话,刷新一下就好了。效果如下地图移动和选择列表的添加所谓选择列表,就是当点击战场人物的时候,出现的移动,情报,待命等选择指令的列表,首先必须要有点击事件,在这里为了管理方便,我新建一个LSouSouSMapClick类来控制点击事件,首先添加和移除点击事件如下。function LSouSouSMapClick(){
var self =
base(self,LSprite,[]);
LSouSouSMapClick.prototype.setClickEvent = function(){
var self =
LSouSouObject.sMap.addEventListener(LMouseEvent.MOUSE_UP,self.onUp);
LSouSouObject.sMap.addEventListener(LMouseEvent.MOUSE_DOWN,self.onDown);
LSouSouSMapClick.prototype.removeClickEvent = function(){
var self =
LSouSouObject.sMap.removeEventListener(LMouseEvent.MOUSE_UP,self.onUp);
LSouSouObject.sMap.removeEventListener(LMouseEvent.MOUSE_DOWN,self.onDown);
};所以,当点击战场的时候,会调用下面的onUp函数和onDown函数LSouSouSMapClick.prototype.onDown = function(e){
if(LSouSouObject.sMap.menu == null)LSouSouObject.sMap.mouseIsDown =
LSouSouObject.sMap.mapIsMove =
LSouSouSMapClick.prototype.onUp = function(e){
LSouSouObject.sMap.mouseIsDown =
var mx = e.selfX;
var my = e.selfY;
var self = LSouSouObject.sMap.smapC
if(LSouSouObject.sMap.roadList != null){
self.clickRoad(mx,my);
}else if(LSouSouObject.sMap.menu != null){
LSouSouObject.sMap.sMenu.onClick(LSouSouObject.sMap.menu,mouseX,mouseY);
var isClick =
//是否点击我军
isClick = self.checkCharacter(LSouSouObject.sMap.ourlist,mx,my);
if(isClick)
//是否点击友军
isClick = self.checkCharacter(LSouSouObject.sMap.friendlist,mx,my);
if(isClick)
//是否点击敌军
isClick = self.checkCharacter(LSouSouObject.sMap.enemylist,mx,my);
if(isClick)
//点击战场,显示地形 暂略...
};先来解释一下LSouSouObject.sMap.mouseIsDown这个变量的用处,它主要来控制地图的显示范围的移动,在Flash版的《三国记》中,点击地图的边缘部分,地图会向相应的方向移动,这里使用同样的方法。具体的做法是,在LSouSouSMap的构造器中,添加时间轴self.addEventListener(LEvent.ENTER_FRAME,self.onframe);
LSouSouSMap.prototype.onframe = function(self){
if(self.mouseIsDown){
self.mapMoveCheck();
self.mapMove();
};由mapMove函数和mapMoveCheck函数来实现地图的移动。LSouSouSMap.prototype.mapMove = function(){
var self =
if(self.mapMove[&left&]){
self.mapIsMove =
self.backLayer.x += self.nodeLength/4;
self.mapToCoordinate.x = self.backLayer.x;
if(self.backLayer.x % self.nodeLength == 0){
self.mapMove[&left&] =
}else if(self.mapMove[&right&]){
self.mapIsMove =
self.backLayer.x -= self.nodeLength/4;
self.mapToCoordinate.x = self.backLayer.x;
if(self.backLayer.x % self.nodeLength == 0){
self.mapMove[&right&] =
if(self.mapMove[&up&]){
self.mapIsMove =
self.backLayer.y += self.nodeLength/4;
self.mapToCoordinate.y = self.backLayer.y;
if(self.backLayer.y % self.nodeLength == 0){
self.mapMove[&up&] =
}else if(self.mapMove[&down&]){
self.mapIsMove =
self.backLayer.y -= self.nodeLength/4;
self.mapToCoordinate.y = self.backLayer.y;
if(self.backLayer.y % self.nodeLength == 0){
self.mapMove[&down&] =
LSouSouSMap.prototype.mapMoveCheck = function(){
var self =
if(mouseX & self.nodeLength && self.backLayer.x & 0 && !self.mapMove[&left&]){
self.mapMove[&left&] =
if(mouseY & self.nodeLength && self.backLayer.y & 0 && !self.mapMove[&up&]){
self.mapMove[&up&] =
if(mouseX & self.SCREEN_WIDTH - self.nodeLength && mouseX & self.SCREEN_WIDTH
&& self.backLayer.x & self.SCREEN_WIDTH - self.mapW && !self.mapMove[&right&]){
self.mapMove[&right&] =
if(mouseY & self.SCREEN_HEIGHT - self.nodeLength && mouseY & self.SCREEN_HEIGHT
&& self.backLayer.y & self.SCREEN_HEIGHT - self.mapH && !self.mapMove[&down&]){
self.mapMove[&down&] =
};其实原理也简单,就是当鼠标按下的时候,判断一下,鼠标是否点击在游戏画面的边缘部分,是的话,则通过设置self.backLayer的坐标让地图向相应的方向移动。以上是地图移动部分,下面看看具体如何来添加一个选择列表。看一下LSouSouSMapClick.prototype.onUp函数中的判断,得知一开始的所进行的判断是下面的部分
var isClick =
//是否点击我军
isClick = self.checkCharacter(LSouSouObject.sMap.ourlist,mx,my);
if(isClick)
//是否点击友军
isClick = self.checkCharacter(LSouSouObject.sMap.friendlist,mx,my);
if(isClick)
//是否点击敌军
isClick = self.checkCharacter(LSouSouObject.sMap.enemylist,mx,my);
if(isClick)
//点击战场,显示地形 暂略...checkCharacter函数用来判断是否点击了相应的我军,友军,或敌军,如下LSouSouSMapClick.prototype.checkCharacter = function(list,mx,my){
var i,isChara,sx,sy,act,_characterS;
//是否点击我军
for(i=0;i&list.i++){
_characterS = list[i];
if(!_characterS.visible)
if(mx & _characterS.x + LSouSouObject.sMap.backLayer.x &&
mx & _characterS.x + LSouSouObject.sMap.backLayer.x + LSouSouObject.sMap.nodeLength &&
my & _characterS.y + LSouSouObject.sMap.backLayer.y &&
my & _characterS.y + LSouSouObject.sMap.backLayer.y + LSouSouObject.sMap.nodeLength){
LSouSouObject.charaSNow = _characterS;
sx = LSouSouObject.charaSNow.x;
sy = LSouSouObject.charaSNow.y;
act = LSouSouObject.charaSNow.
LSouSouObject.returnFunction = function (){
LSouSouObject.charaSNow.x =
LSouSouObject.charaSNow.y =
LSouSouObject.charaSNow.action =
LSouSouObject.charaSNow.tagerCoordinate=new LPoint(LSouSouObject.charaSNow.x,LSouSouObject.charaSNow.y);
LSouSouObject.sMap.menu = LSouSouSMapMenu.addSMenu(LSouSouObject.charaSNow.x,LSouSouObject.charaSNow.y,&select&);
LSouSouObject.sMap.menuLayer.addChild(LSouSouObject.sMap.menu);
LSouSouObject.returnFunction();
};当点中了战场上某一个军队,则会调用LSouSouSMapMenu.addSMenu函数,LSouSouSMapMenu类是为了管理战场上的选择列表而专门创建的,代码如下。LSouSouSMapMenu = function(){};
LSouSouSMapMenu.addSMenu = function(x,y,value){
switch(value){
case &select&:
_menu = new LSouSouSMapMenuSelect(x,y);
_menu.name =
};本次只用到了其中的一小部分,以后会继续完善,上面代码中的LSouSouSMapMenuSelect就是一个选择列表,代码如下function LSouSouSMapMenuSelect(x,y){
var self =
base(self,LSprite,[]);
LSouSouObject.sMap.setLocation();
if(LSouSouObject.charaSNow.belong == LSouSouObject.BELONG_SELF){
self.addMenuOur(x,y);
LSouSouSMapMenuSelect.prototype.addMenuOur = function(x,y){
var self =
LSouSouObject.sMap.smapClick.removeClickEvent();
var menuLayer = new LSprite();
var buttonMove = new LButtonSample1(&武将移动&);
buttonMove.x = 15;
buttonMove.y = 15;
menuLayer.addChild(buttonMove);
var buttonDetailed = new LButtonSample1(&武将情报&);
buttonDetailed.x = 15;
buttonDetailed.y = buttonMove.getHeight() + buttonMove.y;
menuLayer.addChild(buttonDetailed);
var buttonStandby = new LButtonSample1(&原地待命&);
buttonStandby.x = 15;
buttonStandby.y = buttonMove.getHeight()*2 + buttonMove.y;
menuLayer.addChild(buttonStandby);
var buttonCancel = new LButtonSample1(&行动取消&);
buttonCancel.x = 15;
buttonCancel.y = buttonMove.getHeight()*3 + buttonMove.y;
menuLayer.addChild(buttonCancel);
var selfWidth = buttonMove.getWidth()+30;
var selfHeight = buttonMove.getHeight()*4+30;
var bar = LSouSouObject.getBar(selfWidth,selfHeight);
menuLayer.addChild(bar);
self.addChild(menuLayer);
x += LSouSouObject.sMap.nodeL
if(x + LSouSouObject.sMap.backLayer.x + selfWidth & LSouSouObject.sMap.SCREEN_WIDTH){
x -= (selfWidth + LSouSouObject.sMap.nodeLength);
if(y + LSouSouObject.sMap.backLayer.y + selfHeight & LSouSouObject.sMap.SCREEN_HEIGHT){
y = LSouSouObject.sMap.SCREEN_HEIGHT - selfHeight - LSouSouObject.sMap.backLayer.y;
self.x = x + LSouSouObject.sMap.backLayer.x;
self.y = y + LSouSouObject.sMap.backLayer.y;
buttonMove.addEventListener(LMouseEvent.MOUSE_UP,self.onclickMove);
LSouSouSMapMenuSelect.prototype.onclickMove = function(e){
LSouSouObject.sMap.roadList = LSouSouObject.sQuery.makePath(LSouSouObject.charaSNow);
var i,nodeC
for(i=0;i&LSouSouObject.sMap.roadList.i++){
nodeChild = LSouSouObject.sMap.roadList[i];
LSouSouObject.sMap.roadLayer.graphics.drawRect(1,&#000000&,[nodeChild.x*LSouSouObject.sMap.nodeLength,nodeChild.y*LSouSouObject.sMap.nodeLength,LSouSouObject.sMap.nodeLength,LSouSouObject.sMap.nodeLength],true,&#FFFFFF&);
LSouSouObject.sMap.menuLayer.removeChild(LSouSouObject.sMap.menu);
LSouSouObject.sMap.menu =
LSouSouObject.sMap.smapClick.setClickEvent();
};上面代码可以看到,只是当点击我军的情况下,才添加了选择列表,后面会继续添加友军和敌军的列表,有了上面的代码,就可以添加一个选择列表了,效果如下。移动范围之宽度优先有了选择列表,当点击选择列表的【武将移动】按钮的时候,应该出现该武将的可移动的范围了,通过前面的代码可以知道,当点击【武将移动】按钮时,会调用LSouSouSMapMenuSelect.prototype.onclickMove函数,而在这个函数里又是通过LSouSouObject.sQuery.makePath函数来确定可移动路径的范围的,下面来说明一下makePath函数是如何具体来实现的。为了便于控制战场上的寻路和寻路范围的确定,先来创建一个LSouSouSQuery类,并继承自A*算法类LStarQuery,如下function LSouSouSQuery(map){
var self =
base(self,LStarQuery,[]);
self.queryType = 1;
self._map = [];
self._w = map[0].
self._h = map.
for (var y=0; y&self._h; y++) {
self._map.push([]);
for (var x=0; x&self._w; x++) {
self._map[y].push(new LSouSouNode(x,y,map[y][x]));
}这里,不但继承了A*算法类LStarQuery,并对其进行了初始化,设定了_map的值,里面的LSouSouNode等,一会儿讲寻路的时候再具体说。首先这个LSouSouSQuery就拥有了A*算法的寻路功能,下面主要为它添加一个搜索范围的功能。移动范围的确定,主要通过makePath,setPathAll和loopPath三个函数来确定,具体代码如下LSouSouSQuery.prototype.setPathAll = function(px,py,value){
var self =
if(self._enemyCost[px+&-&+py] != null && self._enemyCost[px+&-&+py] &= 200)
if(value == -1){
self._enemyCost[px+&-&+py] = &all&;
self._enemyCost[px+&-&+py] =
LSouSouSQuery.prototype.makePath = function(chara){
var self =
self._chara =
self._path = [];
var isOver =
self.setStart();
self._enemyCost = {};
if(chara.belong == LSouSouObject.BELONG_SELF || chara.belong == LSouSouObject.BELONG_FRIEND){
for(var i=0;i&LSouSouObject.sMap.enemylist.i++){
thisChara = LSouSouObject.sMap.enemylist[i];
if(thisChara.visible){
self._enemyCost[thisChara.locationX() + &-& + thisChara.locationY()] = 255;
self.setPathAll((thisChara.locationX() - 1) , thisChara.locationY() , -1);
self.setPathAll((thisChara.locationX() + 1) , thisChara.locationY() , -1);
self.setPathAll(thisChara.locationX() , (thisChara.locationY() - 1) , -1);
self.setPathAll(thisChara.locationX() , (thisChara.locationY() + 1) , -1);
}else if(chara.belong == LSouSouObject.BELONG_ENEMY){
for(var i=0;i&LSouSouObject.sMap.ourlist.i++){
thisChara = LSouSouObject.sMap.ourlist[i];
if(thisChara.visible){
self._enemyCost[thisChara.locationX() + &-& + thisChara.locationY()] = 255;
self.setPathAll((thisChara.locationX() - 1) , thisChara.locationY() , -1);
self.setPathAll((thisChara.locationX() + 1) , thisChara.locationY() , -1);
self.setPathAll(thisChara.locationX() , (thisChara.locationY() - 1) , -1);
self.setPathAll(thisChara.locationX() , (thisChara.locationY() + 1) , -1);
for(var i=0;i&LSouSouObject.sMap.friendlist.i++){
thisChara = LSouSouObject.sMap.friendlist[i];
if(thisChara.visible){
self._enemyCost[thisChara.locationX() + &-& + thisChara.locationY()] = 255;
self.setPathAll((thisChara.locationX() - 1) , thisChara.locationY() , -1);
self.setPathAll((thisChara.locationX() + 1) , thisChara.locationY() , -1);
self.setPathAll(thisChara.locationX() , (thisChara.locationY() - 1) , -1);
self.setPathAll(thisChara.locationX() , (thisChara.locationY() + 1) , -1);
self._starPoint = self._map[chara.locationY()][chara.locationX()];
self._starPoint.moveLong = chara.member.getDistance();
self.loopPath(self._starPoint);
return self._
LSouSouSQuery.prototype.loopPath = function(thisPoint){
var self =
if (thisPoint.moveLong &= 0)
if (!thisPoint.isChecked) {
self._path.push(thisPoint);
thisPoint.isChecked =
var checkList = [];
//获取周围四个点
if (thisPoint.y & 0)checkList.push(self._map[(thisPoint.y-1)][thisPoint.x]);
if (thisPoint.x & 0)checkList.push(self._map[thisPoint.y][(thisPoint.x-1)]);
if (thisPoint.x & self._w - 1)checkList.push(self._map[thisPoint.y][(thisPoint.x+1)]);
if (thisPoint.y & self._h - 1)checkList.push(self._map[(thisPoint.y+1)][thisPoint.x]);
for (i=0; i&checkList. i++) {
var checkPoint = checkList[i];
if(!checkPoint.moveLong)checkPoint.moveLong = 0;
if(checkPoint.isChecked && checkPoint.moveLong &= thisPoint.moveLong)
var cost = parseInt(LGlobal.arms[&Arms& + self._chara.member.getArms()][&Terrain&][&Terrain& + self._map[checkPoint.y][checkPoint.x].value][&Cost&]);
cost += self._enemyCost[checkPoint.x + &-& + checkPoint.y] != null && self._enemyCost[checkPoint.x + &-& + checkPoint.y] != &all& ? self._enemyCost[checkPoint.x + &-& + checkPoint.y]:0;
checkPoint.moveLong = thisPoint.moveLong -
if (self._enemyCost[checkPoint.x + &-& + checkPoint.y] == &all& && checkPoint.moveLong & 1)checkPoint.moveLong = 1;
self.loopPath(checkPoint);
};setPathAll函数,将地图上有敌军的地方,设置直接消耗移动力为剩余所有移动力。loopPath函数,以当前搜索点为中心,像上下左右四个方向扩散,每扩散一个范围,消耗相应的地形所在的移动力,直到移动力消耗为0。makePath函数,主要进行开始搜索时的初始化工作。因为在战场上,不同的地形所对应的移动消耗是不同的,不同的兵种在不同的地形上的移动消耗也是不同的,下面是兵种设定。{
&Name&:&群雄&,
&Arms_type&:0,
&MoveType&:0,
&Property&:{
&Attack&:&A&,
&Spirit&:&A&,
&Defense&:&A&,
&Breakout&:&A&,
&Morale&:&A&,
&Troops&:&5&,
&Strategy&:&1&
&Distance&:6,
&Helmet&:0,
&Equipment&:0,
&Weapon&:0,
&Horse&:0,
&AttackLong&:1,
&Restrain&:{},
&Terrain&:{
&Terrain0&:{&Addition&:110,&Cost&:1},
&Terrain1&:{&Addition&:110,&Cost&:1},
&Terrain2&:{&Addition&:80,&Cost&:2},
&Terrain3&:{&Addition&:100,&Cost&:100},
&Terrain4&:{&Addition&:100,&Cost&:100},
&Terrain5&:{&Addition&:120,&Cost&:1},
&Terrain6&:{&Addition&:100,&Cost&:1},
&Terrain7&:{&Addition&:110,&Cost&:1},
&Terrain8&:{&Addition&:80,&Cost&:3},
&Terrain9&:{&Addition&:100,&Cost&:1},
&Terrain10&:{&Addition&:100,&Cost&:100},
&Terrain11&:{&Addition&:100,&Cost&:100},
&Terrain12&:{&Addition&:100,&Cost&:1},
&Terrain13&:{&Addition&:80,&Cost&:2}
&RangeAttack&:[{&x&:0,&y&:-1},{&x&:0,&y&:1},{&x&:-1,&y&:0},{&x&:1,&y&:0}],
&RangeAttackTarget&:[{&x&:0,&y&:0}],
&Strategy&:[{&lv&:0,&value&:&2&},{&lv&:2,&value&:&1&},{&lv&:6,&value&:&6&}],
&Introduction&:&各方面都较为突出的兵种。&
}里面包含了该兵种的各种属性,其中Terrain属性是该兵种在不同地形上的移动消耗(Cost)和适应性(Addition)当然,s01.smap地图中的地形设定,也要完善一下。{&data&:[
[1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1],
[3,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1],
[3,3,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1],
[3,3,3,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,3,3],
[3,3,3,3,1,1,1,0,0,0,0,1,1,1,1,1,1,3,3,3],
[3,3,3,3,3,1,1,0,0,0,0,0,1,1,1,1,1,3,3,3],
[3,3,3,3,3,4,4,4,4,0,0,4,4,4,4,1,1,3,3,3],
[3,3,3,3,3,4,6,0,0,0,0,0,0,6,4,1,3,3,3,3],
[3,3,3,3,3,4,6,0,0,0,0,0,0,6,4,1,3,3,3,3],
[3,3,3,3,0,4,6,0,0,0,0,0,0,0,4,1,3,3,3,3],
[3,3,3,0,0,0,0,0,0,4,4,0,0,0,4,3,3,3,3,3],
[3,3,0,0,0,0,0,0,0,5,7,0,0,0,4,3,3,3,3,3],
[3,0,0,0,0,4,0,0,0,0,0,0,0,0,4,3,3,3,3,3],
[0,0,0,0,0,4,6,6,0,0,0,0,0,7,4,0,3,3,3,3],
[0,0,0,0,0,4,4,4,4,0,0,4,4,4,4,0,1,1,3,3],
[0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,3],
[1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
[1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1],
[1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1],
[1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1]
,&img-small&:&01-small.png&
,&img-big&:&01-big.png&}好了,最后,效果如下。当遇到敌军的时候,移动力直接归零,比如下面的效果,左边是友军张飞,右边是敌军关羽,所以张飞左侧是可以到达的,而关羽的右侧是不可到达的,这就是战场上的人物遮挡,在战场上通过适当人物站位,可以有效的阻止敌军的攻击,和保护我军防御较弱的部队。利用A*算法来移动部队在A*寻路类LStarQuery中,是否可通过的判断是通过该节点坐标是0还是1来判断的,而战棋游戏中就不一样了,前面已经确定了可移动的范围,那么该范围内就是它可通过的路径,所以,在它的子类LSouSouSQuery中,要稍微修改一下。首先是节点类function LSouSouNode(_x,_y,_v){
var self =
base(self,LNode,[_x,_y,_v]);
self.isRoad =
LSouSouNode.prototype.init = function(){
var self =
arguments.callee[SUPER][&init&].call(self);
self.isRoad =
LSouSouNode.prototype.toString = function(){
return &[&+this.x+&,&+this.y+&,&+this.isRoad+&]&;
这样LSouSouNode继承自LNode类,并添加了新属性isRoad,当这个属性为true的时候,表示该位置可通过。原来的是否可通过的判断,也要相应的修改一下,如下/*判断是否可通过*/
LSouSouSQuery.prototype.isWay = function(checkPoint,thisPoint){
if (this._map[checkPoint.y][checkPoint.x].isRoad)
};每次搜索前的地图初始化部分,修改如下LSouSouSQuery.prototype.setStart = function(){
var self=this,
arguments.callee[SUPER][&setStart&].call(self);
if(!LSouSouObject.sMap.roadList)
for(var i=0;i&LSouSouObject.sMap.roadList.i++){
node = LSouSouObject.sMap.roadList[i];
self._map[node.y][node.x].isRoad =
};就是提前设定好各节点的isRoad的值,这样一来,在LSouSouSMapClick中,首先判断是否点中了移动路径的范围,代码如下。LSouSouSMapClick.prototype.clickRoad = function(mx,my){
var intX = ((mx - LSouSouObject.sMap.backLayer.x)/LSouSouObject.sMap.nodeLength) &&& 0;
var intY = ((my - LSouSouObject.sMap.backLayer.y)/LSouSouObject.sMap.nodeLength) &&& 0;
var isRoad = false,node,_characterS,i,j;
for(i=0;i&LSouSouObject.sMap.roadList.i++){
node = LSouSouObject.sMap.roadList[i];
for(j=0;j&LSouSouObject.sMap.ourlist.j++){
_characterS = LSouSouObject.sMap.ourlist[j];
if(_characterS.visible && _characterS.member.getIndex() != LSouSouObject.charaSNow.member.getIndex() && _characterS.locationX() == intX && _characterS.locationY() == intY)
for(j=0;j&LSouSouObject.sMap.friendlist.j++){
_characterS = LSouSouObject.sMap.friendlist[j];
if(_characterS.locationX() == intX && _characterS.locationY() == intY)
for(j=0;j&LSouSouObject.sMap.enemylist.j++){
_characterS = LSouSouObject.sMap.enemylist[j];
if(_characterS.locationX() == intX && _characterS.locationY() == intY)
if(mx &= node.x*LSouSouObject.sMap.nodeLength + LSouSouObject.sMap.backLayer.x &&
mx & node.x*LSouSouObject.sMap.nodeLength + LSouSouObject.sMap.backLayer.x + LSouSouObject.sMap.nodeLength &&
my &= node.y*LSouSouObject.sMap.nodeLength + LSouSouObject.sMap.backLayer.y &&
my & node.y*LSouSouObject.sMap.nodeLength + LSouSouObject.sMap.backLayer.y + LSouSouObject.sMap.nodeLength){
if(!isRoad)
LSouSouObject.sMap.moveToCoordinate(intX,intY);
};因为,不可能将人物移动到另一个人物之上,所以有人的地方要排除,最后,点击了路径之后,调用LSouSouObject.sMap.moveToCoordinate函数,如下。LSouSouSMap.prototype.moveToCoordinate = function(intX,intY){
var self =
var toPoint = new LPoint(intX,intY);
LSouSouObject.charaSNow.path = LSouSouObject.sQuery.queryPath(new LPoint(LSouSouObject.charaSNow.locationX(),LSouSouObject.charaSNow.locationY()),toPoint);
trace(&LSouSouObject.charaSNow.path=&+LSouSouObject.charaSNow.path);
if(LSouSouObject.charaSNow.path){
self.roadList =
LSouSouObject.sMap.roadLayer.graphics.clear();
LSouSouObject.charaSNow.addEventListener(LSouSouEvent.CHARACTER_MOVE_COMPLETE,self.onShowAttackMenu);
LSouSouSMap.prototype.onShowAttackMenu = function(){
var self = LSouSouObject.sM
LSouSouObject.charaSNow.removeEventListener(LSouSouEvent.CHARACTER_MOVE_COMPLETE,self.onShowAttackMenu);
trace(&移动结束&);
};上面代码,如果搜索到了路径,则将路径赋值给当前正在控制的军队LSouSouObject.charaSNow,然后最后就是LSouSouCharacterS类的修改了。在LSouSouCharacterS类中判断路径path是否有值,有的话,根据path中的坐标节点,一个一个的移动,直到移动到最后一个节点,然后移动结束。LSouSouCharacterS.prototype.move = function(){
var self =
if(!self.path)
if(self.x == self.tagerCoordinate.x && self.y == self.tagerCoordinate.y){
if(self.path.length == 0){
self.tagerCoordinate.x = self.locationX();
self.tagerCoordinate.y = self.locationY();
self.path =
if(self.onMoveComplete)self.onMoveComplete();
self.tagerCoordinate.x = self.path[0].x*LSouSouObject.sMap.nodeL
self.tagerCoordinate.y = self.path[0].y*LSouSouObject.sMap.nodeL
self.path.shift();
if(self.x & self.tagerCoordinate.x){
self.x -= LStaticSouSouCharacterS.MOVESETP;
self.action = LStaticSouSouCharacterS.MOVE_LEFT;
}else if(self.y & self.tagerCoordinate.y){
self.y += LStaticSouSouCharacterS.MOVESETP;
self.action = LStaticSouSouCharacterS.MOVE_DOWN;
}else if(self.y & self.tagerCoordinate.y){
self.y -= LStaticSouSouCharacterS.MOVESETP;
self.action = LStaticSouSouCharacterS.MOVE_UP;
self.x += LStaticSouSouCharacterS.MOVESETP;
self.action = LStaticSouSouCharacterS.MOVE_RIGHT;
};然后,在LSouSouCharacterS的时间轴函数onframe中调用move函数就可以了,下面的预览图,刘备正在移动中。测试连接如下以上,本章就先讲这么多了,下一章可能会讲一讲攻击?本章为止的源码如下,不包含lufylegend.js引擎源码,请自己到官网下载※源码运行说明:需要服务器支持,详细请看本系列文章《序》和《第一章》《游戏脚本的设计与开发》系列文章目录本章就讲到这里,欢迎继续关注我的博客转载请注明:
本文已收录于以下专栏:
相关文章推荐
上一章介绍了如何读取和解析一个脚本,其实,对于一个游戏来说,文字的显示和操作是最基本的组成元素之一,本章进入正题,就先从文字显示开始讲解一下。显示一个文字,就是将文字绘制到游戏界面上,而在lufyle...
简单说,游戏脚本就是依据一定的格式编写的可执行文件,游戏可以通过脚本中自定义的语句来执行相应的逻辑。
举个例子,舞蹈演员随着音乐翩翩起舞,其实音乐在这里就担当了脚本的角色,音乐里记录了每一个舞蹈动作,...
上次讲了如何快速显示一张战场地图,有了战场没有军队怎么行,本次来向战场上添加军队。一般战棋游戏中,战场上的军队有三种,我军,敌军和友军。我军是可操纵的,敌军是可攻击的,友军是不可操纵,也不可攻击的。敌...
终于到了攻击部分了,战棋游戏中的攻击,主要分为物理攻击和法术攻击,本章就先从物理攻击讲起。物理攻击又分为普通攻击,连击(双击),以及致命攻击,再复杂一点的还有其他特殊攻击,比如我的《三国记-乱世群雄》...
上一篇《游戏脚本的设计与开发》-序中我介绍了游戏脚本的基本概念和准备工作,本篇来说说具体如何解析一个脚本所谓解析脚本,就是按照自己定义的语法,将每一个脚本命令还原成不同的代码逻辑进行执行,比如,我规定...
今天开始脚本设计的第二部分,战棋游戏的开发。战棋游戏中我尤其喜爱光荣的英杰传和曹操传,我的多平台游戏三国记,也是以三国志曹操传为模板而开发的。本次也不例外,就从曹操传的移植为基础来开发,再进行扩展,从...
如果你是一个游戏开发者,或者开发过一些关于人工智能的游戏,你一定知道A*算法,如果没有接触过此类的东东,那么看了这一篇文章,你会对A*算法从不知道变得了解,从了解变得理解。我不是一个纯粹的游戏开发者,...
上一节中已经介绍了RPG游戏中地图怎么实现,在RPG游戏的地图中通常有各种遮挡,比如人物站在房屋的后面的时候,房子应该遮挡住人物,这就涉及到各种建筑物和人物的排序显示。另外,上一节中我为了测试地图,已...
背包系统在游戏中是必不可少的,在游戏中,所有获得的物品都会储存在背包里面。背包的种类,我一般将它分成两大类,一种是类似于《吞食天地》的“个人背包”,在游戏中每个人物都有一个背包,每个人的背包都互不影响...
他的最新文章
讲师:吴岸城
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)}

我要回帖

更多关于 战棋类手机游戏 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信