This commit is contained in:
NaiJi ✨ 2021-01-09 13:41:58 +03:00
commit ac521c3129
23 changed files with 872 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.ogg
*.png
*user*

37
application.cpp Normal file
View File

@ -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<Game>(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();
}
}

23
application.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef APPLICATION_H
#define APPLICATION_H
#include "state.h"
#include <memory>
#include <map>
class Application
{
public:
Application();
void registerStates();
void run();
private:
sf::RenderWindow render_window;
std::map<State::Tag, StateUPtr> map_states;
State::Tag active_state_tag;
};
#endif // APPLICATION_H

36
command.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef COMMAND_H
#define COMMAND_H
#include <functional>
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<void(SceneNode&, sf::Time)> action;
Category::Type category;
};
template<typename GameObject, typename Functon>
std::function<void(SceneNode&, const sf::Time&)> derivedAction(Functon func)
{
return [=] (SceneNode& node, const sf::Time& time)
{
//assert(dynamic_cast<GameObject*>(&node));
func(static_cast<GameObject&>(node), time);
};
}
#endif // COMMAND_H

31
danmaku.pro Normal file
View File

@ -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

48
enemy.cpp Normal file
View File

@ -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);
}

36
enemy.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef FAIRY_H
#define FAIRY_H
#include "entity.h"
#include "resourceholder.h"
#include <SFML/Graphics/Sprite.hpp>
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<Enemy>;
using EnemyUPtr = std::unique_ptr<Enemy>;
#endif // FAIRY_H

34
entity.cpp Normal file
View File

@ -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());
}

26
entity.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef ENTITY_H
#define ENTITY_H
#include "scenenode.h"
#include <SFML/System.hpp>
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

57
game.cpp Normal file
View File

@ -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<Command>& 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();
}

31
game.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef GAME_H
#define GAME_H
#include "world.h"
#include "player.h"
#include "state.h"
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Window/Keyboard.hpp>
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

8
main.cpp Normal file
View File

@ -0,0 +1,8 @@
#include "application.h"
int main()
{
Application app;
app.registerStates();
app.run();
}

57
player.cpp Normal file
View File

@ -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<Command>& 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<Command> &queue)
{
switch (event.type)
{
case sf::Event::KeyPressed:
break;
default:
break;
}
}

37
player.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef PLAYER_H
#define PLAYER_H
#include <queue>
#include <map>
#include <SFML/Window/Event.hpp>
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<Command>& queue);
void handleRealtimeInput(std::queue<Command>& queue);
private:
float player_speed;
std::map<sf::Keyboard::Key, Action> map_key_bindings;
std::map<Action, Command> map_action_bindings;
static bool isRealtime(Action action) {return true;}
};
#endif // PLAYER_H

55
resourceholder.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef RESOURCEHOLDER_H
#define RESOURCEHOLDER_H
#include <assert.h>
#include <string>
#include <memory>
#include <map>
namespace sf { class Texture; class Font; }
template <typename Resource, typename Id>
class ResourceHolder
{
public:
bool load(Id id, const std::string& filename)
{
std::unique_ptr<Resource> 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<Id, std::unique_ptr<Resource>> map_resources;
};
// /////////////////////////////////////////////////////////// //
namespace Textures
{
enum class Id
{
Player,
Weakling,
Shotguner,
Background
};
}
using TextureHolder = ResourceHolder<sf::Texture, Textures::Id>;
using FontHolder = ResourceHolder<sf::Font, Textures::Id>;
#endif // RESOURCEHOLDER_H

92
scenenode.cpp Normal file
View File

@ -0,0 +1,92 @@
#include "scenenode.h"
#include <algorithm>
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);
}

45
scenenode.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef SCENENODE_H
#define SCENENODE_H
#include "command.h"
#include <vector>
#include <memory>
#include <SFML/Graphics.hpp>
class SceneNode;
using SceneNodeUPtr = std::unique_ptr<SceneNode>;
using SceneNodeSPtr = std::shared_ptr<SceneNode>;
using SceneNodeConstSPtr = std::shared_ptr<const SceneNode>;
class SceneNode : public std::enable_shared_from_this<SceneNode>,
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<SceneNodeSPtr> 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

20
spritenode.cpp Normal file
View File

@ -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);
}

22
spritenode.h Normal file
View File

@ -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<SpriteNode>;
using SpriteNodeSPtr = std::shared_ptr<SpriteNode>;
#endif // SPRITENODE_H

9
state.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "state.h"
State::State(sf::RenderWindow &window, Application *application) :
render_window(window),
app(application)
{}
State::~State()
{}

35
state.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef STATE_H
#define STATE_H
#include <SFML/System.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Window/Event.hpp>
#include <memory>
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<State>;
using StateSPtr = std::shared_ptr<State>;
#endif // STATE_H

84
world.cpp Normal file
View File

@ -0,0 +1,84 @@
#include "world.h"
#include "spritenode.h"
#include "enemy.h"
#include <cmath>
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<Command>& 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>(Enemy::Type::Player, texture_holder);
player->setPosition(world_spawn_pos);
player->setVelocity(40.f, world_speed);
arr_layers[Air]->attachChild(player);
}

46
world.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef WORLD_H
#define WORLD_H
#include "enemy.h"
#include "resourceholder.h"
#include "scenenode.h"
#include <queue>
#include <SFML/System.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
class World : private sf::NonCopyable
{
public:
explicit World(sf::RenderWindow& window);
void update(const sf::Time& dt);
void draw();
std::queue<Command>& getCommandQueue();
private:
bool loadTextures();
void buildScene();
enum Layer
{
Background,
Air,
LayerCount
};
std::queue<Command> queue_commands;
sf::RenderWindow& world_window;
sf::View world_view;
TextureHolder texture_holder;
SceneNodeSPtr scene_graph;
std::array<SceneNodeSPtr, LayerCount> arr_layers;
sf::FloatRect world_bounds;
sf::Vector2f world_spawn_pos;
float world_speed;
EnemySPtr player;
};
#endif // WORLD_H