Implemented procedural world generation

This commit is contained in:
Maximilian Giller 2023-07-10 01:46:58 +02:00
parent f50a346e22
commit 851e591c3c
11 changed files with 223 additions and 7 deletions

View file

@ -138,7 +138,7 @@ set(SOURCES
src/typography/font_manager.cpp
src/typography/font_manager.hpp
src/screens/winner_screen.cpp
src/screens/winner_screen.hpp src/screens/screen.cpp src/screens/screen.hpp src/screens/join_screen.cpp src/screens/join_screen.hpp)
src/screens/winner_screen.hpp src/screens/screen.cpp src/screens/screen.hpp src/screens/join_screen.cpp src/screens/join_screen.hpp src/game/level/level_generator.cpp src/game/level/level_generator.hpp src/screens/loading_screen.cpp src/screens/loading_screen.hpp)
set(PHYSICS_00_SOURCES
src/prototypes/physics_00.cpp)

View file

@ -18,7 +18,7 @@
// World
#define WORLD_GRAVITY b2Vec2(0.f, -9.8f)
#define SKY_HEIGHT_SCALE 2.f
#define SKY_HEIGHT_SCALE 5.f
#define CONSIDER_COLLECTABLE_DEPTH_MOVEMENT false // Might cost perfomance and is currently not in use
// FPS
@ -47,7 +47,7 @@
#define DEF_TV_MIN_VIEW_SIZE sf::Vector2f(6, 6) * WORLD_TO_ISO_SCALE
#define DEF_TV_MAX_VIEW_SIZE sf::Vector2f(0, 0)
#define DEF_TV_IS_ABSOLUTE_VIEW_SIZE_PADDING false
#define DEF_TV_VIEW_SIZE_PADDING sf::Vector2f(3.5f, 3.5f)
#define DEF_TV_VIEW_SIZE_PADDING sf::Vector2f(2.f, 2.f)
#define MP_VIEW_ADD_NEW_PLAYERS true
#define MP_VIEW_REMOVE_DISCONNECTED_PLAYERS true
#define MP_VIEW_BORDER_COLOR sf::Color::Black

View file

@ -10,6 +10,8 @@
#include "player/player_collection.hpp"
#include "../screens/winner_screen.hpp"
#include "../screens/join_screen.hpp"
#include "level/level_generator.hpp"
#include "../screens/loading_screen.hpp"
Game::Game(std::shared_ptr<sf::RenderWindow> window) : window(std::move(window))
{
@ -223,3 +225,26 @@ void Game::showJoinScreen()
addGameObject(GlobalLayer::getInstance());
addGameObject(PlayerCollection::getInstance());
}
void Game::generateNewLevel()
{
showLoadingScreen("Generating new level ...");
LevelConfig newLevel = LevelGenerator::generateLevel("Procedural Level");
LevelLoader::loadLevel(newLevel);
}
void Game::showLoadingScreen(const std::string &message)
{
GlobalLayer::getInstance()->clear();
clearGameObjects();
auto loadingScreen = std::make_shared<LoadingScreen>(message);
GlobalLayer::getInstance()->add(loadingScreen);
addGameObject(GlobalLayer::getInstance());
// Mini loop
update();
drawFrame();
}

View file

@ -41,6 +41,8 @@ public:
void showJoinScreen();
void generateNewLevel();
private:
static inline std::shared_ptr<Game> singletonInstance = nullptr;
std::vector<std::shared_ptr<GameObject>> gameObjects = {};
@ -58,6 +60,8 @@ private:
std::shared_ptr<Player> checkForWinner();
void handleWinning();
void showLoadingScreen(const std::string &message = "Loading...");
};

View file

@ -0,0 +1,134 @@
#include "level_generator.hpp"
LevelConfig LevelGenerator::generateLevel(const std::string &name)
{
int levelSize = 50;
TileMapConfig map = generateRandomMap(levelSize, levelSize);
std::vector<CollectableInLevel> collectables = generateRandomCollectables(levelSize, levelSize);
return LevelConfig(name,
120,
{
{1, 2},
{2, 1},
{0, 3},
{3, 0}
},
collectables,
{
// Blues
sf::Color(2, 100, 234),
sf::Color(2, 100, 234),
sf::Color(2, 100, 234),
sf::Color(2, 195, 234),
// Neutral
sf::Color::White,
// Browns
sf::Color(163, 128, 68),
sf::Color(100, 80, 40),
sf::Color(20, 18, 11),
sf::Color::Black
},
map
);
}
TileMapConfig LevelGenerator::generateRandomMap(int width, int height)
{
int tileVariants = 6 * 2;
std::vector<std::vector<int>> tiles;
for (int h = 0; h < height; h++)
{
std::vector<int> row;
for (int w = 0; w < width; w++)
{
row.push_back(rand() % tileVariants);
}
tiles.push_back(row);
}
return TileMapConfig("iso-tiles", tiles);
}
std::vector<CollectableInLevel> LevelGenerator::generateRandomCollectables(int width, int height)
{
std::vector<CollectableInLevel> collectables;
// Add rose fields
for (int i = 0; i < 20; i++)
{
// Random coordinates for center of rose field with enough distance from edges
int x = rand() % int(width - 2 * roseFieldRadius) + roseFieldRadius;
int y = rand() % int(height - 2 * roseFieldRadius) + roseFieldRadius;
GridCoordinates fieldCenter = GridCoordinates(x, y);
// Add rose field by creating a dense field of roses around the center
int roseCount = rand() % 15 + 5;
for (int j = 0; j < roseCount; j++)
{
// Use cosine distribution to get a more dense field around the center
float angle = rand() % 360;
float radius = rand() % 1000 / 1000.f;
// Skew radius to avoid too dense at center
radius = pow(radius, 0.3f) * roseFieldRadius;
float xOffset = radius * cos(angle);
float yOffset = radius * sin(angle);
GridCoordinates roseCoordinates = GridCoordinates(fieldCenter.x + xOffset, fieldCenter.y + yOffset);
collectables.push_back(CollectableInLevel("rose", roseCoordinates));
}
}
// Place rows of lanterns
int rowCount = rand() % 3 + 2;
int heightDelta = height / (rowCount + 1);
int stepSize = 7;
for (int i = 0; i < rowCount; i++)
{
int y = (i + 1) * heightDelta;
for (int j = stepSize; j < width; j += stepSize)
{
collectables.push_back(CollectableInLevel("lantern", GridCoordinates(j, y)));
}
}
// Place random bikes
int bikeCount = rand() % 5 + 5;
for (int i = 0; i < bikeCount; i++)
{
int x = rand() % width;
int y = rand() % height;
collectables.push_back(CollectableInLevel("bike", GridCoordinates(x, y)));
}
// Place random rose bushes
int roseBushCount = rand() % 40 + 40;
for (int i = 0; i < roseBushCount; i++)
{
int x = rand() % width;
int y = rand() % height;
collectables.push_back(CollectableInLevel("rose-bush", GridCoordinates(x, y)));
}
// Place random stones
int stoneCount = rand() % 20 + 20;
for (int i = 0; i < stoneCount; i++)
{
int x = rand() % width;
int y = rand() % height;
collectables.push_back(CollectableInLevel("stone", GridCoordinates(x, y)));
}
// Place a single tree
int x = rand() % width;
int y = rand() % height;
collectables.push_back(CollectableInLevel("small-tree", GridCoordinates(x, y)));
return collectables;
}

View file

@ -0,0 +1,21 @@
#ifndef HOLESOME_LEVEL_GENERATOR_HPP
#define HOLESOME_LEVEL_GENERATOR_HPP
#include "level_config.hpp"
class LevelGenerator
{
public:
static LevelConfig generateLevel(const std::string& name);
static TileMapConfig generateRandomMap(int width, int height);
static std::vector<CollectableInLevel> generateRandomCollectables(int width, int height);
private:
static const inline float roseFieldRadius = 3.f;
};
#endif //HOLESOME_LEVEL_GENERATOR_HPP

View file

@ -9,7 +9,7 @@
std::map<std::string, LevelConfig> const all_levels = {
{"default", LevelConfig("Default",
5,
120,
{
{0, 0},
{18, 18},

View file

@ -4,6 +4,7 @@
#include "../game/player/player_collection.hpp"
#include "../levels.hpp"
#include "../game/level/level_loader.hpp"
#include "../game/game.h"
JoinScreen::JoinScreen()
{
@ -39,7 +40,7 @@ void JoinScreen::update()
if (input->isPerformingAction(GameAction::CONFIRM))
{
// Start game
LevelLoader::loadLevel(INITIAL_LEVEL);
Game::getInstance()->generateNewLevel();
return;
}
}

View file

@ -0,0 +1,17 @@
#include <SFML/Graphics/Text.hpp>
#include "loading_screen.hpp"
#include "../typography/font_manager.hpp"
#include "../config.h"
LoadingScreen::LoadingScreen(const std::string &message)
{
auto text = std::make_shared<sf::Text>();
text->setString(message);
text->setFont(*FontManager::getInstance()->getDefaultFont());
text->setCharacterSize(24);
text->setStyle(sf::Text::Italic);
text->setFillColor(sf::Color::White);
text->setPosition(REFERENCE_SIZE.x / 2.f - text->getGlobalBounds().width / 2.f,
REFERENCE_SIZE.y / 2.f - text->getGlobalBounds().height / 2.f);
add(text);
}

View file

@ -0,0 +1,14 @@
#ifndef HOLESOME_LOADING_SCREEN_HPP
#define HOLESOME_LOADING_SCREEN_HPP
#include "screen.hpp"
class LoadingScreen : public Screen
{
public:
explicit LoadingScreen(const std::string &message = "Loading...");
};
#endif //HOLESOME_LOADING_SCREEN_HPP

View file

@ -9,7 +9,7 @@
#include "sprites/tiling/tileset_config.hpp"
#include "sprites/configs/masked_sprite_config.hpp"
#define PLAYER_SKINS std::vector<std::string> { "player-0", "player-1", "player-2", "player-3" }
#define PLAYER_SKINS std::vector<std::string> { "player", "player-1", "player-2", "player-3" }
/**
* All textures used in the game.
@ -91,7 +91,7 @@ std::map<std::string, MaskedSpriteConfig> const all_masked_sprites = {
* The key is the name of the tileset, the value is the tileset config.
*/
std::map<std::string, TileSetConfig> const all_tilesets = {
{"iso-tiles", TileSetConfig("custom-grass-tiles", {0, 1, 2, 3, 4, 5})}
{"iso-tiles", TileSetConfig("custom-grass-tiles", {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11})}
};
#endif //HOLESOME_TEXTURE_CONFIG_H