diff --git a/assets/sound/ambient/birds.ogg b/assets/sound/ambient/birds.ogg new file mode 100644 index 0000000..807bc65 Binary files /dev/null and b/assets/sound/ambient/birds.ogg differ diff --git a/include/Cubed/audio/audio_engine.hpp b/include/Cubed/audio/audio_engine.hpp index 1d1e9c2..0f7a528 100644 --- a/include/Cubed/audio/audio_engine.hpp +++ b/include/Cubed/audio/audio_engine.hpp @@ -28,6 +28,7 @@ public: void play_bgm(); void play_3d(const std::string& sound, const glm::vec3& pos, bool check = false); + void play_2d(const std::string& sound, bool check = false); void update_listener(const glm::vec3& pos, const glm::vec3& forward, const glm::vec3& up); void update(); diff --git a/include/Cubed/gameplay/client_player.hpp b/include/Cubed/gameplay/client_player.hpp index 39ef411..d68ad0f 100644 --- a/include/Cubed/gameplay/client_player.hpp +++ b/include/Cubed/gameplay/client_player.hpp @@ -4,6 +4,7 @@ #include "Cubed/gameplay/block.hpp" #include "Cubed/gameplay/chunk_pos.hpp" #include "Cubed/gameplay/game_mode.hpp" +#include "Cubed/gameplay/game_time.hpp" #include "Cubed/gameplay/player.hpp" #include "Cubed/input.hpp" @@ -123,7 +124,7 @@ private: float m_angle{0.0f}; float m_walk_time{0.0f}; - float m_footstep_timer = 0.0f; + std::unordered_map m_timers; mutable std::shared_mutex m_player_pos_mutex; mutable std::shared_mutex m_chunk_pos_mutex; diff --git a/include/Cubed/gameplay/client_world.hpp b/include/Cubed/gameplay/client_world.hpp index 406ebb9..99e065e 100644 --- a/include/Cubed/gameplay/client_world.hpp +++ b/include/Cubed/gameplay/client_world.hpp @@ -92,10 +92,10 @@ public: static AABB get_block_aabb(const glm::ivec3& pos); AudioEngine& get_audio(); template - void register_timer(std::string_view id, TickType threshold, Fn&& f) { - m_timers.emplace(std::piecewise_construct, - std::forward_as_tuple(std::string(id)), - std::forward_as_tuple(threshold, std::forward(f))); + void register_ticktimer(std::string_view id, TickType threshold, Fn&& f) { + m_ticktimers.emplace( + std::piecewise_construct, std::forward_as_tuple(std::string(id)), + std::forward_as_tuple(threshold, std::forward(f))); } private: @@ -136,7 +136,10 @@ private: std::deque m_dirty_queue; std::vector m_render_snapshots; std::vector m_render_player_data; - tbb::concurrent_unordered_map m_timers; + + tbb::concurrent_unordered_map m_ticktimers; + std::unordered_map m_timers; + std::atomic m_game_running{false}; std::atomic m_receive_exit{false}; std::atomic m_rendering_distance{24}; diff --git a/include/Cubed/gameplay/game_time.hpp b/include/Cubed/gameplay/game_time.hpp index 64aab77..936f74e 100644 --- a/include/Cubed/gameplay/game_time.hpp +++ b/include/Cubed/gameplay/game_time.hpp @@ -6,7 +6,9 @@ #include #include namespace Cubed { + using TickType = long long; +using TimeType = float; constexpr int DEFAULT_PER_TICK_TIME = 50; @@ -14,10 +16,10 @@ constexpr TickType DAY_TIME = 24000; constexpr TickType PER_HOUR = 1000; -class Timer { +class TickTimer { public: template - Timer(TickType threshold, Fn&& f) + TickTimer(TickType threshold, Fn&& f) : m_fn(std::forward(f)), m_threshold(threshold) { ASSERT_MSG(threshold > 0, "Threshold Must Rreater Than 0"); } @@ -37,4 +39,37 @@ private: TickType m_current = 0; }; +class Timer { +public: + template + Timer(TimeType threshold, Fn&& f) + : m_fn(std::forward(f)), m_threshold(threshold) { + ASSERT_MSG(threshold > 0, "Threshold Must Rreater Than 0"); + } + bool update(TimeType dt) { + m_current += dt; + + bool triggered = false; + + while (m_current >= m_threshold) { + m_current -= m_threshold; + m_fn(); + triggered = true; + } + + return triggered; + } + void reset() { m_current = 0.0f; } + void set_threshold(TimeType threshold) { + + ASSERT_MSG(threshold > 0, "Threshold must be greater than 0"); + m_threshold = threshold; + } + +private: + std::function m_fn; + TimeType m_threshold; + TimeType m_current = 0; +}; + } // namespace Cubed diff --git a/include/Cubed/gameplay/server_world.hpp b/include/Cubed/gameplay/server_world.hpp index 4fc2461..98e32bd 100644 --- a/include/Cubed/gameplay/server_world.hpp +++ b/include/Cubed/gameplay/server_world.hpp @@ -156,7 +156,7 @@ private: PlayerUUIDMap m_uuid_to_name; - tbb::concurrent_unordered_map m_timers; + tbb::concurrent_unordered_map m_timers; tbb::concurrent_queue m_waiting_chunk_requests; tbb::concurrent_queue> m_finished_queue; diff --git a/src/audio/audio_engine.cpp b/src/audio/audio_engine.cpp index c475055..8045a00 100644 --- a/src/audio/audio_engine.cpp +++ b/src/audio/audio_engine.cpp @@ -89,6 +89,29 @@ void AudioEngine::play_3d(const std::string& sound, const glm::vec3& pos, } catch (const std::exception& e) { if (check) { ASSERT_MSG(false, e.what()); + Logger::error("Player Sound Error {}", e.what()); + } + } +} + +void AudioEngine::play_2d(const std::string& sound, bool check) { + if (!m_pool) { + Logger::error("Source Pool is nullptr"); + return; + } + auto* source = m_pool->acquire(); + source->set_volume(m_sfx_volume); + if (!source) { + Logger::error("Source is Full"); + } + + try { + auto& buffer = m_sounds.get_buffer(sound); + source->play_2d(buffer); + } catch (const std::exception& e) { + if (check) { + ASSERT_MSG(false, e.what()); + Logger::error("Player Sound Error {}", e.what()); } } } diff --git a/src/audio/sound_manager.cpp b/src/audio/sound_manager.cpp index c57fd16..f39eaa4 100644 --- a/src/audio/sound_manager.cpp +++ b/src/audio/sound_manager.cpp @@ -11,7 +11,8 @@ SoundManager::~SoundManager() { clear(); } void SoundManager::clear() { m_buffers.clear(); } void SoundManager::init() { try { - // load("bgm/bgm001.mp3"); + load("bgm/bgm001.mp3"); + load("ambient/birds.ogg"); } catch (const std::exception& e) { } } diff --git a/src/gameplay/client_player.cpp b/src/gameplay/client_player.cpp index 5731a8e..a8fa30d 100644 --- a/src/gameplay/client_player.cpp +++ b/src/gameplay/client_player.cpp @@ -4,6 +4,12 @@ #include "Cubed/config.hpp" #include "Cubed/debug_collector.hpp" #include "Cubed/gameplay/client_world.hpp" + +namespace { +constexpr float WALK_SOUND_INTERVAL = 0.45f; +constexpr float RUN_SOUND_INTERVAL = 0.3f; +} // namespace + namespace Cubed { ClientPlayer::ClientPlayer(ClientWorld& world) : m_world(world) {} ClientPlayer::~ClientPlayer() {} @@ -386,7 +392,19 @@ void ClientPlayer::update_move(float delta_time) { m_player_pos = player_pos; } update_player_chunk(); - play_walk_sound(delta_time); + auto it = m_timers.find("Player Walk Sound"); + + if (it != m_timers.end()) { + if (m_sprinting) { + it->second.set_threshold(RUN_SOUND_INTERVAL); + } else { + it->second.set_threshold(WALK_SOUND_INTERVAL); + } + } + + for (auto& [key, timer] : m_timers) { + timer.update(delta_time); + } } void ClientPlayer::update_x_move(glm::vec3& player_pos) { @@ -499,42 +517,6 @@ void ClientPlayer::update_player_chunk() { } } -void ClientPlayer::play_walk_sound(float dt) { - if (!m_moving || is_fly) { - return; - } - - m_footstep_timer += dt; - if (m_sprinting) { - const float WALK_INTERVAL = 0.3f; - - if (m_footstep_timer < WALK_INTERVAL) { - return; - } - } else { - const float WALK_INTERVAL = 0.45f; - - if (m_footstep_timer < WALK_INTERVAL) { - return; - } - } - - m_footstep_timer = 0.0f; - - glm::ivec3 block = glm::floor(m_player_pos); - block.y -= 1; - BlockType id = m_world.get_block_tpye(block); - Logger::info("player Block {} Walk Sound", id); - if (id == 0) { - return; - } - std::string name = BlockManager::name_form_id(id); - std::string sound = "block/" + name + "/walk.ogg"; - auto& audio = m_world.get_audio(); - audio.play_3d(sound, m_player_pos); - Logger::info("Player block {} walk sound", name); -} - Gait ClientPlayer::compute_gait() const { if (m_xz_speed < 0.01f) return Gait::STOP; @@ -610,7 +592,28 @@ std::string ClientPlayer::get_uuid() const { return m_uuid; } const std::string& ClientPlayer::get_name() const { return m_name; } -void ClientPlayer::init(std::string_view name) { m_name = name; } +void ClientPlayer::init(std::string_view name) { + + m_name = name; + + m_timers.try_emplace("Player Walk Sound", WALK_SOUND_INTERVAL, [this]() { + if (!m_moving || is_fly) { + return; + } + glm::ivec3 block = glm::floor(m_player_pos); + block.y -= 1; + BlockType id = m_world.get_block_tpye(block); + Logger::info("player Block {} Walk Sound", id); + if (id == 0) { + return; + } + std::string name = BlockManager::name_form_id(id); + std::string sound = "block/" + name + "/walk.ogg"; + auto& audio = m_world.get_audio(); + audio.play_3d(sound, m_player_pos); + Logger::info("Player block {} walk sound", name); + }); +} float ClientPlayer::yaw() const { return m_yaw; } float ClientPlayer::pitch() const { return m_pitch; } diff --git a/src/gameplay/client_world.cpp b/src/gameplay/client_world.cpp index 04d9bd7..7a4eb47 100644 --- a/src/gameplay/client_world.cpp +++ b/src/gameplay/client_world.cpp @@ -43,7 +43,7 @@ ClientWorld::~ClientWorld() { } m_pending_delete_vao.clear(); } - m_timers.clear(); + m_ticktimers.clear(); } const std::optional& ClientWorld::get_look_block_pos() const { @@ -358,7 +358,23 @@ void ClientWorld::init(std::string_view player_name, m_player.init(player_name); m_client = client; // timer - register_timer("player_pos", 1, [this]() { report_player_info(); }); + register_ticktimer("player_pos", 1, [this]() { report_player_info(); }); + m_timers.try_emplace("Birds Sound", 60.0f, [this]() { + auto player_pos = m_player.get_player_pos(); + if (player_pos.y < SEA_LEVEL) { + return; + } + ChunkPos pos = get_chunk_pos(player_pos.x, player_pos.z); + { + chunk_cacc cacc; + if (m_chunks.find(cacc, pos)) { + if (cacc->second->get_biome() == BiomeType::FOREST) { + m_audio.play_2d("ambient/birds.ogg", true); + } + } + } + }); + LoginReq req; req.set_name(m_player.get_name()); while (!client->is_connected()) { @@ -443,7 +459,7 @@ void ClientWorld::client_run(std::stop_token stoken) { auto next = Clock::now(); while (!stoken.stop_requested()) { next += TICK; - for (auto& x : m_timers) { + for (auto& x : m_ticktimers) { x.second.update(); } std::this_thread::sleep_until(next); @@ -779,6 +795,10 @@ void ClientWorld::update(float delta_time) { while (m_pending_sound.try_pop(pending_sound)) { m_audio.play_3d(pending_sound.sound, pending_sound.sound_pos); } + + for (auto& [pos, timer] : m_timers) { + timer.update(delta_time); + } } glm::vec3 ClientWorld::sunlight_dir() const {