0

I am coding a small game in Raylib with C++ and I am working on a collision/hitbox system for the player and the other game objects. The code I have works with the player and one other object, but not with the other objects. If I remove the object that the player currently collides with, the player now will collide with another object but not with the others. So basically, the player only collides with one objects regardless of what or how many objects there are.

Code so far:

#include <vector>
#include <iostream>
#include <unordered_map>
#include <string>
#include "raylib.h"

class Entity
{
public:
    std::string id;
    Entity(std::string id)
        : id(id)
    {
    }
};

class Component
{
public:
    Entity entity;
    Component(Entity entity)
        : entity(entity)
    {
    }
};
class Renderable : public Component
{
public:
    Rectangle spriteRectangle;
    Texture2D spriteSheet;
    Renderable(Entity entity, Rectangle spriteRectangle, Texture2D spriteSheet)
        : Component(entity)
    {
        this->spriteRectangle = spriteRectangle;
        this->spriteSheet = spriteSheet;
    }
};

class Transform2D : public Component
{
public:
    double x, y, width, height;
    double directionDegrees;
    Transform2D(Entity entity, double x, double y, double width, double height)
        : Component(entity)
    {
        this->x = x;
        this->y = y;
        this->width = width;
        this->height = height;
    }
};

class BoxCollider : public Component
{
public:
    bool isColliding;
    bool leftCollisionDetected = false, rightCollisionDetected = false, topCollisionDetected = false, bottomCollisionDetected = false;
    BoxCollider(Entity entity)
        : Component(entity)
    {
    }
};

void Setup()
{
    InitWindow(800, 450, "Monster Collector");
    SetWindowSize(GetMonitorWidth(GetCurrentMonitor()), GetMonitorHeight(GetCurrentMonitor()));
    // ToggleFullscreen();
}

void FourDirectionController(Transform2D *transform, BoxCollider *boxCollider, double speed, std::unordered_map<std::string, Transform2D *> transforms)
{
    double speedAdjusted = speed * GetFrameTime();

    Rectangle topRectangle = {transform->x, transform->y, transform->width, 1};
    Rectangle bottomRectangle = {transform->x, transform->y + transform->height, transform->width, 1};
    Rectangle rightRectangle = {transform->x + transform->width - 1, transform->y, 1, transform->height};
    Rectangle leftRectangle = {transform->x, transform->y, 1, transform->height};

    for (auto const &[id, targetTransform] : transforms)
    {
        if (transform == targetTransform)
        {
            continue;
        }

        Rectangle targetRectangle = {targetTransform->x, targetTransform->y, targetTransform->width, targetTransform->height};
        boxCollider->topCollisionDetected = CheckCollisionRecs(topRectangle, targetRectangle);
        boxCollider->bottomCollisionDetected = CheckCollisionRecs(bottomRectangle, targetRectangle);
        boxCollider->rightCollisionDetected = CheckCollisionRecs(rightRectangle, targetRectangle);
        boxCollider->leftCollisionDetected = CheckCollisionRecs(leftRectangle, targetRectangle);
    }

    if (IsKeyDown(KEY_W) && !boxCollider->topCollisionDetected)
    {

        transform->y -= speedAdjusted;
        transform->directionDegrees = 0;
    }
    if (IsKeyDown(KEY_S) && !boxCollider->bottomCollisionDetected)
    {
        transform->y += speedAdjusted;
        transform->directionDegrees = 180;
    }
    if (IsKeyDown(KEY_D) && !boxCollider->rightCollisionDetected)
    {
        transform->x += speedAdjusted;
        transform->directionDegrees = 90;
    }
    if (IsKeyDown(KEY_A) && !boxCollider->leftCollisionDetected)
    {
        transform->x -= speedAdjusted;
        transform->directionDegrees = 270;
    }
}

void Render(std::unordered_map<std::string, Renderable *> renderables, std::unordered_map<std::string, Transform2D *> transforms)
{
    for (const auto &element : renderables)
    {
        std::string id = element.first;
        Renderable *renderable = element.second;
        Transform2D *transform = transforms[id];
        DrawTextureRec(renderable->spriteSheet, renderable->spriteRectangle, (Vector2){(float)transform->x, (float)transform->y}, WHITE);
        Rectangle rec1 = {transform->x, transform->y, transform->width, transform->height};
        Rectangle top = {transform->x, transform->y, transform->width, 1};
        Rectangle bottom = {transform->x, transform->y + transform->height, transform->width, 1};
        Rectangle right = {transform->x + transform->width - 1, transform->y, 1, transform->height};
        Rectangle left = {transform->x, transform->y, 1, transform->height};
        DrawRectangleRec(top, PURPLE);
        DrawRectangleRec(bottom, PURPLE);
        DrawRectangleRec(right, PURPLE);
        DrawRectangleRec(left, PURPLE);
    }
}

template <typename T>
void ClearComponentMap(std::unordered_map<std::string, T *> components)
{
    for (const std::pair<std::string, T *> &element : components)
    {
        delete element.second;
    }
}

int main(void)
{

    Setup();
    // SetConfigFlags(FLAG_VSYNC_HINT);

    std::vector<Texture2D> spriteSheets;

    Texture2D tileset = LoadTexture("master-tileset.png");
    spriteSheets.push_back(tileset);

    std::unordered_map<std::string, Renderable *> renderables;
    std::unordered_map<std::string, Transform2D *> transforms;
    std::unordered_map<std::string, BoxCollider *> boxColliders;

    Entity player("player");
    renderables["player"] = new Renderable(player, Rectangle{64, 64, 64, 64}, spriteSheets.at(0));
    transforms["player"] = new Transform2D(player, 300, 563, 64, 64);
    boxColliders["player"] = new BoxCollider(player);

    Entity grunt("grunt");
    renderables["grunt"] = new Renderable(grunt, Rectangle{256, 64, 64, 64}, spriteSheets.at(0));
    transforms["grunt"] = new Transform2D(grunt, 100, 500, 64, 64);
    boxColliders["grunt"] = new BoxCollider(grunt);

    Entity wall("wall");
    renderables["wall"] = new Renderable(wall, Rectangle{0, 0, 64, 64}, spriteSheets.at(0));
    transforms["wall"] = new Transform2D(wall, 0, 100, 64, 64);
    boxColliders["wall"] = new BoxCollider(wall);

    Entity block("block");
    renderables["block"] = new Renderable(block, Rectangle{0, 256, 64, 64}, spriteSheets.at(0));
    transforms["block"] = new Transform2D(block, 300, 500, 64, 64);
    boxColliders["block"] = new BoxCollider(block);

    Entity wall2("wall2");
    renderables["wall2"] = new Renderable(wall2, Rectangle{128, 0, 64, 64}, spriteSheets.at(0));
    transforms["wall2"] = new Transform2D(wall2, 64, 100, 64, 64);
    boxColliders["wall2"] = new BoxCollider(wall2);

    Entity wall3("wall3");
    renderables["wall3"] = new Renderable(wall3, Rectangle{128, 0, 64, 64}, spriteSheets.at(0));
    transforms["wall3"] = new Transform2D(wall3, 128, 100, 64, 64);
    boxColliders["wall3"] = new BoxCollider(wall3);

    BoxCollider *playerBox = boxColliders["player"];
    Transform2D *playT = transforms["player"];

    while (!WindowShouldClose())
    {

        BeginDrawing();
        ClearBackground(RAYWHITE);
        Render(renderables, transforms);
        FourDirectionController(playT, playerBox, 500, transforms);
        EndDrawing();
    }

    ClearComponentMap<Renderable>(renderables);
    ClearComponentMap<Transform2D>(transforms);
    ClearComponentMap<BoxCollider>(boxColliders);

    CloseWindow();

    return 0;
}

My code is loosely structured around the idea of Entity-Component-Systems. So I have components stored in unordered maps with the key being an "entity id" and the value being a pointer to the component object. The function FourDirectionController is where I have the movement and collision code. If you already have Raylib and C++ on your computer, you could probably run this if you change the LoadTexture function paramater to this: master-tileset.png

I have tried a lot and been stuck for 3 days. Originally the collision code was a seperate function that actually tried to find collision for every object, but now it only tries to find collision for the player specfically. I have tried changing the data structure to an ordered map. I have tried a lot of logging and GDB to step through my function. It seems like my collisions flags are set true when the collision happens but doesn't change in the input (this was when my collision and movement were in seperate functions).

  • Unless I'm mis-reading your code, you loop over all the transforms checking for collisions, but then only use the result of the last transform. i.e. you loop over them all setting `boxCollider->topCollisionDetected` for each, and by the end of the loop, you have the results from the last one. – pmacfarlane Jul 25 '23 at 16:35

0 Answers0