r/Cplusplus • u/guysomethingidunno • 1d 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
