Skip to content

Commit 8eb8837

Browse files
authored
More enemy and powerup types. (#52)
* WIP: bgf v0.1.7. * Enable player to 1up spare lives. * Add `powerup::OneUp` and powerup sfx. * Check projectile bounds on both sides. * Fixup spare lives display on HUD. * Prepare for enemy rounds. * Add `enemy::Trooper`. * Move translation logic to `Enemy`. * Nerf responsiveness when holding fire. * Add `Trooper`, `Gunner`. Add 1px border around sprite images. * Cleanup, finalize bgf v0.1.7.
1 parent 1290b70 commit 8eb8837

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+388
-101
lines changed

CMakeLists.txt

+1-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ include(FetchContent)
1111
FetchContent_Declare(
1212
bgf
1313
GIT_REPOSITORY https://github.com/karnkaul/bgf
14-
15-
# GIT_TAG v0.1.5
16-
GIT_TAG adc217f
14+
GIT_TAG v0.1.7
1715
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ext/bgf"
1816
)
1917

assets/images/creep_ship.png

-7.91 KB
Binary file not shown.

assets/images/icon_player.png

6.69 KB
Loading

assets/images/player_ship.png

-7.13 KB
Binary file not shown.

assets/images/player_ship_icon.png

-6.54 KB
Binary file not shown.
File renamed without changes.
Loading

assets/images/round_kinetic_red.png

4.62 KB
Loading

assets/images/ship_creep.png

7.78 KB
Loading

assets/images/ship_gunner.png

7.48 KB
Loading

assets/images/ship_player.png

7.42 KB
Loading

assets/images/ship_trooper.png

8.17 KB
Loading

assets/particles/exhaust.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
},
3434
"lerp": {
3535
"tint": {
36-
"lo": "#36bbf5ff",
36+
"lo": "#ffffffff",
3737
"hi": "#00000000"
3838
},
3939
"scale": {
@@ -58,4 +58,4 @@
5858
"count": 200,
5959
"respawn": true
6060
}
61-
}
61+
}

assets/sfx/kinetic_fire1.wav

105 KB
Binary file not shown.

assets/sfx/powerup_collect.wav

50.1 KB
Binary file not shown.

assets/styles.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"ice": "#d6dbe1e1",
88
"orange": "#f75c03ff",
99
"gun_beam": "#bc96e6ff",
10+
"exhaust": "#36bbf5ff",
1011
"bg_top": "#10020eff",
1112
"bg_bottom": "#040003ff"
1213
},
@@ -36,7 +37,7 @@
3637
"background": "#9f2b68ff",
3738
"fill": "milk",
3839
"corner_ratio": 0.5,
39-
"padding": 10
40+
"padding": 20
4041
},
4142
"enemy": {
4243
"background": "orange",

src/spaced/spaced/game/controllers/follow_controller.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ auto FollowController::tick(Seconds const dt) -> float {
88
auto const y = tick_y(dt);
99

1010
m_spring_arm.target = glm::vec2{0.0f, y};
11+
m_spring_arm.k = is_firing() ? m_spring_k.hi : m_spring_k.lo;
1112
m_spring_arm.tick(dt);
1213

1314
return m_spring_arm.position.y;
@@ -16,6 +17,8 @@ auto FollowController::tick(Seconds const dt) -> float {
1617
void FollowController::do_inspect() {
1718
if constexpr (bave::imgui_v) {
1819
if (ImGui::TreeNode("spring arm")) {
20+
ImGui::DragFloat("k.lo", &m_spring_k.lo, 1.0f, 1.0f, 1000.0f);
21+
ImGui::DragFloat("k.hi", &m_spring_k.hi, 1.0f, 1.0f, 1000.0f);
1922
ImGui::DragFloat("k", &m_spring_arm.k, 1.0f, 1.0f, 1000.0f);
2023
ImGui::DragFloat("damp", &m_spring_arm.damp, 0.01f, 0.01f, 0.99f);
2124
ImGui::DragFloat("min distance", &m_spring_arm.min_distance, 0.01f, 0.01f, 1000.0f);

src/spaced/spaced/game/controllers/follow_controller.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#pragma once
2+
#include <bave/core/inclusive_range.hpp>
23
#include <spaced/game/controller.hpp>
34
#include <spaced/game/spring_arm.hpp>
45

@@ -12,5 +13,6 @@ class FollowController : public IController {
1213
void do_inspect() override;
1314

1415
SpringArm m_spring_arm{};
16+
bave::InclusiveRange<float> m_spring_k{200.0f, 40.0f};
1517
};
1618
} // namespace spaced

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

+6-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ using bave::PointerTap;
1515
using bave::Seconds;
1616
using bave::Services;
1717

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;
20-
min_y = -max_y; // NOLINT(cppcoreguidelines-prefer-member-initializer)
18+
PlayerController::PlayerController(Services const& services, Type const type)
19+
: m_layout(&services.get<Layout>()), m_gamepad_provider(&services.get<IGamepadProvider>()), m_type(type) {
20+
auto const half_size = 0.5f * m_layout->player_size;
21+
auto const play_area = m_layout->play_area;
22+
max_y = play_area.lt.y - half_size.y;
23+
min_y = play_area.rb.y + half_size.y;
2124
}
2225

2326
void PlayerController::on_move(PointerMove const& pointer_move) {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class PlayerController : public FollowController {
1818

1919
[[nodiscard]] auto get_type_name() const -> std::string_view final { return type_name_v; };
2020

21-
explicit PlayerController(bave::Services const& services);
21+
explicit PlayerController(bave::Services const& services, Type type);
2222

2323
void on_move(bave::PointerMove const& pointer_move) final;
2424
void on_tap(bave::PointerTap const& pointer_tap) final;

src/spaced/spaced/game/enemies/creep.cpp

+6-12
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,15 @@
33

44
namespace spaced::enemy {
55
using bave::Resources;
6-
using bave::Seconds;
76
using bave::Texture;
87

98
Creep::Creep(bave::Services const& services) : Enemy(services, "Creep") {
10-
m_sprite.set_texture(services.get<Resources>().get<Texture>("images/creep_ship.png"));
11-
m_sprite.set_size(glm::vec2{80.0f});
12-
health = 2.0f;
13-
}
14-
15-
void Creep::tick(Seconds const dt, bool const in_play) {
16-
if (!in_play) { return; }
17-
18-
m_sprite.transform.position.x -= x_speed * dt.count();
19-
if (m_sprite.transform.position.x < -0.5f * (get_layout().world_space.x + m_sprite.get_shape().size.x)) { set_destroyed(); }
9+
if (auto texture = services.get<Resources>().get<Texture>("images/ship_creep.png")) {
10+
m_sprite.set_size(texture->get_size());
11+
m_sprite.set_texture(std::move(texture));
12+
}
2013

21-
update_health_bar();
14+
health = 1.0f;
15+
speed = 100.0f;
2216
}
2317
} // namespace spaced::enemy

src/spaced/spaced/game/enemies/creep.hpp

-4
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,5 @@ namespace spaced::enemy {
55
class Creep : public Enemy {
66
public:
77
explicit Creep(bave::Services const& services);
8-
9-
void tick(bave::Seconds dt, bool in_play) override;
10-
11-
float x_speed{100.0f};
128
};
139
} // namespace spaced::enemy
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <bave/services/resources.hpp>
2+
#include <spaced/game/enemies/gunner.hpp>
3+
4+
namespace spaced::enemy {
5+
using bave::Resources;
6+
using bave::Texture;
7+
8+
Gunner::Gunner(bave::Services const& services, bave::NotNull<GunKinetic*> gun) : GunnerBase(services, gun, "Gunner") {
9+
if (auto texture = services.get<Resources>().get<Texture>("images/ship_gunner.png")) {
10+
m_sprite.set_size(texture->get_size());
11+
m_sprite.set_texture(std::move(texture));
12+
}
13+
14+
health = 2.0f;
15+
speed = 120.0f;
16+
}
17+
} // namespace spaced::enemy
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#pragma once
2+
#include <spaced/game/enemies/gunner_base.hpp>
3+
4+
namespace spaced::enemy {
5+
class Gunner : public GunnerBase {
6+
public:
7+
explicit Gunner(bave::Services const& services, bave::NotNull<GunKinetic*> gun);
8+
};
9+
} // namespace spaced::enemy
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include <spaced/game/enemies/gunner_base.hpp>
2+
3+
namespace spaced::enemy {
4+
using bave::NotNull;
5+
using bave::Seconds;
6+
using bave::Services;
7+
8+
GunnerBase::GunnerBase(Services const& services, NotNull<GunKinetic*> gun, std::string_view const type) : Enemy(services, type), m_gun(gun) {}
9+
10+
void GunnerBase::do_tick(Seconds const dt) { m_fire_elapsed += dt; }
11+
12+
auto GunnerBase::make_round() -> std::unique_ptr<IWeaponRound> {
13+
if (m_fire_elapsed < m_fire_rate) { return {}; }
14+
15+
m_fire_elapsed = 0s;
16+
return m_gun->fire(get_muzzle_position());
17+
}
18+
19+
auto GunnerBase::get_muzzle_position() const -> glm::vec2 {
20+
auto ret = m_sprite.transform.position;
21+
ret.x -= m_sprite.get_size().x;
22+
return ret;
23+
}
24+
} // namespace spaced::enemy
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#pragma once
2+
#include <spaced/game/enemy.hpp>
3+
#include <spaced/game/weapons/gun_kinetic.hpp>
4+
5+
namespace spaced::enemy {
6+
class GunnerBase : public Enemy {
7+
public:
8+
explicit GunnerBase(bave::Services const& services, bave::NotNull<GunKinetic*> gun, std::string_view type);
9+
10+
protected:
11+
auto make_round() -> std::unique_ptr<IWeaponRound> final;
12+
void do_tick(bave::Seconds dt) override;
13+
14+
[[nodiscard]] auto get_muzzle_position() const -> glm::vec2;
15+
16+
bave::NotNull<GunKinetic*> m_gun;
17+
bave::Seconds m_fire_rate{3.3s};
18+
19+
bave::Seconds m_fire_elapsed{};
20+
};
21+
} // namespace spaced::enemy
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include <bave/core/random.hpp>
2+
#include <bave/services/resources.hpp>
3+
#include <spaced/game/enemies/trooper.hpp>
4+
5+
namespace spaced::enemy {
6+
using bave::NotNull;
7+
using bave::random_in_range;
8+
using bave::Resources;
9+
using bave::Seconds;
10+
using bave::Services;
11+
using bave::Texture;
12+
13+
namespace {
14+
constexpr auto at_end(float const y, float const y_min, float const y_max) { return y_min > y || y > y_max; }
15+
} // namespace
16+
17+
Trooper::Trooper(Services const& services, NotNull<GunKinetic*> gun) : GunnerBase(services, gun, "Trooper") {
18+
if (auto texture = services.get<Resources>().get<Texture>("images/ship_trooper.png")) {
19+
m_sprite.set_size(texture->get_size());
20+
m_sprite.set_texture(std::move(texture));
21+
}
22+
23+
m_direction = random_in_range(0, 1) == 0 ? 1.0f : -1.0f;
24+
25+
health = 3.0f;
26+
speed = 150.0f;
27+
}
28+
29+
void Trooper::do_tick(Seconds const dt) {
30+
GunnerBase::do_tick(dt);
31+
32+
m_sprite.transform.position.y += m_direction * m_y_speed * dt.count();
33+
auto const y_min = get_layout().play_area.rb.y + m_sprite.get_size().y;
34+
auto const y_max = get_layout().play_area.lt.y - m_sprite.get_size().y;
35+
if (at_end(m_sprite.transform.position.y, y_min, y_max)) { m_direction = -m_direction; }
36+
}
37+
} // namespace spaced::enemy
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#pragma once
2+
#include <spaced/game/enemies/gunner_base.hpp>
3+
#include <spaced/game/weapons/gun_kinetic.hpp>
4+
5+
namespace spaced::enemy {
6+
class Trooper : public GunnerBase {
7+
public:
8+
explicit Trooper(bave::Services const& services, bave::NotNull<GunKinetic*> gun);
9+
10+
private:
11+
void do_tick(bave::Seconds dt) final;
12+
13+
float m_y_speed{150.0f};
14+
float m_direction{1.0f};
15+
};
16+
} // namespace spaced::enemy

src/spaced/spaced/game/enemy.cpp

+15-3
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,24 @@ void Enemy::force_death() {
4343
}
4444

4545
void Enemy::update_health_bar() {
46-
m_health_bar.position = m_sprite.transform.position;
47-
m_health_bar.position.y += 0.5f * m_sprite.get_shape().size.y + 20.0f;
48-
m_health_bar.size = {m_sprite.get_shape().size.x, 10.0f};
46+
auto position = m_sprite.transform.position;
47+
position.y += 0.5f * m_sprite.get_shape().size.y + 20.0f;
48+
m_health_bar.set_position(position);
49+
m_health_bar.set_size({m_sprite.get_shape().size.x, 10.0f});
4950
m_health_bar.set_progress(health.get_hit_points() / health.get_total_hit_points());
5051
}
5152

53+
auto Enemy::tick(Seconds const dt) -> std::unique_ptr<IWeaponRound> {
54+
do_tick(dt);
55+
56+
m_sprite.transform.position.x -= speed * dt.count();
57+
if (m_sprite.transform.position.x < -0.5f * (get_layout().world_space.x + m_sprite.get_shape().size.x)) { set_destroyed(); }
58+
59+
update_health_bar();
60+
61+
return make_round();
62+
}
63+
5264
void Enemy::draw(Shader& shader) const {
5365
m_sprite.draw(shader);
5466
m_health_bar.draw(shader);

src/spaced/spaced/game/enemy.hpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <bave/ui/progress_bar.hpp>
77
#include <spaced/game/damageable.hpp>
88
#include <spaced/game/health.hpp>
9+
#include <spaced/game/weapon_round.hpp>
910
#include <spaced/services/layout.hpp>
1011

1112
namespace spaced {
@@ -24,7 +25,7 @@ class Enemy : public IDamageable, public bave::IDrawable {
2425
[[nodiscard]] auto is_destroyed() const -> bool { return is_dead() || m_destroyed; }
2526
void set_destroyed() { m_destroyed = true; }
2627

27-
virtual void tick(bave::Seconds dt, bool in_play) = 0;
28+
auto tick(bave::Seconds dt) -> std::unique_ptr<IWeaponRound>;
2829
void draw(bave::Shader& shader) const override;
2930

3031
void setup(glm::vec2 max_size, float y_position);
@@ -38,11 +39,15 @@ class Enemy : public IDamageable, public bave::IDrawable {
3839

3940
Health health{};
4041
std::int64_t points{10};
42+
float speed{100.0f};
4143

4244
std::string death_emitter{"particles/explode.json"};
4345
std::vector<std::string> death_sfx{"sfx/swish.wav"};
4446

4547
protected:
48+
virtual void do_tick(bave::Seconds /*dt*/) {}
49+
virtual auto make_round() -> std::unique_ptr<IWeaponRound> { return {}; }
50+
4651
bave::Sprite m_sprite{};
4752
std::optional<glm::vec2> m_hitbox{};
4853

src/spaced/spaced/game/hud.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ void Hud::create_lives(Services const& services) {
8686
m_lives_icon = sprite.get();
8787
m_lives_icon->sprite.set_size(glm::vec2{20.0f});
8888
auto const& resources = services.get<Resources>();
89-
if (auto const texture = resources.get<Texture>("images/player_ship_icon.png")) {
89+
if (auto const texture = resources.get<Texture>("images/icon_player.png")) {
9090
m_lives_icon->sprite.set_texture(texture);
9191
m_lives_icon->sprite.set_size(texture->get_size());
9292
}

0 commit comments

Comments
 (0)