最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - Collision doesn't work under very specific circumstances - Stack Overflow

programmeradmin1浏览0评论

This is my player's update function:

void update() {
    IsOnWall = false;
    IsOnFloor = false;
    int collide[4] = { collidelist(Rect{pos.x, pos.y + vel.y, hitbox.width, vel.y + 1}, Stillbodies),
                        collidelist(Rect{pos.x, pos.y + hitbox.height, hitbox.width, vel.y - 1}, Stillbodies),
                        collidelist(Rect{pos.x + vel.x, pos.y + 1, vel.x - 1, hitbox.height - 1}, Stillbodies),
                        collidelist(Rect{pos.x + hitbox.width, pos.y + 1, vel.x + 1, hitbox.height - 1}, Stillbodies) };
    if (collide[0] >= 0 && vel.y <= 0) {
        vel.y = 0;
        pos.y = Stillbodies[collide[0]].y + Stillbodies[collide[0]].height;
        IsOnFloor = true;
    }
    if (collide[1] >= 0 && vel.y > 0) {
        vel.y = 0;
        pos.y = Stillbodies[collide[1]].y - Stillbodies[collide[1]].height;
    }
    if (collide[2] >= 0 && vel.x <= 0) {
        vel.x = 0;
        pos.x = Stillbodies[collide[2]].x + Stillbodies[collide[2]].width;
        IsOnWall = true;
    }
    if (collide[3] >= 0 && vel.x > 0) {
        vel.x = 0;
        pos.x = Stillbodies[collide[3]].x - hitbox.width;
        IsOnWall = true;
    }
    IsOnWallOnly = (IsOnWall && !IsOnFloor);
    IsOnFloorOnly = (IsOnFloor && !IsOnWall);
    pos.x += vel.x;
    pos.y += vel.y;
    hitbox.x = pos.x;
    hitbox.y = pos.y;
}

The if collide[0] part works most of the time, but if my player's y velocity is exactly the height of the object it's colliding with, it clips the player in for 1 frame. At least, that's what I've found so far. All the other ifs work perfect, even under the same circumstances.

Here is a minimal reproducible example:

Code

#include <iostream>
#include <deque>
class Rect {
public:
    int x;
    int y;
    int width;
    int height;
    Rect(int X, int Y, int WIDTH, int HEIGHT) {
        x = X;
        y = Y;
        width = WIDTH;
        height = HEIGHT;
    }
};
class Spot {
public:
    int x;
    int y;
    Spot(int x1, int y1) {
        x = x1;
        y = y1;
    }
};
bool colliderect(Rect rect1, Rect rect2) {
    return (rect1.x < rect2.x + rect2.width &&
        rect1.x + rect1.width > rect2.x &&
        rect1.y < rect2.y + rect2.height &&
        rect1.y + rect1.height > rect2.y);
}
int collidelist(Rect rect, std::deque<Rect> rects) {
    for (float rec = 0; rec < rects.size(); rec++) {
        if (colliderect(rect, rects[rec])) {
            return rec;
        }
    }
    return -1;
}
std::deque<Rect> Stillbodies = { {0, 0, 50, 50} };
int main() {
    Spot pos = Spot(0, 50);
    Rect hitbox = Rect(pos.x, pos.y, 50, 50);
    Spot vel = Spot(0, 0);
    while (true) {
        char move;
        std::cin >> move;
        if (move == 'u') {
            vel.y = -50;
        }
        else if (move == 'd') {
            vel.y = 50;
        }
        else {
            vel.y = 0;
        }
        int collide[4] = { collidelist(Rect{pos.x, pos.y + vel.y, hitbox.width, vel.y + 1}, Stillbodies),
                                collidelist(Rect{pos.x, pos.y + hitbox.height, hitbox.width, vel.y - 1}, Stillbodies),
                                collidelist(Rect{pos.x + vel.x, pos.y + 1, vel.x - 1, hitbox.height - 1}, Stillbodies),
                                collidelist(Rect{pos.x + hitbox.width, pos.y + 1, vel.x + 1, hitbox.height - 1}, Stillbodies) };
        //broken collision (other ones that look same work)
        if (collide[0] >= 0 && vel.y <= 0) {
            vel.y = 0;
            pos.y = Stillbodies[collide[0]].y + Stillbodies[collide[0]].height;
        }
        //this one works
        if (collide[1] >= 0 && vel.y > 0) {
            vel.y = 0;
            pos.y = Stillbodies[collide[1]].y - hitbox.height;
        }
        pos.x += vel.x;
        pos.y += vel.y;
        hitbox.x = pos.x;
        hitbox.y = pos.y;
        std::cout << pos.x << " " << pos.y << std::endl;
    }
    return 0;
}

Input/Output (with added comments)

n //n is just an example for nothing
0 50 //nothing happens. wow
u // u for up
0 0 //you go up but the block is there
n //nothing
0 50 //it pushes you out
u //up
0 0 // same outcome
u //up
0 -50 // now above block, yo can waltz through in the actual game too
d //down
0 -50 // collision working
d //down
0 -50 //double checking

This is my player's update function:

void update() {
    IsOnWall = false;
    IsOnFloor = false;
    int collide[4] = { collidelist(Rect{pos.x, pos.y + vel.y, hitbox.width, vel.y + 1}, Stillbodies),
                        collidelist(Rect{pos.x, pos.y + hitbox.height, hitbox.width, vel.y - 1}, Stillbodies),
                        collidelist(Rect{pos.x + vel.x, pos.y + 1, vel.x - 1, hitbox.height - 1}, Stillbodies),
                        collidelist(Rect{pos.x + hitbox.width, pos.y + 1, vel.x + 1, hitbox.height - 1}, Stillbodies) };
    if (collide[0] >= 0 && vel.y <= 0) {
        vel.y = 0;
        pos.y = Stillbodies[collide[0]].y + Stillbodies[collide[0]].height;
        IsOnFloor = true;
    }
    if (collide[1] >= 0 && vel.y > 0) {
        vel.y = 0;
        pos.y = Stillbodies[collide[1]].y - Stillbodies[collide[1]].height;
    }
    if (collide[2] >= 0 && vel.x <= 0) {
        vel.x = 0;
        pos.x = Stillbodies[collide[2]].x + Stillbodies[collide[2]].width;
        IsOnWall = true;
    }
    if (collide[3] >= 0 && vel.x > 0) {
        vel.x = 0;
        pos.x = Stillbodies[collide[3]].x - hitbox.width;
        IsOnWall = true;
    }
    IsOnWallOnly = (IsOnWall && !IsOnFloor);
    IsOnFloorOnly = (IsOnFloor && !IsOnWall);
    pos.x += vel.x;
    pos.y += vel.y;
    hitbox.x = pos.x;
    hitbox.y = pos.y;
}

The if collide[0] part works most of the time, but if my player's y velocity is exactly the height of the object it's colliding with, it clips the player in for 1 frame. At least, that's what I've found so far. All the other ifs work perfect, even under the same circumstances.

Here is a minimal reproducible example:

Code

#include <iostream>
#include <deque>
class Rect {
public:
    int x;
    int y;
    int width;
    int height;
    Rect(int X, int Y, int WIDTH, int HEIGHT) {
        x = X;
        y = Y;
        width = WIDTH;
        height = HEIGHT;
    }
};
class Spot {
public:
    int x;
    int y;
    Spot(int x1, int y1) {
        x = x1;
        y = y1;
    }
};
bool colliderect(Rect rect1, Rect rect2) {
    return (rect1.x < rect2.x + rect2.width &&
        rect1.x + rect1.width > rect2.x &&
        rect1.y < rect2.y + rect2.height &&
        rect1.y + rect1.height > rect2.y);
}
int collidelist(Rect rect, std::deque<Rect> rects) {
    for (float rec = 0; rec < rects.size(); rec++) {
        if (colliderect(rect, rects[rec])) {
            return rec;
        }
    }
    return -1;
}
std::deque<Rect> Stillbodies = { {0, 0, 50, 50} };
int main() {
    Spot pos = Spot(0, 50);
    Rect hitbox = Rect(pos.x, pos.y, 50, 50);
    Spot vel = Spot(0, 0);
    while (true) {
        char move;
        std::cin >> move;
        if (move == 'u') {
            vel.y = -50;
        }
        else if (move == 'd') {
            vel.y = 50;
        }
        else {
            vel.y = 0;
        }
        int collide[4] = { collidelist(Rect{pos.x, pos.y + vel.y, hitbox.width, vel.y + 1}, Stillbodies),
                                collidelist(Rect{pos.x, pos.y + hitbox.height, hitbox.width, vel.y - 1}, Stillbodies),
                                collidelist(Rect{pos.x + vel.x, pos.y + 1, vel.x - 1, hitbox.height - 1}, Stillbodies),
                                collidelist(Rect{pos.x + hitbox.width, pos.y + 1, vel.x + 1, hitbox.height - 1}, Stillbodies) };
        //broken collision (other ones that look same work)
        if (collide[0] >= 0 && vel.y <= 0) {
            vel.y = 0;
            pos.y = Stillbodies[collide[0]].y + Stillbodies[collide[0]].height;
        }
        //this one works
        if (collide[1] >= 0 && vel.y > 0) {
            vel.y = 0;
            pos.y = Stillbodies[collide[1]].y - hitbox.height;
        }
        pos.x += vel.x;
        pos.y += vel.y;
        hitbox.x = pos.x;
        hitbox.y = pos.y;
        std::cout << pos.x << " " << pos.y << std::endl;
    }
    return 0;
}

Input/Output (with added comments)

n //n is just an example for nothing
0 50 //nothing happens. wow
u // u for up
0 0 //you go up but the block is there
n //nothing
0 50 //it pushes you out
u //up
0 0 // same outcome
u //up
0 -50 // now above block, yo can waltz through in the actual game too
d //down
0 -50 // collision working
d //down
0 -50 //double checking
Share Improve this question edited Nov 21, 2024 at 21:15 mkrieger1 23.3k7 gold badges64 silver badges81 bronze badges asked Nov 21, 2024 at 6:47 jg1121jg1121 254 bronze badges 9
  • Do you consider touching rectangles a collision? Or only if they overlap? (Not targeting a problem, just for my easier understanding.) – Yunnosch Commented Nov 21, 2024 at 7:15
  • 1 I propose to improve your (already good-looking) MRE by hardcoding the inputs instead of telling us what they are (e.g. by calling helper functions like moveup(), moveleft()). Consider also adding some output of interesting variables. Your venting ("grr") I totally understand and do not consider them a problem in your question, but they might be perceived differently by other users. – Yunnosch Commented Nov 21, 2024 at 7:22
  • 1 I kind of miss, inside the second if within update() something like IsAtCeil, just to complete the pattern. Please explain why it is not there. I do see by the way that floor and wall do not have an influence, it just triggers my desire for complete patterns.... – Yunnosch Commented Nov 21, 2024 at 7:33
  • 1 Better focus on your question now. Improve it according to How to Ask. Make a MRE without unneeded future extension points. That does not exclude structure like helper functions. I recommend not to show functions you do not call. And maybe you do not focus on the one thing I said is unimportant in my eyes. – Yunnosch Commented Nov 21, 2024 at 20:38
  • 2 Please post the solution as an answer, not as part of the question. – mkrieger1 Commented Nov 21, 2024 at 21:14
 |  Show 4 more comments

1 Answer 1

Reset to default 0

I found out the issue; When the velocity is negative, I wasn't flipping it yet was accounting for it as if it was negative.

collidelist(Rect{pos.x, pos.y + vel.y, hitbox.width, vel.y + 1}, Stillbodies),//issue here
                    collidelist(Rect{pos.x, pos.y + hitbox.height, hitbox.width, vel.y - 1}, Stillbodies),
                    collidelist(Rect{pos.x + vel.x, pos.y + 1, vel.x - 1, hitbox.height - 1}, Stillbodies),
                    collidelist(Rect{pos.x + hitbox.width, pos.y + 1, vel.x + 1, hitbox.height - 1}, Stillbodies) };

I changed it to this

{ collidelist(Rect{pos.x, pos.y + vel.y, hitbox.width, -vel.y + 1}, Stillbodies)_____________________________________________________^ Negative velocity

发布评论

评论列表(0)

  1. 暂无评论