如何在2D游戏中对物体进行排序?

时间:2019-01-19 00:06:12

标签: javascript sorting math 3d 2d

我正在用JavaScript开发一种基于视角的 2d / 3d游戏

我有X轴和Y轴,如下图所示。


我的问题:我的地图上有一堆具有以下属性的对象(标记为“ 1”和“ 2”):

  • positionX / positionY
  • sizeX / sizeY

在图像中,对象“ 1”确实获得了坐标x:3, y:2,而对象“ 2”确实得到了坐标x:5, y:4。两个对象的SizeX和sizeY均为w:1, h:1

我想用此信息来对所有对象进行升序排序(基于对象的位置和大小),以在3d中知道哪些对象来自另一个对象,以便稍后绘制所有排序的对象进入我的画布(前景/背景中的对象=“图层”)。


img

注意:摄像机必须固定在某个位置上-假设摄像机的X和Y值相同,因此在计算CameraX = CameraY时不得使用摄像机位置。

到目前为止,我已经尝试过:

let objects = [
  {
    name: "objectA",
    x: 8,
    y: 12,
    w: 2,
    h: 2
  }, 
  {
    name: "objectB",
    x: 3,
    y: 5,
    w: 2,
    h: 2
  },
  {
    name: "objectC",
    x: 6,
    y: 2,
    w: 1,
    h: 3
  }
]


let sortObjects = (objects) => {
  return objects.sort((a, b)=> {
    let distanceA = Math.sqrt(a.x**2 + a.y**2);
    let distanceB = Math.sqrt(b.x**2 + b.y**2);
    return distanceA - distanceB;
  });
}


let sortedObjects = sortObjects(objects);
console.log(sortedObjects);

// NOTE in 3d: first Object drawn first, second Object drawn second and so on...

编辑以上代码段:

我尝试根据对象的x / y坐标对它们进行排序,但似乎在计算时也必须使用 width and height参数以避免错误。

如何使用宽度/高度?  Tbh我一无所知,因此不胜感激。

5 个答案:

答案 0 :(得分:1)

我不确定您的意思是什么

  

注意:摄影机必须固定在某个位置上-假设摄影机具有相同的X和Y值,因此在计算CameraX = CameraY时不得使用摄影机位置。

这是一个通用的解决方案。

您必须按照对象到相机的最近距离对它们进行排序。这取决于对象的尺寸及其相对位置。

该算法可以在JS中实现,如下所示:

// If e.g. horizontal distance > width / 2, subtract width / 2; same for vertical
let distClamp = (dim, diff) => {
    let dist = Math.abs(diff);
    return (dist > 0.5 * dim) ? (dist - 0.5 * dim) : dist;
}

// Closest distance to the camera
let closestDistance = (obj, cam) => {
    let dx = distClamp(obj.width, obj.x - cam.x);
    let dy = distClamp(obj.height, obj.y - cam.y);
    return Math.sqrt(dx * dx + dy * dy);
}

// Sort using this as the metric
let sortObject = (objects, camera) => {
    return objects.sort((a, b) => {
        return closestDistance(a, camera) - closestDistance(b, camera);
    });
}

编辑,该解决方案也不起作用,因为它天真的假设会很快更新或删除。

答案 1 :(得分:1)

好吧,让我们在此一展身手!我们必须订购这些对象不是距离,而是阻塞。这样,我的意思是对于两个对象A和B,A可以明显地阻碍B,B可以明显地阻碍A,或者两个都不能。如果A阻碍了B,我们将首先绘制B,反之亦然。为了解决这个问题,我们需要能够说A是否学障碍B,或周围的其他方法。

这就是我想出的。我对此的测试能力很有限,因此可能仍然存在缺陷,但是思考过程是合理的。

步骤1.地图每个对象其边界,从而节省购买原始对象:

let step1 = objects.map(o => ({
  original: o,
  xmin: o.x,
  xmax: o.x + o.w,
  ymin: o.y,
  ymax: o.y + o.h
}));

第2步。将每个对象映射到两个角,当在它们之间画一条线时,它们会成为相机视场的最大障碍物:

let step2 = step1.map(o => {
  const [closestX, farthestX] = [o.xmin, o.xmax].sort((a, b) => Math.abs(camera.x - a) - Math.abs(camera.x - b));
  const [closestY, farthestY] = [o.ymin, o.ymax].sort((a, b) => Math.abs(camera.y - a) - Math.abs(camera.y - b));

  return {
    original: o.original,
    x1: closestX,
    y1: o.xmin <= camera.x && camera.x <= o.xmax ? closestY : farthestY,
    x2: o.ymin <= camera.y && camera.y <= o.ymax ? closestX : farthestX,
    y2: closestY
  };
});

步骤3.对对象进行排序。从摄像机到一个对象的每个端点绘制一条线段。如果另一个对象的端点之间的线段相交,则另一个对象更近,必须在之后绘制。

let step3 = step2.sort((a, b) => {
  const camSegmentA1 = {
    x1: camera.x,
    y1: camera.y,
    x2: a.x1,
    y2: a.y1
  };
  const camSegmentA2 = {
    x1: camera.x,
    y1: camera.y,
    x2: a.x2,
    y2: a.y2
  };
  const camSegmentB1 = {
    x1: camera.x,
    y1: camera.y,
    x2: b.x1,
    y2: b.y1
  };
  const camSegmentB2 = {
    x1: camera.x,
    y1: camera.y,
    x2: b.x2,
    y2: b.y2
  };

  // Intersection function taken from here: https://stackoverflow.com/a/24392281
  function intersects(seg1, seg2) {
    const a = seg1.x1, b = seg1.y1, c = seg1.x2, d = seg1.y2,
          p = seg2.x1, q = seg2.y1, r = seg2.x2, s = seg2.y2;
    const det = (c - a) * (s - q) - (r - p) * (d - b);
    if (det === 0) {
      return false;
    } else {
      lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / det;
      gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / det;
      return (0 < lambda && lambda < 1) && (0 < gamma && gamma < 1);
    }
  }

  function squaredDistance(pointA, pointB) {
    return Math.pow(pointB.x - pointA.x, 2) + Math.pow(pointB.y - pointA.y, 2);
  }

  if (intersects(camSegmentA1, b) || intersects(camSegmentA2, b)) {
    return -1;
  } else if (intersects(camSegmentB1, a) || intersects(camSegmentB2, a)) {
    return 1;
  } else {
    return Math.max(squaredDistance(camera, {x: b.x1, y: b.y1}), squaredDistance(camera, {x: b.x2, y: b.y2})) - Math.max(squaredDistance(camera, {x: a.x1, y: a.y1}), squaredDistance(camera, {x: a.x2, y: a.y2}));
  }
});

第4步。最后一步-取回原始对象,并按照最远的顺序排序:

let results = step3.map(o => o.original);

现在,把它们放在一起:

results = objects.map(o => {
  const xmin = o.x,
        xmax = o.x + o.w,
        ymin = o.y,
        ymax = o.y + o.h;

  const [closestX, farthestX] = [xmin, xmax].sort((a, b) => Math.abs(camera.x - a) - Math.abs(camera.x - b));
  const [closestY, farthestY] = [ymin, ymax].sort((a, b) => Math.abs(camera.y - a) - Math.abs(camera.y - b));

  return {
    original: o,
    x1: closestX,
    y1: xmin <= camera.x && camera.x <= xmax ? closestY : farthestY,
    x2: ymin <= camera.y && camera.y <= ymax ? closestX : farthestX,
    y2: closestY
  };
}).sort((a, b) => {
  const camSegmentA1 = {
    x1: camera.x,
    y1: camera.y,
    x2: a.x1,
    y2: a.y1
  };
  const camSegmentA2 = {
    x1: camera.x,
    y1: camera.y,
    x2: a.x2,
    y2: a.y2
  };
  const camSegmentB1 = {
    x1: camera.x,
    y1: camera.y,
    x2: b.x1,
    y2: b.y1
  };
  const camSegmentB2 = {
    x1: camera.x,
    y1: camera.y,
    x2: b.x2,
    y2: b.y2
  };

  // Intersection function taken from here: https://stackoverflow.com/a/24392281
  function intersects(seg1, seg2) {
    const a = seg1.x1, b = seg1.y1, c = seg1.x2, d = seg1.y2,
          p = seg2.x1, q = seg2.y1, r = seg2.x2, s = seg2.y2;
    const det = (c - a) * (s - q) - (r - p) * (d - b);
    if (det === 0) {
      return false;
    } else {
      lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / det;
      gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / det;
      return (0 < lambda && lambda < 1) && (0 < gamma && gamma < 1);
    }
  }

  function squaredDistance(pointA, pointB) {
    return Math.pow(pointB.x - pointA.x, 2) + Math.pow(pointB.y - pointA.y, 2);
  }

  if (intersects(camSegmentA1, b) || intersects(camSegmentA2, b)) {
    return -1;
  } else if (intersects(camSegmentB1, a) || intersects(camSegmentB2, a)) {
    return 1;
  }
  return Math.max(squaredDistance(camera, {x: b.x1, y: b.y1}), squaredDistance(camera, {x: b.x2, y: b.y2})) - Math.max(squaredDistance(camera, {x: a.x1, y: a.y1}), squaredDistance(camera, {x: a.x2, y: a.y2}));
}).map(o => o.original);

让我知道是否可行!

答案 2 :(得分:0)

这里的问题是,您正在使用欧几里德距离来测量对象与(0, 0)的距离,以尝试测量与y = -x线的距离。这将不起作用,但是曼哈顿距离将起作用。

let sortObjects = (objects) => {
  return objects.sort((a, b)=> {
    let distanceA = a.x + a.y;
    let distanceB = b.x + b.y;
    return distanceA - distanceB;
  });
}

这将在旋转坐标系中垂直排列对象。

答案 3 :(得分:0)

在您的图表中,考虑每个单元格的x + y值

diagram with X+Y for each cell

要从上至下对单元格进行排序,您可以简单地按x+y的值进行排序。

答案 4 :(得分:0)

也许您可以找到有用的here(使用Firefox并检查DEMO

在我看来,深度基本上是import * as CRC32 from 'crc-32'; (window as any).global.Blob = function(content, options) { // for xlxs blob testing just return the CRC of the ArrayBuffer // and not the actual content of it. if (typeof content[0] !== 'string') { content = CRC32.buf(content); } return {content: JSON.stringify(content), options}; }; [assetHelper.js-> get depth(){...]正是第一个答案所描述的。然后排序是一个简单的比较[canvasRenderer-> depthSortAssets(){...]