本帖最后由 喵呜喵5 于 2015-11-18 18:04 编辑
千葉玖濑 发表于 2015-11-12 00:04
用鼠标的话会不会斜向寻路?还是只会走直线然后拐弯?我瞎糊的就不行 ...
寻路在 Game_Character 的 findDirectionTo 中,算法为 A*
为了分析方便单独拎出来:
Game_Character.prototype.findDirectionTo = function(goalX, goalY) { var searchLimit = this.searchLimit(); var mapWidth = $gameMap.width(); var nodeList = []; var openList = []; var closedList = []; var start = {}; var best = start; if (this.x === goalX && this.y === goalY) { return 0 } start.parent = null; start.x = this.x; start.y = this.y; start.g = 0; start.f = $gameMap.distance(start.x, start.y, goalX, goalY); nodeList.push(start); openList.push(start.y * mapWidth + start.x); while (nodeList.length > 0) { var bestIndex = 0; for (var i = 0; i < nodeList.length; i++) { if (nodeList[i].f < nodeList[bestIndex].f) { bestIndex = i } } var current = nodeList[bestIndex]; var x1 = current.x; var y1 = current.y; var pos1 = y1 * mapWidth + x1; var g1 = current.g; nodeList.splice(bestIndex, 1); openList.splice(openList.indexOf(pos1), 1); closedList.push(pos1); if (current.x === goalX && current.y === goalY) { best = current; goaled = true; break } if (g1 >= searchLimit) { continue } for (var j = 0; j < 4; j++) { var direction = 2 + j * 2; var x2 = $gameMap.roundXWithDirection(x1, direction); var y2 = $gameMap.roundYWithDirection(y1, direction); var pos2 = y2 * mapWidth + x2; if (closedList.contains(pos2)) { continue } if (!this.canPass(x1, y1, direction)) { continue } var g2 = g1 + 1; var index2 = openList.indexOf(pos2); if (index2 < 0 || g2 < nodeList[index2].g) { var neighbor; if (index2 >= 0) { neighbor = nodeList[index2] } else { neighbor = {}; nodeList.push(neighbor); openList.push(pos2) } neighbor.parent = current; neighbor.x = x2; neighbor.y = y2; neighbor.g = g2; neighbor.f = g2 + $gameMap.distance(x2, y2, goalX, goalY); if (!best || neighbor.f - neighbor.g < best.f - best.g) { best = neighbor } } } } var node = best; while (node.parent && node.parent !== start) { node = node.parent } var deltaX1 = $gameMap.deltaX(node.x, start.x); var deltaY1 = $gameMap.deltaY(node.y, start.y); if (deltaY1 > 0) { return 2 } else if (deltaX1 < 0) { return 4 } else if (deltaX1 > 0) { return 6 } else if (deltaY1 < 0) { return 8 } var deltaX2 = this.deltaXFrom(goalX); var deltaY2 = this.deltaYFrom(goalY); if (Math.abs(deltaX2) > Math.abs(deltaY2)) { return deltaX2 > 0 ? 4 : 6 } else if (deltaY2 !== 0) { return deltaY2 > 0 ? 8 : 2 } return 0 };
Game_Character.prototype.findDirectionTo = function(goalX, goalY) {
var searchLimit = this.searchLimit();
var mapWidth = $gameMap.width();
var nodeList = [];
var openList = [];
var closedList = [];
var start = {};
var best = start;
if (this.x === goalX && this.y === goalY) {
return 0
}
start.parent = null;
start.x = this.x;
start.y = this.y;
start.g = 0;
start.f = $gameMap.distance(start.x, start.y, goalX, goalY);
nodeList.push(start);
openList.push(start.y * mapWidth + start.x);
while (nodeList.length > 0) {
var bestIndex = 0;
for (var i = 0; i < nodeList.length; i++) {
if (nodeList[i].f < nodeList[bestIndex].f) {
bestIndex = i
}
}
var current = nodeList[bestIndex];
var x1 = current.x;
var y1 = current.y;
var pos1 = y1 * mapWidth + x1;
var g1 = current.g;
nodeList.splice(bestIndex, 1);
openList.splice(openList.indexOf(pos1), 1);
closedList.push(pos1);
if (current.x === goalX && current.y === goalY) {
best = current;
goaled = true;
break
}
if (g1 >= searchLimit) {
continue
}
for (var j = 0; j < 4; j++) {
var direction = 2 + j * 2;
var x2 = $gameMap.roundXWithDirection(x1, direction);
var y2 = $gameMap.roundYWithDirection(y1, direction);
var pos2 = y2 * mapWidth + x2;
if (closedList.contains(pos2)) {
continue
}
if (!this.canPass(x1, y1, direction)) {
continue
}
var g2 = g1 + 1;
var index2 = openList.indexOf(pos2);
if (index2 < 0 || g2 < nodeList[index2].g) {
var neighbor;
if (index2 >= 0) {
neighbor = nodeList[index2]
} else {
neighbor = {};
nodeList.push(neighbor);
openList.push(pos2)
}
neighbor.parent = current;
neighbor.x = x2;
neighbor.y = y2;
neighbor.g = g2;
neighbor.f = g2 + $gameMap.distance(x2, y2, goalX, goalY);
if (!best || neighbor.f - neighbor.g < best.f - best.g) {
best = neighbor
}
}
}
}
var node = best;
while (node.parent && node.parent !== start) {
node = node.parent
}
var deltaX1 = $gameMap.deltaX(node.x, start.x);
var deltaY1 = $gameMap.deltaY(node.y, start.y);
if (deltaY1 > 0) {
return 2
} else if (deltaX1 < 0) {
return 4
} else if (deltaX1 > 0) {
return 6
} else if (deltaY1 < 0) {
return 8
}
var deltaX2 = this.deltaXFrom(goalX);
var deltaY2 = this.deltaYFrom(goalY);
if (Math.abs(deltaX2) > Math.abs(deltaY2)) {
return deltaX2 > 0 ? 4 : 6
} else if (deltaY2 !== 0) {
return deltaY2 > 0 ? 8 : 2
}
return 0
};
42~45行是选择要寻路的点,
具体就是四次循环得出 2/4/6/8 这四个方向然后根据当前方向算出朝指定方向移动后的 x2 和 y2 值
for (var j = 0; j < 4; j++) { var direction = 2 + j * 2; var x2 = $gameMap.roundXWithDirection(x1, direction); var y2 = $gameMap.roundYWithDirection(y1, direction);
for (var j = 0; j < 4; j++) {
var direction = 2 + j * 2;
var x2 = $gameMap.roundXWithDirection(x1, direction);
var y2 = $gameMap.roundYWithDirection(y1, direction);
要八方向的话稍微改改就好,仅提供一个简单的思路,出自兰触(https://rpg.blue/forum.php?mod=viewthread&tid=368249):
for (var j = 1; j <= 9; j++) { var direction = j; if (direction == 5) continue; var x2 = x1 + (direction - 4) % 3 - 1; var y2 = y1 - ((direction - 4) / 3);
for (var j = 1; j <= 9; j++) {
var direction = j;
if (direction == 5) continue;
var x2 = x1 + (direction - 4) % 3 - 1;
var y2 = y1 - ((direction - 4) / 3);
50行的 canPass 函数是判定是否能从当前位置向指定方向移动一步的,因为加了八方向,所以自己根据自己的需要稍微更改一下判定条件,比如如果左边有个老太太是否还能向左上角移动啥的
if (!this.canPass(x1, y1, direction)) {
if (!this.canPass(x1, y1, direction)) {
79行开始的代码就是最终找到路径
var deltaX1 = $gameMap.deltaX(node.x, start.x); var deltaY1 = $gameMap.deltaY(node.y, start.y); if (deltaY1 > 0) { return 2 } else if (deltaX1 < 0) { return 4 } else if (deltaX1 > 0) { return 6 } else if (deltaY1 < 0) { return 8 } var deltaX2 = this.deltaXFrom(goalX); var deltaY2 = this.deltaYFrom(goalY); if (Math.abs(deltaX2) > Math.abs(deltaY2)) { return deltaX2 > 0 ? 4 : 6 } else if (deltaY2 !== 0) { return deltaY2 > 0 ? 8 : 2 } return 0
var deltaX1 = $gameMap.deltaX(node.x, start.x);
var deltaY1 = $gameMap.deltaY(node.y, start.y);
if (deltaY1 > 0) {
return 2
} else if (deltaX1 < 0) {
return 4
} else if (deltaX1 > 0) {
return 6
} else if (deltaY1 < 0) {
return 8
}
var deltaX2 = this.deltaXFrom(goalX);
var deltaY2 = this.deltaYFrom(goalY);
if (Math.abs(deltaX2) > Math.abs(deltaY2)) {
return deltaX2 > 0 ? 4 : 6
} else if (deltaY2 !== 0) {
return deltaY2 > 0 ? 8 : 2
}
return 0
node.x 和 node.y 就是下一步要移动的XY坐标,但是这个函数原本的返回值是移动方向的方位数(2/4/6/8)而不是最终的坐标,所以继续用兰触公式转换一下
return 5 + 3 * -(node.y - start.y) + (node.x - start.x)
return 5 + 3 * -(node.y - start.y) + (node.x - start.x)
这样寻路部分的逻辑就支持八方向寻路了
不过要真的让角色支持八方向寻路,还有一个要改的地方是这里:
Game_Player.prototype.executeMove = function(direction) { this.moveStraight(direction) };
Game_Player.prototype.executeMove = function(direction) {
this.moveStraight(direction)
};
上面整个寻路过程最终得出的方位数是传到这个函数中的,原生的 moveStraight 应该不支持八方向,因此这里也要修改一下让direction为1379时角色能走的动
==========
以上纯粹是理论,是否正确还需要测试。因为我完全没有用过MV,只看过MV的源代码 |