我在javascript中编写了一个小型2D游戏,它使用一个网格,玩家从位置[0,0]开始,并且可以在任一方向上移动几乎无限的距离。
现在我想实现A *寻路,但是我遇到了一些问题,找到了存储世界的最佳方法,包括不同的障碍,敌人和地形。这是我到目前为止所尝试或想过的。
数组数组
这里我将世界存储在数组[x][y]
中。
var world = [[]];
world[312][11] = 0;
world[312][12] = 0;
world[312][13] = 1;
world[312][14] = 1;
...
这适用于A *寻路!访问特定坐标并填充世界非常简单快捷。在上面的例子中,我只存储了可通过(0)或不可通过的(1)地形,但我可以存储我想要的任何东西。但是,如果我的玩家位于[-12][-230]
,这对负坐标不起作用。 javascript数组中的否定键实际上不是数组的一部分,它们不会包含在world.length
或world[3].length
中,根据我的理解,它是整体不好的做法,可能会对表现也是如此。我在某处读到,如果你在数组中使用负键,那你就错了。
由于显而易见的原因,我仍然不会将整个世界传递到A *函数中。只是靠近我的播放器的一小部分,但坐标将对应于阵列中易于使用的位置。
仅用于A *寻路的单独数组数组
这就是我现在所处的位置。我有一个名为pathMap = [[]]
的单独的50x50网格,仅用于寻路。
var pathMap = [[]];
pathMap[0][0] = 0;
pathMap[0][1] = 0;
pathMap[0][2] = 1;
pathMap[0][3] = 1;
...
从pathMap[0][0]
开始并转到pathMap[50][50]
,并作为我当前位置的叠加层,我(作为玩家)将始终位于中心位置。我的真实的坐标可能类似于[-5195,323]
,但它会转换为pathMap[25][25]
,而我附近的所有内容都会根据我的位置放在pathMap上。
现在这个工作,但这是一个巨大的混乱。从一个坐标到另一个坐标的所有翻译都会让我的大脑受到伤害。此外,当我从A *返回路径时,我必须将它的每一步转换回我的元素应该移动到现实世界中的实际位置。我还必须在每次更新时将相同的对象填充到2个不同的网格中,这也会影响性能。
对象数组
我认为这是我想要的地方,但我也有一些问题。
var world = [];
world[0] = { x: -10, y: 3, impassable: 0 };
world[1] = { x: -10, y: 4, impassable: 0 };
world[2] = { x: -10, y: 5, impassable: 1 };
world[3] = { x: -10, y: 6, impassable: 1 };
...
适用于负x或y值!但是,在此数组中找到[10,3]
并不容易。我必须遍历整个数组以查找x == 10
和y == 3
的对象,而不是第一个示例中非常简单快速的方法world[10][3]
。此外,我不能真正依赖使用此版本的正确顺序的坐标,排序变得更加困难,使用数组数组更容易的其他事情。
重建游戏以保持积极的一面
我不想这样做,但我考虑将球员的起始位置放在[1000000,1000000]
之类的位置,并使负坐标不受限制。如果我不得不移除我无穷无尽的愿景只是为了使寻路工作用更少的代码,这似乎是一个失败。我知道总会有一些上限或下限,但我只是想从[0,0]开始,而不是因为数组相关原因而没有任意坐标。
其他吗
在javascript中,是否有其他选项更好用,上面没有描述?我很乐意接受建议!
类似案例是否有最佳做法?
答案 0 :(得分:0)
您必须区分三个坐标系:
屏幕坐标系统取决于:
为避免头痛,请构建一个小抽象层,为您做数学计算。
您可能希望使用Object.defineProperty来定义属性,这将提供流畅的界面。
var canvas = ... ;
var canvasWidth = canvas.width;
var canvasHeigth = canvas.heigth;
var world = {
width : 1000, // meters
height : 1000, // meters
tileSize : 0.5, // height and width of a tile, in meter
model : null, // 2D array sized ( width/tileSize, XtileSize )
};
// possibles world coordinates range from -width/2 to width/2 ; - height/2 height/2.
var camera = {
x : -1,
y : -1,
viewWidth : 10, // we see 10 meters wide scene
viewHeight : -1 // height is deduced from canvas aspect ratio
};
camera.viewHeight = camera.viewWidth * canvasWidth / canvasHeight ;
然后你的角色看起来像:
// (x,y) is the center of the character in centered world coordinates
// here (0,0) means (500,500) world coords
// (1000,1000) array coords
// (320, 240) screen coords in 640X480
function /*Class*/ Character(x, y) {
var _x=x;
var _y=y;
var _col=0;
var _row=0;
var _sx=0.0;
var _sy=0.0;
var dirty = true;
Object.defineProperty(this,'x',
{ get : function() {return _x; }
set : function(v) { _x=v;
dirty=true; } });
Object.defineProperty(this,'x',
{ get : function() {return _y; }
set : function(v) { _y=v;
dirty=true; } });
Object.defineProperty(this,'col',
{ get : function() {
if (dirty) updateCoords();
return _col; } });
Object.defineProperty(this,'row',
{ get : function() {
if (dirty) updateCoords();
return _row; } });
Object.defineProperty(this,'sx',
{ get : function() {
if (dirty) updateCoords();
return _sx; } });
Object.defineProperty(this,'sy',
{ get : function() {
if (dirty) updateCoords();
return _sy; } });
function updateCoords() {
_row = ( ( _x + 0.5 * world.width )/ world.tileSize ) | 0 ;
_col = ( ( _x + 0.5 * world.height )/ world.tileSize ) | 0 ;
_sx = canvasWidth * ( 0.5 + ( _x - camera.x ) / camera.viewWidth ) ;
_sy = canvasHeight * ( 0.5 + ( _y - camera.y ) / camera.viewHeight ) ;
dirty = false;
}
}