From 8ff789c576e4062e1c7678e511bc73c3588b714c Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Wed, 5 Aug 2026 16:56:02 +0800 Subject: [PATCH] feat(gameplay): spawn creatures during chunk generation Add creature spawning to chunk generation with a configurable SpawnConfig, including a default pig spawn. Spawning occurs in the final generation phase and registers entities through the server world's entity manager. Also destroy entities when their chunk is unloaded to prevent orphaned entities and expose the entity manager from ServerWorld. --- include/Cubed/gameplay/chunk_generator.hpp | 2 ++ include/Cubed/gameplay/creatures/spawn.hpp | 20 ++++++++++++ .../Cubed/gameplay/server_entity_manager.hpp | 2 +- include/Cubed/gameplay/server_world.hpp | 1 + src/gameplay/chunk_generator.cpp | 32 +++++++++++++++++++ src/gameplay/server_chunk.cpp | 1 + src/gameplay/server_entity_manager.cpp | 6 ++-- src/gameplay/server_world.cpp | 2 +- 8 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 include/Cubed/gameplay/creatures/spawn.hpp diff --git a/include/Cubed/gameplay/chunk_generator.hpp b/include/Cubed/gameplay/chunk_generator.hpp index 27faca9..a61f4f6 100644 --- a/include/Cubed/gameplay/chunk_generator.hpp +++ b/include/Cubed/gameplay/chunk_generator.hpp @@ -50,6 +50,8 @@ public: void generate_cave(); void generate_river(); + void spawn_creature(); + private: static inline std::atomic is_init{false}; static inline unsigned m_generator_seed{0}; diff --git a/include/Cubed/gameplay/creatures/spawn.hpp b/include/Cubed/gameplay/creatures/spawn.hpp new file mode 100644 index 0000000..e591dcf --- /dev/null +++ b/include/Cubed/gameplay/creatures/spawn.hpp @@ -0,0 +1,20 @@ +#pragma once +#include "Cubed/gameplay/biome.hpp" + +#include +#include +#include +namespace Cubed { +struct SpawnConfig { + std::string_view name; // factory key, e.g. "cubed:pig" + std::span biomes; // allowed biomes + float probability = 0.0f; // per chunk spawn probability + unsigned max_spawn_count = 0; // per chunk_max_spawn_sum +}; + +namespace SpawnDefaults { +constexpr std::array PIG_BIOMES{BiomeType::PLAIN, + BiomeType::FOREST}; +constexpr SpawnConfig PIG{"cubed:pig", PIG_BIOMES, 0.02f, 3}; +} // namespace SpawnDefaults +} // namespace Cubed diff --git a/include/Cubed/gameplay/server_entity_manager.hpp b/include/Cubed/gameplay/server_entity_manager.hpp index 38dddad..15010ba 100644 --- a/include/Cubed/gameplay/server_entity_manager.hpp +++ b/include/Cubed/gameplay/server_entity_manager.hpp @@ -15,7 +15,7 @@ public: void init(); void update(); // not thread safe - void add_entity(std::string_view name, const glm::vec3& pos); + void add_entity(std::string_view name, const glm::vec3& world_pos); void destory(EntityID id); void handle_player_login(std::shared_ptr session); diff --git a/include/Cubed/gameplay/server_world.hpp b/include/Cubed/gameplay/server_world.hpp index 6b46be4..b82dfe1 100644 --- a/include/Cubed/gameplay/server_world.hpp +++ b/include/Cubed/gameplay/server_world.hpp @@ -95,6 +95,7 @@ public: std::vector> get_all_session() const; uint32_t get_chunk_ref_count(const glm::vec3& pos) const; + ServerEntityManager& entity_manager(); int get_block(const glm::ivec3& block_pos) const override; bool is_solid(const glm::ivec3& block_pos) const override; diff --git a/src/gameplay/chunk_generator.cpp b/src/gameplay/chunk_generator.cpp index bc80670..b10bdf1 100644 --- a/src/gameplay/chunk_generator.cpp +++ b/src/gameplay/chunk_generator.cpp @@ -9,6 +9,7 @@ #include "Cubed/gameplay/builders/river_builder.hpp" #include "Cubed/gameplay/builders/snowy_plain_builder.hpp" #include "Cubed/gameplay/cave_path.hpp" +#include "Cubed/gameplay/creatures/spawn.hpp" #include "Cubed/gameplay/river.path.hpp" #include "Cubed/gameplay/server_chunk.hpp" #include "Cubed/gameplay/server_world.hpp" @@ -17,6 +18,8 @@ #include "Cubed/tools/cubed_hash.hpp" #include "Cubed/tools/math_tools.hpp" #include "Cubed/tools/perlin_noise.hpp" + +#include namespace Cubed { namespace { @@ -805,6 +808,35 @@ void ChunkGenerator::generate_river() { } } +void ChunkGenerator::spawn_creature() { + auto biome = m_chunk.biome(); + const auto& blocks = m_chunk.blocks(); + const auto& heightmap = m_chunk.heightmap(); + const auto& chunk_pos = m_chunk.chunk_pos(); + if (std::ranges::contains(SpawnDefaults::PIG.biomes, biome)) { + + if (m_random.random_bool(SpawnDefaults::PIG.probability)) { + int want = + m_random.random_int(0, SpawnDefaults::PIG.max_spawn_count); + for (int i = 0; i < want; ++i) { + int x = m_random.random_int(0, CHUNK_SIZE - 1); + int z = m_random.random_int(0, CHUNK_SIZE - 1); + int y = static_cast(heightmap[x][z]); + glm::vec3 pos(x, y + 1, z); + auto type = blocks[Chunk::index(pos)]; + if (type != 0) { + continue; + } + auto [world_x, world_y, world_z] = Chunk::block_to_world( + x, y + 1, z, chunk_pos.x, chunk_pos.z); + m_chunk.world().entity_manager().add_entity( + SpawnDefaults::PIG.name, + glm::vec3{world_x, world_y, world_z}); + } + } + } +} + ServerChunk& ChunkGenerator::chunk() { return m_chunk; } Random& ChunkGenerator::random() { return m_random; } diff --git a/src/gameplay/server_chunk.cpp b/src/gameplay/server_chunk.cpp index ee7f711..1d65371 100644 --- a/src/gameplay/server_chunk.cpp +++ b/src/gameplay/server_chunk.cpp @@ -141,6 +141,7 @@ void ServerChunk::gen_phase_five() { m_generator->generate_cave(); m_generator->generate_vegetation(); + m_generator->spawn_creature(); m_generator = nullptr; } diff --git a/src/gameplay/server_entity_manager.cpp b/src/gameplay/server_entity_manager.cpp index 7837ff4..9b3fcc9 100644 --- a/src/gameplay/server_entity_manager.cpp +++ b/src/gameplay/server_entity_manager.cpp @@ -37,6 +37,8 @@ void ServerEntityManager::update() { for (auto e : view) { const auto& c = view.get(e); if (!m_world.get_chunk_ref_count(c.transform.position.value)) { + const auto& entity = m_registry.get(e); + destory(entity.id); continue; } update_ai(e); @@ -102,9 +104,9 @@ void ServerEntityManager::handle_task() { } void ServerEntityManager::add_entity(std::string_view name, - const glm::vec3& pos) { + const glm::vec3& world_pos) { m_tasks.emplace(Command::CREATE, - EntityCreateElement{std::string(name), pos}); + EntityCreateElement{std::string(name), world_pos}); } void ServerEntityManager::destory(EntityID id) { diff --git a/src/gameplay/server_world.cpp b/src/gameplay/server_world.cpp index f53b847..f359603 100644 --- a/src/gameplay/server_world.cpp +++ b/src/gameplay/server_world.cpp @@ -1114,5 +1114,5 @@ BlockType ServerWorld::get_block_tpye(const glm::ivec3& block_pos) const { } int ServerWorld::get_per_tick_time() const { return m_per_tick_time; } - +ServerEntityManager& ServerWorld::entity_manager() { return m_entity_manager; } } // namespace Cubed \ No newline at end of file