如何在SFML中生成无重叠的随机平台?——Doodle Jump类游戏开发
Hey there! Nice start on your Doodle Jump-style game—three platform types is such a fun touch. The overlapping issue makes total sense right now because you’re generating positions completely randomly without checking if they clash with existing platforms. Let’s fix that with collision checks during spawn.
The Core Idea
Every time you generate a new platform’s position, you need to:
- Calculate the bounding box of the new platform (accounting for its scaled Sprite size)
- Check if this box overlaps with the bounding boxes of all previously generated platforms (regardless of type)
- If it overlaps, generate a new position and repeat the check until you find a clear spot
Step 1: Add a Collision Check Helper Function
First, create a reusable function to check if two platforms overlap. This uses SFML’s FloatRect and intersects() method to handle the collision math for you:
#include <SFML/Graphics.hpp> #include <cstdlib> #include <ctime> // Assuming your point struct looks like this: struct point { int x, y; }; bool doPlatformsOverlap(const point& pPos, const sf::Sprite& pSprite, const point& otherPos, const sf::Sprite& otherSprite) { // Calculate the actual bounds of each platform (accounting for scale) sf::FloatRect platformBounds( pPos.x, pPos.y, pSprite.getLocalBounds().width * pSprite.getScale().x, pSprite.getLocalBounds().height * pSprite.getScale().y ); sf::FloatRect otherPlatformBounds( otherPos.x, otherPos.y, otherSprite.getLocalBounds().width * otherSprite.getScale().x, otherSprite.getLocalBounds().height * otherSprite.getScale().y ); return platformBounds.intersects(otherPlatformBounds); }
Step 2: Rewrite Platform Generation with Collision Checks
Modify your platform spawn loop to use the helper function and retry until a non-overlapping position is found. We’ll handle each platform type one by one, checking against all already spawned platforms:
int main() { srand(time(NULL)); sf::RenderWindow app(sf::VideoMode(600, 600), "Doodle Game!"); app.setFramerateLimit(60); sf::Texture t1,t2,t3,t4,t5; t1.loadFromFile("4.png"); t2.loadFromFile("platform.png"); t3.loadFromFile("cartoon.png"); t4.loadFromFile("rock.png"); t5.loadFromFile("wood.png"); sf::Sprite sBackground(t1), sPlat(t2), sPlat1(t4), sPlat2(t5), sPers(t3); sPers.setScale(0.3f, 0.3f); sPlat.setScale(0.06f, 0.06f); sPlat1.setScale(0.06f, 0.06f); sPlat2.setScale(0.06f, 0.06f); point plat[20]; point plat1[20]; point plat2[20]; // Generate regular platforms first for (int i=0;i<10;i++) { bool isOverlapping = true; while (isOverlapping) { // Generate a new random position plat[i].x = rand() % 600; plat[i].y = rand() % 600; isOverlapping = false; // Check against all previously spawned regular platforms for (int j = 0; j < i; j++) { if (doPlatformsOverlap(plat[i], sPlat, plat[j], sPlat)) { isOverlapping = true; break; // No need to check further—we found an overlap } } } } // Generate rock platforms (check against regular platforms and previous rocks) for (int i=0;i<10;i++) { bool isOverlapping = true; while (isOverlapping) { plat1[i].x = rand() % 600; plat1[i].y = rand() % 600; isOverlapping = false; // Check against regular platforms for (int j = 0; j < 10; j++) { if (doPlatformsOverlap(plat1[i], sPlat1, plat[j], sPlat)) { isOverlapping = true; break; } } // If no overlap with regulars, check against previous rocks if (!isOverlapping) { for (int j = 0; j < i; j++) { if (doPlatformsOverlap(plat1[i], sPlat1, plat1[j], sPlat1)) { isOverlapping = true; break; } } } } } // Generate wood platforms (check against regular, rocks, and previous woods) for (int i=0;i<10;i++) { bool isOverlapping = true; while (isOverlapping) { plat2[i].x = rand() % 600; plat2[i].y = rand() % 600; isOverlapping = false; // Check against regular platforms for (int j = 0; j < 10; j++) { if (doPlatformsOverlap(plat2[i], sPlat2, plat[j], sPlat)) { isOverlapping = true; break; } } // Check against rock platforms if (!isOverlapping) { for (int j = 0; j < 10; j++) { if (doPlatformsOverlap(plat2[i], sPlat2, plat1[j], sPlat1)) { isOverlapping = true; break; } } } // Check against previous wood platforms if (!isOverlapping) { for (int j = 0; j < i; j++) { if (doPlatformsOverlap(plat2[i], sPlat2, plat2[j], sPlat2)) { isOverlapping = true; break; } } } } } // Rest of your game loop goes here... while (app.isOpen()) { sf::Event e; while (app.pollEvent(e)) { if (e.type == sf::Event::Closed) app.close(); } app.clear(); app.draw(sBackground); // Draw all platforms and player here app.display(); } return 0; }
Optional: Clean Up with a Platform Container
For even cleaner code (especially if you add more platform types later), you could store all platforms in a single vector of a custom struct. This way you only need one check loop instead of multiple:
struct Platform { point pos; sf::Sprite sprite; }; std::vector<Platform> allPlatforms; // When spawning a new platform: bool isOverlapping; do { isOverlapping = false; Platform newPlat; newPlat.pos.x = rand() % 600; newPlat.pos.y = rand() % 600; newPlat.sprite = sPlat; // Or whichever sprite type you need // Check against all existing platforms for (const auto& plat : allPlatforms) { if (doPlatformsOverlap(newPlat.pos, newPlat.sprite, plat.pos, plat.sprite)) { isOverlapping = true; break; } } if (!isOverlapping) { allPlatforms.push_back(newPlat); } } while (isOverlapping);
This approach keeps your code scalable and easier to maintain as your game grows.
内容的提问来源于stack exchange,提问作者Zuzanna

