From ac521c3129cc7d6cda602a998ae5cd5643ab6b50 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Sat, 9 Jan 2021 13:41:58 +0300 Subject: [PATCH] Init --- .gitignore | 3 ++ application.cpp | 37 +++++++++++++++++++ application.h | 23 ++++++++++++ command.h | 36 +++++++++++++++++++ danmaku.pro | 31 ++++++++++++++++ enemy.cpp | 48 +++++++++++++++++++++++++ enemy.h | 36 +++++++++++++++++++ entity.cpp | 34 ++++++++++++++++++ entity.h | 26 ++++++++++++++ game.cpp | 57 ++++++++++++++++++++++++++++++ game.h | 31 ++++++++++++++++ main.cpp | 8 +++++ player.cpp | 57 ++++++++++++++++++++++++++++++ player.h | 37 +++++++++++++++++++ resourceholder.h | 55 +++++++++++++++++++++++++++++ scenenode.cpp | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ scenenode.h | 45 +++++++++++++++++++++++ spritenode.cpp | 20 +++++++++++ spritenode.h | 22 ++++++++++++ state.cpp | 9 +++++ state.h | 35 ++++++++++++++++++ world.cpp | 84 +++++++++++++++++++++++++++++++++++++++++++ world.h | 46 ++++++++++++++++++++++++ 23 files changed, 872 insertions(+) create mode 100644 .gitignore create mode 100644 application.cpp create mode 100644 application.h create mode 100644 command.h create mode 100644 danmaku.pro create mode 100644 enemy.cpp create mode 100644 enemy.h create mode 100644 entity.cpp create mode 100644 entity.h create mode 100644 game.cpp create mode 100644 game.h create mode 100644 main.cpp create mode 100644 player.cpp create mode 100644 player.h create mode 100644 resourceholder.h create mode 100644 scenenode.cpp create mode 100644 scenenode.h create mode 100644 spritenode.cpp create mode 100644 spritenode.h create mode 100644 state.cpp create mode 100644 state.h create mode 100644 world.cpp create mode 100644 world.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..94bcf1c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.ogg +*.png +*user* diff --git a/application.cpp b/application.cpp new file mode 100644 index 0000000..1fc7a7c --- /dev/null +++ b/application.cpp @@ -0,0 +1,37 @@ +#include "application.h" +#include "game.h" + +constexpr int SCREEN_WIDTH = 640; +constexpr int SCREEN_HEIGHT = 480; +const sf::Time TIME_PER_SECOND = sf::seconds(1.f / 60.f); + +Application::Application() : + render_window({SCREEN_WIDTH, SCREEN_HEIGHT}, "Test") +{} + +void Application::registerStates() +{ + map_states[State::Tag::Game] = std::make_unique(render_window, this); + + active_state_tag = State::Tag::Game; +} + +void Application::run() +{ + sf::Clock clock; + sf::Time since_last_update = sf::Time::Zero; + + while (render_window.isOpen()) + { + map_states[active_state_tag]->processInput(); + sf::Time delta = clock.restart(); + + since_last_update -= TIME_PER_SECOND; + map_states[active_state_tag]->processInput(); + + //if (!b_pause) + map_states[active_state_tag]->update(delta); + + map_states[active_state_tag]->draw(); + } +} diff --git a/application.h b/application.h new file mode 100644 index 0000000..a9743eb --- /dev/null +++ b/application.h @@ -0,0 +1,23 @@ +#ifndef APPLICATION_H +#define APPLICATION_H + +#include "state.h" +#include +#include + +class Application +{ +public: + Application(); + + void registerStates(); + void run(); + +private: + sf::RenderWindow render_window; + + std::map map_states; + State::Tag active_state_tag; +}; + +#endif // APPLICATION_H diff --git a/command.h b/command.h new file mode 100644 index 0000000..f754cdb --- /dev/null +++ b/command.h @@ -0,0 +1,36 @@ +#ifndef COMMAND_H +#define COMMAND_H + +#include + +namespace Category +{ + enum Type + { + None = 0, + Scene = 1 << 0, + Player = 1 << 1, + Enemy = 1 << 2 + }; +} + +class SceneNode; +namespace sf { class Time; } + +struct Command +{ + std::function action; + Category::Type category; +}; + +template +std::function derivedAction(Functon func) +{ + return [=] (SceneNode& node, const sf::Time& time) + { + //assert(dynamic_cast(&node)); + func(static_cast(node), time); + }; +} + +#endif // COMMAND_H diff --git a/danmaku.pro b/danmaku.pro new file mode 100644 index 0000000..1299662 --- /dev/null +++ b/danmaku.pro @@ -0,0 +1,31 @@ +TEMPLATE = app +CONFIG += c++17 +CONFIG -= app_bundle +CONFIG -= qt + +SOURCES += \ + application.cpp \ + entity.cpp \ + enemy.cpp \ + game.cpp \ + main.cpp \ + player.cpp \ + scenenode.cpp \ + spritenode.cpp \ + state.cpp \ + world.cpp + +HEADERS += \ + application.h \ + command.h \ + entity.h \ + enemy.h \ + game.h \ + player.h \ + resourceholder.h \ + scenenode.h \ + spritenode.h \ + state.h \ + world.h + +unix:LIBS += -lsfml-graphics -lsfml-audio -lsfml-window -lsfml-system diff --git a/enemy.cpp b/enemy.cpp new file mode 100644 index 0000000..4899ced --- /dev/null +++ b/enemy.cpp @@ -0,0 +1,48 @@ +#include "enemy.h" + +// /////////////////////////////////////////////////////////// // + +static Textures::Id toTextureID(const Enemy::Type& type) +{ + switch (type) + { + case Enemy::Type::Shotguner: + return Textures::Id::Shotguner; + + case Enemy::Type::Weakling: + return Textures::Id::Weakling; + + case Enemy::Type::Player: + return Textures::Id::Player; + } + + return Textures::Id::Weakling; +} + +// /////////////////////////////////////////////////////////// // + +Enemy::Enemy(Type type, const TextureHolder& texture_holder) : + enemy_type(type), + enemy_sprite(texture_holder.get(toTextureID(type))) +{ + const sf::FloatRect bounds = enemy_sprite.getLocalBounds(); + enemy_sprite.setOrigin(bounds.width / 2.f, bounds.height / 2.f); +} + +Enemy::~Enemy() +{} + +Enemy::Type Enemy::type() const +{ + return enemy_type; +} + +Category::Type Enemy::category() const +{ + return Category::Type::Enemy; +} + +void Enemy::drawCurrent(sf::RenderTarget& target, sf::RenderStates states) const +{ + target.draw(enemy_sprite, states); +} diff --git a/enemy.h b/enemy.h new file mode 100644 index 0000000..1c0f924 --- /dev/null +++ b/enemy.h @@ -0,0 +1,36 @@ +#ifndef FAIRY_H +#define FAIRY_H + +#include "entity.h" +#include "resourceholder.h" + +#include + +class Enemy : public Entity +{ +public: + + enum class Type + { + Player, + Weakling, + Shotguner + }; + + explicit Enemy(Type type, const TextureHolder& texture_holder); + virtual ~Enemy() override; + + Type type() const; + Category::Type category() const override; + +private: + Type enemy_type; + sf::Sprite enemy_sprite; + + virtual void drawCurrent(sf::RenderTarget& target, sf::RenderStates states) const override; +}; + +using EnemySPtr = std::shared_ptr; +using EnemyUPtr = std::unique_ptr; + +#endif // FAIRY_H diff --git a/entity.cpp b/entity.cpp new file mode 100644 index 0000000..bf220f5 --- /dev/null +++ b/entity.cpp @@ -0,0 +1,34 @@ +#include "entity.h" + +Entity::Entity() : + vector_velocity(0.f, 0.f) +{} + +Entity::~Entity() +{} + +void Entity::setVelocity(sf::Vector2f vel) +{ + vector_velocity = vel; +} + +void Entity::setVelocity(float vx, float vy) +{ + vector_velocity.x = vx; + vector_velocity.y = vy; +} + +const sf::Vector2f& Entity::velocity() const +{ + return vector_velocity; +} + +void Entity::accelerate(sf::Vector2f vel) +{ + vector_velocity += vel; +} + +void Entity::updateCurrent(const sf::Time& dt) +{ + move(vector_velocity * dt.asSeconds()); +} diff --git a/entity.h b/entity.h new file mode 100644 index 0000000..c67bf7c --- /dev/null +++ b/entity.h @@ -0,0 +1,26 @@ +#ifndef ENTITY_H +#define ENTITY_H + +#include "scenenode.h" + +#include + +class Entity : public SceneNode +{ +public: + explicit Entity(); + virtual ~Entity() = 0; + + void setVelocity(sf::Vector2f vel); + void setVelocity(float vx, float vy); + const sf::Vector2f& velocity() const; + void accelerate(sf::Vector2f vel); + + +private: + sf::Vector2f vector_velocity; + + virtual void updateCurrent(const sf::Time& dt) override; +}; + +#endif // ENTITY_H diff --git a/game.cpp b/game.cpp new file mode 100644 index 0000000..41b8c91 --- /dev/null +++ b/game.cpp @@ -0,0 +1,57 @@ +#include "game.h" + +Game::Game(sf::RenderWindow &window, Application *application) : + State(window, application), + world(render_window) +{} + +Game::~Game() +{} + +bool Game::processInput() +{ + std::queue& queue = world.getCommandQueue(); + + sf::Event event; + while (render_window.pollEvent(event)) + { + player.handleEvent(event, queue); + + switch (event.type) + { + case sf::Event::GainedFocus: + b_pause = false; + break; + + case sf::Event::LostFocus: + b_pause = true; + break; + + case sf::Event::Closed: + render_window.close(); + break; + + default: + break; + } + } + + player.handleRealtimeInput(queue); + + return true; +} + +bool Game::update(const sf::Time &dt) +{ + world.update(dt); + + return true; +} + +void Game::draw() +{ + render_window.clear(); + world.draw(); + render_window.setView(render_window.getDefaultView()); + render_window.display(); +} diff --git a/game.h b/game.h new file mode 100644 index 0000000..c174b46 --- /dev/null +++ b/game.h @@ -0,0 +1,31 @@ +#ifndef GAME_H +#define GAME_H + +#include "world.h" +#include "player.h" +#include "state.h" + +#include +#include + +class Game final : public State +{ +public: + Game(sf::RenderWindow& window, Application *application); + virtual ~Game() override; + + void run(); + +private: + World world; + Player player; + + bool b_pause; + bool b_to_right, b_to_left, b_to_up, b_to_down; + + bool processInput() override; + bool update(const sf::Time& dt) override; + void draw() override; +}; + +#endif // GAME_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..55c251b --- /dev/null +++ b/main.cpp @@ -0,0 +1,8 @@ +#include "application.h" + +int main() +{ + Application app; + app.registerStates(); + app.run(); +} diff --git a/player.cpp b/player.cpp new file mode 100644 index 0000000..81e5599 --- /dev/null +++ b/player.cpp @@ -0,0 +1,57 @@ +#include "player.h" +#include "command.h" +#include "enemy.h" + +Player::Player() : + player_speed(45.f) +{ + map_key_bindings[sf::Keyboard::Left] = Action::MoveLeft; + map_key_bindings[sf::Keyboard::Right] = Action::MoveRight; + map_key_bindings[sf::Keyboard::Up] = Action::MoveUp; + map_key_bindings[sf::Keyboard::Down] = Action::MoveDown; + + map_action_bindings[Action::MoveLeft].action = + [&] (SceneNode& node, const sf::Time& time) + { + node.move(-player_speed * time.asSeconds(), 0.f); + }; + + map_action_bindings[Action::MoveRight].action = + [&] (SceneNode& node, const sf::Time& time) + { + node.move(player_speed * time.asSeconds(), 0.f); + }; + + map_action_bindings[Action::MoveUp].action = + [&] (SceneNode& node, const sf::Time& time) + { + node.move(0.f, -player_speed * time.asSeconds()); + }; + + map_action_bindings[Action::MoveDown].action = + [&] (SceneNode& node, const sf::Time& time) + { + node.move(0.f, player_speed * time.asSeconds()); + }; + + for (auto& pair : map_action_bindings) + pair.second.category = Category::Enemy; +} + +void Player::handleRealtimeInput(std::queue& queue) +{ + for (const auto& pair : map_key_bindings) + if (sf::Keyboard::isKeyPressed(pair.first) && isRealtime(pair.second)) + queue.push(map_action_bindings[pair.second]); +} + +void Player::handleEvent(const sf::Event &event, std::queue &queue) +{ + switch (event.type) + { + case sf::Event::KeyPressed: + break; + default: + break; + } +} diff --git a/player.h b/player.h new file mode 100644 index 0000000..afd281a --- /dev/null +++ b/player.h @@ -0,0 +1,37 @@ +#ifndef PLAYER_H +#define PLAYER_H + +#include +#include +#include + +class Command; + +class Player +{ +public: + Player(); + + enum class Action + { + MoveLeft, + MoveRight, + MoveUp, + MoveDown + }; + + void assignKey(Action action, sf::Keyboard::Key key); + sf::Keyboard::Key getAssignedKey(Action action) const; + + void handleEvent(const sf::Event& event, std::queue& queue); + void handleRealtimeInput(std::queue& queue); + +private: + float player_speed; + std::map map_key_bindings; + std::map map_action_bindings; + + static bool isRealtime(Action action) {return true;} +}; + +#endif // PLAYER_H diff --git a/resourceholder.h b/resourceholder.h new file mode 100644 index 0000000..5c220f5 --- /dev/null +++ b/resourceholder.h @@ -0,0 +1,55 @@ +#ifndef RESOURCEHOLDER_H +#define RESOURCEHOLDER_H + +#include +#include +#include +#include + +namespace sf { class Texture; class Font; } + +template +class ResourceHolder +{ +public: + bool load(Id id, const std::string& filename) + { + std::unique_ptr resource(new Resource()); + if (!resource->loadFromFile(filename)) + return false; + + map_resources[id] = std::move(resource); + + return true; + } + + Resource& get(Id id) const + { + const auto found = map_resources.find(id); + assert(found != map_resources.end()); + + return *found->second; + } + +private: + std::map> map_resources; +}; + +// /////////////////////////////////////////////////////////// // + +namespace Textures +{ + enum class Id + { + Player, + Weakling, + Shotguner, + + Background + }; +} + +using TextureHolder = ResourceHolder; +using FontHolder = ResourceHolder; + +#endif // RESOURCEHOLDER_H diff --git a/scenenode.cpp b/scenenode.cpp new file mode 100644 index 0000000..d1d5a9d --- /dev/null +++ b/scenenode.cpp @@ -0,0 +1,92 @@ +#include "scenenode.h" +#include + +SceneNode::SceneNode() +{} + +SceneNode::~SceneNode() +{} + +bool SceneNode::attachChild(const SceneNodeSPtr &child) +{ + child->parent = shared_from_this(); + vec_children.emplace_back(std::move(child)); + + return true; +} + +SceneNodeSPtr SceneNode::detachChild(const SceneNode &node) +{ + const auto found = std::find_if(vec_children.begin(), vec_children.end(), + [&] (SceneNodeSPtr& p) -> bool + { + return p.get() == &node; + }); + + if (found == vec_children.end()) + return nullptr; + + SceneNodeSPtr result = *found; + result->parent = nullptr; + vec_children.erase(found); + + return result; +} + +sf::Transform SceneNode::getWorldTransform() const +{ + sf::Transform transform = sf::Transform::Identity; + + for (SceneNodeConstSPtr node = shared_from_this(); node != nullptr; node = node->parent) + { + transform *= node->getTransform(); + } + + return transform; +} + +sf::Vector2f SceneNode::getWorldPosition() const +{ + return getWorldTransform() * sf::Vector2f(); +} + +void SceneNode::draw(sf::RenderTarget& target, sf::RenderStates states) const +{ + states.transform *= getTransform(); + drawCurrent(target, states); + + for (const auto& ch : vec_children) + ch->draw(target, states); +} + +void SceneNode::update(const sf::Time& dt) +{ + updateCurrent(dt); + updateChildren(dt); +} + +void SceneNode::onCommand(const Command& command, const sf::Time& dt) +{ + if (command.category & category()) + command.action(*shared_from_this(), dt); + + for (auto& ch : vec_children) + ch->onCommand(command, dt); +} + +Category::Type SceneNode::category() const +{ + return Category::Type::Scene; +} + +void SceneNode::drawCurrent(sf::RenderTarget& target, sf::RenderStates states) const +{} + +void SceneNode::updateCurrent(const sf::Time& dt) +{} + +void SceneNode::updateChildren(const sf::Time &dt) +{ + for (SceneNodeSPtr& child : vec_children) + child->update(dt); +} diff --git a/scenenode.h b/scenenode.h new file mode 100644 index 0000000..b5c9313 --- /dev/null +++ b/scenenode.h @@ -0,0 +1,45 @@ +#ifndef SCENENODE_H +#define SCENENODE_H + +#include "command.h" +#include +#include +#include + +class SceneNode; +using SceneNodeUPtr = std::unique_ptr; +using SceneNodeSPtr = std::shared_ptr; +using SceneNodeConstSPtr = std::shared_ptr; + +class SceneNode : public std::enable_shared_from_this, + public sf::Transformable, + public sf::Drawable, + private sf::NonCopyable +{ +public: + explicit SceneNode(); + virtual ~SceneNode(); + + bool attachChild(const SceneNodeSPtr &child); + SceneNodeSPtr detachChild(const SceneNode& node); + + sf::Transform getWorldTransform() const; + sf::Vector2f getWorldPosition() const; + + void update(const sf::Time& dt); + void onCommand(const Command& command, const sf::Time& dt); + + virtual Category::Type category() const; + +private: + std::vector vec_children; + SceneNodeSPtr parent; + + virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override final; + virtual void drawCurrent(sf::RenderTarget& target, sf::RenderStates states) const; + virtual void updateCurrent(const sf::Time& dt); + + void updateChildren(const sf::Time& dt); +}; + +#endif // SCENENODE_H diff --git a/spritenode.cpp b/spritenode.cpp new file mode 100644 index 0000000..8f40449 --- /dev/null +++ b/spritenode.cpp @@ -0,0 +1,20 @@ +#include "spritenode.h" + +SpriteNode::SpriteNode(const sf::Texture& texture) +{ + sprite.setTexture(texture); +} + +SpriteNode::~SpriteNode() +{} + +SpriteNode::SpriteNode(const sf::Texture& texture, const sf::IntRect& rect) +{ + sprite.setTexture(texture); + sprite.setTextureRect(rect); +} + +void SpriteNode::drawCurrent(sf::RenderTarget& target, sf::RenderStates states) const +{ + target.draw(sprite, states); +} diff --git a/spritenode.h b/spritenode.h new file mode 100644 index 0000000..8a398d6 --- /dev/null +++ b/spritenode.h @@ -0,0 +1,22 @@ +#ifndef SPRITENODE_H +#define SPRITENODE_H + +#include "scenenode.h" + +class SpriteNode : public SceneNode +{ +public: + explicit SpriteNode(const sf::Texture& texture); + explicit SpriteNode(const sf::Texture& texture, + const sf::IntRect& rect); + virtual ~SpriteNode(); + +private: + sf::Sprite sprite; + virtual void drawCurrent(sf::RenderTarget& target, sf::RenderStates states) const; +}; + +using SpriteNodeUPtr = std::unique_ptr; +using SpriteNodeSPtr = std::shared_ptr; + +#endif // SPRITENODE_H diff --git a/state.cpp b/state.cpp new file mode 100644 index 0000000..8d041d7 --- /dev/null +++ b/state.cpp @@ -0,0 +1,9 @@ +#include "state.h" + +State::State(sf::RenderWindow &window, Application *application) : + render_window(window), + app(application) +{} + +State::~State() +{} diff --git a/state.h b/state.h new file mode 100644 index 0000000..0cc132d --- /dev/null +++ b/state.h @@ -0,0 +1,35 @@ +#ifndef STATE_H +#define STATE_H + +#include +#include +#include +#include + +class Application; + +class State +{ +public: + explicit State(sf::RenderWindow& window, Application *application); + virtual ~State() = 0; + + virtual void draw() = 0; + virtual bool update(const sf::Time& dt) = 0; + virtual bool processInput() = 0; + + enum class Tag + { + Menu, + Game + }; + +protected: + sf::RenderWindow& render_window; + Application *app; +}; + +using StateUPtr = std::unique_ptr; +using StateSPtr = std::shared_ptr; + +#endif // STATE_H diff --git a/world.cpp b/world.cpp new file mode 100644 index 0000000..69d6e9d --- /dev/null +++ b/world.cpp @@ -0,0 +1,84 @@ +#include "world.h" +#include "spritenode.h" +#include "enemy.h" +#include + +World::World(sf::RenderWindow& window) : + world_window(window), + world_view(window.getDefaultView()), + scene_graph(new SceneNode()), + world_bounds(0.f, 0.f, world_view.getSize().x, 2000.f), + world_spawn_pos(world_view.getSize().x / 2.f, world_bounds.height - world_view.getSize().y), + player(nullptr) +{ + loadTextures(); + buildScene(); + + world_view.setCenter(world_spawn_pos); +} + +void World::update(const sf::Time &dt) +{ + world_view.move(0.f, world_speed * dt.asSeconds()); + player->setVelocity(0.f, 0.f); + + while (!queue_commands.empty()) + { + scene_graph->onCommand(queue_commands.front(), dt); + queue_commands.pop(); + } + + //sf::Vector2f position = player->getPosition(); + sf::Vector2f velocity = player->velocity(); + + if (velocity.x != 0.f && velocity.y != 0.f) + player->setVelocity(velocity / std::sqrt(2.f)); + + player->accelerate({0.f, world_speed}); + + scene_graph->update(dt); +} + +void World::draw() +{ + world_window.setView(world_view); + world_window.draw(*scene_graph); +} + +std::queue& World::getCommandQueue() +{ + return queue_commands; +} + +bool World::loadTextures() +{ + return /*texture_holder.load(Textures::Id::Weakling, "sprite_weakling.png") + && texture_holder.load(Textures::Id::Shotguner, "sprite_shotguner.png") + && */texture_holder.load(Textures::Id::Background, "sprite_back.png") + && texture_holder.load(Textures::Id::Player, "sprite.png"); +} + +void World::buildScene() +{ + for (std::size_t i = 0; i < LayerCount; ++i) + { + SceneNodeSPtr layer(new SceneNode()); + arr_layers[i] = layer; + + scene_graph->attachChild(layer); + } + + sf::Texture texture = texture_holder.get(Textures::Id::Background); + sf::IntRect texture_rect(world_bounds); + texture.setRepeated(true); + + SpriteNodeSPtr background_sprite(new SpriteNode(texture, texture_rect)); + background_sprite->setPosition(world_bounds.left, world_bounds.top); + + arr_layers[Background]->attachChild(background_sprite); + + player = std::make_shared(Enemy::Type::Player, texture_holder); + player->setPosition(world_spawn_pos); + player->setVelocity(40.f, world_speed); + arr_layers[Air]->attachChild(player); +} diff --git a/world.h b/world.h new file mode 100644 index 0000000..6f48770 --- /dev/null +++ b/world.h @@ -0,0 +1,46 @@ +#ifndef WORLD_H +#define WORLD_H + +#include "enemy.h" +#include "resourceholder.h" +#include "scenenode.h" +#include +#include +#include + +class World : private sf::NonCopyable +{ +public: + explicit World(sf::RenderWindow& window); + + void update(const sf::Time& dt); + void draw(); + + std::queue& getCommandQueue(); + +private: + bool loadTextures(); + void buildScene(); + + enum Layer + { + Background, + Air, + LayerCount + }; + + std::queue queue_commands; + + sf::RenderWindow& world_window; + sf::View world_view; + TextureHolder texture_holder; + SceneNodeSPtr scene_graph; + std::array arr_layers; + + sf::FloatRect world_bounds; + sf::Vector2f world_spawn_pos; + float world_speed; + EnemySPtr player; +}; + +#endif // WORLD_H