Merge branch 'master' into renderer

This commit is contained in:
Clément Le Bihan
2021-05-21 09:30:45 +02:00
32 changed files with 1227 additions and 123 deletions
+8 -3
View File
@@ -1,9 +1,14 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.11)
cmake_minimum_required(VERSION 3.11)
project(Bomberman)
set(CMAKE_CXX_STANDARD 20)
add_subdirectory(${PROJECT_SOURCE_DIR}/lib/wal)
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/lib/raylib)
add_executable(bomberman
sources/main.cpp
sources/main.cpp
)
target_link_libraries(bomberman raylib)
find_package(raylib REQUIRED)
target_link_libraries(bomberman wal raylib)
+20
View File
@@ -0,0 +1,20 @@
# Usage:
# FIND_PACKAGE(raylib REQUIRED)
# [...]
# TARGET_LINK_LIBRARIES(target_name raylib)
CMAKE_MINIMUM_REQUIRED(VERSION 3.11)
if (NOT raylib_FOUND)
INCLUDE(FetchContent)
FetchContent_Declare(raylib URL https://github.com/raysan5/raylib/archive/master.tar.gz)
FetchContent_GetProperties(raylib)
if (NOT raylib_POPULATED)
SET(FETCHCONTENT_QUIET NO)
FetchContent_Populate(raylib)
SET(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
ADD_SUBDIRECTORY(${raylib_SOURCE_DIR} ${raylib_BINARY_DIR})
SET(raylib_FOUND TRUE)
endif()
endif()
+42
View File
@@ -0,0 +1,42 @@
cmake_minimum_required(VERSION 3.19)
project(wal)
set(CMAKE_CXX_STANDARD 20)
add_library(wal
sources/Entity/Entity.hpp
sources/Component/Component.hpp
sources/System/System.hpp
sources/Wal.cpp
sources/Wal.hpp
sources/Scene/SceneManager.cpp
sources/Scene/SceneManager.hpp
sources/Scene/Scene.cpp
sources/Scene/Scene.hpp
sources/Events/EventManager.cpp
sources/Events/EventManager.hpp
sources/Exception/WalError.cpp
sources/Exception/WalError.hpp
sources/Entity/Entity.cpp
sources/Component/Component.cpp
sources/Component/Position/PositionComponent.cpp
sources/Component/Position/PositionComponent.hpp
sources/Models/Vector3.hpp
sources/Component/Movable/MovableComponent.cpp
sources/Component/Movable/MovableComponent.hpp
sources/System/Movable/MovableSystem.cpp
sources/System/Movable/MovableSystem.hpp
sources/System/System.cpp
)
target_include_directories(wal PUBLIC sources)
add_executable(wal_tests EXCLUDE_FROM_ALL
tests/EntityTests.cpp
tests/MainTest.cpp
tests/EngineTests.cpp
)
target_link_libraries(wal_tests PRIVATE wal)
find_package(Catch2 REQUIRED)
target_link_libraries(wal_tests PRIVATE Catch2::Catch2)
-64
View File
@@ -1,64 +0,0 @@
#pragma once
#include <string>
#include <vector>
#include "Component/Component.hpp"
//! @brief I'm deeply sorry for this name
//! @note I am not, he is a liar
namespace WAL
{
//! @brief An entity of the WAL's ECS.
class Entity
{
private:
//! @brief The unique ID of the entity
unsigned _uid;
//! @brief An entity name (this is useful for debugging)
std::string _name;
//! @brief Is this entity enabled?
bool _disabled;
//! @brief The list of the components of this entity
std::vector<Component> _components;
public:
//! @brief Get the ID of the entity.
unsigned getUid() const;
//! @brief Get the name fo the entity
std::string getName() const;
//! @brief Used if the entity is disabled
bool isDisable() const;
//! @brief Disable this entity.
void setDisable(bool disabled);
//! @brief Get a component of a specific type
//! @throw ComponentNotFoundError if the component could not be found
template<typename T>
T getComponent();
//! @brief Add a component to this entity. The component is constructed in place.
//! @return This entity is returned
template<typename T, ...Types>
Entity &addComponent(...params);
//! @brief Copy a component to this entity.
//! @return This entity is returned.
Entity &addComponent(const Component &component);
//! @brief Remove a specific component (by type).
template<typename T>
Entity &removeComponent();
//! @brief A default constructor
Entity(const std::string &name);
//! @brief An entity is copyable
Entity(const Entity &);
//! @brief A default destructor
~Entity() = default;
//! @brief An entity is assignable
Entity &operator=(const Entity &);
};
}
-30
View File
@@ -1,30 +0,0 @@
#pragma once
#include "Entity/Entity.hpp"
namespace WAL
{
//! @brief A base system of WAL
class System
{
public:
//! @brief A virtual, default, destructor
virtual ~System() = default;
//! @brief Get the name of the component corresponding to this system.
virtual std::string getComponentName() const = 0;
//! @brief Update the corresponding component of the given entity
//! @param entity The entity to update.
virtual void onUpdate(Entity &entity) = 0;
protected:
//! @brief A system can't be instantiated, it should be derived.
System() = default;
//! @brief A system can't be instantiated, it should be derived.
System(const System &) = default;
//! @brief A system can't be instantiated, it should be derived.
System &operator=(const System &) = default;
};
}
+37
View File
@@ -0,0 +1,37 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#include "Component/Component.hpp"
namespace WAL
{
Component::Component(Entity &entity)
: _entity(entity)
{ }
bool Component::isDisabled() const
{
return this->_disabled;
}
void Component::setDisable(bool disabled)
{
this->_disabled = disabled;
}
const std::vector<std::type_index> &Component::getDependencies() const
{
return this->_dependencies;
}
void Component::onStart()
{
//TODO handle events here
}
void Component::onStop()
{
//TODO handle events here
}
}
@@ -1,10 +1,12 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#pragma once
#include <vector>
#include <string>
#include <typeindex>
namespace WAL
{
@@ -15,38 +17,40 @@ namespace WAL
class Component
{
private:
//! @brief The name of this component
std::string _name;
//! @brief Is this component disabled?
bool _disabled;
bool _disabled = false;
protected:
//! @brief The entity that own this component
Entity &_entity;
//! @brief The list of dependencies of this component.
// TODO check if there is a better type than strings
std::vector<std::string> _dependencies;
std::vector<std::type_index> _dependencies;
//! @brief A component can't be instantiated, it should be derived.
explicit Component(Entity &entity);
//! @brief A component can't be instantiated, it should be derived.
Component(const Component &) = default;
public:
//! @brief Get the name of this component
std::string _getName() const;
//! @brief A component can't be assigned
Component &operator=(const Component &) = delete;
//! @brief A virtual destructor
virtual ~Component() = default;
//! @brief Clone a component for another or the same entity.
//! @param entity The entity that owns the ne component.
virtual Component *clone(Entity &entity) const = 0;
//! @brief Used if the component is disabled
bool isDisable() const;
bool isDisabled() const;
//! @brief Disable this component.
void setDisable(bool disabled);
//! @brief Get the dependencies of this component.
const std::vector<std::type_index> &getDependencies() const;
//! @brief The entity or this component has just been enabled.
//! @param entity The entity that has this component
virtual void onStart(Entity &entity);
virtual void onStart();
//! @brief The entity or this component has just been disable.
//! @param entity The entity that has this component
virtual void onStop(Entity &entity);
//! @brief A virtual destructor (that also calls onStop)
virtual ~Component();
protected:
//! @brief A component can't be instantiated, it should be derived.
Component() = default;
//! @brief A component can't be instantiated, it should be derived.
Component(const Component &) = default;
//! @brief A component can't be instantiated, it should be derived.
Component &operator=(const Component &) = default;
virtual void onStop();
};
}
@@ -0,0 +1,22 @@
//
// Created by Zoe Roux on 5/17/21.
//
#include "MovableComponent.hpp"
namespace WAL
{
MovableComponent::MovableComponent(Entity &entity)
: Component(entity)
{}
Component *MovableComponent::clone(Entity &entity) const
{
return new MovableComponent(entity);
}
void MovableComponent::addForce(Vector3f force)
{
this->_acceleration += force;
}
}
@@ -0,0 +1,39 @@
//
// Created by Zoe Roux on 5/17/21.
//
#pragma once
#include "Models/Vector3.hpp"
#include "Entity/Entity.hpp"
namespace WAL
{
//! @brief A component to place on entities that can move or be moved.
class MovableComponent : public Component
{
private:
//! @brief The acceleration of this entity.
Vector3f _acceleration;
//! @brief The velocity of the entity.
Vector3f _velocity;
public:
//! @brief Add an instant force to this entity.
//! @param force The force to add to this entity's acceleration. The force is added instantly and in one go.
void addForce(Vector3f force);
//! @inherit
Component *clone(Entity &entity) const override;
//! @brief Create a new movable component.
explicit MovableComponent(Entity &entity);
//! @brief A movable component is copy constructable.
MovableComponent(const MovableComponent &) = default;
//! @brief A default destructor
~MovableComponent() override = default;
//! @brief A movable component is not assignable.
MovableComponent &operator=(const MovableComponent &) = delete;
friend class MovableSystem;
};
}
@@ -0,0 +1,43 @@
//
// Created by Zoe Roux on 5/17/21.
//
#include "PositionComponent.hpp"
namespace WAL
{
PositionComponent::PositionComponent(Entity &entity)
: Component(entity),
position()
{}
PositionComponent::PositionComponent(Entity &entity, Vector3f pos)
: Component(entity),
position(pos)
{}
PositionComponent::PositionComponent(Entity &entity, float x, float y, float z)
: Component(entity),
position(x, y, z)
{}
Component *PositionComponent::clone(WAL::Entity &entity) const
{
return new PositionComponent(entity, this->position);
}
float PositionComponent::getX() const
{
return this->position.x;
}
float PositionComponent::getY() const
{
return this->position.y;
}
float PositionComponent::getZ() const
{
return this->position.z;
}
}
@@ -0,0 +1,42 @@
//
// Created by Zoe Roux on 5/17/21.
//
#pragma once
#include "Models/Vector3.hpp"
#include "Component/Component.hpp"
namespace WAL
{
//! @brief A basic position component
class PositionComponent : public Component
{
public:
//! @brief Get the editable position of this entity
Vector3f position;
//! @brief Get the X position of this entity.
float getX() const;
//! @brief Get the Y position of this entity.
float getY() const;
//! @brief Get the Z position of this entity.
float getZ() const;
//! @inherit
Component *clone(Entity &entity) const override;
//! @brief Create a new PositionComponent linked to a specific entity
explicit PositionComponent(Entity &entity);
//! @brief Create a new PositionComponent at a certain position
PositionComponent(Entity &entity, Vector3f pos);
//! @brief Create a new PositionComponent at a certain position
PositionComponent(Entity &entity, float x, float y, float z);
//! @brief A position component is copy constructable
PositionComponent(const PositionComponent &) = default;
//! @brief A default destructor
~PositionComponent() override = default;
//! @brief A position component is not assignable
PositionComponent &operator=(const PositionComponent &) = delete;
};
}
+62
View File
@@ -0,0 +1,62 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#include "Entity/Entity.hpp"
#include <utility>
namespace WAL
{
unsigned Entity::nextID = 0;
Entity::Entity(std::string name)
: _uid(Entity::nextID++),
_name(std::move(name))
{ }
Entity::Entity(const Entity &other)
: _uid(Entity::nextID++),
_name(other._name),
_disabled(other._disabled)
{
for (const auto &cmp : other._components)
this->addComponent(*cmp);
}
unsigned Entity::getUid() const
{
return this->_uid;
}
std::string Entity::getName() const
{
return this->_name;
}
bool Entity::isDisable() const
{
return this->_disabled;
}
void Entity::setDisable(bool disabled)
{
this->_disabled = disabled;
}
Entity &Entity::addComponent(const Component &component)
{
if (this->hasComponent(typeid(component)))
throw DuplicateError("A component of the type \"" + std::string(typeid(component).name()) + "\" already exists.");
this->_components.emplace_back(component.clone(*this));
return *this;
}
bool Entity::hasComponent(const std::type_info &type) const
{
auto existing = std::find_if(this->_components.begin(), this->_components.end(), [&type] (const auto &cmp) {
return typeid(*cmp) == type;
});
return existing != this->_components.end();
}
}
+113
View File
@@ -0,0 +1,113 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#pragma once
#include <string>
#include <vector>
#include <typeinfo>
#include <memory>
#include "Component/Component.hpp"
#include "Exception/WalError.hpp"
namespace WAL
{
//! @brief An entity of the WAL's ECS.
class Entity
{
private:
//! @brief The unique ID of the entity
unsigned _uid;
//! @brief An entity name (this is useful for debugging)
std::string _name;
//! @brief Is this entity enabled?
bool _disabled = false;
//! @brief The list of the components of this entity
std::vector<std::unique_ptr<Component>> _components = {};
//! @brief This ID will be the one of the next entity created.
static unsigned nextID;
public:
//! @brief Get the ID of the entity.
unsigned getUid() const;
//! @brief Get the name fo the entity
std::string getName() const;
//! @brief Used if the entity is disabled
bool isDisable() const;
//! @brief Disable this entity.
void setDisable(bool disabled);
//! @brief Get a component of a specific type
//! @throw NotFoundError if the component could not be found
template<typename T>
T &getComponent()
{
const std::type_info &type = typeid(T);
auto existing = std::find_if(this->_components.begin(), this->_components.end(), [&type] (const auto &cmp) {
return typeid(*cmp) == type;
});
if (existing == this->_components.end())
throw NotFoundError("No component could be found with the type \"" + std::string(type.name()) + "\".");
return *static_cast<T *>(existing->get());
}
//! @brief Check if this entity has a component.
//! @tparam T The type of the component
template<typename T>
bool hasComponent() const
{
const std::type_info &type = typeid(T);
return this->hasComponent(type);
}
//! @brief Check if this entity has a component.
//! @param type The type of the component
bool hasComponent(const std::type_info &type) const;
//! @brief Add a component to this entity. The component is constructed in place.
//! @throw DuplicateError is thrown if a component with the same type already exist.
//! @return This entity is returned
template<typename T, typename ...Types>
Entity &addComponent(Types ...params)
{
if (this->hasComponent<T>())
throw DuplicateError("A component of the type \"" + std::string(typeid(T).name()) + "\" already exists.");
this->_components.push_back(std::make_unique<T>(*this, params...));
return *this;
}
//! @brief Copy a component to this entity.
//! @return This entity is returned.
Entity &addComponent(const Component &component);
//! @brief Remove a specific component (by type).
//! @throw NotFoundError is thrown if the component could not be found.
//! @return This entity is returned.
template<typename T>
Entity &removeComponent()
{
const std::type_info &type = typeid(T);
auto existing = std::find_if(this->_components.begin(), this->_components.end(), [&type] (const auto &cmp) {
return typeid(*cmp) == type;
});
if (existing == this->_components.end())
throw NotFoundError("No component could be found with the type \"" + std::string(type.name()) + "\".");
this->_components.erase(existing);
return *this;
}
//! @brief A default constructor
explicit Entity(std::string name);
//! @brief An entity is copyable
Entity(const Entity &);
//! @brief An entity is movable.
Entity(Entity &&) = default;
//! @brief A default destructor
~Entity() = default;
//! @brief An entity is assignable
Entity &operator=(const Entity &) = default;
};
}
+5
View File
@@ -0,0 +1,5 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#include "EventManager.hpp"
+15
View File
@@ -0,0 +1,15 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#pragma once
namespace WAL
{
//! @brief A class to handle events.
class EventManager
{
};
}
+20
View File
@@ -0,0 +1,20 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#include "WalError.hpp"
namespace WAL
{
WalError::WalError(const std::string &what)
: std::runtime_error(what)
{}
DuplicateError::DuplicateError(const std::string &what)
: WalError(what)
{}
NotFoundError::NotFoundError(const std::string &what)
: WalError(what)
{}
}
+54
View File
@@ -0,0 +1,54 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#pragma once
#include <exception>
#include <stdexcept>
namespace WAL
{
//! @brief The base class for WAL's exceptions.
class WalError : public std::runtime_error
{
public:
//! @brief Create a new wal exception
explicit WalError(const std::string &what);
//! @brief A wal exception is copy constructable
WalError(const WalError &) = default;
//! @brief A default destructor
~WalError() override = default;
//! @brief A default assignment operator
WalError &operator=(const WalError &) = default;
};
//! @brief An exception informing the user that something already exists.
class DuplicateError : public WalError
{
public:
//! @brief Create a new wal exception
explicit DuplicateError(const std::string &what);
//! @brief A wal exception is copy constructable
DuplicateError(const DuplicateError &) = default;
//! @brief A default destructor
~DuplicateError() override = default;
//! @brief A default assignment operator
DuplicateError &operator=(const DuplicateError &) = default;
};
//! @brief An exception informing the user that something could not be found
class NotFoundError : public WalError
{
public:
//! @brief Create a new wal exception
explicit NotFoundError(const std::string &what);
//! @brief A wal exception is copy constructable
NotFoundError(const NotFoundError &) = default;
//! @brief A default destructor
~NotFoundError() override = default;
//! @brief A default assignment operator
NotFoundError &operator=(const NotFoundError &) = default;
};
}
+167
View File
@@ -0,0 +1,167 @@
//
// Created by Zoe Roux on 5/17/21.
//
#pragma once
#include <iostream>
#include <cmath>
namespace WAL
{
//! @brief A Vector3 data type. (templated to allow any kind of vector3)
template<typename T>
class Vector3
{
public:
//! @brief The x value of the vector
T x;
//! @brief The y value of the vector
T y;
//! @brief The y value of the vector
T z;
//! @brief Create a new nil vector3.
Vector3<T>()
: x(0), y(0), z(0)
{}
//! @brief Create a new vector3 representing a specific coordinate.
Vector3<T>(T x, T y, T z)
: x(x), y(y), z(z)
{}
//! @brief A default destructor
~Vector3() = default;
bool operator==(const Vector3<T> &other) const
{
return this->x == other.x && this->y == other.y && this->z == other.z;
}
bool operator!=(const Vector3<T> &other) const
{
return !this->operator==(other);
}
template<typename T2>
Vector3<T> &operator+=(const Vector3<T2> &vec)
{
this->x += vec.x;
this->y += vec.y;
this->z += vec.z;
return *this;
}
template<typename T2>
Vector3<T> operator+(const Vector3<T2> &vec) const
{
return Vector3<T>(this->x + vec.x, this->y + vec.y, this->z + vec.z);
}
template<typename T2>
Vector3<T> &operator-=(const Vector3<T2> &vec)
{
this->x -= vec.x;
this->y -= vec.y;
this->z -= vec.z;
return *this;
}
template<typename T2>
Vector3<T> &operator*=(T2 d)
{
this->x *= d;
this->y *= d;
this->z *= d;
return *this;
}
template<typename T2>
Vector3<T> operator*(T2 d) const
{
return Vector3<T>(this->x * d, this->y * d, this->z * d);
}
template<typename T2>
Vector3<T> operator*(Vector3<T2> &b) const
{
return Vector3<T>(this->x * b.x, this->y * b.y, this->z * b.z);
}
template<typename T2>
Vector3<T> operator/=(Vector3<T2> &b)
{
this->x /= b.x;
this->y /= b.y;
this->z /= b.z;
return this;
}
template<typename T2>
Vector3<T> operator/(Vector3<T2> &b) const
{
return Vector3<T>(this->x / b.x, this->y / b.y, this->z / b.z);
}
template<typename T2>
Vector3<T> operator/=(T2 b)
{
this->x /= b;
this->y /= b;
this->z /= b;
return this;
}
template<typename T2>
Vector3<T> operator/(T2 b) const
{
return Vector3<T>(this->x / b, this->y / b, this->z / b);
}
template<typename T2>
double distance(const Vector3<T2> &o) const
{
return std::sqrt(std::pow(this->x - o.x, 2) + std::pow(this->y - o.y, 2) + std::pow(this->z - o.z, 2));
}
double magnitude() const
{
return (std::sqrt(std::pow(this->x, 2) + std::pow(this->y, 2), std::pow(this->z, 2)));
}
Vector3<T> normalize()
{
double mag = this->magnitude();
this->x /= mag;
this->y /= mag;
this->z /= mag;
return *this;
}
Vector3<T> normalized() const
{
T mag = this->magnitude();
return Vector3<T>(this->x / mag, this->y / mag, this->z / mag);
}
Vector3<T> projection(const Vector3<T> &point) const
{
return (point * this) / std::pow(this->magnitude(), 2) * this;
}
};
typedef Vector3<float> Vector3f;
typedef Vector3<unsigned> Vector3u;
typedef Vector3<int> Vector3i;
}
template<typename T>
std::ostream &operator<<(std::ostream &s, const WAL::Vector3<T> &v)
{
s << "Vector3<" << typeid(T).name() << ">("<< v.x << ", " << v.y << ", " << v.z << ")";
return s;
}
+14
View File
@@ -0,0 +1,14 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#include "Scene.hpp"
namespace WAL
{
std::vector<Entity> &Scene::getEntities()
{
return this->_entities;
}
}
+23
View File
@@ -0,0 +1,23 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#pragma once
#include <vector>
#include "Entity/Entity.hpp"
namespace WAL
{
//! @brief Represent a single scene that contains entities.
class Scene
{
private:
//! @brief The list of registered entities
std::vector<Entity> _entities;
public:
//! @brief Get the list of entities.
std::vector<Entity> &getEntities();
};
}
+33
View File
@@ -0,0 +1,33 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#include "SceneManager.hpp"
namespace WAL
{
SceneManager &WAL::SceneManager::addScene(WAL::Scene &&scene)
{
this->_scenes.push_front(scene);
return *this;
}
SceneManager &SceneManager::addBackScene(Scene &&scene)
{
this->_scenes.insert(++this->_scenes.begin(), scene);
return *this;
}
Scene &SceneManager::getCurrent()
{
if (this->_scenes.empty())
throw NotFoundError("No scene exists.");
return this->_scenes.front();
}
SceneManager &SceneManager::closeCurrent()
{
this->_scenes.pop_front();
return *this;
}
}
+43
View File
@@ -0,0 +1,43 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#pragma once
#include <queue>
#include "Scene/Scene.hpp"
namespace WAL
{
//! @brief A class to manage scenes
class SceneManager
{
private:
std::deque<Scene> _scenes = {};
public:
//! @brief Add a scene to the container and move to it.
//! @return The manager instance used to call this function is returned. This allow method chaining.
SceneManager &addScene(Scene &&scene);
//! @brief Add a scene before the current scene. This could be useful for lobbies or scene where the next scene can be constructed.
//! @return The manager instance used to call this function is returned. This allow method chaining.
SceneManager &addBackScene(Scene &&scene);
//! @breif Get the current scene
Scene &getCurrent();
//! @brief Remove the current scene and switch to the previous scene on the stack.
//! @return The manager instance used to call this function is returned. This allow method chaining.
SceneManager &closeCurrent();
//! @brief A default constructor
SceneManager() = default;
//! @brief A scene manager is copy constructable
SceneManager(const SceneManager &) = default;
//! @brief A default destructor.
~SceneManager() = default;
//! @brief A scene manager is assignable
SceneManager &operator=(const SceneManager &) = default;
};
}
@@ -0,0 +1,26 @@
//
// Created by Zoe Roux on 5/17/21.
//
#include "Component/Position/PositionComponent.hpp"
#include "System/Movable/MovableSystem.hpp"
#include "Component/Movable/MovableComponent.hpp"
#include "Wal.hpp"
namespace WAL
{
const std::type_info &MovableSystem::getComponent() const
{
return typeid(MovableComponent);
}
void MovableSystem::onFixedUpdate(Entity &entity)
{
auto &movable = entity.getComponent<MovableComponent>();
auto &position = entity.getComponent<PositionComponent>();
position.position += movable._velocity * Wal::timestep.count();
movable._velocity = movable._acceleration * Wal::timestep.count();
movable._acceleration = Vector3f();
}
}
@@ -0,0 +1,30 @@
//
// Created by Zoe Roux on 5/17/21.
//
#pragma once
#include "System/System.hpp"
namespace WAL
{
//! @brief A system to handle movable entities. This system update velocity based on accelerations and positions based on velocity.
class MovableSystem : public System
{
public:
//! @inherit
const std::type_info &getComponent() const override;
//! @inherit
void onFixedUpdate(Entity &entity) override;
//! @brief A default constructor
MovableSystem() = default;
//! @brief A movable system is copy constructable
MovableSystem(const MovableSystem &) = default;
//! @brief A default destructor
~MovableSystem() override = default;
//! @brief A movable system is assignable.
MovableSystem &operator=(const MovableSystem &) = default;
};
}
+18
View File
@@ -0,0 +1,18 @@
//
// Created by Zoe Roux on 5/17/21.
//
#include "System.hpp"
namespace WAL
{
void System::onUpdate(Entity &entity, std::chrono::nanoseconds dtime)
{}
void System::onFixedUpdate(Entity &entity)
{}
void System::onSelfUpdate()
{}
}
+44
View File
@@ -0,0 +1,44 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#pragma once
#include <chrono>
#include "Entity/Entity.hpp"
namespace WAL
{
//! @brief A base system of WAL
class System
{
public:
//! @brief A virtual, default, destructor
virtual ~System() = default;
//! @brief A system can be moved
System(System &&) = default;
//! @brief Get the name of the component corresponding to this system.
virtual const std::type_info &getComponent() const = 0;
//! @brief Update the corresponding component of the given entity
//! @param entity The entity to update.
//! @param dtime The delta time.
virtual void onUpdate(Entity &entity, std::chrono::nanoseconds dtime);
//! @brief An alternative of onUpdate that is called every 8ms (120 times per seconds). If the system slow down, it will try to catch up.
//! @remark This should be used for Physics, AI and everything that could be imprecise due to float rounding.
//! @param entity The entity to update.
virtual void onFixedUpdate(Entity &entity);
//! @brief A method called after all entities that this system manage has been updated.
virtual void onSelfUpdate();
protected:
//! @brief A system can't be instantiated, it should be derived.
System() = default;
//! @brief A system can't be instantiated, it should be derived.
System(const System &) = default;
//! @brief A system can't be instantiated, it should be derived.
System &operator=(const System &) = default;
};
}
+68
View File
@@ -0,0 +1,68 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#include <chrono>
#include "Wal.hpp"
using namespace std::chrono_literals;
namespace WAL
{
std::chrono::nanoseconds Wal::timestep = 8ms;
SceneManager &Wal::getSceneManager()
{
return this->_sceneManager;
}
void Wal::run()
{
auto lastTick = std::chrono::steady_clock::now();
std::chrono::nanoseconds fBehind(0);
while (!this->_shouldClose) {
auto now = std::chrono::steady_clock::now();
std::chrono::nanoseconds dtime = now - lastTick;
fBehind += dtime;
lastTick = now;
while (fBehind > Wal::timestep) {
fBehind -= Wal::timestep;
this->_fixedUpdate();
}
this->_update(dtime);
}
}
void Wal::_update(std::chrono::nanoseconds dtime)
{
auto &entities = this->_sceneManager.getCurrent().getEntities();
for (auto &system : this->_systems) {
for (auto &entity : entities) {
const auto &cmp = system->getComponent();
if (!entity.hasComponent(cmp))
continue;
// TODO handle dependencies.
system->onUpdate(entity, dtime);
}
system->onSelfUpdate();
}
}
void Wal::_fixedUpdate()
{
auto &entities = this->_sceneManager.getCurrent().getEntities();
for (auto &system : this->_systems) {
for (auto &entity : entities) {
auto &cmp = system->getComponent();
if (!entity.hasComponent(cmp))
continue;
// TODO handle dependencies.
system->onFixedUpdate(entity);
}
}
}
}
+115
View File
@@ -0,0 +1,115 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#pragma once
#include <vector>
#include <memory>
#include <typeinfo>
#include <Exception/WalError.hpp>
#include "Events/EventManager.hpp"
#include "Scene/SceneManager.hpp"
#include "Entity/Entity.hpp"
#include "System/System.hpp"
namespace WAL
{
//! @brief The main WAL class, it is used to setup and run the ECS.
class Wal
{
private:
//! @brief The scene manager that allow multiple scene to work together.
SceneManager _sceneManager;
//! @brief The event manager
EventManager _eventManager;
//! @brief The list of registered systems
std::vector<std::unique_ptr<System>> _systems = {};
//! @brief True if the engine should close after the end of the current tick.
bool _shouldClose = false;
//! @brief Call the onUpdate of every system with every component
void _update(std::chrono::nanoseconds dtime);
//! @brief Call the onFixedUpdate of every system with every component
void _fixedUpdate();
public:
//! @brief The time between each fixed update.
static std::chrono::nanoseconds timestep;
//! @brief Create a new system in place.
//! @return The wal instance used to call this function is returned. This allow method chaining.
template<typename T, class ...Types>
Wal &addSystem(Types ...params)
{
const std::type_info &type = typeid(T);
auto existing = std::find_if(this->_systems.begin(), this->_systems.end(), [&type] (auto &sys) {
return typeid(*sys) == type;
});
if (existing != this->_systems.end())
throw DuplicateError("A system of the type \"" + std::string(type.name()) + "\" already exists.");
this->_systems.push_back(std::make_unique<T>(params...));
return *this;
}
//! @brief Add a system by copy.
//! @return The wal instance used to call this function is returned. This allow method chaining.
template<typename T>
Wal &addSystem(const T &system)
{
const std::type_info &type = typeid(T);
auto existing = std::find_if(this->_systems.begin(), this->_systems.end(), [&type] (auto &sys) {
return typeid(*sys) == type;
});
if (existing != this->_systems.end())
throw DuplicateError("A system of the type \"" + std::string(type.name()) + "\" already exists.");
this->_systems.push_back(std::make_unique<T>(system));
return *this;
}
//! @brief Get a system of a specific type
//! @tparam T the type of the system.
template<typename T>
T &getSystem()
{
const std::type_info &type = typeid(T);
auto existing = std::find_if(this->_systems.begin(), this->_systems.end(), [&type] (auto &sys) {
return typeid(*sys) == type;
});
if (existing == this->_systems.end())
throw NotFoundError("A system of the type \"" + std::string(type.name()) + "\" could not be found.");
return *static_cast<T *>(existing->get());
}
//! @brief Remove a system using it's type.
template<typename T>
Wal &removeSystem()
{
const std::type_info &type = typeid(T);
auto existing = std::find_if(this->_systems.begin(), this->_systems.end(), [&type] (auto &sys) {
return typeid(*sys) == type;
});
if (existing == this->_systems.end())
throw NotFoundError("No system could be found with the type \"" + std::string(type.name()) + "\".");
this->_systems.erase(existing);
return *this;
}
//! @brief Get the scene manager.
SceneManager &getSceneManager();
//! @brief Start the game loop
void run();
//! @brief A default constructor
Wal() = default;
//! @brief A WAL can't be copy constructed
Wal(const Wal &) = delete;
//! @brief A default destructor
~Wal() = default;
//! @brief A WAL can't be assigned.
Wal &operator=(const Wal &) = delete;
};
}
+36
View File
@@ -0,0 +1,36 @@
//
// Created by Zoe Roux on 5/17/21.
//
#include "Wal.hpp"
#include "System/Movable/MovableSystem.hpp"
#include <catch2/catch.hpp>
using namespace WAL;
TEST_CASE("Create system", "[Engine][System]")
{
Wal wal;
wal.addSystem<MovableSystem>();
SECTION("Check existence") {
REQUIRE_NOTHROW(wal.getSystem<MovableSystem>());
}
SECTION("Duplicate check") {
REQUIRE_THROWS_AS(wal.addSystem<MovableSystem>(), DuplicateError);
}
SECTION("Remove system") {
wal.removeSystem<MovableSystem>();
REQUIRE_THROWS_AS(wal.getSystem<MovableSystem>(), NotFoundError);
REQUIRE_THROWS_AS(wal.removeSystem<MovableSystem>(), NotFoundError);
}
}
TEST_CASE("Create system by reference", "[Engine][System]")
{
Wal wal;
MovableSystem system;
wal.addSystem(system);
REQUIRE_THROWS_AS(wal.addSystem<MovableSystem>(), DuplicateError);
}
+44
View File
@@ -0,0 +1,44 @@
//
// Created by Zoe Roux on 5/17/21.
//
#include "Entity/Entity.hpp"
#include "Component/Position/PositionComponent.hpp"
#include <catch2/catch.hpp>
using namespace WAL;
TEST_CASE("Component", "[Entity]")
{
Entity entity("Bob");
entity.addComponent<PositionComponent>(2, 3, 4);
SECTION("Check value") {
auto &pos = entity.getComponent<PositionComponent>();
REQUIRE(entity.hasComponent<PositionComponent>());
REQUIRE(pos.position == Vector3f(2, 3, 4));
}
SECTION("Prevent duplicates") {
REQUIRE_THROWS_AS(entity.addComponent<PositionComponent>(), DuplicateError);
}
SECTION("Remove component") {
entity.removeComponent<PositionComponent>();
REQUIRE_THROWS_AS(entity.getComponent<PositionComponent>(), NotFoundError);
REQUIRE_THROWS_AS(entity.removeComponent<PositionComponent>(), NotFoundError);
}
}
TEST_CASE("ComponentNotFound", "[Entity]")
{
Entity entity("Bob");
REQUIRE_THROWS_AS(entity.getComponent<PositionComponent>(), NotFoundError);
}
TEST_CASE("Add component by reference", "[Entity]")
{
Entity entity("Bob");
PositionComponent component(entity, 4, 5, 6);
REQUIRE(&entity.addComponent(component) == &entity);
REQUIRE(entity.getComponent<PositionComponent>().position == Vector3f(4, 5, 6));
}
+6
View File
@@ -0,0 +1,6 @@
//
// Created by Zoe Roux on 5/17/21.
//
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
+10 -2
View File
@@ -1,7 +1,15 @@
#include <iostream>
#include <Wal.hpp>
int main()
{
std::cout << "Hello, World!" << std::endl;
return 0;
WAL::Wal wal;
try {
wal.run();
return 0;
} catch (const std::exception &ex) {
std::cerr << ex.what() << std::endl;
return 84;
}
}