区分 box2d 中的碰撞面方向
我一直在使用 Cocos2D 1.0 和 Box2D 开发一个 iOS 项目,但遇到了一些问题。
我需要做的是确定我的玩家击中的表面的方向。例如,如果我们有一个矩形平台,并且玩家与它发生碰撞,我需要知道玩家是否撞到了它的左面、右面、顶面或底面。游戏中的所有物体都是正方形的,唯一移动的就是玩家。
我目前正在 Box2D 中使用 b2ContactListener(无论如何,我自己的一个子类),并且一直在尝试使用 BeginContact 中接触的流形的局部法线。我遇到的主要问题是,法线似乎受到玩家身体旋转的影响(例如,玩家旋转了 90 度,或者玩家在撞击时疯狂旋转 - 这两种情况都给我带来了麻烦),而且我似乎如果我尝试允许这一点,最终会产生歧义(即与给出相同法线的不同面孔的碰撞......) - 尽管当然我可能只是做了一些可怕的错误。现在,我不太了解流形,所以我的问题可能源于此,或者也许我错过了一些明显的东西。
有什么建议吗? 我更愿意以最干净、最不丑陋的方式来做这件事。请记住,我关心的主要分类是“玩家从上方降落在某物上”与“其他所有物体”,但我最终可能需要确切的分类 如果您需要更多信息或澄清任何事情,尽管询问。
编辑:只是为了澄清一下,我知道按照 Box2D 中的约定,从 A 到 B 的法线点(在 A 和 B 之间的碰撞中),并且我的代码会检查哪一个是玩家,并在执行之前考虑到这一点确定哪个面被击中的任何计算。
I've been working on an iOS project, using Cocos2D 1.0 and Box2D, and I've run into a bit of a problem.
What I need to be able to do is determine the orientation of a surface my player has hit. For example, if we have a rectangular platform, and the player collides with it, I need to know whether the player has hit the left, right, top, or bottom face of it. ALL the objects in the game are square, and the ONLY one moving is the player.
I'm currently using a b2ContactListener in Box2D (well, my own subclass of one, anyway), and have been playing around with the local normal of the manifold from the contact in BeginContact. The main problem I have is that that normal seems to be affected by the rotation of the player body (e.g. the player has rotated 90 degrees, OR the player is spinning wildly on impact - both situations are giving me trouble), and I seem to end up with ambiguity (i.e. collisions with different faces that give the same normal...) if I try to allow for that - although of course I could just be doing something horribly wrong. Now, I don't understand manifolds very well, so it's possible that my problem stems from that, or maybe I'm missing something obvious.
Any suggestions?
I would prefer to do this in the cleanest and least ugly manner possible. Bear in mind that the main categorisation I care about is "player is landing on something from above" vs "everything else", but I may end up needing the exact
If you need more information or clarification about anything, just ask.
EDIT: Just to clarify, I am aware that the normal points from A to B (in a collision between A and B) by convention in Box2D, and my code does check to see which one is the player and takes this into account before doing any calculations to determine which face has been hit.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
所以,我对回答自己的问题感到有点尴尬,但显然这是官方鼓励的。
无论如何,我处理事情的方式存在双重问题。首先,我使用接触流形的局部法线而不是世界法线。其次,我用于反转对象转换的代码有错误(如果我一直使用世界流形,我将永远不需要这样做)。
世界流形考虑了对象变换和大小,因此包含更容易应用于世界坐标系的数据。
按照 Box2d 中的约定,碰撞法线(对于世界流形和接触流形)从 A 指向 B - 在某些用途中必须考虑到这一点,因为从 A 到 B 的法线是从 A 到 B 的法线的倒数。 B 到 A,所以你不能仅仅假设一个物体总是 A。
因此,解决方案是使用获取每次碰撞的世界流形,检查其法线,然后做出你想要做出的任何决定。
例如,在 b2ContactListener 子类的
BeginContact
方法中(如果您不知道我在说什么,请查看 第 2 部分 教程):由于您可能需要检查哪些物体正在发生碰撞,以及哪个是 A,哪个是 B,因此您可能需要保留一个包含发生碰撞的装置的结构体向量(如 教程)和普通方法,并在
tick()
方法或类似方法中迭代向量。 (您可以使用contact->GetFixtureA()
和contact->GetFixtureB()
从接触中获取这些内容。)现在,您可以 从世界流形中获取点数据,并据此做出决策,但是当法线已经可用时,为什么要这样做,因为在这种特定情况下,法线(与形状相结合)正常点从和到)是所需要的一切。
编辑(针对@iBradApps):
首先,我假设您已按照我链接到的教程进行操作并设置了联系人侦听器。如果您还没有,请遵循它,因为 Ray 对此进行了很好的深入解释。
其次,我想指出的是,不能绝对保证哪个对象是 A,哪个是 B(嗯,这取决于它们是什么类型的 Box2D 对象;只要说它们都可以移动,就不能保证排序,至少据我所知),所以在我的例子中,我想看看玩家对象是否击中了某些东西,所以我在我的联系人侦听器中创建了一个类变量(
b2Fixture *playerF
)存储对玩家对象的引用这样我就可以确定联系人 A 还是联系人 B 是玩家。您询问了检测其他物体与 B 顶部碰撞的碰撞。尽管我没有机会为您测试它,但类似以下的内容应该有效:
在您的 ContactListener.h 中:
当您在您的 ContactListener 中创建 ContactListener 时,
init()
(假设您将其称为_contactListener
):BeginContact
方法:至于在
tick()
中执行此操作> 方法代替,是的,你 能。实际上,我在接触侦听器中的PostSolve
中完成了所有工作,因为我需要知道玩家击打的力度有多大,但除此之外我关心的是玩家击打的力度是否足以杀死他们,所以我不需要或不想迭代tick()
中的所有联系人 - 我只是在联系人监听器中设置了一个标志,表示玩家遭受了致命的影响。如果您想在 update 方法中完成这一切,那么从 Ray 开始,将
b2Vec2
添加到 MyContact 结构,然后在BeginContact
中添加两个固定装置(就像雷一样)和获得碰撞法线(就像我一样)并添加它。修改后的
MyContact
结构:新的
BeginContact
方法:这将为您提供进行我最初在
tick().
再次编辑:
如果您想在那里进行处理,您的
tick()
方法可能包含类似的内容,假设您已经调用了玩家固定装置(或球固定装置,如教程中所示,或者您正在调用的任何东西)感兴趣)_playerFixture,您有一个与教程中同名的联系人侦听器,您将 b2Vec2 法线添加到 MyContact 结构,您正在 BeginContact 中将联系人添加到向量(如上所述),并且您正在从 EndContact 中的向量中删除联系人(如教程中所示 - 它可能没问题):So, I feel a little awkward about answering my own question, but apparently it's officially encouraged.
Anyway, the problem with the way I was approaching things was twofold. Firstly, I was using the contact manifold's local normal instead of the world normal. Secondly, my code for reversing the object transformations was buggy (I would never have needed to do this if I had been using the world manifold).
The world manifold takes into account object transformations and sizes and as such contains data more easily applicable to the world co-ordinate system.
By convention in Box2d, the collision normal (for both the world manifold and the contact manifold) points from A to B - this has to be taken into account for some uses, since the normal from A to B is the inverse of the normal from B to A, so you can't just assume that one body will always be A.
So, the solution is to use get the world manifold for each collision, examine its normal, and then make whatever decisions you want to make.
For example, in the
BeginContact
method of a b2ContactListener subclass (if you have no idea what I'm talking about then check out part 2 of this tutorial):Since you'll likely need to check what bodies are colliding, and which one is A and which one is B, you may want to keep a vector of structs containing the fixtures that collided (as in that tutorial) and the normal, and iterate over the vector in your
tick()
method or similar. (You can get these out of the contact withcontact->GetFixtureA()
andcontact->GetFixtureB()
.)Now, you could get the point data from the world manifold, and make your decisions based on that, but why would you when the normal is already available, since in this particular case the normal (combined with which shapes the normal points from and to) is all that is needed.
Edit (for @iBradApps):
First, I'm assuming here that you have followed the tutorial I linked to and have a contact listener set up. If you haven't, follow it because Ray explains it in depth quite well.
Second, I want to point out that there is no absolute guarantee which object is A and which is B (well, it depends on what kind of Box2D objects they are; suffice to say if they can both move, you can't guarantee the ordering, at least as far as I know), so in my case I wanted to see if the player object had hit something, so I created a class variable (
b2Fixture *playerF
) in my contact listener that stored a reference to the player object so I could determine whether contact A or contact B was the player.You asked about detecting a collision where something else collided with the top of B. Something like the following should work, although I haven't had a chance to test it for you:
In your ContactListener.h:
When you make the ContactListener in your
init()
(assuming you called it_contactListener
):BeginContact
method:As for doing it in your
tick()
method instead, yes, you can. I actually did all my stuff inPostSolve
in the contact listener because I needed to know how hard the player hit, but all I cared about beyond that was whether the player had hit hard enough to kill them, so I didn't need or want to iterate over all the contacts in mytick()
- I just set a flag in the contact listener that said the player had suffered a fatal impact.If you want to do this all in the update method, then starting from what Ray has, add a
b2Vec2
to the MyContact struct, and inBeginContact
, add both the two fixtures (like Ray does) and get the collision normal (as I do) and add it too.The modified
MyContact
struct:The new
BeginContact
method:This will give you all the information you need to do the checking I initially described in your
tick()
.Edit again:
Your
tick()
method might contain something like this if you want to do the processing there, assuming you have called the player fixture (or ball fixture, like in the tutorial, or whatever it is you're interested in) _playerFixture, that you've got a contact listener with the same name as in the tutorial, that you added the b2Vec2 normal to the MyContact struct, that you are adding contacts to the vector (as above) in BeginContact, and that you are deleting contacts from the vector in the EndContact (as shown in the tutorial - it's probably fine as is):