2015-09-27 56 views
2

我有一个关于libGDX碰撞检测的问题。因为这是一个相当具体的问题,我还没有在互联网上找到任何好的解决方案。libGDX-完全碰撞检测 - 创建多边形?

因此,我已经创建了由不同身体部位组成的“人类”,每个人体都具有矩形形状的碰撞检测。

现在我想要实现的武器和技能,这对于例如是这样的:

Skill example image

问题

在碰撞检测的矩形工作将是玩家真正令人沮丧时,有是这样的技能:他们会成功躲避技能,但碰撞检测器仍然会损坏它们。

方法1:

之前,我开始与Libgdx工作,我已经创建了一个带有自定义引擎和类似的技能的Android游戏。在那里,我的问题解决了以下方法:

  1. 检测矩形碰撞
  2. 计算重叠矩形截面
  3. 检查技能的重叠部分的透明度
  4. 的每一个像素。如果有任何非透明像素发现 - >碰撞

这是一种沉重的方式,但由于只有重叠的像素被检查和游戏的其余部分真的很轻,它工作得很好。

此刻我的技能图像被加载为“TextureRegion”,无法访问单个像素。 我发现libGDX有一个Pixmap类,它允许进行像素检查。问题是:让它们像Pixmaps一样被加载时会更加沉重,并且会打败Texture系统的整个目的。

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

方法2:

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

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

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

B) 如果有任何光滑的方式自动生成多边形外之象,这可能是解决方案。我可以简单地将每个精灵部分连接到生成的多边形,然后通过这种方式检查碰撞。有几个问题,但:

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

我现在的想法

我被困在这一点上,因为有几种可能的方法,但他们都有自己的困难。在我选择一条路径并继续编码之前,如果您能留下一些您的想法和知识,那将是非常棒的。

libGDX中可能包含有帮助的类和代码,可以在几秒钟内解决我的问题 - 因为我真的是libGDX的新手,我对此还不甚了解。

目前我想我会采用方法1:使用像素检测。这样我就可以在我以前的Android游戏中实现准确的碰撞检测。

您认为如何?

问候 菲利克斯

+0

您是否找到一个好的解决方案?我有同样的问题。我不知道如何从物理编辑器工具的坐标中创建多边形,并在没有box2d的情况下检查碰撞。 – trinity420

回答

0

我用你提到确切的体编辑器,它有生成多边形和/或圆形你的能力。我还为杰克逊库生成的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

+0

感谢您的帮助!我仍然会尝试在没有Box2d的情况下解决问题,但如果我被迫与Box2d和身体编辑器一起工作,我将使用您的提示:) – Logende

+0

身体编辑器使用Farseer以务实的方式生成多边形。 https://code.google.com/p/box2d-editor/source/browse/editor/src/aurelienribon/bodyeditor/maths/trace/TextureConverter.java,如果你能从中获得任何有趣的东西。 –

+0

你最终在这种情况下做了什么?你使用的是更古老的代码吗? –

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

+0

看起来有趣! :)如果我有这个权利,你预先计算你的脚本的边缘,将它们存储到一个文本文件,并在你的libGDX项目中使用该文本文件。将您的脚本添加到我的库文件中。我可能会使用它,但首先我需要确保一切都会正常工作:游戏拥有超过100种独特技能,每种技能都有不同的动画。实施你的方法将是相当大的努力。 – Logende

+0

@Logende你是对的,我的脚本完成你所说的......但请记住,将图像中的所有点都导出。在我的测试中,这意味着要计算的点太多了。更好的方法是获得上面脚本导出的一些要点。 如果你有更多的精灵,我的建议会改善上述脚本导出更少的点和阅读更多的图像......这可能是一个新的游戏开发工具,即时通讯,box2d有这样的工具,但你必须亲手做(如果我没有弄错)! – rdenadai

1

我个人而言,会感觉像像素 - (我被斧头的手柄撞到了?

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

这里空白处添加示例图像 - 在此期间

对于你的斧头谷歌“街头霸王击中格”,有可能是沿着一个两端的边缘或定义的矩形击中格 - 甚至在整个斧头的金属头。

这样可以保持相当简单,而不必混淆精确的多边形,但也不会像每个像素都有自己的hitbox那样过于沉重。

+0

像素检测是一项艰巨的任务,但我受到了限制:尽管技能使用像素碰撞,但所有玩家和角色都被检测为一组矩形。只要矩形和技能碰撞,技能的受影响像素将被检查透明度。加快进程:只需检查每秒或第四个像素。这并不是我打算打击你的想法,这肯定已经被证明是有效的,并被流行的游戏所使用,但我认为将它用于我的游戏将会花费太多精力:> 100个技能精灵,每个都有12张图像,的hitboxes +不太确切。 – Logende

+0

@Logende重要的是要花费时间在游戏中获得最多可玩性的作品,因为这些作品可用时间很长,所以我完全理解。尽管如此,如果你想扩展到支持更多玩家和他们的技能,你可以通过改变你的碰撞来做到这一点,优化应该是未来的事情。 – DoubleDouble

+0

(*或其他任何可能需要处理时间的东西*) - 更好的人工智能,更多的背景设置在背景中,更多的东西在屏幕上,等等。 – DoubleDouble