Skip to content

Commit

Permalink
bgf updates, star field, signals. (#50)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
karnkaul committed Jun 24, 2024
1 parent 3cf1c29 commit 82d8f9a
Show file tree
Hide file tree
Showing 37 changed files with 435 additions and 170 deletions.
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ include(FetchContent)
FetchContent_Declare(
bgf
GIT_REPOSITORY https://github.com/karnkaul/bgf
GIT_TAG v0.1.4

# GIT_TAG v0.1.5
GIT_TAG adc217f
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ext/bgf"
)

Expand Down
Binary file added assets/images/beam_round.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/star_blue.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/star_red.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/star_yellow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/music/game.mp3
Binary file not shown.
8 changes: 4 additions & 4 deletions assets/particles/explode.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
}
},
"angular": {
"lo": -90.000000,
"hi": 90.000000
"lo": -360.000000,
"hi": 360.000000
}
},
"lerp": {
Expand All @@ -52,8 +52,8 @@
"hi": 0.800000
},
"quad_size": [
100.000000,
100.000000
150.000000,
150.000000
],
"count": 40,
"respawn": true
Expand Down
4 changes: 3 additions & 1 deletion assets/styles.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
"milk": "#e5cdaeff",
"ice": "#d6dbe1e1",
"orange": "#f75c03ff",
"gun_beam": "#bc96e6ff"
"gun_beam": "#bc96e6ff",
"bg_top": "#10020eff",
"bg_bottom": "#040003ff"
},
"buttons": {
"default": {
Expand Down
16 changes: 13 additions & 3 deletions src/spaced/spaced/game/arsenal.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
#include <spaced/game/arsenal.hpp>
#include <spaced/services/game_signals.hpp>
#include <spaced/services/stats.hpp>

namespace spaced {
using bave::Seconds;
using bave::Services;
using bave::Shader;

Arsenal::Arsenal(Services const& services) : m_primary(services), m_stats(&services.get<Stats>()) {}
Arsenal::Arsenal(Services const& services)
: m_stats(&services.get<Stats>()), m_weapon_changed_signal(&services.get<GameSignals>().weapon_changed), m_primary(services) {
m_weapon_changed_signal->dispatch(get_weapon());
}

auto Arsenal::get_weapon() const -> Weapon const& {
if (m_special) { return *m_special; }
Expand Down Expand Up @@ -34,13 +38,19 @@ void Arsenal::tick_weapons(Seconds const dt) {
if (m_special) {
m_special->tick(dt);
// if the special weapon has no more rounds and is idle, reset it.
if (m_special->get_rounds_remaining() == 0 && m_special->is_idle()) { m_special.reset(); }
if (m_special->get_rounds_remaining() == 0 && m_special->is_idle()) {
m_special.reset();
m_weapon_changed_signal->dispatch(get_weapon());
}
}
}

void Arsenal::check_switch_weapon() {
// if there is a next weapon on standby and the current weapon is idle, switch to the next weapon.
if (m_next && get_weapon().is_idle()) { m_special = std::move(m_next); }
if (m_next && get_weapon().is_idle()) {
m_special = std::move(m_next);
m_weapon_changed_signal->dispatch(get_weapon());
}
}

void Arsenal::fire_weapon(glm::vec2 const muzzle_position) {
Expand Down
5 changes: 4 additions & 1 deletion src/spaced/spaced/game/arsenal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

namespace spaced {
struct Stats;
struct SigWeaponChanged;

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

GunKinetic m_primary; // main weapon
bave::NotNull<Stats*> m_stats;
bave::NotNull<SigWeaponChanged*> m_weapon_changed_signal;

GunKinetic m_primary; // main weapon
std::unique_ptr<Weapon> m_special{}; // special weapon
std::unique_ptr<Weapon> m_next{}; // next special weapon (on standby until current weapon is idle)
std::vector<std::unique_ptr<Weapon::Round>> m_rounds{};
Expand Down
11 changes: 5 additions & 6 deletions src/spaced/spaced/game/controllers/player_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,19 @@ using bave::Action;
using bave::EnumArray;
using bave::GamepadAxis;
using bave::GamepadButton;
using bave::IDisplay;
using bave::im_text;
using bave::PointerMove;
using bave::PointerTap;
using bave::Seconds;
using bave::Services;

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

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

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

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

auto PlayerController::is_in_move_area(glm::vec2 const position) const -> bool {
auto const width = m_display->get_world_space().x;
auto const width = m_layout->world_space.x;
auto const n_pos = (position.x + 0.5f * width) / width;
return n_pos <= n_move_area;
}
Expand Down
6 changes: 3 additions & 3 deletions src/spaced/spaced/game/controllers/player_controller.hpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#pragma once
#include <bave/core/ptr.hpp>
#include <bave/input/gamepad.hpp>
#include <bave/services/display.hpp>
#include <bave/services/services.hpp>
#include <spaced/game/controllers/follow_controller.hpp>
#include <spaced/game/spring_arm.hpp>
#include <spaced/services/gamepad_provider.hpp>
#include <spaced/services/layout.hpp>

namespace spaced {
class PlayerController : public FollowController {
Expand Down Expand Up @@ -45,8 +45,8 @@ class PlayerController : public FollowController {
auto tick_y(bave::Seconds dt) -> float final;
void do_inspect() final;

bave::Ptr<bave::IDisplay const> m_display{};
bave::Ptr<IGamepadProvider const> m_gamepad_provider{};
bave::NotNull<Layout const*> m_layout;
bave::NotNull<IGamepadProvider const*> m_gamepad_provider;

Type m_type{};
float m_y{};
Expand Down
103 changes: 55 additions & 48 deletions src/spaced/spaced/game/hud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,54 @@
#include <bave/services/styles.hpp>
#include <bave/ui/button.hpp>
#include <spaced/game/hud.hpp>
#include <spaced/services/game_signals.hpp>
#include <spaced/services/layout.hpp>

namespace spaced {
using bave::IDisplay;
using bave::Display;
using bave::Resources;
using bave::Seconds;
using bave::Services;
using bave::Shader;
using bave::Styles;
using bave::Text;
using bave::TextHeight;
using bave::Texture;

namespace ui = bave::ui;

Hud::Hud(Services const& services)
: ui::View(services), m_display(&services.get<IDisplay>()), m_layout(&services.get<Layout>()), m_styles(&services.get<Styles>()) {
Hud::Hud(Services const& services) : ui::View(services), m_layout(&services.get<Layout>()), m_styles(&services.get<Styles>()) {
create_background();
create_score(services);
create_lives_icon(services);
create_lives(services);
create_weapon(services);

block_input_events = false;
render_view = m_display->get_world_view();
}

void Hud::set_lives(int const lives) {
if (lives <= 0) {
m_lives_icon.instances.clear();
return;
}
render_view = services.get<Display>().world.render_view;

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

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

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

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

void Hud::render(Shader& shader) const {
View::render(shader);
m_lives_icon.draw(shader);
void Hud::set_weapon(std::shared_ptr<Texture const> texture) {
if (texture) { m_weapon_icon->sprite.set_size(texture->get_size()); }
m_weapon_icon->sprite.set_texture(std::move(texture));
}

auto Hud::make_text(Services const& services) const -> std::unique_ptr<ui::Text> {
auto const& rgbas = m_styles->rgbas;
auto text = std::make_unique<ui::Text>(services);
text->text.set_height(TextHeight{60});
text->text.transform.position = m_layout->hud_area.centre();
text->text.tint = rgbas["grey"];
return text;
}

void Hud::create_background() {
Expand All @@ -66,45 +64,54 @@ void Hud::create_background() {
}

void Hud::create_score(Services const& services) {
auto const& rgbas = m_styles->rgbas;

auto make_text = [&] {
auto text = std::make_unique<ui::Text>(services);
text->text.set_height(TextHeight{60});
text->text.transform.position = m_layout->hud_area.centre();
text->text.tint = rgbas["grey"];
return text;
};

auto text = make_text();
auto text = make_text(services);
m_score = text.get();
text->text.set_string("9999999999");
m_text_bounds_size = text->text.get_bounds().size();
text->text.transform.position.y -= 0.5f * m_text_bounds_size.y;
set_score(0);

push(std::move(text));

text = make_text();
text = make_text(services);
m_hi_score = text.get();
text->text.transform.position.x = m_layout->hud_area.rb.x - 50.0f;
text->text.transform.position.y -= 0.5f * m_text_bounds_size.y;
text->text.set_align(bave::Text::Align::eLeft);
text->text.set_align(Text::Align::eLeft);
set_hi_score(0);

push(std::move(text));
}

void Hud::create_lives_icon(Services const& services) {
auto quad = m_lives_icon.get_shape();
quad.size = glm::vec2{20.0f};
void Hud::create_lives(Services const& services) {
auto sprite = std::make_unique<ui::Sprite>();
m_lives_icon = sprite.get();
m_lives_icon->sprite.set_size(glm::vec2{20.0f});
auto const& resources = services.get<Resources>();
if (auto const texture = resources.get<Texture>("images/player_ship_icon.png")) {
quad.size = texture->get_size();
m_lives_icon.set_texture(texture);
m_lives_icon->sprite.set_texture(texture);
m_lives_icon->sprite.set_size(texture->get_size());
}
m_lives_icon.set_shape(quad);
m_lives_icon.transform.position = m_layout->hud_area.centre();
m_lives_icon.transform.position.x = m_layout->hud_area.lt.x + 100.0f;
auto position = m_layout->hud_area.centre();
position.x = m_layout->hud_area.lt.x + 100.0f;
m_lives_icon->set_position(position);
push(std::move(sprite));

auto text = make_text(services);
text->text.transform.position.y -= 0.5f * m_text_bounds_size.y;
text->text.transform.position.x = m_lives_icon->get_position().x + m_lives_icon->get_size().x;
text->text.set_align(Text::Align::eRight);
text->text.set_string("0");
m_lives_count = text.get();
push(std::move(text));
}

void Hud::create_weapon(Services const& /*services*/) {
auto sprite = std::make_unique<ui::Sprite>();
m_weapon_icon = sprite.get();
sprite->sprite.set_size(glm::vec2{50.0f});
auto position = m_lives_icon->get_position();
position.y -= 5.0f;
position.x = m_lives_count->get_position().x + 200.0f;
sprite->set_position(position);
push(std::move(sprite));
}
} // namespace spaced
18 changes: 12 additions & 6 deletions src/spaced/spaced/game/hud.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#pragma once
#include <bave/graphics/instanced.hpp>
#include <bave/services/styles.hpp>
#include <bave/ui/outline_quad.hpp>
#include <bave/ui/sprite.hpp>
#include <bave/ui/text.hpp>
#include <bave/ui/view.hpp>
#include <spaced/signal.hpp>

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

void set_lives(int lives);
void on_death();
void set_score(std::int64_t score);
void set_hi_score(std::int64_t score);
void set_weapon(std::shared_ptr<bave::Texture const> texture);

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

void create_background();
void create_score(bave::Services const& services);
void create_lives_icon(bave::Services const& services);
void create_lives(bave::Services const& services);
void create_weapon(bave::Services const& services);

bave::NotNull<bave::IDisplay const*> m_display;
bave::NotNull<Layout const*> m_layout;
bave::NotNull<bave::Styles const*> m_styles;
glm::vec2 m_text_bounds_size{};

SignalHandle m_on_weapon_changed{};

bave::Ptr<bave::ui::OutlineQuad> m_background{};
bave::Ptr<bave::ui::Text> m_score{};
bave::Ptr<bave::ui::Text> m_hi_score{};

bave::Instanced<bave::QuadShape> m_lives_icon{};
bave::Ptr<bave::ui::Sprite> m_lives_icon{};
bave::Ptr<bave::ui::Text> m_lives_count{};

bave::Ptr<bave::ui::Sprite> m_weapon_icon{};
};
} // namespace spaced
2 changes: 1 addition & 1 deletion src/spaced/spaced/game/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ using bave::Shader;
using bave::Texture;

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

Expand Down
Loading

0 comments on commit 82d8f9a

Please sign in to comment.