From b605d7393d80a6a8ef457e41aa24b78f06db8405 Mon Sep 17 00:00:00 2001 From: bog Date: Tue, 14 Nov 2023 20:18:13 +0100 Subject: [PATCH] :sparkles: Players can move. --- meson.build | 6 +++++ src/EntityFactory.cpp | 33 +++++++++++++++++++++++++++ src/EntityFactory.hpp | 23 +++++++++++++++++++ src/arena/Arena.cpp | 12 +++++++--- src/comps/BodyC.hpp | 8 ++++--- src/comps/PlayerC.hpp | 28 +++++++++++++++++++++++ src/comps/SpriteC.hpp | 39 ++++++++++++++++++++++++++++++++ src/conf.in.hpp | 1 + src/gfx/Canvas.cpp | 2 +- src/gfx/Canvas.hpp | 3 +++ src/gfx/Window.cpp | 4 +++- src/input/Joystick.cpp | 41 ++++++++++++++++++++++++++++++++++ src/input/Joystick.hpp | 28 +++++++++++++++++++++++ src/sys/ActorSystem.cpp | 45 +++++++++++++++++++++++++++++++++++++ src/sys/ActorSystem.hpp | 24 ++++++++++++++++++++ src/sys/BodySystem.cpp | 29 +++++++++++++++--------- src/sys/PlayerSystem.cpp | 48 ++++++++++++++++++++++++++++++++++++++++ src/sys/PlayerSystem.hpp | 23 +++++++++++++++++++ 18 files changed, 379 insertions(+), 18 deletions(-) create mode 100644 src/EntityFactory.cpp create mode 100644 src/EntityFactory.hpp create mode 100644 src/comps/PlayerC.hpp create mode 100644 src/comps/SpriteC.hpp create mode 100644 src/input/Joystick.cpp create mode 100644 src/input/Joystick.hpp create mode 100644 src/sys/ActorSystem.cpp create mode 100644 src/sys/ActorSystem.hpp create mode 100644 src/sys/PlayerSystem.cpp create mode 100644 src/sys/PlayerSystem.hpp diff --git a/meson.build b/meson.build index 9c3f06e..cc278ad 100644 --- a/meson.build +++ b/meson.build @@ -31,9 +31,12 @@ executable( 'src/ECS.cpp', 'src/BaseComponent.cpp', 'src/BaseSystem.cpp', + 'src/EntityFactory.cpp', # systems 'src/sys/BodySystem.cpp', + 'src/sys/ActorSystem.cpp', + 'src/sys/PlayerSystem.cpp', # arena 'src/arena/Arena.cpp', @@ -43,6 +46,9 @@ executable( 'src/gfx/Shaders.cpp', 'src/gfx/Canvas.cpp', 'src/gfx/Texture.cpp', + + # input + 'src/input/Joystick.cpp', ], dependencies: [ dependency('glew'), diff --git a/src/EntityFactory.cpp b/src/EntityFactory.cpp new file mode 100644 index 0000000..d3cba89 --- /dev/null +++ b/src/EntityFactory.cpp @@ -0,0 +1,33 @@ +#include "EntityFactory.hpp" +#include "comps/BodyC.hpp" +#include "comps/SpriteC.hpp" +#include "comps/PlayerC.hpp" + +namespace rid +{ + /*explicit*/ EntityFactory::EntityFactory(ECS& ecs) + : m_ecs { ecs } + { + } + + /*virtual*/ EntityFactory::~EntityFactory() + { + } + + size_t EntityFactory::create_actor(float x, float y) + { + size_t e = m_ecs.create_entity(); + m_ecs.attach_component(e, glm::vec2 {x, y}); + m_ecs.attach_component(e, Frame {0, 0, 32, 32}, glm::vec2 {96, 96}); + + return e; + } + + size_t EntityFactory::create_player(int controller, float x, float y) + { + size_t e = create_actor(x, y); + m_ecs.attach_component(e, controller); + + return e; + } +} diff --git a/src/EntityFactory.hpp b/src/EntityFactory.hpp new file mode 100644 index 0000000..a33c984 --- /dev/null +++ b/src/EntityFactory.hpp @@ -0,0 +1,23 @@ +#ifndef rid_ENTITYFACTORY_HPP +#define rid_ENTITYFACTORY_HPP + +#include "conf.hpp" +#include "ECS.hpp" + +namespace rid +{ + class EntityFactory + { + public: + explicit EntityFactory(ECS& ecs); + virtual ~EntityFactory(); + + size_t create_actor(float x, float y); + size_t create_player(int controller, float x, float y); + + private: + ECS& m_ecs; + }; +} + +#endif diff --git a/src/arena/Arena.cpp b/src/arena/Arena.cpp index a3cad69..6dc3959 100644 --- a/src/arena/Arena.cpp +++ b/src/arena/Arena.cpp @@ -1,14 +1,18 @@ #include "Arena.hpp" #include "conf.hpp" +#include "../EntityFactory.hpp" // Components // ---------- #include "../comps/BodyC.hpp" +#include "../comps/SpriteC.hpp" +#include "../comps/PlayerC.hpp" // Systems // ------- - #include "../sys/BodySystem.hpp" +#include "../sys/ActorSystem.hpp" +#include "../sys/PlayerSystem.hpp" namespace rid { @@ -16,9 +20,11 @@ namespace rid : BaseScene(game) { m_ecs.add_system(std::make_unique()); + m_ecs.add_system(std::make_unique()); + m_ecs.add_system(std::make_unique()); - m_ecs.attach_component(m_ecs.create_entity(), glm::vec2 {64.f, 64.f}, glm::vec2 {64.f, 64.f}); - m_ecs.attach_component(m_ecs.create_entity(), glm::vec2 {640.f, 640.f}, glm::vec2 {128.f, 300.f}); + EntityFactory factory {m_ecs}; + factory.create_player(0, 512.f, 512.f); } /*virtual*/ Arena::~Arena() diff --git a/src/comps/BodyC.hpp b/src/comps/BodyC.hpp index 4742910..81a9d0b 100644 --- a/src/comps/BodyC.hpp +++ b/src/comps/BodyC.hpp @@ -9,11 +9,13 @@ namespace rid struct BodyC: public BaseComponent { glm::vec2 pos; - glm::vec2 size; + glm::vec2 vel { 0.f, 0.f }; + glm::vec2 heading = { 0.f, -1.f}; + glm::vec2 side = {-heading.y, heading.x}; + float max_speed = 256.f; - explicit BodyC(glm::vec2 _pos, glm::vec2 _size) + explicit BodyC(glm::vec2 _pos) : pos { _pos } - , size { _size } { } }; diff --git a/src/comps/PlayerC.hpp b/src/comps/PlayerC.hpp new file mode 100644 index 0000000..44f3004 --- /dev/null +++ b/src/comps/PlayerC.hpp @@ -0,0 +1,28 @@ +#ifndef rid_PLAYERC_HPP +#define rid_PLAYERC_HPP + +#include +#include "conf.hpp" +#include "../BaseComponent.hpp" +#include "../input/Joystick.hpp" + +namespace rid +{ + RID_ERROR(player_error); + + struct PlayerC: public BaseComponent + { + std::unique_ptr joystick; + + explicit PlayerC(int _index) + : joystick { std::make_unique(_index) } + { + } + + virtual ~PlayerC() + { + } + }; +} + +#endif diff --git a/src/comps/SpriteC.hpp b/src/comps/SpriteC.hpp new file mode 100644 index 0000000..85c545f --- /dev/null +++ b/src/comps/SpriteC.hpp @@ -0,0 +1,39 @@ +#ifndef rid_SPRITEC_HPP +#define rid_SPRITEC_HPP + +#include "conf.hpp" +#include "../BaseComponent.hpp" +#include "../gfx/Texture.hpp" + +namespace rid +{ + struct Frame + { + Frame(int _x, int _y, int _w, int _h) + : x { _x } + , y { _y } + , w { _w } + , h { _h } + { + } + + int x; + int y; + int w; + int h; + }; + + struct SpriteC: public BaseComponent + { + Frame frame; + glm::vec2 size; + + explicit SpriteC(Frame _frame, glm::vec2 _size) + : frame { _frame } + , size { _size } + { + } + }; +} + +#endif diff --git a/src/conf.in.hpp b/src/conf.in.hpp index 7c50a25..c36570e 100644 --- a/src/conf.in.hpp +++ b/src/conf.in.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include diff --git a/src/gfx/Canvas.cpp b/src/gfx/Canvas.cpp index a88cd96..21ba3e2 100644 --- a/src/gfx/Canvas.cpp +++ b/src/gfx/Canvas.cpp @@ -108,8 +108,8 @@ namespace rid use(); m_shaders->set_mat4("proj", transform); - auto rot = glm::rotate(glm::mat4 {1.0f}, m_angle, glm::vec3 {0.f, 0.f, 1.f}); auto loc = glm::translate(glm::mat4 {1.0f}, glm::vec3 {m_pos.x, m_pos.y, 0.f}); + auto rot = glm::rotate(glm::mat4 {1.0f}, m_angle, glm::vec3 {0.f, 0.f, 1.f}); m_shaders->set_mat4("model", loc * rot); glDrawArrays(GL_TRIANGLES, 0, m_vertices.size()); diff --git a/src/gfx/Canvas.hpp b/src/gfx/Canvas.hpp index 47cb04f..9587790 100644 --- a/src/gfx/Canvas.hpp +++ b/src/gfx/Canvas.hpp @@ -22,6 +22,9 @@ namespace rid void move(glm::vec2 pos); void rotate(float rad); + void set_pos(glm::vec2 pos) { m_pos = pos; } + void set_angle(float rad) { m_angle = rad; } + void set_texture(std::unique_ptr texture); diff --git a/src/gfx/Window.cpp b/src/gfx/Window.cpp index db61203..856dacf 100644 --- a/src/gfx/Window.cpp +++ b/src/gfx/Window.cpp @@ -1,4 +1,5 @@ #include "Window.hpp" +#include "SDL.h" #include "SDL_image.h" namespace rid @@ -7,7 +8,8 @@ namespace rid : m_width { width } , m_height { height } { - if (SDL_Init(SDL_INIT_VIDEO) != 0) + if (SDL_Init(SDL_INIT_VIDEO + | SDL_INIT_GAMECONTROLLER) != 0) { throw display_error {"Cannot initialize SDL 2."}; } diff --git a/src/input/Joystick.cpp b/src/input/Joystick.cpp new file mode 100644 index 0000000..76509ee --- /dev/null +++ b/src/input/Joystick.cpp @@ -0,0 +1,41 @@ +#include "Joystick.hpp" + +namespace rid +{ + /*explicit*/ Joystick::Joystick(int index) + : m_controller { SDL_GameControllerOpen(index) } + { + if (!m_controller) + { + throw joystick_error {"Cannot open game controller '" + std::to_string(index) + "'."}; + } + } + + /*virtual*/ Joystick::~Joystick() + { + SDL_GameControllerClose(m_controller); + } + + bool Joystick::is_pressed(SDL_GameControllerButton btn) const + { + return SDL_GameControllerGetButton(m_controller, btn); + } + + bool Joystick::is_just_pressed(SDL_GameControllerButton btn) const + { + bool ok = is_pressed(btn); + + if (m_prev_btn != ok) + { + m_prev_btn = ok; + return ok; + } + + return false; + } + + float Joystick::axis(SDL_GameControllerAxis axis) const + { + return SDL_GameControllerGetAxis(m_controller, axis) / 32767.0f; + } +} diff --git a/src/input/Joystick.hpp b/src/input/Joystick.hpp new file mode 100644 index 0000000..c4f4810 --- /dev/null +++ b/src/input/Joystick.hpp @@ -0,0 +1,28 @@ +#ifndef rid_JOYSTICK_HPP +#define rid_JOYSTICK_HPP + +#include +#include "conf.hpp" + +namespace rid +{ + RID_ERROR(joystick_error); + + class Joystick + { + public: + explicit Joystick(int index); + virtual ~Joystick(); + + bool is_pressed(SDL_GameControllerButton btn) const; + bool is_just_pressed(SDL_GameControllerButton btn) const; + + float axis(SDL_GameControllerAxis axis) const; + + private: + SDL_GameController* m_controller; + mutable bool m_prev_btn = false; + }; +} + +#endif diff --git a/src/sys/ActorSystem.cpp b/src/sys/ActorSystem.cpp new file mode 100644 index 0000000..721af69 --- /dev/null +++ b/src/sys/ActorSystem.cpp @@ -0,0 +1,45 @@ +#include "ActorSystem.hpp" +#include "../ECS.hpp" +#include "../comps/SpriteC.hpp" +#include "../comps/BodyC.hpp" + +namespace rid +{ + /*explicit*/ ActorSystem::ActorSystem() + { + auto texture = std::make_unique(); + texture->load(RID_DATADIR / "assets" / "images" / "walk_0.png"); + + m_canvas.set_texture(std::move(texture)); + } + + /*virtual*/ ActorSystem::~ActorSystem() + { + } + + void ActorSystem::update(ECS&, std::vector const&) /*override*/ + { + } + + void ActorSystem::draw(ECS& ecs, std::vector const& entities) /*override*/ + { + for (auto entity: entities) + { + m_canvas.clear(); + + auto const& body = ecs.getc(entity); + auto const& sprite = ecs.getc(entity); + + m_canvas.draw_tex(glm::vec2 {0.f}, + glm::vec2 {sprite.size.x, sprite.size.y}, + glm::vec4 {1, 1, 1, 1}, + glm::vec2 {sprite.frame.x, sprite.frame.y}, + glm::vec2 {sprite.frame.w, sprite.frame.h}); + + m_canvas.set_pos(body.pos); + m_canvas.set_angle(std::atan2(body.heading.y, body.heading.x) - M_PI / 2.0f); + + ecs.window().draw(m_canvas); + } + } +} diff --git a/src/sys/ActorSystem.hpp b/src/sys/ActorSystem.hpp new file mode 100644 index 0000000..950e81b --- /dev/null +++ b/src/sys/ActorSystem.hpp @@ -0,0 +1,24 @@ +#ifndef rid_ACTORSYSTEM_HPP +#define rid_ACTORSYSTEM_HPP + +#include "conf.hpp" +#include "../gfx/Canvas.hpp" +#include "../BaseSystem.hpp" + +namespace rid +{ + class ActorSystem: public BaseSystem + { + public: + explicit ActorSystem(); + virtual ~ActorSystem(); + + void update(ECS& ecs, std::vector const& entities) override; + void draw(ECS& ecs, std::vector const& entities) override; + + private: + Canvas m_canvas; + }; +} + +#endif diff --git a/src/sys/BodySystem.cpp b/src/sys/BodySystem.cpp index 936cc85..a39c878 100644 --- a/src/sys/BodySystem.cpp +++ b/src/sys/BodySystem.cpp @@ -13,21 +13,30 @@ namespace rid { } - void BodySystem::update(ECS&, std::vector const&) /*override*/ + void BodySystem::update(ECS& ecs, std::vector const& entities) /*override*/ { - } - - void BodySystem::draw(ECS& ecs, std::vector const& entities) /*override*/ - { - Canvas canvas; - for (auto entity: entities) { - auto const& body = ecs.getc(entity); + auto& body = ecs.getc(entity); - canvas.draw_rect(body.pos, body.size, glm::vec4 {1, 1, 1, 1}); + float vel_len = glm::length(body.vel); + + if (vel_len > body.max_speed) + { + body.vel = glm::normalize(body.vel) * body.max_speed; + } + + body.pos += body.vel * ecs.dt(); + + if (vel_len > 0.f) + { + body.heading = body.vel / vel_len; + body.side = glm::vec2 {-body.heading.y, body.heading.x}; + } } + } - ecs.window().draw(canvas); + void BodySystem::draw(ECS&, std::vector const&) /*override*/ + { } } diff --git a/src/sys/PlayerSystem.cpp b/src/sys/PlayerSystem.cpp new file mode 100644 index 0000000..0ace52c --- /dev/null +++ b/src/sys/PlayerSystem.cpp @@ -0,0 +1,48 @@ +#include +#include "PlayerSystem.hpp" +#include "../ECS.hpp" +#include "../comps/PlayerC.hpp" +#include "../comps/BodyC.hpp" + +namespace rid +{ + /*explicit*/ PlayerSystem::PlayerSystem() + { + } + + /*virtual*/ PlayerSystem::~PlayerSystem() + { + } + + void PlayerSystem::update(ECS& ecs, std::vector const& entities) /*override*/ + { + for (auto entity: entities) + { + auto& player = ecs.getc(entity); + auto& body = ecs.getc(entity); + + glm::vec2 dir { + player.joystick->axis(SDL_CONTROLLER_AXIS_LEFTX), + player.joystick->axis(SDL_CONTROLLER_AXIS_LEFTY) + }; + + float len = glm::length(dir); + float const threshold = 0.25f; + + if (len >= threshold) + { + dir /= len; + body.vel = dir * body.max_speed; + } + else + { + body.vel = glm::vec2 {0.f}; + } + + } + } + + void PlayerSystem::draw(ECS&, std::vector const&) /*override*/ + { + } +} diff --git a/src/sys/PlayerSystem.hpp b/src/sys/PlayerSystem.hpp new file mode 100644 index 0000000..b182316 --- /dev/null +++ b/src/sys/PlayerSystem.hpp @@ -0,0 +1,23 @@ +#ifndef rid_PLAYERSYSTEM_HPP +#define rid_PLAYERSYSTEM_HPP + +#include "conf.hpp" +#include "../BaseSystem.hpp" + +namespace rid +{ + class PlayerSystem: public BaseSystem + { + public: + explicit PlayerSystem(); + virtual ~PlayerSystem(); + + void update(ECS& ecs, std::vector const& entities) override; + void draw(ECS& ecs, std::vector const& entities) override; + + + private: + }; +} + +#endif