简体   繁体   English

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

[英]How to sort objects in 2d game?

I'm developing a kind of perspective based 2d/3d game in javascript. 我正在用javascript开发一种基于透视的2d / 3d游戏

I've got an X- and an Y-axis like I've displayed in the image below. 我有一个X轴和Y轴,就像我在下面的图像中显示的那样。


To my question: I've got a bunch of objects (marked with "1" and "2") on my map with properties like: 我的问题:我的地图上有一堆对象(标有“1”和“2”),其属性如下:

  • positionX / positionY positionX / positionY
  • sizeX / sizeY sizeX / sizeY

In the image Object "1" does get the coordinates x:3, y:2 , and the Object "2" does get the coordinates x:5, y:4 . 在图像中,对象“1”确实得到坐标x:3, y:2 ,而对象“2”确实得到坐标x:5, y:4 SizeX and sizeY is w:1, h:1 for both objects. 对于两个对象,SizeX和sizeY是w:1, h:1

What I'd like to do with this info is to sort all of the objects in ascending order (based on the objects position and size) to know in 3d which objects comes for another object to lateron draw all of my objects sorted into my canvas (objects in foreground/background = "layers"). 我想用这个信息做的是按升序对所有对象进行排序(基于对象的位置和大小),以便在3d中知道哪些对象来自另一个对象,以便稍后绘制排序到我的画布中的所有对象(前景/背景中的对象=“图层”)。


IMG

Note: The Camera has to fixed position - lets say the camera has the identical X and Y value so that the camera position must not be used while calculation CameraX = CameraY . 注意:摄像机必须固定位置 - 假设摄像机具有相同的X和Y值,因此在计算CameraX = CameraY时不得使用摄像机位置。

What I've tried so far: 到目前为止我尝试过的:

 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(ax**2 + ay**2); let distanceB = Math.sqrt(bx**2 + by**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... 

Edit to the snippet above: 编辑上面的代码段:

I've tried to sort the objects based on their x/y coordinate but it seems like the width and height parameter must be used also while calculation to avoid errors. 我试图根据它们的x / y坐标对对象进行排序,但似乎必须在计算时使用width和height参数以避免错误。

How do I have to make use of width/height? 我如何利用宽度/高度? Tbh I've got no clue so any help would be really appreciated. Tbh我没有任何线索,所以任何帮助都会非常感激。

I'm not sure what you meant by: 我不确定你的意思是:

Note: The Camera has to fixed position - lets say the camera has the identical X and Y value so that the camera position must not be used while calculation CameraX = CameraY. 注意:摄像机必须固定位置 - 假设摄像机具有相同的X和Y值,因此在计算CameraX = CameraY时不得使用摄像机位置。

So here's a general-case solution. 所以这是一个通用案例解决方案。

You must sort the objects by their closest distance to the camera. 您必须按照距离相机最近的距离对对象进行排序。 This depends on an object's dimensions as well as its relative position. 这取决于对象的尺寸及其相对位置。

The algorithm can be implemented in JS as follows: 该算法可以在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);
    });
}

EDIT this solution also does not work because it makes naive assumptions, will update soon or delete. 编辑此解决方案也不起作用,因为它做出天真的假设,将很快更新或删除。

Alright, let's have a go at this! 好吧,让我们去吧! We'll have to order these objects not by distance, but by obstruction. 我们不得不按距离订购这些物体,而是按障碍物。 By that, I mean for two objects A and B, A can visibly obstruct B, B can visibly obstruct A, or neither obstructs the other. 由此,我的意思是两个物体A和B,A可以明显地阻挡B,B可以明显地阻挡A,或者两者都不会阻挡另一个。 If A obstructs B, we'll want to draw B first, and vice-versa. 如果A阻碍B,我们首先要绘制B,反之亦然。 To solve this, we need to be able to say whether A obstructs B, or the other way around. 要解决这个问题,我们需要能够说出A是阻碍B,还是反过来。

Here's what I've come up with. 这就是我想出的。 I only had a limited ability to test this, so there may still be flaws, but the thought process is sound. 我只有有限的测试能力,所以可能仍有缺陷,但思考过程是合理的。

Step 1. Map each object to its bounds, saving the original object for later: 步骤1.将每个对象映射到其边界,保存原始对象以供以后使用:

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

Step 2. Map each object to the two corners that, when a line is drawn between them, form the largest obstruction to the camera's field of view: 步骤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
  };
});

Step 3. Sort the objects. 第3步。对对象进行排序。 Draw a line segment from the camera to each endpoint of one object. 从相机到一个对象的每个端点绘制一条线段。 If the line segment between the endpoints of the other object intersects, the other object is closer and must be drawn after. 如果另一个对象的端点之间的线段相交,则另一个对象更近,必须在之后绘制。

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}));
  }
});

Step 4. Final step -- get the original objects back, sorted in order of farthest to closest: 步骤4.最后一步 - 返回原始对象,按最远到最近的顺序排序:

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

Now, to put it all together: 现在,把它们放在一起:

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);

Let me know if that works! 如果有效,请告诉我!

The issue here is that you are using euclidean distance to measure how far away an object is from (0, 0) , to try to measure the distance from the y = -x line. 这里的问题是你使用欧氏距离来测量物体距离(0, 0)的距离,以尝试测量距离y = -x线的距离。 This will not work, however Manhattan distance will. 这不起作用,但曼哈顿的距离会有所不同。

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

This will order your objects vertically in your rotated coordinate system. 这将在旋转的坐标系中垂直排序对象。

In your diagram consider the value of x+y for each cell 在您的图表中,考虑每个单元格的x + y值

每个单元格的X + Y图表

To sort top-to-bottom the cells you can simply sort on the value of x+y . 要从上到下排序单元格,您只需对x+y的值进行排序即可。

Maybe you can find something useful here (use Firefox & check the DEMO ) 也许你可以在这里找到有用的东西 (使用Firefox并查看DEMO

In my case depth is basically pos.x + pos.y [assetHelper.js -> get depth() {...] exactly as the first answer describes it. 在我的情况下,深度基本上是pos.x + pos.y [assetHelper.js - > get depth(){...],正如第一个答案所描述的那样。 Then the sorting is a simple compare [canvasRenderer -> depthSortAssets() {...] 然后排序是一个简单的比较[canvasRenderer - > depthSortAssets(){...]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM