diff --git a/assets/game_assets/assets_audio.map b/assets/game_assets/assets_audio.map index 4462a86a17bbb04460a30b2d434280ebda97afa2..572a158bda8d86be819bed8f88992b85f61ffd2a 100644 --- a/assets/game_assets/assets_audio.map +++ b/assets/game_assets/assets_audio.map @@ -1,4 +1,2 @@ audio: = audio/*.opus - -cfg:sound_effects = settings/sound_effects.json - +music: = music/*.opus diff --git a/assets/game_assets/audio/Finish_01.ogg b/assets/game_assets/audio/Finish_01.ogg deleted file mode 100644 index 00e56fb39afba6f494a01cc89870602c4ce9de13..0000000000000000000000000000000000000000 Binary files a/assets/game_assets/audio/Finish_01.ogg and /dev/null differ diff --git a/assets/game_assets/audio/Finish_01.opus b/assets/game_assets/audio/Finish_01.opus new file mode 100644 index 0000000000000000000000000000000000000000..a611d875b00429363d99c5d75a71af8abb7f7cf0 --- /dev/null +++ b/assets/game_assets/audio/Finish_01.opus @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4b081c18b8fd488aae71fc5a04af82c93ab0fb2bf554ad0d74605d11b64aac8 +size 92030 diff --git a/assets/game_assets/audio/beat.opus b/assets/game_assets/audio/beat.opus new file mode 100644 index 0000000000000000000000000000000000000000..969b98db0591c9edb8b06f89f92351726af71097 --- /dev/null +++ b/assets/game_assets/audio/beat.opus @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b615bd2935ccefe3fa5f61110bbe037fb1ed29448c296161d6d936a6e89ae36 +size 12568 diff --git a/assets/game_assets/music/pulse.opus b/assets/game_assets/music/pulse.opus new file mode 100644 index 0000000000000000000000000000000000000000..8dba48b62e753ac0aa93a11288ec68efd68c0c17 --- /dev/null +++ b/assets/game_assets/music/pulse.opus @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cdf9d010bcf91fa165cbd3b5d07d415eeeb8957255ea5362b8472da4fbc13559 +size 2087114 diff --git a/assets/game_assets/settings/gui.json b/assets/game_assets/settings/gui.json index 77eafef25357d6ae431a07a425d83f6e2f0f4dc4..41d192d1adaa704ea1e3c174c6a0b56567fd1f1a 100644 --- a/assets/game_assets/settings/gui.json +++ b/assets/game_assets/settings/gui.json @@ -1,9 +1,24 @@ { "fonts": [ { - "id" : "default", - "aid" : "font:ui/RobotoCondensed-Regular.ttf", - "size" : 20 + "id": "default", + "aid": "font:ui/blanka-regular.otf", + "size": 30 + }, + { + "id": "heading", + "aid": "font:ui/blanka-regular.otf", + "size": 70 + }, + { + "id": "sub_heading", + "aid": "font:ui/blanka-regular.otf", + "size": 50 + }, + { + "id": "huge", + "aid": "font:ui/blanka-regular.otf", + "size": 300 } ] -} \ No newline at end of file +} diff --git a/assets/game_assets/ui/blanka-regular.otf b/assets/game_assets/ui/blanka-regular.otf new file mode 100644 index 0000000000000000000000000000000000000000..60b18d7816c9ea5635f8c276851970e575b5f953 Binary files /dev/null and b/assets/game_assets/ui/blanka-regular.otf differ diff --git a/src/game_engine.cpp b/src/game_engine.cpp index f42a43caa0a621c14fb6d72698c15fb5aee2aa81..0c7c67a256c01e0ce23ed1bac8fb3206f15622df 100644 --- a/src/game_engine.cpp +++ b/src/game_engine.cpp @@ -20,6 +20,8 @@ #include #include +#include + namespace phase_shifter { using namespace mirrage; @@ -61,6 +63,10 @@ namespace phase_shifter { { util::erase_fast(_render_pass_mask, renderer::render_pass_id_of()); util::erase_fast(_render_pass_mask, renderer::render_pass_id_of()); + + eam::settings setting{{16, true, false, false}, 2, 48000, 1024}; + _audio.set_sink(setting); + _audio.set_master_volume(35); } Game_engine::~Game_engine() diff --git a/src/game_engine.hpp b/src/game_engine.hpp index cd38361f0ecc6335453343f581a09b45c53bba0b..9fc48087ccfbcb68dfab938103ae8f5031b22d86 100644 --- a/src/game_engine.hpp +++ b/src/game_engine.hpp @@ -14,11 +14,13 @@ #include #include +#include + namespace mirrage::renderer { class Deferred_renderer_factory; } - + namespace phase_shifter { class Game_engine : public mirrage::Engine { @@ -37,6 +39,11 @@ namespace phase_shifter { auto renderer_factory() noexcept -> auto& { return *_renderer_factory; } auto global_render() noexcept -> auto& { return *_global_render; } auto render_pass_mask() noexcept -> auto& { return _render_pass_mask; } + auto audio() noexcept -> auto& { return _audio; } + auto audio_latency() const noexcept { return _audio_latency; } + auto audio_latency(float latency) noexcept { _audio_latency = latency; } + auto video_latency() const noexcept { return _video_latency; } + auto video_latency(float latency) noexcept { _video_latency = latency; } protected: void _on_post_frame(mirrage::util::Time) override; @@ -46,6 +53,9 @@ namespace phase_shifter { std::unique_ptr _renderer_factory; std::unique_ptr _global_render; mirrage::renderer::Render_pass_mask _render_pass_mask; - }; - -} // namespace seapp + eam::audio _audio; + float _audio_latency = 0.f; + float _video_latency = 0.f; + }; // namespace phase_shifter + +} // namespace phase_shifter diff --git a/src/meta_system.cpp b/src/meta_system.cpp index 141a282dc03dab12cb1af630bfbb12565c3dc06b..706b442b606f2641bf914a86719b23c21513d881 100644 --- a/src/meta_system.cpp +++ b/src/meta_system.cpp @@ -32,10 +32,9 @@ namespace phase_shifter { Meta_system::Meta_system(Game_engine& engine) : _entities(engine.assets(), this) - , _audio() , _renderer(engine.renderer_factory().create_renderer(_entities, engine.render_pass_mask())) , _model_loading(std::make_unique(_entities, engine.assets())) - , _sound_manager(std::make_unique(_audio, engine.bus(), engine.assets())) + , _sound_manager(std::make_unique(engine.audio(), engine.bus(), engine.assets())) , _beat_system(std::make_unique(engine.bus(), engine.assets())) , _level_system(std::make_unique(_entities, engine.assets())) , _movement_system(std::make_unique( @@ -59,9 +58,6 @@ namespace phase_shifter { //s.scene_luminance_override = 300.f; //_renderer->settings(s, false); - eam::settings setting{{16, true, false, false}, 2, 48000, 1024}; - _audio.set_sink(setting); - _audio.set_master_volume(35); /*auto bg_music = engine.assets().load>("audio:bg_music_01"_aid).get_blocking(); auto bg_context = _audio.generate_context(bg_music); @@ -93,29 +89,32 @@ namespace phase_shifter { LOG(plog::info) << "Renderer Memory usage: " << msg.str(); } }); - _level_system->load("dummy"); - entities() - .entity_builder("basic_enemy") - .position({20, 0, 15}) - .post_create([=](auto entity) { - entity.process([&](gameplay::Fixed_path_comp& fixed_path) { fixed_path.update_path({}); }); - entity.process([&](gameplay::Shooting_comp& shooting) { - shooting.set_patterns({gameplay::Bulletpattern({{0, 0}})}); - }); - }) - .create(); + auto bg_music = engine.assets().load>("music:pulse"_aid).get_blocking(); + _music = engine.audio().generate_context_ptr(bg_music); + _music->get_object()->set_loops(-1); + _music->get_object()->set_position(engine.audio_latency() + / _music->get_object()->get_length_in_sec()); } Meta_system::~Meta_system() { + _music->stop_fade(2.f); _renderer->device().wait_idle(); _entities.clear(); } void Meta_system::update(mirrage::util::Time dt) { + if(_play_music) { + _play_music = false; + _music->play(); + } else if(_first_frame) { + _play_music = true; + _first_frame = false; + } + _entities.process_queued_actions(); _beat_system->update(dt); diff --git a/src/meta_system.hpp b/src/meta_system.hpp index e424a475f4a0edf58eb579332d87774f4abcf494..cbf157f05f66d140cd33446ed111329e5ab02f3e 100644 --- a/src/meta_system.hpp +++ b/src/meta_system.hpp @@ -1,9 +1,10 @@ #pragma once -#include #include #include #include + +#include #include namespace mirrage { @@ -64,7 +65,6 @@ namespace phase_shifter { private: mirrage::ecs::Entity_manager _entities; - eam::audio _audio; std::unique_ptr _renderer; std::unique_ptr _model_loading; std::unique_ptr _sound_manager; @@ -81,6 +81,9 @@ namespace phase_shifter { std::unique_ptr _dash_system; std::unique_ptr _stationary_attack_system; std::unique_ptr _effect_system; + std::shared_ptr _music; + bool _first_frame = true; + bool _play_music = false; mirrage::util::Console_command_container _commands; }; diff --git a/src/sync_screen.cpp b/src/sync_screen.cpp index 0470a2d9c6afdc98f6955dea37bc04cd1b56fa3e..799e6dde30e97e1f0524de455c70502e6e57c8b6 100644 --- a/src/sync_screen.cpp +++ b/src/sync_screen.cpp @@ -1,7 +1,9 @@ #include "sync_screen.hpp" +#include "audio/audio_asset.hpp" #include "game_engine.hpp" #include "intro_screen.hpp" +#include "ui/util.hpp" #include #include @@ -12,13 +14,14 @@ #include #include +#include #include #include #include #include #include - +#include namespace phase_shifter { using namespace mirrage; @@ -33,6 +36,12 @@ namespace phase_shifter { , _mailbox(engine.bus()) , _renderer(dynamic_cast(engine).renderer_factory().create_renderer( mirrage::util::nothing, {renderer::render_pass_id_of()})) + , _gui(&_engine.gui()) + , _audio(&static_cast(_engine).audio()) + , _start_audio_calibration_text("Press any button to start . . .") + , _start_video_calibration_text("Press any button to start . . .") + , _countdown(3) + , _signal("o") { _mailbox.subscribe_to([&](Once_action& e) { switch(e.id) { @@ -40,12 +49,19 @@ namespace phase_shifter { // TODO: show pause menu, instead _engine.screens().leave(); break; + case "the_button"_strid: _on_button_press(); break; } }); } Sync_screen::~Sync_screen() noexcept = default; - void Sync_screen::_on_enter(mirrage::util::maybe) { _mailbox.enable(); } + void Sync_screen::_on_enter(mirrage::util::maybe) + { + _mailbox.enable(); + auto beat_sound = _engine.assets().load>("audio:beat"_aid); + _beat = _audio->generate_context_ptr(beat_sound.get_blocking()); + _beat->get_object()->set_loops(5); + } void Sync_screen::_on_leave(mirrage::util::maybe) { _mailbox.disable(); } @@ -54,21 +70,186 @@ namespace phase_shifter { std::this_thread::sleep_for(std::chrono::milliseconds(8)); _mailbox.update_subscriptions(); _renderer->update(dt); + switch(_current_mode) { + case mode::start_audio_calibration: _start_audio_calibration_text.update(dt); break; + case mode::countdown_audio_calibration: + _countdown.update(dt); + if(_countdown.finished()) { + _countdown.reset(3); + _current_mode = mode::audio_calibration; + } + break; + case mode::audio_calibration: + if(_play_sample) { + _beat->play(); + _play_sample = false; + _start = std::chrono::high_resolution_clock::now(); + } + if(_audio_latencies.size() == 5) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + _audio_latency = + std::accumulate(_audio_latencies.begin() + 1, _audio_latencies.end() - 1, 0.f) + / 3.f; + _current_mode = mode::start_video_calibration; + } + break; + case mode::start_video_calibration: _start_video_calibration_text.update(dt); break; + case mode::countdown_video_calibration: + _countdown.update(dt); + if(_countdown.finished()) { + _countdown.reset(3); + _current_mode = mode::video_calibration; + } + break; + case mode::video_calibration: + if(_play_sample) { + _play_sample = false; + _start = std::chrono::high_resolution_clock::now(); + } + _signal.update(dt); + if(_video_latencies.size() == 5) { + _video_latency = + std::accumulate(_video_latencies.begin() + 1, _video_latencies.end() - 1, 0.f) + / 3.f; + static_cast(_engine).audio_latency(_audio_latency); + static_cast(_engine).video_latency(_video_latency); + LOG(plog::debug) << "audio latency: " << _audio_latency; + LOG(plog::debug) << "video latency: " << _video_latency; + _engine.screens().enter(); + } + break; + default: break; + } + } + + auto Sync_screen::_on_button_press() -> void + { + switch(_current_mode) { + case mode::start_audio_calibration: + _play_sample = true; + _current_mode = mode::countdown_audio_calibration; + break; + case mode::audio_calibration: + _end = std::chrono::high_resolution_clock::now(); + _audio_latencies.emplace_back( + std::chrono::duration_cast(_end - _start).count() + / 1000.f); + _start = std::chrono::high_resolution_clock::now(); + break; + case mode::start_video_calibration: + _play_sample = true; + _current_mode = mode::countdown_video_calibration; + break; + case mode::video_calibration: + _end = std::chrono::high_resolution_clock::now(); + _video_latencies.emplace_back( + std::chrono::duration_cast(_end - _start).count() + / 1000.f); + _start = std::chrono::high_resolution_clock::now(); + break; + default: break; + } } void Sync_screen::_draw() { _renderer->draw(); //< clear screen - ImGui::PositionNextWindow( - glm::vec2(500, 500), ImGui::WindowPosition_X::center, ImGui::WindowPosition_Y::center); - if(ImGui::Begin("Sync")) { - ImGui::TextUnformatted("TODO"); - if(ImGui::Button("OK")) { - _engine.screens().enter(); - } + auto viewport = _gui->viewport(); + auto font = _gui->find_font("default"_strid); + + ui::push_font(font); + + ImGui::PositionNextWindow(glm::vec2(viewport.z, viewport.w), + ImGui::WindowPosition_X::center, + ImGui::WindowPosition_Y::center); + if(ImGui::Begin("Sync", + nullptr, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize + | ImGuiWindowFlags_NoBackground)) {} + switch(_current_mode) { + case mode::start_audio_calibration: _draw_start_sync_audio_ui(); break; + case mode::countdown_audio_calibration: _draw_countdown_sync_audio_ui(); break; + case mode::start_video_calibration: _draw_start_sync_video_ui(); break; + case mode::countdown_video_calibration: _draw_countdown_sync_video_ui(); break; + case mode::video_calibration: _draw_sync_video_ui(); break; + default: break; } ImGui::End(); + + ui::pop_font(font); } + auto Sync_screen::_draw_start_sync_audio_ui() -> void + { + auto viewport = _gui->viewport(); + auto heading_font = _gui->find_font("heading"_strid); + auto sub_heading = _gui->find_font("sub_heading"_strid); + + ui::push_font(heading_font); + ui::text_centered("Calibrate your audio output", + glm::vec2{viewport.z, viewport.w}, + glm::vec2{0.f, viewport.w / 2.f - 70.f}, + ui::center::horizontal); + ui::pop_font(heading_font); + ui::text_centered("Press any button when you hear the sound", + glm::vec2{viewport.z, viewport.w}, + glm::vec2{0.f, viewport.w / 2.f}, + ui::center::horizontal); + ui::push_font(sub_heading); + _start_audio_calibration_text.draw_centered(glm::vec2{viewport.z, viewport.w}, + glm::vec2{0.f, viewport.w / 2.f + 220.f}, + ui::center::horizontal); + ui::pop_font(sub_heading); + } + + auto Sync_screen::_draw_countdown_sync_audio_ui() -> void + { + auto viewport = _gui->viewport(); + auto huge = _gui->find_font("huge"_strid); + ui::push_font(huge); + _countdown.draw_centered(glm::vec2{viewport.z, viewport.w}); + ui::pop_font(huge); + } + + auto Sync_screen::_draw_sync_audio_ui() -> void {} + + auto Sync_screen::_draw_start_sync_video_ui() -> void + { + auto viewport = _gui->viewport(); + auto heading_font = _gui->find_font("heading"_strid); + auto sub_heading = _gui->find_font("sub_heading"_strid); + + ui::push_font(heading_font); + ui::text_centered("Calibrate your video output", + glm::vec2{viewport.z, viewport.w}, + glm::vec2{0.f, viewport.w / 2.f - 70.f}, + ui::center::horizontal); + ui::pop_font(heading_font); + ui::text_centered("Press any button when you see the dot", + glm::vec2{viewport.z, viewport.w}, + glm::vec2{0.f, viewport.w / 2.f}, + ui::center::horizontal); + ui::push_font(sub_heading); + _start_video_calibration_text.draw_centered(glm::vec2{viewport.z, viewport.w}, + glm::vec2{0.f, viewport.w / 2.f + 220.f}, + ui::center::horizontal); + ui::pop_font(sub_heading); + } + auto Sync_screen::_draw_countdown_sync_video_ui() -> void + { + auto viewport = _gui->viewport(); + auto huge = _gui->find_font("huge"_strid); + ui::push_font(huge); + _countdown.draw_centered(glm::vec2{viewport.z, viewport.w}); + ui::pop_font(huge); + } + auto Sync_screen::_draw_sync_video_ui() -> void + { + auto viewport = _gui->viewport(); + auto huge = _gui->find_font("huge"_strid); + ui::push_font(huge); + _signal.draw_centered(glm::vec2{viewport.z, viewport.w}); + ui::pop_font(huge); + } } // namespace phase_shifter diff --git a/src/sync_screen.hpp b/src/sync_screen.hpp index c7bd4afc47ffcc090558225250c4e567e051e119..b6d744ee136d74f3729f6841ecdbc657dfe6c65c 100644 --- a/src/sync_screen.hpp +++ b/src/sync_screen.hpp @@ -1,11 +1,18 @@ #pragma once +#include "ui/countdown.hpp" +#include "ui/fading_text.hpp" + #include #include #include #include #include +#include + +#include + namespace phase_shifter { @@ -17,6 +24,15 @@ namespace phase_shifter { auto name() const -> std::string override { return "Gameover"; } protected: + enum class mode { + start_audio_calibration, + countdown_audio_calibration, + audio_calibration, + start_video_calibration, + countdown_video_calibration, + video_calibration + }; + void _update(mirrage::util::Time delta_time) override; void _draw() override; @@ -28,9 +44,32 @@ namespace phase_shifter { return mirrage::Prev_screen_policy::discard; } + auto _on_button_press() -> void; + auto _draw_start_sync_audio_ui() -> void; + auto _draw_countdown_sync_audio_ui() -> void; + auto _draw_sync_audio_ui() -> void; + auto _draw_start_sync_video_ui() -> void; + auto _draw_countdown_sync_video_ui() -> void; + auto _draw_sync_video_ui() -> void; + protected: mirrage::util::Mailbox_collection _mailbox; std::unique_ptr _renderer; + mirrage::gui::Gui* _gui; + eam::audio* _audio; + mode _current_mode = mode::start_audio_calibration; + std::chrono::time_point _start; + std::chrono::time_point _end; + bool _play_sample = true; + std::vector _audio_latencies; + float _audio_latency = 0.f; + std::shared_ptr _beat; + std::vector _video_latencies; + float _video_latency = 0.f; + ui::Fading_text _start_audio_calibration_text; + ui::Fading_text _start_video_calibration_text; + ui::Countdown _countdown; + ui::Fading_text _signal; }; } // namespace phase_shifter diff --git a/src/ui/countdown.hpp b/src/ui/countdown.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b40c8ff3160fced3f36cc85f926e624ac950a889 --- /dev/null +++ b/src/ui/countdown.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include "util.hpp" + +namespace phase_shifter::ui { + class Countdown { + public: + Countdown(int initial_value) : _value(initial_value) {} + + auto update(mirrage::util::Time dt) + { + if(_value == 0) { + return; + } + + _elapsed_time += dt.value(); + if(_elapsed_time >= 1.f) { + _elapsed_time = 0.f; + --_value; + } + } + + auto draw(const glm::vec2& position) + { + if(_value == 0) { + return; + } + text(std::to_string(_value), position); + } + auto draw_centered(const glm::vec2& window_size, + const glm::vec2& position = glm::vec2{0.f}, + center center = center::all) + { + if(_value == 0) { + return; + } + text_centered(std::to_string(_value), window_size, position, center); + } + auto finished() const noexcept { return _value == 0; } + auto reset(int initial_value) + { + _value = initial_value; + _elapsed_time = 0.f; + } + + private: + int _value; + float _elapsed_time = 0.f; + }; +} // namespace phase_shifter::ui diff --git a/src/ui/fading_text.hpp b/src/ui/fading_text.hpp new file mode 100644 index 0000000000000000000000000000000000000000..61462898170dc813737d0a79c4a9777470003037 --- /dev/null +++ b/src/ui/fading_text.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "util.hpp" + +namespace phase_shifter::ui { + class Fading_text { + public: + Fading_text(const std::string& text, float duration = 0.5f) : _text(text), _duration(duration) {} + + auto update(mirrage::util::Time dt) + { + _elapsed_time += dt.value(); + if(_elapsed_time >= _duration) { + _draw = !_draw; + _elapsed_time = 0.f; + } + } + auto draw(const glm::vec2& position) + { + if(_draw) { + text(_text, position); + } + } + auto draw_centered(const glm::vec2& window_size, + const glm::vec2& position = glm::vec2{0.f}, + center center = center::all) + { + if(_draw) { + text_centered(_text, window_size, position, center); + } + } + + private: + std::string _text; + float _duration; + float _elapsed_time = 0.f; + bool _draw = true; + }; +} // namespace phase_shifter::ui diff --git a/src/ui/util.hpp b/src/ui/util.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7c257658ca524fd95c64091c3e845d1454ccb3f3 --- /dev/null +++ b/src/ui/util.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include + +namespace phase_shifter::ui { + inline auto text(const std::string& text, const glm::vec2& position) + { + auto cursor = ImGui::GetCursorPos(); + ImGui::SetCursorPos(ImVec2(position.x, position.y)); + ImGui::TextUnformatted(text.c_str()); + ImGui::SetCursorPos(cursor); + } + + enum class center { horizontal, vertical, all }; + + inline auto text_centered(const std::string& text, + const glm::vec2& window_size, + const glm::vec2& position = glm::vec2{0.f}, + center center = center::all) + { + auto text_size = ImGui::CalcTextSize(text.c_str()); + auto centered_position = + 0.5f * glm::vec2{window_size.x, window_size.y} - 0.5f * glm::vec2{text_size.x, text_size.y}; + if(center == center::vertical) { + centered_position.x = position.x; + } else if(center == center::horizontal) { + centered_position.y = position.y; + } + ui::text(text, centered_position); + } + + inline auto push_font(mirrage::util::maybe font) + { + if(font.is_some()) { + ImGui::PushFont(font.get_or_throw()); + } + } + + inline auto pop_font(mirrage::util::maybe font) + { + if(font.is_some()) { + ImGui::PopFont(); + } + } +} // namespace phase_shifter::ui