From 4e53a20169143f4987f9de5a78a17e5593640266 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Sat, 18 Jun 2022 13:16:16 +0800 Subject: [PATCH] Add extras menu. Add menu transition animations --- CMakeLists.txt | 1 - src/game/context.hpp | 2 + src/game/menu.cpp | 84 +++++++++++++- src/game/menu.hpp | 11 ++ src/game/states/controls-menu.cpp | 65 ++++++++--- src/game/states/credits.cpp | 8 +- src/game/states/extras-menu.cpp | 137 +++++++++++++++++++++++ src/game/states/extras-menu.hpp | 39 +++++++ src/game/states/gamepad-config-menu.cpp | 25 ++++- src/game/states/graphics-menu.cpp | 25 ++++- src/game/states/keyboard-config-menu.cpp | 25 ++++- src/game/states/language-menu.cpp | 25 ++++- src/game/states/loading.cpp | 2 +- src/game/states/main-menu.cpp | 126 +++++++++++++++++---- src/game/states/main-menu.hpp | 2 +- src/game/states/options-menu.cpp | 109 +++++++++++++----- src/game/states/sound-menu.cpp | 26 ++++- src/game/states/splash.cpp | 8 +- 18 files changed, 621 insertions(+), 99 deletions(-) create mode 100644 src/game/states/extras-menu.cpp create mode 100644 src/game/states/extras-menu.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d3614e6..7f0f857 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,5 @@ cmake_minimum_required(VERSION 3.7) - option(VERSION_STRING "Project version string" "0.0.0") project(antkeeper VERSION ${VERSION_STRING} LANGUAGES CXX) diff --git a/src/game/context.hpp b/src/game/context.hpp index d5513f3..cea8bac 100644 --- a/src/game/context.hpp +++ b/src/game/context.hpp @@ -216,6 +216,8 @@ struct context std::vector> menu_item_texts; std::unordered_map menu_item_indices; int* menu_item_index; + animation* menu_fade_animation; + animation* title_fade_animation; // Surface scene scene::collection* surface_scene; diff --git a/src/game/menu.cpp b/src/game/menu.cpp index 7a0648d..cd137a8 100644 --- a/src/game/menu.cpp +++ b/src/game/menu.cpp @@ -20,6 +20,9 @@ #include "game/menu.hpp" #include "scene/text.hpp" #include "application.hpp" +#include "animation/animation.hpp" +#include "animation/animator.hpp" +#include "animation/ease.hpp" #include namespace game { @@ -117,14 +120,12 @@ void align_text(game::context* ctx, bool center, bool has_back, float anchor_y) for (std::size_t i = 0; i < ctx->menu_item_texts.size(); ++i) { auto [name, value] = ctx->menu_item_texts[i]; - float x = menu_x; float y = menu_y - ctx->menu_font.get_font_metrics().linespace * i; if (has_back && i == ctx->menu_item_texts.size() - 1) y -= ctx->menu_font.get_font_metrics().linespace; - if (center || i == ctx->menu_item_texts.size() - 1) { const auto& name_bounds = static_cast&>(name->get_local_bounds()); @@ -190,6 +191,13 @@ void delete_text(game::context* ctx) ctx->menu_item_texts.clear(); } +void delete_animations(game::context* ctx) +{ + ctx->animator->remove_animation(ctx->menu_fade_animation); + delete ctx->menu_fade_animation; + ctx->menu_fade_animation = nullptr; +} + void clear_callbacks(game::context* ctx) { // Clear menu item callbacks @@ -199,6 +207,78 @@ void clear_callbacks(game::context* ctx) ctx->menu_back_callback = nullptr; } +void setup_animations(game::context* ctx) +{ + ctx->menu_fade_animation = new animation(); + animation_channel* opacity_channel = ctx->menu_fade_animation->add_channel(0); + + ctx->menu_fade_animation->set_frame_callback + ( + [ctx](int channel, const float& opacity) + { + for (std::size_t i = 0; i < ctx->menu_item_texts.size(); ++i) + { + auto [name, value] = ctx->menu_item_texts[i]; + + float4 color = (i == *ctx->menu_item_index) ? active_color : inactive_color; + color[3] = color[3] * opacity; + + if (name) + name->set_color(color); + if (value) + value->set_color(color); + } + } + ); + + ctx->animator->add_animation(ctx->menu_fade_animation); +} + +void fade_in(game::context* ctx, const std::function& end_callback) +{ + ctx->menu_fade_animation->set_interpolator(ease::out_cubic); + animation_channel* opacity_channel = ctx->menu_fade_animation->get_channel(0); + opacity_channel->remove_keyframes(); + opacity_channel->insert_keyframe({0.0, 0.0f}); + opacity_channel->insert_keyframe({game::menu::fade_in_duration, 1.0f}); + ctx->menu_fade_animation->set_end_callback(end_callback); + + for (std::size_t i = 0; i < ctx->menu_item_texts.size(); ++i) + { + auto [name, value] = ctx->menu_item_texts[i]; + + float4 color = (i == *ctx->menu_item_index) ? active_color : inactive_color; + color[3] = 0.0f; + + if (name) + { + name->set_color(color); + name->update_tweens(); + } + if (value) + { + value->set_color(color); + value->update_tweens(); + } + } + + ctx->menu_fade_animation->stop(); + ctx->menu_fade_animation->play(); +} + +void fade_out(game::context* ctx, const std::function& end_callback) +{ + ctx->menu_fade_animation->set_interpolator(ease::out_cubic); + animation_channel* opacity_channel = ctx->menu_fade_animation->get_channel(0); + opacity_channel->remove_keyframes(); + opacity_channel->insert_keyframe({0.0, 1.0f}); + opacity_channel->insert_keyframe({game::menu::fade_out_duration, 0.0f}); + ctx->menu_fade_animation->set_end_callback(end_callback); + + ctx->menu_fade_animation->stop(); + ctx->menu_fade_animation->play(); +} + void setup_controls(game::context* ctx) { ctx->controls["menu_up"]->set_activated_callback diff --git a/src/game/menu.hpp b/src/game/menu.hpp index 91a3544..093ec43 100644 --- a/src/game/menu.hpp +++ b/src/game/menu.hpp @@ -37,13 +37,24 @@ static constexpr float4 inactive_color{1.0f, 1.0f, 1.0f, 0.5f}; /// Padding of the mouseover bounds, as a percentage of the font size. static constexpr float mouseover_padding = 0.1f; +/// Duration of the menu fade in animation +static constexpr float fade_in_duration = 0.25f; + +/// Duration of the menu fade out animation +static constexpr float fade_out_duration = 0.25f; + void init_menu_item_index(game::context* ctx, const std::string& menu_name); void setup_controls(game::context* ctx); +void setup_animations(game::context* ctx); void clear_controls(game::context* ctx); void clear_callbacks(game::context* ctx); void remove_text_from_ui(game::context* ctx); void delete_text(game::context* ctx); +void delete_animations(game::context* ctx); + +void fade_in(game::context* ctx, const std::function& end_callback); +void fade_out(game::context* ctx, const std::function& end_callback); void update_text_color(game::context* ctx); void update_text_font(game::context* ctx); diff --git a/src/game/states/controls-menu.cpp b/src/game/states/controls-menu.cpp index 5327977..1a30c4a 100644 --- a/src/game/states/controls-menu.cpp +++ b/src/game/states/controls-menu.cpp @@ -59,31 +59,62 @@ void enter(game::context* ctx) game::menu::align_text(ctx); game::menu::update_text_tweens(ctx); game::menu::add_text_to_ui(ctx); + game::menu::setup_animations(ctx); // Construct menu item callbacks auto select_keyboard_callback = [ctx]() { - application::state next_state; - next_state.name = "keyboard_config_menu"; - next_state.enter = std::bind(game::state::keyboard_config_menu::enter, ctx); - next_state.exit = std::bind(game::state::keyboard_config_menu::exit, ctx); - ctx->app->change_state(next_state); + // Disable controls + game::menu::clear_controls(ctx); + + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "keyboard_config_menu"; + next_state.enter = std::bind(game::state::keyboard_config_menu::enter, ctx); + next_state.exit = std::bind(game::state::keyboard_config_menu::exit, ctx); + ctx->app->queue_state(next_state); + } + ); }; auto select_gamepad_callback = [ctx]() { - application::state next_state; - next_state.name = "gamepad_config_menu"; - next_state.enter = std::bind(game::state::gamepad_config_menu::enter, ctx); - next_state.exit = std::bind(game::state::gamepad_config_menu::exit, ctx); - ctx->app->change_state(next_state); + // Disable controls + game::menu::clear_controls(ctx); + + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "gamepad_config_menu"; + next_state.enter = std::bind(game::state::gamepad_config_menu::enter, ctx); + next_state.exit = std::bind(game::state::gamepad_config_menu::exit, ctx); + ctx->app->queue_state(next_state); + } + ); }; auto select_back_callback = [ctx]() { - application::state next_state; - next_state.name = "options_menu"; - next_state.enter = std::bind(game::state::options_menu::enter, ctx); - next_state.exit = std::bind(game::state::options_menu::exit, ctx); - ctx->app->change_state(next_state); + // Disable controls + game::menu::clear_controls(ctx); + + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "options_menu"; + next_state.enter = std::bind(game::state::options_menu::enter, ctx); + next_state.exit = std::bind(game::state::options_menu::exit, ctx); + ctx->app->queue_state(next_state); + } + ); }; // Build list of menu select callbacks @@ -108,6 +139,9 @@ void enter(game::context* ctx) timeline* timeline = ctx->timeline; float t = timeline->get_position(); timeline->add_sequence({{t + game::menu::input_delay, std::bind(game::menu::setup_controls, ctx)}}); + + // Fade in menu + game::menu::fade_in(ctx, nullptr); } void exit(game::context* ctx) @@ -115,6 +149,7 @@ void exit(game::context* ctx) // Destruct menu game::menu::clear_controls(ctx); game::menu::clear_callbacks(ctx); + game::menu::delete_animations(ctx); game::menu::remove_text_from_ui(ctx); game::menu::delete_text(ctx); diff --git a/src/game/states/credits.cpp b/src/game/states/credits.cpp index 5cf6795..500f8af 100644 --- a/src/game/states/credits.cpp +++ b/src/game/states/credits.cpp @@ -18,7 +18,7 @@ */ #include "game/states/credits.hpp" -#include "game/states/main-menu.hpp" +#include "game/states/extras-menu.hpp" #include "animation/ease.hpp" #include "animation/animation.hpp" #include "animation/animator.hpp" @@ -101,9 +101,9 @@ void enter(game::context* ctx) // Change state application::state next_state; - next_state.name = "main_menu"; - next_state.enter = std::bind(game::state::main_menu::enter, ctx); - next_state.exit = std::bind(game::state::main_menu::exit, ctx); + next_state.name = "extras_menu"; + next_state.enter = std::bind(game::state::extras_menu::enter, ctx); + next_state.exit = std::bind(game::state::extras_menu::exit, ctx); ctx->app->change_state(next_state); } } diff --git a/src/game/states/extras-menu.cpp b/src/game/states/extras-menu.cpp new file mode 100644 index 0000000..914a1d9 --- /dev/null +++ b/src/game/states/extras-menu.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/states/extras-menu.hpp" +#include "game/states/main-menu.hpp" +#include "game/states/credits.hpp" +#include "application.hpp" +#include "scene/text.hpp" +#include "render/passes/clear-pass.hpp" +#include "debug/logger.hpp" +#include "game/fonts.hpp" +#include "game/menu.hpp" +#include "animation/timeline.hpp" + +namespace game { +namespace state { +namespace extras_menu { + +void enter(game::context* ctx) +{ + ctx->ui_clear_pass->set_cleared_buffers(true, true, false); + + // Construct menu item texts + scene::text* credits_text = new scene::text(); + scene::text* back_text = new scene::text(); + + // Build list of menu item texts + ctx->menu_item_texts.push_back({credits_text, nullptr}); + ctx->menu_item_texts.push_back({back_text, nullptr}); + + // Set content of menu item texts + credits_text->set_content((*ctx->strings)["extras_menu_credits"]); + back_text->set_content((*ctx->strings)["back"]); + + // Init menu item index + game::menu::init_menu_item_index(ctx, "extras"); + + game::menu::update_text_color(ctx); + game::menu::update_text_font(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); + game::menu::add_text_to_ui(ctx); + game::menu::setup_animations(ctx); + + // Construct menu item callbacks + auto select_credits_callback = [ctx]() + { + // Disable controls + game::menu::clear_controls(ctx); + + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "credits"; + next_state.enter = std::bind(game::state::credits::enter, ctx); + next_state.exit = std::bind(game::state::credits::exit, ctx); + ctx->app->queue_state(next_state); + } + ); + }; + auto select_back_callback = [ctx]() + { + // Disable controls + game::menu::clear_controls(ctx); + + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "main_menu"; + next_state.enter = std::bind(game::state::main_menu::enter, ctx, false); + next_state.exit = std::bind(game::state::main_menu::exit, ctx); + ctx->app->queue_state(next_state); + } + ); + }; + + // Build list of menu select callbacks + ctx->menu_select_callbacks.push_back(select_credits_callback); + ctx->menu_select_callbacks.push_back(select_back_callback); + + // Build list of menu left callbacks + ctx->menu_left_callbacks.push_back(nullptr); + ctx->menu_left_callbacks.push_back(nullptr); + + // Build list of menu right callbacks + ctx->menu_right_callbacks.push_back(nullptr); + ctx->menu_right_callbacks.push_back(nullptr); + + // Set menu back callback + ctx->menu_back_callback = select_back_callback; + + // Schedule menu control setup + timeline* timeline = ctx->timeline; + float t = timeline->get_position(); + timeline->add_sequence({{t + game::menu::input_delay, std::bind(game::menu::setup_controls, ctx)}}); + + // Fade in menu + game::menu::fade_in(ctx, nullptr); +} + +void exit(game::context* ctx) +{ + // Destruct menu + game::menu::clear_controls(ctx); + game::menu::clear_callbacks(ctx); + game::menu::delete_animations(ctx); + game::menu::remove_text_from_ui(ctx); + game::menu::delete_text(ctx); + + ctx->ui_clear_pass->set_cleared_buffers(false, true, false); +} + +} // namespace extras_menu +} // namespace state +} // namespace game diff --git a/src/game/states/extras-menu.hpp b/src/game/states/extras-menu.hpp new file mode 100644 index 0000000..ee1a9ac --- /dev/null +++ b/src/game/states/extras-menu.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_STATE_EXTRAS_MENU_HPP +#define ANTKEEPER_GAME_STATE_EXTRAS_MENU_HPP + +#include "game/context.hpp" + +namespace game { +namespace state { + +/// Extras menu screen game state functions. +namespace extras_menu { + +void enter(game::context* ctx); +void exit(game::context* ctx); + +} // namespace extras_menu + +} // namespace state +} // namespace game + +#endif // ANTKEEPER_GAME_STATE_EXTRAS_MENU_HPP diff --git a/src/game/states/gamepad-config-menu.cpp b/src/game/states/gamepad-config-menu.cpp index 1ff433d..bd81464 100644 --- a/src/game/states/gamepad-config-menu.cpp +++ b/src/game/states/gamepad-config-menu.cpp @@ -310,15 +310,26 @@ void enter(game::context* ctx) game::menu::align_text(ctx); game::menu::update_text_tweens(ctx); game::menu::add_text_to_ui(ctx); + game::menu::setup_animations(ctx); // Construct menu item callbacks auto select_back_callback = [ctx]() { - application::state next_state; - next_state.name = "controls_menu"; - next_state.enter = std::bind(game::state::controls_menu::enter, ctx); - next_state.exit = std::bind(game::state::controls_menu::exit, ctx); - ctx->app->change_state(next_state); + // Disable controls + game::menu::clear_controls(ctx); + + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "controls_menu"; + next_state.enter = std::bind(game::state::controls_menu::enter, ctx); + next_state.exit = std::bind(game::state::controls_menu::exit, ctx); + ctx->app->queue_state(next_state); + } + ); }; // Build list of menu select callbacks @@ -337,6 +348,9 @@ void enter(game::context* ctx) timeline* timeline = ctx->timeline; float t = timeline->get_position(); timeline->add_sequence({{t + game::menu::input_delay, std::bind(game::menu::setup_controls, ctx)}}); + + // Fade in menu + game::menu::fade_in(ctx, nullptr); } void exit(game::context* ctx) @@ -344,6 +358,7 @@ void exit(game::context* ctx) // Destruct menu game::menu::clear_controls(ctx); game::menu::clear_callbacks(ctx); + game::menu::delete_animations(ctx); game::menu::remove_text_from_ui(ctx); game::menu::delete_text(ctx); diff --git a/src/game/states/graphics-menu.cpp b/src/game/states/graphics-menu.cpp index 046776c..40ec570 100644 --- a/src/game/states/graphics-menu.cpp +++ b/src/game/states/graphics-menu.cpp @@ -91,6 +91,7 @@ void enter(game::context* ctx) game::menu::align_text(ctx); game::menu::update_text_tweens(ctx); game::menu::add_text_to_ui(ctx); + game::menu::setup_animations(ctx); // Construct menu item callbacks auto toggle_fullscreen_callback = [ctx]() @@ -271,11 +272,21 @@ void enter(game::context* ctx) }; auto select_back_callback = [ctx]() { - application::state next_state; - next_state.name = "options_menu"; - next_state.enter = std::bind(game::state::options_menu::enter, ctx); - next_state.exit = std::bind(game::state::options_menu::exit, ctx); - ctx->app->change_state(next_state); + // Disable controls + game::menu::clear_controls(ctx); + + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "options_menu"; + next_state.enter = std::bind(game::state::options_menu::enter, ctx); + next_state.exit = std::bind(game::state::options_menu::exit, ctx); + ctx->app->queue_state(next_state); + } + ); }; // Build list of menu select callbacks @@ -309,6 +320,9 @@ void enter(game::context* ctx) timeline* timeline = ctx->timeline; float t = timeline->get_position(); timeline->add_sequence({{t + game::menu::input_delay, std::bind(game::menu::setup_controls, ctx)}}); + + // Fade in menu + game::menu::fade_in(ctx, nullptr); } void exit(game::context* ctx) @@ -316,6 +330,7 @@ void exit(game::context* ctx) // Destruct menu game::menu::clear_controls(ctx); game::menu::clear_callbacks(ctx); + game::menu::delete_animations(ctx); game::menu::remove_text_from_ui(ctx); game::menu::delete_text(ctx); diff --git a/src/game/states/keyboard-config-menu.cpp b/src/game/states/keyboard-config-menu.cpp index e895f47..f197f10 100644 --- a/src/game/states/keyboard-config-menu.cpp +++ b/src/game/states/keyboard-config-menu.cpp @@ -265,15 +265,26 @@ void enter(game::context* ctx) game::menu::align_text(ctx); game::menu::update_text_tweens(ctx); game::menu::add_text_to_ui(ctx); + game::menu::setup_animations(ctx); // Construct menu item callbacks auto select_back_callback = [ctx]() { - application::state next_state; - next_state.name = "controls_menu"; - next_state.enter = std::bind(game::state::controls_menu::enter, ctx); - next_state.exit = std::bind(game::state::controls_menu::exit, ctx); - ctx->app->change_state(next_state); + // Disable controls + game::menu::clear_controls(ctx); + + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "controls_menu"; + next_state.enter = std::bind(game::state::controls_menu::enter, ctx); + next_state.exit = std::bind(game::state::controls_menu::exit, ctx); + ctx->app->queue_state(next_state); + } + ); }; // Build list of menu select callbacks @@ -292,6 +303,9 @@ void enter(game::context* ctx) timeline* timeline = ctx->timeline; float t = timeline->get_position(); timeline->add_sequence({{t + game::menu::input_delay, std::bind(game::menu::setup_controls, ctx)}}); + + // Fade in menu + game::menu::fade_in(ctx, nullptr); } void exit(game::context* ctx) @@ -299,6 +313,7 @@ void exit(game::context* ctx) // Destruct menu game::menu::clear_controls(ctx); game::menu::clear_callbacks(ctx); + game::menu::delete_animations(ctx); game::menu::remove_text_from_ui(ctx); game::menu::delete_text(ctx); diff --git a/src/game/states/language-menu.cpp b/src/game/states/language-menu.cpp index 6101ab1..13b17a5 100644 --- a/src/game/states/language-menu.cpp +++ b/src/game/states/language-menu.cpp @@ -65,6 +65,7 @@ void enter(game::context* ctx) game::menu::align_text(ctx); game::menu::update_text_tweens(ctx); game::menu::add_text_to_ui(ctx); + game::menu::setup_animations(ctx); // Construct menu item callbacks auto next_language_callback = [ctx]() @@ -137,11 +138,21 @@ void enter(game::context* ctx) }; auto select_back_callback = [ctx]() { - application::state next_state; - next_state.name = "options_menu"; - next_state.enter = std::bind(game::state::options_menu::enter, ctx); - next_state.exit = std::bind(game::state::options_menu::exit, ctx); - ctx->app->change_state(next_state); + // Disable controls + game::menu::clear_controls(ctx); + + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "options_menu"; + next_state.enter = std::bind(game::state::options_menu::enter, ctx); + next_state.exit = std::bind(game::state::options_menu::exit, ctx); + ctx->app->queue_state(next_state); + } + ); }; // Build list of menu select callbacks @@ -163,6 +174,9 @@ void enter(game::context* ctx) timeline* timeline = ctx->timeline; float t = timeline->get_position(); timeline->add_sequence({{t + game::menu::input_delay, std::bind(game::menu::setup_controls, ctx)}}); + + // Fade in menu + game::menu::fade_in(ctx, nullptr); } void exit(game::context* ctx) @@ -170,6 +184,7 @@ void exit(game::context* ctx) // Destruct menu game::menu::clear_controls(ctx); game::menu::clear_callbacks(ctx); + game::menu::delete_animations(ctx); game::menu::remove_text_from_ui(ctx); game::menu::delete_text(ctx); diff --git a/src/game/states/loading.cpp b/src/game/states/loading.cpp index 4b9ca3d..41deb6a 100644 --- a/src/game/states/loading.cpp +++ b/src/game/states/loading.cpp @@ -130,7 +130,7 @@ void enter(game::context* ctx) if (ctx->option_quick_start.has_value()) { next_state.name = "main_menu"; - next_state.enter = std::bind(game::state::main_menu::enter, ctx); + next_state.enter = std::bind(game::state::main_menu::enter, ctx, true); next_state.exit = std::bind(game::state::main_menu::exit, ctx); } else diff --git a/src/game/states/main-menu.cpp b/src/game/states/main-menu.cpp index 075203a..cd58ea0 100644 --- a/src/game/states/main-menu.cpp +++ b/src/game/states/main-menu.cpp @@ -19,9 +19,9 @@ #include "game/states/main-menu.hpp" #include "game/states/options-menu.hpp" +#include "game/states/extras-menu.hpp" #include "game/states/forage.hpp" #include "game/states/nuptial-flight.hpp" -#include "game/states/credits.hpp" #include "game/menu.hpp" #include "render/passes/clear-pass.hpp" #include "resources/resource-manager.hpp" @@ -38,7 +38,29 @@ namespace game { namespace state { namespace main_menu { -void enter(game::context* ctx) +static void fade_in_title(game::context* ctx) +{ + ctx->title_fade_animation->set_interpolator(ease::out_cubic); + animation_channel* opacity_channel = ctx->title_fade_animation->get_channel(0); + opacity_channel->remove_keyframes(); + opacity_channel->insert_keyframe({0.0, 0.0f}); + opacity_channel->insert_keyframe({game::menu::fade_in_duration, 1.0f}); + ctx->title_fade_animation->stop(); + ctx->title_fade_animation->play(); +} + +static void fade_out_title(game::context* ctx) +{ + ctx->title_fade_animation->set_interpolator(ease::out_cubic); + animation_channel* opacity_channel = ctx->title_fade_animation->get_channel(0); + opacity_channel->remove_keyframes(); + opacity_channel->insert_keyframe({0.0, 1.0f}); + opacity_channel->insert_keyframe({game::menu::fade_out_duration, 0.0f}); + ctx->title_fade_animation->stop(); + ctx->title_fade_animation->play(); +} + +void enter(game::context* ctx, bool fade_in) { ctx->ui_clear_pass->set_cleared_buffers(true, true, false); @@ -54,27 +76,42 @@ void enter(game::context* ctx) float title_w = title_aabb.max_point.x - title_aabb.min_point.x; float title_h = title_aabb.max_point.y - title_aabb.min_point.y; ctx->title_text->set_translation({std::round(-title_w * 0.5f), std::round(-title_h * 0.5f + (std::get<1>(ctx->app->get_viewport_dimensions()) / 3.0f) / 2.0f), 0.0f}); + ctx->title_text->update_tweens(); // Add title text to UI ctx->ui_scene->add_object(ctx->title_text); - ctx->title_text->update_tweens(); + + // Construct title fade animation + ctx->title_fade_animation = new animation(); + animation_channel* opacity_channel = ctx->title_fade_animation->add_channel(0); + + ctx->title_fade_animation->set_frame_callback + ( + [ctx](int channel, const float& opacity) + { + float4 color = ctx->title_text->get_color(); + color[3] = opacity; + ctx->title_text->set_color(color); + } + ); + ctx->animator->add_animation(ctx->title_fade_animation); // Construct menu item texts scene::text* start_text = new scene::text(); scene::text* options_text = new scene::text(); - scene::text* credits_text = new scene::text(); + scene::text* extras_text = new scene::text(); scene::text* quit_text = new scene::text(); // Build list of menu item texts ctx->menu_item_texts.push_back({start_text, nullptr}); ctx->menu_item_texts.push_back({options_text, nullptr}); - ctx->menu_item_texts.push_back({credits_text, nullptr}); + ctx->menu_item_texts.push_back({extras_text, nullptr}); ctx->menu_item_texts.push_back({quit_text, nullptr}); // Set content of menu item texts start_text->set_content((*ctx->strings)["main_menu_start"]); options_text->set_content((*ctx->strings)["main_menu_options"]); - credits_text->set_content((*ctx->strings)["main_menu_credits"]); + extras_text->set_content((*ctx->strings)["main_menu_extras"]); quit_text->set_content((*ctx->strings)["main_menu_quit"]); // Init menu item index @@ -85,6 +122,7 @@ void enter(game::context* ctx) game::menu::align_text(ctx, true, false, (-std::get<1>(ctx->app->get_viewport_dimensions()) / 3.0f) / 2.0f); game::menu::update_text_tweens(ctx); game::menu::add_text_to_ui(ctx); + game::menu::setup_animations(ctx); auto select_start_callback = [ctx]() { @@ -112,23 +150,50 @@ void enter(game::context* ctx) // Start fade out to white ctx->fade_transition_color->set_value({1, 1, 1}); - ctx->fade_transition->transition(fade_out_duration, false, ease::out_quad, false); + ctx->fade_transition->transition(fade_out_duration, false, ease::out_cubic, false); }; auto select_options_callback = [ctx]() { - application::state next_state; - next_state.name = "options_menu"; - next_state.enter = std::bind(game::state::options_menu::enter, ctx); - next_state.exit = std::bind(game::state::options_menu::exit, ctx); - ctx->app->change_state(next_state); + game::menu::clear_controls(ctx); + + // Fade out title + fade_out_title(ctx); + + // Fade out menu + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "options_menu"; + next_state.enter = std::bind(game::state::options_menu::enter, ctx); + next_state.exit = std::bind(game::state::options_menu::exit, ctx); + ctx->app->queue_state(next_state); + } + ); }; - auto select_credits_callback = [ctx]() + auto select_extras_callback = [ctx]() { - application::state next_state; - next_state.name = "credits"; - next_state.enter = std::bind(game::state::credits::enter, ctx); - next_state.exit = std::bind(game::state::credits::exit, ctx); - ctx->app->change_state(next_state); + // Disable controls + game::menu::clear_controls(ctx); + + // Fade out title + fade_out_title(ctx); + + // Fade out menu + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "extras_menu"; + next_state.enter = std::bind(game::state::extras_menu::enter, ctx); + next_state.exit = std::bind(game::state::extras_menu::exit, ctx); + ctx->app->queue_state(next_state); + } + ); }; auto select_quit_callback = [ctx]() { @@ -138,7 +203,7 @@ void enter(game::context* ctx) // Build list of menu select callbacks ctx->menu_select_callbacks.push_back(select_start_callback); ctx->menu_select_callbacks.push_back(select_options_callback); - ctx->menu_select_callbacks.push_back(select_credits_callback); + ctx->menu_select_callbacks.push_back(select_extras_callback); ctx->menu_select_callbacks.push_back(select_quit_callback); // Build list of menu left callbacks @@ -160,16 +225,37 @@ void enter(game::context* ctx) timeline* timeline = ctx->timeline; float t = timeline->get_position(); timeline->add_sequence({{t + game::menu::input_delay, std::bind(game::menu::setup_controls, ctx)}}); + + if (fade_in) + { + ctx->fade_transition->transition(0.5f, true, ease::out_cubic); + } + else + { + // Fade in title + ctx->title_text->set_color({1.0f, 1.0f, 1.0f, 0.0f}); + ctx->title_text->update_tweens(); + fade_in_title(ctx); + + // Fade in menu + game::menu::fade_in(ctx, nullptr); + } } void exit(game::context* ctx) -{ +{ // Destruct menu game::menu::clear_controls(ctx); game::menu::clear_callbacks(ctx); + game::menu::delete_animations(ctx); game::menu::remove_text_from_ui(ctx); game::menu::delete_text(ctx); + // Destruct title animation + ctx->animator->remove_animation(ctx->title_fade_animation); + delete ctx->title_fade_animation; + ctx->title_fade_animation = nullptr; + // Destruct title text ctx->ui_scene->remove_object(ctx->title_text); delete ctx->title_text; diff --git a/src/game/states/main-menu.hpp b/src/game/states/main-menu.hpp index 4164422..da607cd 100644 --- a/src/game/states/main-menu.hpp +++ b/src/game/states/main-menu.hpp @@ -28,7 +28,7 @@ namespace state { /// Main menu screen game state functions. namespace main_menu { -void enter(game::context* ctx); +void enter(game::context* ctx, bool fade_in); void exit(game::context* ctx); } // namespace main_menu diff --git a/src/game/states/options-menu.cpp b/src/game/states/options-menu.cpp index 907b697..ffc59b6 100644 --- a/src/game/states/options-menu.cpp +++ b/src/game/states/options-menu.cpp @@ -70,51 +70,106 @@ void enter(game::context* ctx) game::menu::align_text(ctx, true); game::menu::update_text_tweens(ctx); game::menu::add_text_to_ui(ctx); + game::menu::setup_animations(ctx); // Construct menu item callbacks auto select_controls_callback = [ctx]() { - application::state next_state; - next_state.name = "controls_menu"; - next_state.enter = std::bind(game::state::controls_menu::enter, ctx); - next_state.exit = std::bind(game::state::controls_menu::exit, ctx); - ctx->app->change_state(next_state); + // Disable controls + game::menu::clear_controls(ctx); + + // Return to main menu + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "controls_menu"; + next_state.enter = std::bind(game::state::controls_menu::enter, ctx); + next_state.exit = std::bind(game::state::controls_menu::exit, ctx); + ctx->app->queue_state(next_state); + } + ); }; auto select_graphics_callback = [ctx]() { - application::state next_state; - next_state.name = "graphics_menu"; - next_state.enter = std::bind(game::state::graphics_menu::enter, ctx); - next_state.exit = std::bind(game::state::graphics_menu::exit, ctx); - ctx->app->change_state(next_state); + // Disable controls + game::menu::clear_controls(ctx); + + // Return to main menu + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "graphics_menu"; + next_state.enter = std::bind(game::state::graphics_menu::enter, ctx); + next_state.exit = std::bind(game::state::graphics_menu::exit, ctx); + ctx->app->queue_state(next_state); + } + ); }; auto select_sound_callback = [ctx]() { - application::state next_state; - next_state.name = "sound_menu"; - next_state.enter = std::bind(game::state::sound_menu::enter, ctx); - next_state.exit = std::bind(game::state::sound_menu::exit, ctx); - ctx->app->change_state(next_state); + // Disable controls + game::menu::clear_controls(ctx); + + // Return to main menu + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "sound_menu"; + next_state.enter = std::bind(game::state::sound_menu::enter, ctx); + next_state.exit = std::bind(game::state::sound_menu::exit, ctx); + ctx->app->queue_state(next_state); + } + ); }; auto select_language_callback = [ctx]() { - application::state next_state; - next_state.name = "language_menu"; - next_state.enter = std::bind(game::state::language_menu::enter, ctx); - next_state.exit = std::bind(game::state::language_menu::exit, ctx); - ctx->app->change_state(next_state); + // Disable controls + game::menu::clear_controls(ctx); + + // Return to main menu + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "language_menu"; + next_state.enter = std::bind(game::state::language_menu::enter, ctx); + next_state.exit = std::bind(game::state::language_menu::exit, ctx); + ctx->app->queue_state(next_state); + } + ); }; auto select_back_callback = [ctx]() { + // Disable controls + game::menu::clear_controls(ctx); + // Save config game::save_config(ctx); // Return to main menu - application::state next_state; - next_state.name = "main_menu"; - next_state.enter = std::bind(game::state::main_menu::enter, ctx); - next_state.exit = std::bind(game::state::main_menu::exit, ctx); - ctx->app->change_state(next_state); + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "main_menu"; + next_state.enter = std::bind(game::state::main_menu::enter, ctx, false); + next_state.exit = std::bind(game::state::main_menu::exit, ctx); + ctx->app->queue_state(next_state); + } + ); }; // Build list of menu select callbacks @@ -137,6 +192,9 @@ void enter(game::context* ctx) timeline* timeline = ctx->timeline; float t = timeline->get_position(); timeline->add_sequence({{t + game::menu::input_delay, std::bind(game::menu::setup_controls, ctx)}}); + + // Fade in menu + game::menu::fade_in(ctx, nullptr); } void exit(game::context* ctx) @@ -144,6 +202,7 @@ void exit(game::context* ctx) // Destruct menu game::menu::clear_controls(ctx); game::menu::clear_callbacks(ctx); + game::menu::delete_animations(ctx); game::menu::remove_text_from_ui(ctx); game::menu::delete_text(ctx); diff --git a/src/game/states/sound-menu.cpp b/src/game/states/sound-menu.cpp index b27f345..69c17f2 100644 --- a/src/game/states/sound-menu.cpp +++ b/src/game/states/sound-menu.cpp @@ -89,7 +89,7 @@ void enter(game::context* ctx) game::menu::align_text(ctx); game::menu::update_text_tweens(ctx); game::menu::add_text_to_ui(ctx); - + game::menu::setup_animations(ctx); // Construct menu item callbacks auto increase_volume_callback = [ctx](float* volume) @@ -178,11 +178,21 @@ void enter(game::context* ctx) }; auto select_back_callback = [ctx]() { - application::state next_state; - next_state.name = "options_menu"; - next_state.enter = std::bind(game::state::options_menu::enter, ctx); - next_state.exit = std::bind(game::state::options_menu::exit, ctx); - ctx->app->change_state(next_state); + // Disable controls + game::menu::clear_controls(ctx); + + game::menu::fade_out + ( + ctx, + [ctx]() + { + application::state next_state; + next_state.name = "options_menu"; + next_state.enter = std::bind(game::state::options_menu::enter, ctx); + next_state.exit = std::bind(game::state::options_menu::exit, ctx); + ctx->app->queue_state(next_state); + } + ); }; // Build list of menu select callbacks @@ -219,6 +229,9 @@ void enter(game::context* ctx) timeline* timeline = ctx->timeline; float t = timeline->get_position(); timeline->add_sequence({{t + game::menu::input_delay, std::bind(game::menu::setup_controls, ctx)}}); + + // Fade in menu + game::menu::fade_in(ctx, nullptr); } void exit(game::context* ctx) @@ -226,6 +239,7 @@ void exit(game::context* ctx) // Destruct menu game::menu::clear_controls(ctx); game::menu::clear_callbacks(ctx); + game::menu::delete_animations(ctx); game::menu::remove_text_from_ui(ctx); game::menu::delete_text(ctx); diff --git a/src/game/states/splash.cpp b/src/game/states/splash.cpp index 07acbcc..8019c34 100644 --- a/src/game/states/splash.cpp +++ b/src/game/states/splash.cpp @@ -49,7 +49,7 @@ void enter(game::context* ctx) // Build splash fade in animation ctx->splash_fade_in_animation = new animation(); animation_channel* splash_fade_in_opacity_channel = ctx->splash_fade_in_animation->add_channel(0); - ctx->splash_fade_in_animation->set_interpolator(ease::in_quad); + ctx->splash_fade_in_animation->set_interpolator(ease::out_cubic); splash_fade_in_opacity_channel->insert_keyframe({0.0, 0.0f}); splash_fade_in_opacity_channel->insert_keyframe({splash_fade_in_duration, 1.0f}); splash_fade_in_opacity_channel->insert_keyframe({splash_fade_in_duration + splash_duration, 1.0f}); @@ -57,7 +57,7 @@ void enter(game::context* ctx) // Build splash fade out animation ctx->splash_fade_out_animation = new animation(); animation_channel* splash_fade_out_opacity_channel = ctx->splash_fade_out_animation->add_channel(0); - ctx->splash_fade_out_animation->set_interpolator(ease::out_quad); + ctx->splash_fade_out_animation->set_interpolator(ease::out_cubic); splash_fade_out_opacity_channel->insert_keyframe({0.0, 1.0f}); splash_fade_out_opacity_channel->insert_keyframe({splash_fade_out_duration, 0.0f}); @@ -95,7 +95,7 @@ void enter(game::context* ctx) { application::state next_state; next_state.name = "main_menu"; - next_state.enter = std::bind(game::state::main_menu::enter, ctx); + next_state.enter = std::bind(game::state::main_menu::enter, ctx, true); next_state.exit = std::bind(game::state::main_menu::exit, ctx); ctx->app->queue_state(next_state); } @@ -124,7 +124,7 @@ void enter(game::context* ctx) // Change state application::state next_state; next_state.name = "main_menu"; - next_state.enter = std::bind(game::state::main_menu::enter, ctx); + next_state.enter = std::bind(game::state::main_menu::enter, ctx, true); next_state.exit = std::bind(game::state::main_menu::exit, ctx); ctx->app->change_state(next_state); }