使用投影向量的基本 AABB 碰撞

发布于 2024-12-21 10:38:51 字数 4468 浏览 10 评论 0原文

过去几天我一直在四处寻找,并研究向量,但仍然无法完全理解数学。

我有两个 AABB 的。在发生碰撞时,我希望我的方法返回一个向量,然后我可以将其添加到位置向量以使我的对象回到边界内。

这是我当前的代码:(

位置向量是 AABB 的中心)

public Vector2f collide(Sprite other) {
    if(!collideBoolean(other)) return null; //no collision

    float xAxis = Math.abs(this.position.x - other.getX()); //distance between centers on x axis
    float yAxis = Math.abs(this.position.y - other.getY()); //distance between centers on y axis

    //these combined values show the minimum distance apart the objects need to be to not collide
    int cw = this.getHalfWidth() + other.getHalfWidth(); //combined width
    int ch = this.getHalfHeight() + other.getHalfHeight(); //combined height

    float ox = Math.abs(xAxis - cw); //overlap on x
    float oy = Math.abs(yAxis - ch); //overlap on y

    //direction
    Vector2f dir = new Vector2f(this.position); 
    dir.sub(other.getPosition()); //subtract other.position from this.position
    dir.normalise();

    return new Vector2f(dir.x * ox, dir.y * oy);
}

(不言自明,但这里也是 collideBoolean(Sprite other) 的代码)

public boolean collideBoolean(Sprite other) {
    //code using halfwidths and centers
    if(Math.abs(this.position.x - other.getX()) > (this.getHalfWidth() + other.getHalfWidth())) return false;
    if(Math.abs(this.position.y - other.getY()) > (this.getHalfHeight() + other.getHalfHeight())) return false;

    return true;
}

我当前的代码或多或少有效。但是(这个)对象正在碰撞反对“其他”的物体被推出并朝向“其他”最近的角落。

我想我真的很接近了。当然,对于不同的人来说,这将是显而易见的事情,但我无法完全弄清楚。任何帮助将不胜感激!

谢谢,

编辑:

将其添加到 collide(Sprite other) 方法的末尾是可行的,只是没有我想要的那么整洁。此外,当仅沿一个轴移动到“另一个”主体时,它工作得很好,但如果你以一定角度推入主体,你就会进入形状内部,它会随机地将你抛出去。

这可能只是因为我每步移动了太多像素,但我应该扫描测试我的碰撞

(此代码查看投影向量以查看哪个分量更大,然后 0 表示最大分量​​。这意味着我'我只沿着最短路径突出形状)

    ....
    //direction
    ....

    Vector2f projection = new Vector2f(dir.x * (ox+1), dir.y * (oy+1));
    if(Math.abs(projection.x) > Math.abs(projection.y)) projection.x = 0;
    else if(Math.abs(projection.y) > Math.abs(projection.x)) projection.y = 0;

    return projection;
}

编辑两个

随着伊什塔尔答案的实施,事情看起来不错。但我发现,如果我将一个小物体与一个宽物体碰撞,它会准确地修复中心附近的碰撞,但当你离开角落附近时,你就会陷入形状中。

像这样:

         _
 _______l_l_________
|                   |
|______OK___________|


 _--________________
| --                |
|_____SINKS IN______|

编辑三个

当前碰撞代码:

public class Collision {

/** fix collision based on mass */
public static void collide(Sprite s1, Sprite s2) {
    float xAxis = Math.abs(s1.getX() - s2.getX()); //distance between centers
    float yAxis = Math.abs(s1.getY() - s2.getY()); //distance between centers

    int cw = s1.getHalfWidth() + s2.getHalfWidth(); //combined width
    int ch = s1.getHalfHeight() + s2.getHalfHeight(); //combined height

    //early exit
    if(xAxis > cw) return;
    if(yAxis > ch) return;

    float ox = Math.abs(xAxis - cw); //overlap on x
    float oy = Math.abs(yAxis - ch); //overlap on y

    if(s1.getMass() <= s2.getMass())
        fixCollision(s1, s2, ox+1, oy+1);    //the +1's make you get out of the shape instead of 
    else //if(s1.getMass() > s2.getMass())    //correcting you onto the edge where you'll be in constant collision
        fixCollision(s2, s1, ox+1, oy+1);
}

/**
 * Fixes the collision
 * @param s1 : this gets pushed out (should be lower mass)
 * @param s2 : this stays where it is
 * @param ox : the overlap along the x axis
 * @param oy : the overlap along the y axis
 */
private static void fixCollision(Sprite s1, Sprite s2, float ox, float oy) {
    //direction
    Vector2f dir = new Vector2f(s1.getPosition()); 
    dir.sub(s2.getPosition());
    dir.normalise();

    Vector2f projection = new Vector2f(dir.x * (ox), dir.y * (oy));
    if(Math.abs(projection.x) > Math.abs(projection.y)) projection.x = 0;
    else if(Math.abs(projection.y) > Math.abs(projection.x)) projection.y = 0;

    if(ox > oy) s1.getPosition().add( new Vector2f(0, dir.y * oy) ); //overlap is bigger on x so project on y
    else if(ox < oy) s1.getPosition().add( new Vector2f(dir.x * ox, 0)); //overlap is bigger on x so project on x
    else s1.getPosition().add( new Vector2f(dir.x * ox, dir.y * oy)); //corner to corner
}

I've been searching around for the past few days, and researching about Vectors, but still can't quite get my head around the math..

I have two AABB's. On collision I want my method to return a Vector that I can then add to the position Vector to bring my object back into bounds.

Here's my current code:

(The position Vector is the center of the AABB)

public Vector2f collide(Sprite other) {
    if(!collideBoolean(other)) return null; //no collision

    float xAxis = Math.abs(this.position.x - other.getX()); //distance between centers on x axis
    float yAxis = Math.abs(this.position.y - other.getY()); //distance between centers on y axis

    //these combined values show the minimum distance apart the objects need to be to not collide
    int cw = this.getHalfWidth() + other.getHalfWidth(); //combined width
    int ch = this.getHalfHeight() + other.getHalfHeight(); //combined height

    float ox = Math.abs(xAxis - cw); //overlap on x
    float oy = Math.abs(yAxis - ch); //overlap on y

    //direction
    Vector2f dir = new Vector2f(this.position); 
    dir.sub(other.getPosition()); //subtract other.position from this.position
    dir.normalise();

    return new Vector2f(dir.x * ox, dir.y * oy);
}

(self explanatory but here is also the code for collideBoolean(Sprite other) )

public boolean collideBoolean(Sprite other) {
    //code using halfwidths and centers
    if(Math.abs(this.position.x - other.getX()) > (this.getHalfWidth() + other.getHalfWidth())) return false;
    if(Math.abs(this.position.y - other.getY()) > (this.getHalfHeight() + other.getHalfHeight())) return false;

    return true;
}

My current code more or less works.. But the (this) object that is colliding against 'other' gets pushed out AND sides towards the closest corner of 'other'.

I think I'm really close. Surely it'll be something blindingly obvious to a different set of eyes, but I can't quite work it out. Any help would be greatly appreciated!

Thanks,

EDIT:

Adding this into the end of the collide(Sprite other) method works, except isn't as tidy as I'd like. Also when moving into the 'other' body along only one axis it works fine, but if you push into the body at an angle you get inside the shape and it throws you out randomly.

This is probably just because I'm moving too many pixels per step, and I should sweep test my collisions though

(this code looks at the projection vector to see which component is bigger, then 0's out the biggest component. This means that I'm only projecting out of the shape along the shortest path)

    ....
    //direction
    ....

    Vector2f projection = new Vector2f(dir.x * (ox+1), dir.y * (oy+1));
    if(Math.abs(projection.x) > Math.abs(projection.y)) projection.x = 0;
    else if(Math.abs(projection.y) > Math.abs(projection.x)) projection.y = 0;

    return projection;
}

EDIT TWO

With the implementation of Ishtar's answer things were looking good. But I've found if I'm colliding a small object with a wide object, it accurately fixes the collision near the centers, but as you get out near the corners you sink into the shape.

Like this:

         _
 _______l_l_________
|                   |
|______OK___________|


 _--________________
| --                |
|_____SINKS IN______|

EDIT THREE

Current collision code:

public class Collision {

/** fix collision based on mass */
public static void collide(Sprite s1, Sprite s2) {
    float xAxis = Math.abs(s1.getX() - s2.getX()); //distance between centers
    float yAxis = Math.abs(s1.getY() - s2.getY()); //distance between centers

    int cw = s1.getHalfWidth() + s2.getHalfWidth(); //combined width
    int ch = s1.getHalfHeight() + s2.getHalfHeight(); //combined height

    //early exit
    if(xAxis > cw) return;
    if(yAxis > ch) return;

    float ox = Math.abs(xAxis - cw); //overlap on x
    float oy = Math.abs(yAxis - ch); //overlap on y

    if(s1.getMass() <= s2.getMass())
        fixCollision(s1, s2, ox+1, oy+1);    //the +1's make you get out of the shape instead of 
    else //if(s1.getMass() > s2.getMass())    //correcting you onto the edge where you'll be in constant collision
        fixCollision(s2, s1, ox+1, oy+1);
}

/**
 * Fixes the collision
 * @param s1 : this gets pushed out (should be lower mass)
 * @param s2 : this stays where it is
 * @param ox : the overlap along the x axis
 * @param oy : the overlap along the y axis
 */
private static void fixCollision(Sprite s1, Sprite s2, float ox, float oy) {
    //direction
    Vector2f dir = new Vector2f(s1.getPosition()); 
    dir.sub(s2.getPosition());
    dir.normalise();

    Vector2f projection = new Vector2f(dir.x * (ox), dir.y * (oy));
    if(Math.abs(projection.x) > Math.abs(projection.y)) projection.x = 0;
    else if(Math.abs(projection.y) > Math.abs(projection.x)) projection.y = 0;

    if(ox > oy) s1.getPosition().add( new Vector2f(0, dir.y * oy) ); //overlap is bigger on x so project on y
    else if(ox < oy) s1.getPosition().add( new Vector2f(dir.x * ox, 0)); //overlap is bigger on x so project on x
    else s1.getPosition().add( new Vector2f(dir.x * ox, dir.y * oy)); //corner to corner
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

零度° 2024-12-28 10:38:51
float ox = Math.abs(xAxis - cw); //overlap on x
float oy = Math.abs(yAxis - ch); //overlap on y

//direction
Vector2f dir = new Vector2f(this.position); 
dir.sub(other.getPosition()); //subtract other.position from this.position
dir.normalise();

return new Vector2f(dir.x * ox, dir.y * oy);

返回的平移向量将使 ox(x 中的重叠)和 oy(y 中的重叠)都为零。但这不是您需要的。如果 ox 为 0,则 x 方向上没有重叠,因此根本没有重叠。如果 oy 为 0,同上。因此,您需要找到仅使 ox oy 为零的向量。

if (ox > oy )
  return new Vector3f(0,dir.y*oy);//top-bottom collision
else if (ox < oy )
  return new Vector3f(dir.x*ox,0);//left-right collision
else //if(ox == oy)
  return new Vector3f(dir.x*ox,dir.y*oy); //corner-corner collision, 
                                          //unlikely with float's.
float ox = Math.abs(xAxis - cw); //overlap on x
float oy = Math.abs(yAxis - ch); //overlap on y

//direction
Vector2f dir = new Vector2f(this.position); 
dir.sub(other.getPosition()); //subtract other.position from this.position
dir.normalise();

return new Vector2f(dir.x * ox, dir.y * oy);

The returned translation vector will make both ox (overlap in x) and oy (overlap in y) zero. But that's not what you need. If ox is 0, there is no overlap in the x direction, thus no overlap at all. If oy is 0, idem. So, you need to find the vector that will make only ox or oy zero.

if (ox > oy )
  return new Vector3f(0,dir.y*oy);//top-bottom collision
else if (ox < oy )
  return new Vector3f(dir.x*ox,0);//left-right collision
else //if(ox == oy)
  return new Vector3f(dir.x*ox,dir.y*oy); //corner-corner collision, 
                                          //unlikely with float's.
不忘初心 2024-12-28 10:38:51

要解决“滑动问题”,请执行以下操作:(但是是 C# XNA 代码)

Vector2 direction = (this.Center - other.Center);
direction.Normalize();

direction.X = (int)Math.Round(direction.X);
direction.Y = (int)Math.Round(direction.Y);

首先,您将获得一个单位向量,其中包含您应该退出的方向。
但是,当你靠近角落时,按原样使用它是不可靠的。
舍入使最强值为 1,其他值为 0。

解释为什么需要舍入:

例如,假设 Y 轴上的两个对象中心相等,并且我们从左侧退出。方向单位向量是 (-1, 0),为我们提供了一个可靠的数字来乘以重叠。但是当你越来越接近形状的角时,数字会变成(-0.88,*)这个非整数,当相乘时会导致像素丢失并且你会陷入形状中。

我希望此后所做的解释对这些事情不要太过分。

顺便说一句,我是OP ^_^ ..感谢大家的帮助

To solve the "sliding problem" do this: (C# XNA code however)

Vector2 direction = (this.Center - other.Center);
direction.Normalize();

direction.X = (int)Math.Round(direction.X);
direction.Y = (int)Math.Round(direction.Y);

First you get a unit vector with the direction you should exit from.
But using that as it is, will be unreliable when you're near the corners.
the rounding makes the strongest value to be 1, and the other 0.

Explanation why the rounding is necessary:

For example say the two objects centers on the Y axis are equal and we are exiting left. The direction unit vector is (-1, 0) giving us a reliable number to multiply the overlap with. but as you get closer and closer to the corner of the shape the number turns more into (-0.88, *) that non-whole number, when multiplied leads to losing pixels and you sink into the shape.

I hope the explanation made since, not too great at these things.

By the way, I am the OP ^_^ ..thanks for the help everyone

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文