2020-04-13 11:24:53 +00:00
|
|
|
#include "menu.h"
|
|
|
|
|
|
|
|
#include <cmath>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "../base/collusion_test.h"
|
|
|
|
#include "../base/interpolation.h"
|
|
|
|
#include "../base/log.h"
|
|
|
|
#include "../engine/engine.h"
|
|
|
|
#include "../engine/font.h"
|
|
|
|
#include "../engine/image.h"
|
|
|
|
#include "../engine/input_event.h"
|
|
|
|
#include "demo.h"
|
|
|
|
|
|
|
|
using namespace base;
|
|
|
|
using namespace eng;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
constexpr char kMenuOption[Menu::kOption_Max][10] = {"continue", "start",
|
|
|
|
"credits", "exit"};
|
|
|
|
|
|
|
|
constexpr float kMenuOptionSpace = 1.5f;
|
|
|
|
|
|
|
|
const Vector4 kColorNormal = {1, 1, 1, 1};
|
|
|
|
const Vector4 kColorHighlight = {5, 5, 5, 1};
|
|
|
|
constexpr float kBlendingSpeed = 0.12f;
|
|
|
|
|
|
|
|
const Vector4 kColorFadeOut = {1, 1, 1, 0};
|
|
|
|
constexpr float kFadeSpeed = 0.2f;
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2020-06-30 22:23:07 +00:00
|
|
|
Menu::Menu() = default;
|
2020-04-13 11:24:53 +00:00
|
|
|
|
|
|
|
Menu::~Menu() = default;
|
|
|
|
|
|
|
|
bool Menu::Initialize() {
|
|
|
|
const Font& font = static_cast<Demo*>(Engine::Get().GetGame())->GetFont();
|
|
|
|
|
|
|
|
max_text_width_ = -1;
|
|
|
|
for (int i = 0; i < kOption_Max; ++i) {
|
|
|
|
int width, height;
|
|
|
|
font.CalculateBoundingBox(kMenuOption[i], width, height);
|
|
|
|
if (width > max_text_width_)
|
|
|
|
max_text_width_ = width;
|
|
|
|
}
|
|
|
|
|
2020-06-30 22:23:07 +00:00
|
|
|
if (!CreateRenderResources())
|
|
|
|
return false;
|
2020-04-13 11:24:53 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < kOption_Max; ++i) {
|
2020-06-30 22:23:07 +00:00
|
|
|
items_[i].text.Create("menu_tex", {1, 4});
|
|
|
|
items_[i].text.SetZOrder(40);
|
2020-04-13 11:24:53 +00:00
|
|
|
items_[i].text.Scale(1.5f);
|
|
|
|
items_[i].text.SetColor(kColorFadeOut);
|
|
|
|
items_[i].text.SetVisible(false);
|
|
|
|
items_[i].text.SetFrame(i);
|
|
|
|
|
|
|
|
items_[i].select_item_cb_ = [&, i]() -> void {
|
|
|
|
items_[i].text_animator.SetEndCallback(
|
|
|
|
Animator::kBlending, [&, i]() -> void {
|
|
|
|
items_[i].text_animator.SetEndCallback(Animator::kBlending,
|
|
|
|
nullptr);
|
|
|
|
selected_option_ = (Option)i;
|
|
|
|
});
|
|
|
|
items_[i].text_animator.SetBlending(kColorNormal, kBlendingSpeed);
|
|
|
|
items_[i].text_animator.Play(Animator::kBlending, false);
|
|
|
|
};
|
|
|
|
items_[i].text_animator.Attach(&items_[i].text);
|
|
|
|
}
|
|
|
|
// Get the item positions calculated.
|
|
|
|
SetOptionEnabled(kContinue, true);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::OnInputEvent(std::unique_ptr<InputEvent> event) {
|
2020-06-30 22:23:07 +00:00
|
|
|
if (event->GetType() == InputEvent::kDragStart)
|
2020-09-17 22:03:21 +00:00
|
|
|
tap_pos_[0] = tap_pos_[1] = event->GetVector();
|
2020-04-13 11:24:53 +00:00
|
|
|
else if (event->GetType() == InputEvent::kDrag)
|
2020-09-17 22:03:21 +00:00
|
|
|
tap_pos_[1] = event->GetVector();
|
2020-04-13 11:24:53 +00:00
|
|
|
|
2020-06-30 22:23:07 +00:00
|
|
|
if (event->GetType() != InputEvent::kDragEnd || IsAnimating())
|
2020-04-13 11:24:53 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
for (int i = 0; i < kOption_Max; ++i) {
|
|
|
|
if (items_[i].hide)
|
|
|
|
continue;
|
2020-09-17 22:03:21 +00:00
|
|
|
if (!Intersection(items_[i].text.GetPosition(),
|
|
|
|
items_[i].text.GetSize() * Vector2(1.2f, 2), tap_pos_[0]))
|
2020-04-13 11:24:53 +00:00
|
|
|
continue;
|
2020-09-17 22:03:21 +00:00
|
|
|
if (!Intersection(items_[i].text.GetPosition(),
|
|
|
|
items_[i].text.GetSize() * Vector2(1.2f, 2), tap_pos_[1]))
|
2020-04-13 11:24:53 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
items_[i].text_animator.SetEndCallback(Animator::kBlending,
|
|
|
|
items_[i].select_item_cb_);
|
|
|
|
items_[i].text_animator.SetBlending(kColorHighlight, kBlendingSpeed);
|
|
|
|
items_[i].text_animator.Play(Animator::kBlending, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::SetOptionEnabled(Option o, bool enable) {
|
|
|
|
int first = -1, last = -1;
|
|
|
|
for (int i = 0; i < kOption_Max; ++i) {
|
|
|
|
if (i == o)
|
|
|
|
items_[i].hide = !enable;
|
|
|
|
if (!items_[i].hide) {
|
2020-09-17 22:03:21 +00:00
|
|
|
items_[i].text.SetPosition({0, 0});
|
2020-04-13 11:24:53 +00:00
|
|
|
if (last >= 0) {
|
|
|
|
items_[i].text.PlaceToBottomOf(items_[last].text);
|
2020-09-17 22:03:21 +00:00
|
|
|
items_[i].text.Translate(items_[last].text.GetPosition() *
|
|
|
|
Vector2(0, 1));
|
2020-04-13 11:24:53 +00:00
|
|
|
items_[i].text.Translate(
|
2020-09-17 22:03:21 +00:00
|
|
|
{0, items_[last].text.GetSize().y * -kMenuOptionSpace});
|
2020-04-13 11:24:53 +00:00
|
|
|
}
|
|
|
|
if (first < 0)
|
|
|
|
first = i;
|
|
|
|
last = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
float center_offset_y =
|
2020-09-17 22:03:21 +00:00
|
|
|
(items_[first].text.GetPosition().y - items_[last].text.GetPosition().y) /
|
|
|
|
2;
|
2020-04-13 11:24:53 +00:00
|
|
|
for (int i = 0; i < kOption_Max; ++i) {
|
|
|
|
if (!items_[i].hide)
|
|
|
|
items_[i].text.Translate({0, center_offset_y});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::Show() {
|
|
|
|
for (int i = 0; i < kOption_Max; ++i) {
|
|
|
|
if (items_[i].hide)
|
|
|
|
continue;
|
|
|
|
items_[i].text_animator.SetEndCallback(
|
|
|
|
Animator::kBlending, [&, i]() -> void {
|
|
|
|
items_[i].text_animator.SetEndCallback(Animator::kBlending, nullptr);
|
|
|
|
});
|
|
|
|
items_[i].text_animator.SetBlending(kColorNormal, kFadeSpeed);
|
|
|
|
items_[i].text_animator.Play(Animator::kBlending, false);
|
|
|
|
items_[i].text.SetVisible(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Menu::Hide() {
|
|
|
|
selected_option_ = kOption_Invalid;
|
|
|
|
for (int i = 0; i < kOption_Max; ++i) {
|
|
|
|
if (items_[i].hide)
|
|
|
|
continue;
|
|
|
|
items_[i].text_animator.SetEndCallback(
|
|
|
|
Animator::kBlending, [&, i]() -> void {
|
|
|
|
items_[i].text_animator.SetEndCallback(Animator::kBlending, nullptr);
|
|
|
|
items_[i].text.SetVisible(false);
|
|
|
|
});
|
|
|
|
items_[i].text_animator.SetBlending(kColorFadeOut, kFadeSpeed);
|
|
|
|
items_[i].text_animator.Play(Animator::kBlending, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-30 22:23:07 +00:00
|
|
|
bool Menu::CreateRenderResources() {
|
|
|
|
Engine::Get().SetImageSource("menu_tex", std::bind(&Menu::CreateImage, this));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-04-13 11:24:53 +00:00
|
|
|
std::unique_ptr<Image> Menu::CreateImage() {
|
|
|
|
const Font& font = static_cast<Demo*>(Engine::Get().GetGame())->GetFont();
|
|
|
|
|
|
|
|
int line_height = font.GetLineHeight() + 1;
|
|
|
|
auto image = std::make_unique<Image>();
|
|
|
|
image->Create(max_text_width_, line_height * kOption_Max);
|
|
|
|
|
|
|
|
// Fill the area of each menu item with gradient.
|
|
|
|
image->GradientV({1.0f, 1.0f, 1.0f, 0}, {.0f, .0f, 1.0f, 0}, line_height);
|
|
|
|
|
|
|
|
for (int i = 0; i < kOption_Max; ++i) {
|
|
|
|
int w, h;
|
|
|
|
font.CalculateBoundingBox(kMenuOption[i], w, h);
|
|
|
|
float x = (image->GetWidth() - w) / 2;
|
|
|
|
float y = line_height * i;
|
2020-06-30 22:23:07 +00:00
|
|
|
font.Print(x, y, kMenuOption[i], image->GetBuffer(), image->GetWidth());
|
2020-04-13 11:24:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return image;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Menu::IsAnimating() {
|
|
|
|
for (int i = 0; i < kOption_Max; ++i) {
|
|
|
|
if (items_[i].text_animator.IsPlaying(Animator::kBlending))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|