【问题标题】:Negative coordinates in a grid based game基于网格的游戏中的负坐标
【发布时间】:2015-02-27 13:48:47
【问题描述】:

我用 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.lengthworld[3].length 中,据我了解,这总体上是不好的做法,也可能对性能产生一些影响。我在某处读到,如果您在数组中使用否定键,那么您做错了。 出于显而易见的原因,我仍然不会将整个世界传递给 A* 函数。只是靠近我的播放器的一小部分,但坐标将对应于易于使用的数组中的位置。

仅用于 A* 寻路的单独数组数组

这就是我现在所处的位置。我有一个单独的 50x50 网格,名为 pathMap = [[]]用于寻路。

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 == 10y == 3 的对象,而不是第一个示例中非常简单快速的方法 world[10][3]。此外,我不能真正依赖使用此版本的正确顺序的坐标,排序变得更难,就像数组数组更容易的其他事情一样。

重建游戏以始终保持积极的一面

我不希望这样做,但我考虑过将玩家的起始位置设置在[1000000,1000000] 之类的位置,并将负坐标设为禁区。如果我必须消除我对无穷无尽的愿景只是为了用更少的代码进行寻路工作,这似乎是一个失败。我知道无论如何总会有一些上限或下限,但我只想从 [0,0] 开始,而不是出于数组相关原因的任意坐标。

其他?

在 javascript 中,是否还有另一个更好的选项并且上面没有描述?我非常愿意接受建议!

是否有类似案例的最佳实践?

【问题讨论】:

  • 如果你将世界设为一个对象,键为X;Y:world = {}; world['-10;-10'] = 0,它会起作用吗?或者制作对象的对象,所以world[10][10] 会像数组一样工作。
  • 有趣!就像 对象数组 版本一样,它可以简单地找到 数组数组 的特定坐标。我有点喜欢它,但由于某种原因感觉很脏。我想是键的连接和拆分让我有点失望。
  • world = {}; world[-10] = {}; world[-10][-10] = 0。您必须维护自己的length 属性和Array.prototype 方法(如果您需要world 上的forEach 之类的东西)。这可以在生成时完成,也可以使用自定义方法将坐标添加到world,或者使用 setter/getter。与Array[Array] 相比,我认为在性能方面不会有太大的损失,因为数组基本上是具有不同原型的对象。
  • 如果我没记错的话,如果我尝试向数组添加否定键,这正是已经发生的事情。它创造了你所描述的东西。我遇到的最大问题是不能保证订单会得到维护,除了我必须重写一堆数组功能之外,但我已经准备好了。但我想我会更多地探索你的选择。

标签: javascript arrays canvas game-engine path-finding


【解决方案1】:

你必须区分三个坐标系:

  • 世界坐标。
  • 世界模型/寻路(数组)坐标。
  • 屏幕坐标。

屏幕坐标系依赖于:

  • 视口 = 画布。 (宽度、高度,以像素为单位)。
  • 相机 = (x,y) 世界坐标中心 + 视图宽度(世界坐标)。

为避免头疼,请构建一个小型抽象层来为您计算。

您可能想使用 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;            
     }
}

【讨论】:

  • 这是对我已经在使用的“仅用于寻路的单独数组数组” 选项的精彩说明。似乎只是接受我正在使用几个不同的坐标系并使其工作的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-19
  • 1970-01-01
  • 2023-03-11
相关资源
最近更新 更多