Skip to content

Commit d4d7b0e

Browse files
committed
Add some flair to countdown
Games should not have "static" screens with nothing happening.
1 parent 9725757 commit d4d7b0e

4 files changed

Lines changed: 112 additions & 11 deletions

File tree

lib/include/chomper/runtimes/game.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
#include "chomper/engine.hpp"
44
#include "chomper/player.hpp"
55
#include "chomper/runtime.hpp"
6+
#include "chomper/ui/countdown.hpp"
67
#include "chomper/world.hpp"
78
#include <klib/ptr.hpp>
89
#include <le2d/drawable/text.hpp>
910
#include <le2d/input/action.hpp>
1011
#include <le2d/input/scoped_mapping.hpp>
1112
#include <le2d/random.hpp>
1213
#include <le2d/resource/texture.hpp>
14+
#include <optional>
1315
#include <unordered_set>
1416

1517
namespace chomper::runtime {
@@ -57,7 +59,6 @@ class Game : public IRuntime, public klib::Pinned {
5759

5860
std::vector<int> m_emptyTiles{};
5961

60-
le::drawable::Text m_countdownText{};
61-
kvf::Seconds m_countdown{3};
62+
std::optional<ui::Countdown> m_countdown{};
6263
};
6364
} // namespace chomper::runtime
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#pragma once
2+
#include "le2d/drawable/shape.hpp"
3+
#include "le2d/drawable/text.hpp"
4+
#include <kvf/time.hpp>
5+
6+
namespace chomper::ui {
7+
class Countdown {
8+
public:
9+
static constexpr auto textHeight_v = le::TextHeight{120};
10+
11+
explicit Countdown(gsl::not_null<le::IFont*> font, le::TextHeight textHeight = textHeight_v, kvf::Seconds timer = 3s);
12+
13+
[[nodiscard]] auto getRemain() const -> kvf::Seconds {
14+
return m_remain;
15+
}
16+
17+
void tick(kvf::Seconds dt);
18+
void draw(le::IRenderer& renderer) const;
19+
20+
private:
21+
void setTimerText(std::chrono::seconds value);
22+
void updateSector();
23+
24+
gsl::not_null<le::IFont*> m_font;
25+
le::TextHeight m_textHeight{120};
26+
27+
le::drawable::Sector m_sector{};
28+
le::drawable::Circle m_background{};
29+
le::drawable::Text m_text{};
30+
31+
kvf::Seconds m_timer{};
32+
kvf::Seconds m_remain{};
33+
};
34+
} // namespace chomper::ui

lib/src/runtimes/game.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@
99

1010
namespace chomper::runtime {
1111
namespace {
12-
constexpr auto countdownParams_v = le::drawable::Text::Params{
13-
.height = le::TextHeight{60},
14-
};
1512
constexpr auto collectibleAmount_v = 10;
1613
} // namespace
14+
1715
using ActionValue = le::input::action::Value;
1816

1917
Game::Game(gsl::not_null<Engine*> engine) : m_engine(engine), m_mapping(&engine->getInputRouter()) {
@@ -24,7 +22,7 @@ Game::Game(gsl::not_null<Engine*> engine) : m_engine(engine), m_mapping(&engine-
2422

2523
spawnCollectibles();
2624

27-
m_countdownText.set_string(engine->getResources().getMainFont(), "3", countdownParams_v);
25+
m_countdown.emplace(&engine->getResources().getMainFont());
2826
}
2927

3028
void Game::tick(kvf::Seconds const dt) {
@@ -34,9 +32,11 @@ void Game::tick(kvf::Seconds const dt) {
3432
}
3533
ImGui::End();
3634

37-
if (m_countdown.count() > 0) {
38-
m_countdown -= dt;
39-
m_countdownText.set_string(m_engine->getResources().getMainFont(), std::format("{}", static_cast<int>(m_countdown.count() + 1)), countdownParams_v);
35+
if (m_countdown) {
36+
m_countdown->tick(dt);
37+
if (m_countdown->getRemain() <= 0s) {
38+
m_countdown.reset();
39+
}
4040
return;
4141
}
4242

@@ -56,8 +56,8 @@ void Game::render(le::IRenderer& renderer) const {
5656
collectible.draw(renderer);
5757
}
5858
m_player->draw(renderer);
59-
if (m_countdown.count() > 0) {
60-
m_countdownText.draw(renderer);
59+
if (m_countdown) {
60+
m_countdown->draw(renderer);
6161
}
6262
}
6363

lib/src/ui/countdown.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#include "chomper/ui/countdown.hpp"
2+
#include "chomper/theme.hpp"
3+
#include <klib/assert.hpp>
4+
5+
namespace chomper::ui {
6+
Countdown::Countdown(gsl::not_null<le::IFont*> font, le::TextHeight const textHeight, kvf::Seconds const timer)
7+
: m_font(font), m_textHeight(textHeight), m_timer(timer), m_remain(timer) {
8+
auto const diameter = float(m_textHeight) + 30.0f;
9+
m_background.create(diameter);
10+
m_background.tint = theme::clearColor_v;
11+
}
12+
13+
void Countdown::tick(kvf::Seconds const dt) {
14+
if (m_remain <= 0s) {
15+
return;
16+
}
17+
18+
auto const prevSeconds = std::chrono::duration_cast<std::chrono::seconds>(m_remain);
19+
m_remain -= dt;
20+
auto const currSeconds = std::chrono::duration_cast<std::chrono::seconds>(m_remain);
21+
if (prevSeconds > currSeconds) {
22+
setTimerText(currSeconds + 1s);
23+
}
24+
25+
updateSector();
26+
}
27+
28+
void Countdown::draw(le::IRenderer& renderer) const {
29+
if (m_remain <= 0s) {
30+
return;
31+
}
32+
33+
m_sector.draw(renderer);
34+
m_background.draw(renderer);
35+
m_text.draw(renderer);
36+
}
37+
38+
void Countdown::setTimerText(std::chrono::seconds const value) {
39+
auto const params = le::drawable::Text::Params{
40+
.height = m_textHeight,
41+
.expand = le::drawable::TextExpand::eBoth,
42+
};
43+
auto const text = std::format("{}", value.count());
44+
m_text.set_string(*m_font, text, params);
45+
46+
// technically this isn't correct y-centering because parts of glyphs can be above/below the baseline,
47+
// and Text::get_size() is insufficient to adjust for that.
48+
// here, since each displayed glyph (0-9) is entirely above the baseline (unlike say 'g'),
49+
// it can be pushed down by half the size and it will "look" y-centered.
50+
// this is what is known as a "hack".
51+
m_text.transform.position.y = -0.5f * m_text.get_size().y;
52+
}
53+
54+
void Countdown::updateSector() {
55+
KLIB_ASSERT(m_timer > 0s);
56+
auto const ratio = m_remain / m_timer;
57+
static constexpr auto degreesBegin{90.0f};
58+
auto const degreesEnd = degreesBegin + (ratio * 360.0f);
59+
auto const diameter = m_background.get_diameter() + 30.0f;
60+
auto const sectorParams = le::shape::Sector::Params{
61+
.degrees_begin = degreesBegin,
62+
.degrees_end = degreesEnd,
63+
};
64+
m_sector.create(diameter, sectorParams);
65+
}
66+
} // namespace chomper::ui

0 commit comments

Comments
 (0)