libGDX-精确碰撞检测 - 创建多边形?

时间:2015-09-27 12:41:37

标签: image libgdx textures polygon collision

我对libGDX碰撞检测有疑问。因为这是一个相当具体的问题,我还没有在互联网上找到任何好的解决方案。

所以,我已经创造了#34;人类"由不同的身体部位组成,每个部位都有矩形碰撞检测。

现在我想实施武器和技能,例如:

Skill example image

问题

在有这样的技能的情况下,在碰撞检测中使用矩形会让玩家感到非常沮丧:他们会成功躲避技能,但碰撞探测器仍然会对它们造成伤害。

方法1:

在我开始使用Libgdx之前,我创建了一款带有自定义引擎和类似技能的Android游戏。我在那里解决了问题:

  1. 检测矩形碰撞
  2. 计算重叠矩形部分
  3. 检查技能重叠部分的每个像素是否透明
  4. 如果找到任何非透明像素 - >碰撞
  5. 这是一种沉重的方式,但由于只检查重叠的像素,而游戏的其余部分非常清晰,所以它完全正常。

    目前我的技能图像被加载为" TextureRegion",其中无法访问单个像素。 我发现libGDX有一个Pixmap类,可以进行像素检查。问题是:将它们加载为Pixmaps另外1.将更加沉重和2.击败纹理系统的整个目的。

    另一种方法是将所有技能加载为Pixmap。你怎么想:这会是一个好方法吗?是否有可能在屏幕上绘制许多Pixmaps而没有任何问题和滞后?

    方法2:

    另一种方法是创建具有技能形状的多边形并将其用于碰撞检测。

    A) 但是,我如何为每一项技能定义一个多边形形状(其中有超过150种)?浏览片刻后,我找到了这个有用的工具:http://www.aurelienribon.com/blog/projects/physics-body-editor/ 它允许手动创建多边形形状,然后将它们保存为JSON文件,可由libGDX应用程序读取。现在来了困难:

    • 物理主体编辑器连接到Box2d(我没有使用)。我要么必须添加整个Box2d物理引擎(我根本不需要)只是因为一次微小的碰撞检测或者我必须编写一个自定义的BodyEditorLoader,这将是一项艰巨,复杂且耗时的任务
    • 相同技能精灵的一些图像在形状上有很大差异(如第二个技能精灵示例)。使用BodyEditor工具时,我不仅要定义每个技能的形状,还要定义每个技能的几个图像(最多12个)的形状。这将是非常耗时的,并且在实现这些数十个多边形时非常混乱

    b)中 如果有任何平滑的方法可以自动生成图像中的多边形,那么这可能是解决方案。我可以简单地将每个精灵部分连接到生成的多边形,并检查那种冲突。但是有一些问题:

    • 是否有任何可以从图像中生成多边形形状的平滑工具(并且不需要太多时间)?
    • 我不认为像这样的工具(如果存在的话)可以直接使用Textures。它可能需要Pixmaps。在创建Polygon之后,不需要保持te Pixmaps加载。仍然是一项非常繁重的任务!

    我目前的想法

    我在这一点上陷入困​​境,因为有几种可能的方法,但所有方法都有困难。在我选择一条路径并继续编码之前,如果你能留下一些你的想法和知识,那就太好了。

    libGDX中可能包含有用的类和代码,可以在几秒钟内解决我的问题 - 因为我是libGDX的新手,但我还不太了解它。

    目前我认为我会采用方法1:处理像素检测。这样我就可以在之前的Android游戏中进行精确的碰撞检测。

    您怎么看?

    问候 菲利克斯

3 个答案:

答案 0 :(得分:1)

我个人认为,像素到像素的碰撞会对性能造成过大影响,并提供一些我仍然会感到受骗的情况 - (我被斧头的手柄击中了?

如果是我,我会为每个技能添加一个“Hitbox”。 StreetFighter是一款使用这种技术的流行游戏。 (较新的版本是3D版,但是hitbox碰撞仍然是2D )Hitbox可以与动画一起逐帧更改。

这里的空白点添加示例图片 - 同时谷歌“Streetfighter hitbox”

对于你的斧头,沿着一端或两端的边缘可能有一个定义的矩形命中框 - 甚至在斧头的整个金属头上。

这使得它非常简单,不必弄乱精确的多边形,但也不会像每个像素都是自己的命中箱一样过于沉重。

答案 1 :(得分:0)

我使用了您引用的精确正文编辑器,它可以为您生成多边形和/或圆形。我还使用Jackson库为生成的JSON创建了一个加载器。这可能不是你的答案,因为你必须实现box2d。但无论如何我都是这样做的。

/**
* Adds all the fixtures defined in jsonPath with the name'lookupName', and 
* attach them to the 'body' with the properties defined in 'fixtureDef'. 
* Then converts to the proper scale with 'width'.
*
* @param body the body to attach fixtures to
* @param fixtureDef the fixture's properties
* @param jsonPath the path to the collision shapes definition file
* @param lookupName the name to find in jsonPath json file
* @param width the width of the sprite, used to scale fixtures and find origin.
* @param height the height of the sprite, used to find origin.
*/
public void addFixtures(Body body, FixtureDef fixtureDef, String jsonPath, String lookupName, float width, float height) {
  JsonNode collisionShapes = null;
  try {
    collisionShapes = json.readTree(Gdx.files.internal(jsonPath).readString());
  } catch (JsonProcessingException e) {
    e.printStackTrace();
  } catch (IOException e) {
    e.printStackTrace();
  }
  for (JsonNode node : collisionShapes.findPath("rigidBodies")) {
    if (node.path("name").asText().equals(lookupName)) {
      Array<PolygonShape> polyShapes = new Array<PolygonShape>();
      Array<CircleShape> circleShapes = new Array<CircleShape>();

      for (JsonNode polygon : node.findPath("polygons")) {
        Array<Vector2> vertices = new Array<Vector2>(Vector2.class);
        for (JsonNode vector : polygon) {
          vertices.add(new Vector2(
            (float)vector.path("x").asDouble() * width,
            (float)vector.path("y").asDouble() * width)
            .sub(width/2, height/2));
        }
        polyShapes.add(new PolygonShape());
        polyShapes.peek().set(vertices.toArray());
      }

      for (final JsonNode circle : node.findPath("circles")) {
        circleShapes.add(new CircleShape());
        circleShapes.peek().setPosition(new Vector2(
          (float)circle.path("cx").asDouble() * width,
          (float)circle.path("cy").asDouble() * width)
          .sub(width/2, height/2));

        circleShapes.peek().setRadius((float)circle.path("r").asDouble() * width);
      }

      for (PolygonShape shape : polyShapes) {
        Vector2 vectors[] = new Vector2[shape.getVertexCount()];
        for (int i = 0; i < shape.getVertexCount(); i++) {
          vectors[i] = new Vector2();
          shape.getVertex(i, vectors[i]);
        }
        shape.set(vectors);
        fixtureDef.shape = shape;
        body.createFixture(fixtureDef);
      }

      for (CircleShape shape : circleShapes) {
        fixtureDef.shape = shape;
        body.createFixture(fixtureDef);
      }
    }
  }
}

我会这样称呼它:

physics.addFixtures(body, fixtureDef, "ship/collision_shapes.json", shipType, width, height);

然后进行碰撞检测:

public ContactListener shipsExplode() {
  ContactListener listener = new ContactListener() {

    @Override
    public void beginContact(Contact contact) {
      Body bodyA = contact.getFixtureA().getBody();
      Body bodyB = contact.getFixtureB().getBody();

      for (Ship ship : ships) {
        if (ship.body == bodyA) {
          ship.setExplode();
        }
        if (ship.body == bodyB) {
          ship.setExplode();
        }
      }
    }
  };
  return listener;
}

然后你会把听众添加到世界:

world.setContactListener(physics.shipsExplode());

我的精灵的宽度和高度都很小,因为一旦开始使用box2d,你的处理米而不是像素。一个子画面高度为0.8f,宽度为1.2f。如果您以精灵像素的宽度和高度为单位,物理引擎会达到http://www.iforce2d.net/b2dtut/gotchas

中构建的速度限制

答案 2 :(得分:0)

不知道这对你们来说是否仍然重要,但我建立了一个小的python脚本,它返回图像边缘点的像素位置。有改进剧本的空间,但对我来说,现在还可以......

from PIL import Image, ImageFilter

filename = "dship1"

image = Image.open(filename + ".png")
image = image.filter(ImageFilter.FIND_EDGES)
image.save(filename + "_edge.png")
cols = image.width
rows = image.height
points = []
w = 1
h = 1
i = 0
for pixel in list(image.getdata()):
    if pixel[3] > 0:
        points.append((w, h))

    if i == cols:
        w = 0
        i = 0
        h += 1
    w += 1
    i += 1

with open(filename + "_points.txt", "wb") as nf:
    nf.write(',\n'.join('%s, %s' % x for x in points))

如果有更新,您可以在此处找到它们:export positions