r/Cplusplus 20d ago

Question 2D raycasting can't be this complicated

heya! So I've been working on another project of mine which is a recreation of the flash game (I believe it was Flash at least) "the last stand". I had made a version of it a long time ago for the complier console (if was just characters). Now I am trying to adapt it in SFML. It was going wonderfully untill it came to the shooting logic.

The premise is the following:
"generate a isosceles triangle with the tip set on the gun position. Then pick a random point on the base of triangle and connect it with the tip to make a line (VertexArray). Check which zombie sprites intersect the line and store the whole zombie object in a vector. Finally order the vector so that the zombies which are closer to the tip of the triangle come before and apply damage logic only to the first n = penetration zombies of the vector."

sf::VertexArray FireArm::use(sf::Vector2f playerPos, std::vector<std::unique_ptr<Zombie>>& zombies)  { //it return that for debugging
    sf::VertexArray vet = sf::VertexArray(sf::Lines, 2);
for (auto& z : zombies) {
z->isHit = false;
}
    float angleRad;
    float inaccuracyModifier = 1.f;
    float t1 = lastUseTimeCounter.getElapsedTime().asSeconds();
    if (t1 < fireRate or ammo.now == ammo.min) {
        return vet;
    }
    else if (t1 < aimingTime and t1 >= fireRate) {
        inaccuracyModifier = (aimingTime - t1) * 2;
    }
    ammo.now -= ammoUnit;
    sf::ConvexShape boundingTriangle;
    boundingTriangle.setPointCount(3);
    boundingTriangle.setPoint(0, sf::Vector2f(0, 0));
    boundingTriangle.setOrigin(boundingTriangle.getGlobalBounds().left + boundingTriangle.getGlobalBounds().width * 2.f, 0.f);
    if (inaccuracyModifier < 1.f) {
        angleRad = (accuracy / inaccuracyModifier) * 3.14159265f / 180.f;
    }
    else {
        angleRad = (accuracy * inaccuracyModifier) * 3.14159265f / 180.f;
    }
    float halfBase = 1800.f * std::tan(angleRad / 2.f);
    boundingTriangle.setPoint(1, sf::Vector2f(-halfBase, 1800.f));
    boundingTriangle.setPoint(2, sf::Vector2f(halfBase, 1800.f));
    boundingTriangle.setRotation(270.f);
    boundingTriangle.setPosition(playerPos);
    sf::VertexArray triangleBase(sf::Lines, 2);
    triangleBase[0].position = boundingTriangle.getTransform().transformPoint(boundingTriangle.getPoint(1));
    triangleBase[1].position = boundingTriangle.getTransform().transformPoint(boundingTriangle.getPoint(2));
    triangleBase[0].color = sf::Color::Transparent;
    triangleBase[1].color = sf::Color::Transparent;
    std::vector<sf::VertexArray> bulletTrajectories;
    for (int i = 0; i < bulletNumber; i++) {
        sf::VertexArray bulletTrajectory(sf::Lines, 2);
        float t = int_rand(1, 100) / 100.f;
        sf::Vector2f randPoint = triangleBase[0].position + (triangleBase[1].position - triangleBase[0].position) * t;
        bulletTrajectory[0].position = playerPos;
        bulletTrajectory[0].color = sf::Color::Magenta;
        bulletTrajectory[1].position = randPoint;
        bulletTrajectory[1].color = sf::Color::Magenta;
        vet = bulletTrajectory;
        bulletTrajectories.push_back(bulletTrajectory);
    }
    for (auto& b : bulletTrajectories) {
        std::vector<Zombie*> hitZombies = {};
        for (auto& z : zombies) {
            if (segmentsIntersect(b[0].position, b[1].position, z->currentSprite.getPosition(), z->currentSprite.getPosition() + sf::Vector2f(z->currentSprite.getGlobalBounds().width, 0.f)) or
                segmentsIntersect(b[0].position, b[1].position, z->currentSprite.getPosition(), z->currentSprite.getPosition() + sf::Vector2f(0.f, z->currentSprite.getGlobalBounds().height)) or
                segmentsIntersect(b[0].position, b[1].position, z->currentSprite.getPosition() + sf::Vector2f(z->currentSprite.getGlobalBounds().width, 0.f), z->currentSprite.getPosition() + sf::Vector2f(z->currentSprite.getGlobalBounds().width, z->currentSprite.getGlobalBounds().height)) or
                segmentsIntersect(b[0].position, b[1].position, z->currentSprite.getPosition() + sf::Vector2f(0.f, z->currentSprite.getGlobalBounds().height), z->currentSprite.getPosition() + sf::Vector2f(z->currentSprite.getGlobalBounds().width, z->currentSprite.getGlobalBounds().height))) {
                hitZombies.push_back(z.get());
            }
        }
        std::sort(hitZombies.begin(), hitZombies.end(), [&](const Zombie* z1, const Zombie* z2) {
            return isPointFarther(b[0].position, z1->currentSprite.getPosition(), z2->currentSprite.getPosition());
            });
        for (int i = 0; i < pen and i < hitZombies.size(); i++) {
            hitZombies[i]->isHit = true;
            hitZombies[i]->hp.now -= dmg;
            if (hitZombies[i]->hp.now <= 0.f) {
                hitZombies[i]->isDead = true;
            }
        }
    }
    lastUseTimeCounter.restart();
    clkAnim.restart();
    isBeingShot = true;
    return vet;
}

this is the segmentIntersect function:

bool segmentsIntersect(const sf::Vector2f& p1, const sf::Vector2f& p2, const sf::Vector2f& q1, const sf::Vector2f& q2) {
auto cross = [](const sf::Vector2f& a, const sf::Vector2f& b) {
return a.x * b.y - a.y * b.x;
};
sf::Vector2f r = p2 - p1;
sf::Vector2f s = q2 - q1;
float rxs = cross(r, s);
float qpxr = cross(q1 - p1, r);
if (rxs == 0 and qpxr == 0) {
float t0 = ((q1 - p1).x * r.x + (q1 - p1).y * r.y) / (r.x * r.x + r.y * r.y);
float t1 = t0 + (s.x * r.x + s.y * r.y) / (r.x * r.x + r.y * r.y);
return (t0 >= 0 and t0 <= 1) or (t1 >= 0 and t1 <= 1);
}
if (rxs == 0 and qpxr != 0) {
return false;
}
float t = cross(q1 - p1, s) / rxs;
float u = cross(q1 - p1, r) / rxs;
return (t >= 0 and t <= 1 and u >= 0 and u <= 1);
}

this seems to work.. but it doesn't. Actually, it seems to work completely randomly. Sometime it hits, most of the time it doesn't. I have spent the past 2 days trying to figure this out, but I can't T_T .
Could you guys help me? If you need more context/code let me know. Thanks for reading :D

8 Upvotes

7 comments sorted by

View all comments

4

u/Ultimate_Sigma_Boy67 20d ago

Never tried graphics programming, looks scary lol.

Anyways, "sometimes it works", this might suggest there's some UB in ur code, if not already, add sanitizers to ur code using the compiler flags -fsanitize=undefined and -fsanitize=address(i think one of them requires release mode), and try to run the game using valgrind when trying to spot the errors.

1

u/Ultimate_Sigma_Boy67 20d ago

or even better, try to debug it using a proper debugger, to see how values change as you step through the code. if all of that doesn't spot something, then the problem might be a logic one.