简体   繁体   English

二维 AABB 碰撞解决多角案例问题 C#

[英]2D AABB Collision Resolution Multiple Corner Case Issues C#

I've been at this for a few days, and I can't seem to get it perfect.我已经在这几天了,我似乎无法让它完美。 I'm making a top down procedurally generated adventure game, and my collision resolution system is leaving something to be desired.我正在制作一个自上而下的程序生成冒险游戏,我的碰撞解决系统还有一些不足之处。

The Problem:问题:

Attempting to move between 2 hitboxes where the gap normally would not let you enter, you can enter if you are moving diagonally, this can either cause an infinite loop where the collision is never removed from the list of collisionsToResolve (rare), or temporarily allows you to walk around freely inside a solid hitbox (most common effect).尝试在间隙通常不允许您进入的 2 个碰撞箱之间移动,如果您沿对角线移动,则可以进入,这可能会导致无限循环,其中碰撞永远不会从 collisionsToResolve 列表中删除(罕见),或者暂时允许您可以在坚固的碰撞箱内自由走动(最常见的效果)。

The strange part is that other corner cases resolve correctly, ie if there is no gap, you don't clip, and you can slide along any solid hitbox in any direction without "catching" or anything if there are 2 next to each other.奇怪的是,其他角落案例正确解决,即如果没有间隙,您不会剪辑,并且您可以在任何方向上沿着任何实心碰撞盒滑动而不会“捕捉”或任何其他情况(如果有 2 个彼此相邻)。 IN FACT this ONLY seems to occur when the gap you're trying to go through is exactly one pixel smaller than your hitbox, so what I'm guessing is happening is that when the collision is checked at the beginning of the frame, only one of the 2 hitboxes is being added, and when it resolves, it pushes you into the other one.事实上,这似乎只发生在你试图通过go的间隙正好比你的命中框小一个像素时,所以我猜正在发生的是,当在帧的开头检查碰撞时,只有一个正在添加 2 个碰撞箱中的一个,当它解决时,它会将您推入另一个碰撞箱。 I'm at a loss for how to combat this, so any help is much appreciated.我不知道如何解决这个问题,所以非常感谢任何帮助。

Video demonstrating the issues:演示问题的视频:

https://youtu.be/jARoEfy9u2Q https://youtu.be/jARoEfy9u2Q

Code for collision resolution:碰撞解决代码:

public void Collide(float newX, float newY, List<GameObject> colObj, int seed, GraphicsDeviceManager graphics, Player p) {

            //newx is intermediatePosition + float value of controller left stick X
            //newy "                                                            " Y
            //colObj is a list of gameobjects that are collidable that are within a certain distance to the player (possible collisions)

            var fr = new Rectangle((int)Math.Round(newX) + p.bbofsx, (int)Math.Round(newY) + p.bbofsy, p.boundingBox.Width, p.boundingBox.Height);

            for (int i = 0; i < colObj.Count; i++) {
                if (fr.Intersects(colObj[i].boundingBox)) {
                    colObj[i].distanceToPlayer = (int)AABB.GetDistanceSquared(fr, colObj[i].boundingBox);
                    p.collisionsToResolve.Add(colObj[i]);
                }
            }

            //order list
            p.collisionsToResolve = p.collisionsToResolve.OrderBy(o => o.distanceToPlayer).ToList();

            for (int w = 0; w < p.collisionsToResolve.Count; w++) {

                //overworld collision code
                    var side = AABB.GetCollisionSide(p.boundingBox, p.collisionsToResolve[w].boundingBox, new Vector2((float)Math.Round(newX) - p.worldPosition.X, (float)Math.Round(newY) - p.worldPosition.Y));

                    switch (side) {
                        case CollisionSide.Left:
                            while (fr.Intersects(p.collisionsToResolve[w].boundingBox)) {
                                newX--;
                                p.intermediateWorldPosition.X = p.worldPosition.X;
                                fr = new Rectangle(p.boundingBox.X + (int)((float)Math.Round(newX) - p.worldPosition.X), p.boundingBox.Y + (int)((float)Math.Round(newY) - p.worldPosition.Y), p.boundingBox.Width, p.boundingBox.Height);
                            }
                            break;
                        case CollisionSide.Right:
                            while (fr.Intersects(p.collisionsToResolve[w].boundingBox)) {
                                newX++;
                                p.intermediateWorldPosition.X = p.worldPosition.X;
                                fr = new Rectangle(p.boundingBox.X + (int)((float)Math.Round(newX) - p.worldPosition.X), p.boundingBox.Y + (int)((float)Math.Round(newY) - p.worldPosition.Y), p.boundingBox.Width, p.boundingBox.Height);
                            }
                            break;
                        case CollisionSide.Top:
                            while (fr.Intersects(p.collisionsToResolve[w].boundingBox)) {
                                newY--;
                                p.intermediateWorldPosition.Y = p.worldPosition.Y;
                                fr = new Rectangle(p.boundingBox.X + (int)((float)Math.Round(newX) - p.worldPosition.X), p.boundingBox.Y + (int)((float)Math.Round(newY) - p.worldPosition.Y), p.boundingBox.Width, p.boundingBox.Height);
                            }
                            break;
                        case CollisionSide.Bottom:
                            while (fr.Intersects(p.collisionsToResolve[w].boundingBox)) {
                                newY++;
                                p.intermediateWorldPosition.Y = p.worldPosition.Y;
                                fr = new Rectangle(p.boundingBox.X + (int)((float)Math.Round(newX) - p.worldPosition.X), p.boundingBox.Y + (int)((float)Math.Round(newY) - p.worldPosition.Y), p.boundingBox.Width, p.boundingBox.Height);
                            }
                            break;
                    }

                p.UpdateBoundingBox();

                for (int i = 0; i < colObj.Count; i++) {
                    if (fr.Intersects(colObj[i].boundingBox)) {
                        p.collisionsToResolve.RemoveAt(w);
                        colObj[i].distanceToPlayer = (int)AABB.GetDistanceSquared(p.boundingBox, colObj[i].boundingBox);
                        p.collisionsToResolve.Add(colObj[i]);
                        p.collisionsToResolve = p.collisionsToResolve.OrderBy(o => o.distanceToPlayer).ToList();
                        w = 0;
                    }
                }

                var done = true;
                for (int i = 0; i < p.collisionsToResolve.Count; i++) {
                    if (fr.Intersects(p.collisionsToResolve[i].boundingBox)) {
                        done = false;
                        break;
                    }
                }

                if (done) {
                    break;
                }
            }
            //move player and update bounding box

            p.worldPosition.X = (float)Math.Round(newX);
            p.worldPosition.Y = (float)Math.Round(newY);

            p.UpdateBoundingBox();
        }

In case this helps anyone else, I figured out the way to solve it, instead of trying to resolve the collision at the new position (x and y), first resolve at the new x position, then the y position (or vice versa).万一这对其他人有帮助,我想出了解决它的方法,而不是尝试解决新 position(x 和 y)的碰撞,首先解决新的 x position,然后是 y Z4757FE07FD492A8BE0EA6A76(反之亦然) . You can choose which axis to resolve first dynamically based on the positions of the 2 bounding boxes.您可以根据 2 个边界框的位置选择首先动态解析哪个轴。

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

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