Skip to content

Commit 82d8f9a

Browse files
authored
bgf updates, star field, signals. (#50)
* bgf v0.1.5, faster game restart (avoid scene reload). * Show weapon and rounds on HUD. * Use gradient texture for beam. * Keep `GunBeam` busy until both fire and reload delay have elapsed. * Add `StarField`. * Use vertical color gradient for background. * Remove invalid `sync()` call. * Use `Layout` instead of `IDisplay`. * WIP: bgf v0.1.6 * Add `Signal`, `GameSignals`. Use for HUD weapon icon changes. * Use circles for powerups. * Add game music.
1 parent 3cf1c29 commit 82d8f9a

37 files changed

+435
-170
lines changed

CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ include(FetchContent)
1111
FetchContent_Declare(
1212
bgf
1313
GIT_REPOSITORY https://github.com/karnkaul/bgf
14-
GIT_TAG v0.1.4
14+
15+
# GIT_TAG v0.1.5
16+
GIT_TAG adc217f
1517
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ext/bgf"
1618
)
1719

assets/images/beam_round.png

4.86 KB
Loading

assets/images/star_blue.png

5.42 KB
Loading

assets/images/star_red.png

4.85 KB
Loading

assets/images/star_yellow.png

5.07 KB
Loading

assets/music/game.mp3

1.75 MB
Binary file not shown.

assets/particles/explode.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
}
2828
},
2929
"angular": {
30-
"lo": -90.000000,
31-
"hi": 90.000000
30+
"lo": -360.000000,
31+
"hi": 360.000000
3232
}
3333
},
3434
"lerp": {
@@ -52,8 +52,8 @@
5252
"hi": 0.800000
5353
},
5454
"quad_size": [
55-
100.000000,
56-
100.000000
55+
150.000000,
56+
150.000000
5757
],
5858
"count": 40,
5959
"respawn": true

assets/styles.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
"milk": "#e5cdaeff",
77
"ice": "#d6dbe1e1",
88
"orange": "#f75c03ff",
9-
"gun_beam": "#bc96e6ff"
9+
"gun_beam": "#bc96e6ff",
10+
"bg_top": "#10020eff",
11+
"bg_bottom": "#040003ff"
1012
},
1113
"buttons": {
1214
"default": {

src/spaced/spaced/game/arsenal.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
#include <spaced/game/arsenal.hpp>
2+
#include <spaced/services/game_signals.hpp>
23
#include <spaced/services/stats.hpp>
34

45
namespace spaced {
56
using bave::Seconds;
67
using bave::Services;
78
using bave::Shader;
89

9-
Arsenal::Arsenal(Services const& services) : m_primary(services), m_stats(&services.get<Stats>()) {}
10+
Arsenal::Arsenal(Services const& services)
11+
: m_stats(&services.get<Stats>()), m_weapon_changed_signal(&services.get<GameSignals>().weapon_changed), m_primary(services) {
12+
m_weapon_changed_signal->dispatch(get_weapon());
13+
}
1014

1115
auto Arsenal::get_weapon() const -> Weapon const& {
1216
if (m_special) { return *m_special; }
@@ -34,13 +38,19 @@ void Arsenal::tick_weapons(Seconds const dt) {
3438
if (m_special) {
3539
m_special->tick(dt);
3640
// if the special weapon has no more rounds and is idle, reset it.
37-
if (m_special->get_rounds_remaining() == 0 && m_special->is_idle()) { m_special.reset(); }
41+
if (m_special->get_rounds_remaining() == 0 && m_special->is_idle()) {
42+
m_special.reset();
43+
m_weapon_changed_signal->dispatch(get_weapon());
44+
}
3845
}
3946
}
4047

4148
void Arsenal::check_switch_weapon() {
4249
// if there is a next weapon on standby and the current weapon is idle, switch to the next weapon.
43-
if (m_next && get_weapon().is_idle()) { m_special = std::move(m_next); }
50+
if (m_next && get_weapon().is_idle()) {
51+
m_special = std::move(m_next);
52+
m_weapon_changed_signal->dispatch(get_weapon());
53+
}
4454
}
4555

4656
void Arsenal::fire_weapon(glm::vec2 const muzzle_position) {

src/spaced/spaced/game/arsenal.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
namespace spaced {
55
struct Stats;
6+
struct SigWeaponChanged;
67

78
// Arsenal models a main/primary weapon, and an possible special weapon.
89
// Weapons only switch when they are idle.
@@ -26,8 +27,10 @@ class Arsenal {
2627
void fire_weapon(glm::vec2 muzzle_position);
2728
void tick_rounds(IWeaponRound::State const& round_state, bave::Seconds dt);
2829

29-
GunKinetic m_primary; // main weapon
3030
bave::NotNull<Stats*> m_stats;
31+
bave::NotNull<SigWeaponChanged*> m_weapon_changed_signal;
32+
33+
GunKinetic m_primary; // main weapon
3134
std::unique_ptr<Weapon> m_special{}; // special weapon
3235
std::unique_ptr<Weapon> m_next{}; // next special weapon (on standby until current weapon is idle)
3336
std::vector<std::unique_ptr<Weapon::Round>> m_rounds{};

src/spaced/spaced/game/controllers/player_controller.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,19 @@ using bave::Action;
99
using bave::EnumArray;
1010
using bave::GamepadAxis;
1111
using bave::GamepadButton;
12-
using bave::IDisplay;
1312
using bave::im_text;
1413
using bave::PointerMove;
1514
using bave::PointerTap;
1615
using bave::Seconds;
1716
using bave::Services;
1817

19-
PlayerController::PlayerController(Services const& services) : m_display(&services.get<IDisplay>()), m_gamepad_provider(&services.get<IGamepadProvider>()) {
20-
max_y = 0.5f * m_display->get_world_space().y;
18+
PlayerController::PlayerController(Services const& services) : m_layout(&services.get<Layout>()), m_gamepad_provider(&services.get<IGamepadProvider>()) {
19+
max_y = 0.5f * m_layout->world_space.y;
2120
min_y = -max_y; // NOLINT(cppcoreguidelines-prefer-member-initializer)
2221
}
2322

2423
void PlayerController::on_move(PointerMove const& pointer_move) {
25-
auto const world_pos = m_display->project_to_world(pointer_move.pointer.position);
24+
auto const world_pos = m_layout->project(pointer_move.pointer.position);
2625

2726
if (m_type == Type::eTouch) {
2827
if (!is_in_move_area(world_pos)) {
@@ -41,7 +40,7 @@ void PlayerController::on_move(PointerMove const& pointer_move) {
4140
}
4241

4342
void PlayerController::on_tap(PointerTap const& pointer_tap) {
44-
auto const world_pos = m_display->project_to_world(pointer_tap.pointer.position);
43+
auto const world_pos = m_layout->project(pointer_tap.pointer.position);
4544
if (m_type == Type::eTouch && is_in_move_area(world_pos)) {
4645
// pointer tap in move area is ingored
4746
return;
@@ -68,7 +67,7 @@ void PlayerController::stop_firing() {
6867
}
6968

7069
auto PlayerController::is_in_move_area(glm::vec2 const position) const -> bool {
71-
auto const width = m_display->get_world_space().x;
70+
auto const width = m_layout->world_space.x;
7271
auto const n_pos = (position.x + 0.5f * width) / width;
7372
return n_pos <= n_move_area;
7473
}

src/spaced/spaced/game/controllers/player_controller.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
#pragma once
22
#include <bave/core/ptr.hpp>
33
#include <bave/input/gamepad.hpp>
4-
#include <bave/services/display.hpp>
54
#include <bave/services/services.hpp>
65
#include <spaced/game/controllers/follow_controller.hpp>
76
#include <spaced/game/spring_arm.hpp>
87
#include <spaced/services/gamepad_provider.hpp>
8+
#include <spaced/services/layout.hpp>
99

1010
namespace spaced {
1111
class PlayerController : public FollowController {
@@ -45,8 +45,8 @@ class PlayerController : public FollowController {
4545
auto tick_y(bave::Seconds dt) -> float final;
4646
void do_inspect() final;
4747

48-
bave::Ptr<bave::IDisplay const> m_display{};
49-
bave::Ptr<IGamepadProvider const> m_gamepad_provider{};
48+
bave::NotNull<Layout const*> m_layout;
49+
bave::NotNull<IGamepadProvider const*> m_gamepad_provider;
5050

5151
Type m_type{};
5252
float m_y{};

src/spaced/spaced/game/hud.cpp

Lines changed: 55 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,56 +3,54 @@
33
#include <bave/services/styles.hpp>
44
#include <bave/ui/button.hpp>
55
#include <spaced/game/hud.hpp>
6+
#include <spaced/services/game_signals.hpp>
67
#include <spaced/services/layout.hpp>
78

89
namespace spaced {
9-
using bave::IDisplay;
10+
using bave::Display;
1011
using bave::Resources;
1112
using bave::Seconds;
1213
using bave::Services;
13-
using bave::Shader;
1414
using bave::Styles;
15+
using bave::Text;
1516
using bave::TextHeight;
1617
using bave::Texture;
1718

1819
namespace ui = bave::ui;
1920

20-
Hud::Hud(Services const& services)
21-
: ui::View(services), m_display(&services.get<IDisplay>()), m_layout(&services.get<Layout>()), m_styles(&services.get<Styles>()) {
21+
Hud::Hud(Services const& services) : ui::View(services), m_layout(&services.get<Layout>()), m_styles(&services.get<Styles>()) {
2222
create_background();
2323
create_score(services);
24-
create_lives_icon(services);
24+
create_lives(services);
25+
create_weapon(services);
2526

2627
block_input_events = false;
27-
render_view = m_display->get_world_view();
28-
}
29-
30-
void Hud::set_lives(int const lives) {
31-
if (lives <= 0) {
32-
m_lives_icon.instances.clear();
33-
return;
34-
}
28+
render_view = services.get<Display>().world.render_view;
3529

36-
m_lives_icon.instances.resize(static_cast<std::size_t>(lives));
37-
auto x_offset = 0.0f;
38-
for (auto& instance : m_lives_icon.instances) {
39-
instance.transform.position.x += x_offset;
40-
x_offset += 2.0f * m_lives_icon.get_shape().size.x;
41-
}
30+
m_on_weapon_changed = services.get<GameSignals>().weapon_changed.connect([this](Weapon const& weapon) { set_weapon(weapon.get_icon()); });
4231
}
4332

44-
void Hud::on_death() {
45-
if (m_lives_icon.instances.empty()) { return; }
46-
m_lives_icon.instances.pop_back();
33+
void Hud::set_lives(int lives) {
34+
lives = std::clamp(lives, 0, 99); // TODO: max_lives
35+
m_lives_count->text.set_string(fmt::format("x{}", lives));
4736
}
4837

4938
void Hud::set_score(std::int64_t const score) { m_score->text.set_string(fmt::format("{}", score)); }
5039

5140
void Hud::set_hi_score(std::int64_t const score) { m_hi_score->text.set_string(fmt::format("HI {}", score)); }
5241

53-
void Hud::render(Shader& shader) const {
54-
View::render(shader);
55-
m_lives_icon.draw(shader);
42+
void Hud::set_weapon(std::shared_ptr<Texture const> texture) {
43+
if (texture) { m_weapon_icon->sprite.set_size(texture->get_size()); }
44+
m_weapon_icon->sprite.set_texture(std::move(texture));
45+
}
46+
47+
auto Hud::make_text(Services const& services) const -> std::unique_ptr<ui::Text> {
48+
auto const& rgbas = m_styles->rgbas;
49+
auto text = std::make_unique<ui::Text>(services);
50+
text->text.set_height(TextHeight{60});
51+
text->text.transform.position = m_layout->hud_area.centre();
52+
text->text.tint = rgbas["grey"];
53+
return text;
5654
}
5755

5856
void Hud::create_background() {
@@ -66,45 +64,54 @@ void Hud::create_background() {
6664
}
6765

6866
void Hud::create_score(Services const& services) {
69-
auto const& rgbas = m_styles->rgbas;
70-
71-
auto make_text = [&] {
72-
auto text = std::make_unique<ui::Text>(services);
73-
text->text.set_height(TextHeight{60});
74-
text->text.transform.position = m_layout->hud_area.centre();
75-
text->text.tint = rgbas["grey"];
76-
return text;
77-
};
78-
79-
auto text = make_text();
67+
auto text = make_text(services);
8068
m_score = text.get();
8169
text->text.set_string("9999999999");
8270
m_text_bounds_size = text->text.get_bounds().size();
8371
text->text.transform.position.y -= 0.5f * m_text_bounds_size.y;
8472
set_score(0);
85-
8673
push(std::move(text));
8774

88-
text = make_text();
75+
text = make_text(services);
8976
m_hi_score = text.get();
9077
text->text.transform.position.x = m_layout->hud_area.rb.x - 50.0f;
9178
text->text.transform.position.y -= 0.5f * m_text_bounds_size.y;
92-
text->text.set_align(bave::Text::Align::eLeft);
79+
text->text.set_align(Text::Align::eLeft);
9380
set_hi_score(0);
94-
9581
push(std::move(text));
9682
}
9783

98-
void Hud::create_lives_icon(Services const& services) {
99-
auto quad = m_lives_icon.get_shape();
100-
quad.size = glm::vec2{20.0f};
84+
void Hud::create_lives(Services const& services) {
85+
auto sprite = std::make_unique<ui::Sprite>();
86+
m_lives_icon = sprite.get();
87+
m_lives_icon->sprite.set_size(glm::vec2{20.0f});
10188
auto const& resources = services.get<Resources>();
10289
if (auto const texture = resources.get<Texture>("images/player_ship_icon.png")) {
103-
quad.size = texture->get_size();
104-
m_lives_icon.set_texture(texture);
90+
m_lives_icon->sprite.set_texture(texture);
91+
m_lives_icon->sprite.set_size(texture->get_size());
10592
}
106-
m_lives_icon.set_shape(quad);
107-
m_lives_icon.transform.position = m_layout->hud_area.centre();
108-
m_lives_icon.transform.position.x = m_layout->hud_area.lt.x + 100.0f;
93+
auto position = m_layout->hud_area.centre();
94+
position.x = m_layout->hud_area.lt.x + 100.0f;
95+
m_lives_icon->set_position(position);
96+
push(std::move(sprite));
97+
98+
auto text = make_text(services);
99+
text->text.transform.position.y -= 0.5f * m_text_bounds_size.y;
100+
text->text.transform.position.x = m_lives_icon->get_position().x + m_lives_icon->get_size().x;
101+
text->text.set_align(Text::Align::eRight);
102+
text->text.set_string("0");
103+
m_lives_count = text.get();
104+
push(std::move(text));
105+
}
106+
107+
void Hud::create_weapon(Services const& /*services*/) {
108+
auto sprite = std::make_unique<ui::Sprite>();
109+
m_weapon_icon = sprite.get();
110+
sprite->sprite.set_size(glm::vec2{50.0f});
111+
auto position = m_lives_icon->get_position();
112+
position.y -= 5.0f;
113+
position.x = m_lives_count->get_position().x + 200.0f;
114+
sprite->set_position(position);
115+
push(std::move(sprite));
109116
}
110117
} // namespace spaced

src/spaced/spaced/game/hud.hpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
#pragma once
2-
#include <bave/graphics/instanced.hpp>
32
#include <bave/services/styles.hpp>
43
#include <bave/ui/outline_quad.hpp>
4+
#include <bave/ui/sprite.hpp>
55
#include <bave/ui/text.hpp>
66
#include <bave/ui/view.hpp>
7+
#include <spaced/signal.hpp>
78

89
namespace spaced {
910
struct Layout;
@@ -13,26 +14,31 @@ class Hud : public bave::ui::View {
1314
explicit Hud(bave::Services const& services);
1415

1516
void set_lives(int lives);
16-
void on_death();
1717
void set_score(std::int64_t score);
1818
void set_hi_score(std::int64_t score);
19+
void set_weapon(std::shared_ptr<bave::Texture const> texture);
1920

2021
private:
21-
void render(bave::Shader& shader) const final;
22+
[[nodiscard]] auto make_text(bave::Services const& services) const -> std::unique_ptr<bave::ui::Text>;
2223

2324
void create_background();
2425
void create_score(bave::Services const& services);
25-
void create_lives_icon(bave::Services const& services);
26+
void create_lives(bave::Services const& services);
27+
void create_weapon(bave::Services const& services);
2628

27-
bave::NotNull<bave::IDisplay const*> m_display;
2829
bave::NotNull<Layout const*> m_layout;
2930
bave::NotNull<bave::Styles const*> m_styles;
3031
glm::vec2 m_text_bounds_size{};
3132

33+
SignalHandle m_on_weapon_changed{};
34+
3235
bave::Ptr<bave::ui::OutlineQuad> m_background{};
3336
bave::Ptr<bave::ui::Text> m_score{};
3437
bave::Ptr<bave::ui::Text> m_hi_score{};
3538

36-
bave::Instanced<bave::QuadShape> m_lives_icon{};
39+
bave::Ptr<bave::ui::Sprite> m_lives_icon{};
40+
bave::Ptr<bave::ui::Text> m_lives_count{};
41+
42+
bave::Ptr<bave::ui::Sprite> m_weapon_icon{};
3743
};
3844
} // namespace spaced

src/spaced/spaced/game/player.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ using bave::Shader;
2222
using bave::Texture;
2323

2424
Player::Player(Services const& services, std::unique_ptr<IController> controller)
25-
: m_services(&services), m_stats(&services.get<Stats>()), m_controller(std::move(controller)), m_shield(services) {
25+
: m_services(&services), m_stats(&services.get<Stats>()), m_controller(std::move(controller)), m_shield(services), m_arsenal(services) {
2626
auto const& layout = services.get<Layout>();
2727
ship.transform.position.x = layout.player_x;
2828

0 commit comments

Comments
 (0)