mirror of
https://github.com/zoriya/Bomberman.git
synced 2026-06-06 19:22:20 +00:00
Trying to merge with develop
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
//
|
||||
|
||||
#include "Entity/Entity.hpp"
|
||||
#include "Scene/Scene.hpp"
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
@@ -10,18 +11,20 @@ namespace WAL
|
||||
{
|
||||
unsigned Entity::nextID = 0;
|
||||
|
||||
Entity::Entity(std::string name)
|
||||
Entity::Entity(Scene &scene, std::string name)
|
||||
: _uid(Entity::nextID++),
|
||||
_scene(scene),
|
||||
_name(std::move(name))
|
||||
{ }
|
||||
|
||||
Entity::Entity(const Entity &other)
|
||||
: _uid(Entity::nextID++),
|
||||
_scene(other._scene),
|
||||
_name(other._name),
|
||||
_disabled(other._disabled)
|
||||
{
|
||||
for (const auto &cmp : other._components)
|
||||
this->addComponent(*cmp);
|
||||
this->addComponent(*cmp.second);
|
||||
}
|
||||
|
||||
unsigned Entity::getUid() const
|
||||
@@ -46,34 +49,35 @@ namespace WAL
|
||||
|
||||
Entity &Entity::addComponent(const Component &component)
|
||||
{
|
||||
if (this->hasComponent(typeid(component), false))
|
||||
throw DuplicateError("A component of the type \"" + std::string(typeid(component).name()) + "\" already exists.");
|
||||
this->_components.emplace_back(component.clone(*this));
|
||||
const std::type_index &type = typeid(component);
|
||||
if (this->hasComponent(type, false))
|
||||
throw DuplicateError("A component of the type \"" + std::string(type.name()) + "\" already exists.");
|
||||
this->_components.emplace(type, component.clone(*this));
|
||||
this->_scene._componentAdded(*this, type);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Entity::hasComponent(const std::type_info &type, bool skipDisabled) const
|
||||
{
|
||||
auto existing = std::find_if(this->_components.begin(), this->_components.end(), [&type] (const auto &cmp) {
|
||||
return typeid(*cmp) == type;
|
||||
});
|
||||
if (existing == this->_components.end())
|
||||
return false;
|
||||
if (skipDisabled)
|
||||
return !(*existing)->isDisabled();
|
||||
return true;
|
||||
return this->hasComponent(static_cast<const std::type_index &>(type), skipDisabled);
|
||||
}
|
||||
|
||||
bool Entity::hasComponent(const std::type_index &type, bool skipDisabled) const
|
||||
{
|
||||
auto existing = std::find_if(this->_components.begin(), this->_components.end(), [&type] (const auto &cmp) {
|
||||
return std::type_index(typeid(*cmp)) == type;
|
||||
});
|
||||
if (existing == this->_components.end())
|
||||
auto cmp = this->_components.find(type);
|
||||
if (cmp == this->_components.end())
|
||||
return false;
|
||||
if (skipDisabled)
|
||||
return !(*existing)->isDisabled();
|
||||
return true;
|
||||
return !cmp->second->isDisabled();
|
||||
}
|
||||
|
||||
void Entity::_componentAdded(const std::type_index &type)
|
||||
{
|
||||
this->_scene._componentAdded(*this, type);
|
||||
}
|
||||
|
||||
void Entity::_componentRemoved(const std::type_index &type)
|
||||
{
|
||||
this->_scene._componentRemoved(*this, type);
|
||||
}
|
||||
|
||||
bool Entity::shouldDelete() const
|
||||
|
||||
@@ -5,14 +5,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <typeinfo>
|
||||
#include <memory>
|
||||
#include "Component/Component.hpp"
|
||||
#include "Exception/WalError.hpp"
|
||||
#include "Models/TypeHolder.hpp"
|
||||
|
||||
namespace WAL
|
||||
{
|
||||
class Scene;
|
||||
|
||||
//! @brief An entity of the WAL's ECS.
|
||||
class Entity
|
||||
{
|
||||
@@ -26,10 +29,20 @@ namespace WAL
|
||||
//! @brief Has this entity been scheduled for deletion?
|
||||
bool _shouldDelete;
|
||||
//! @brief The list of the components of this entity
|
||||
std::vector<std::unique_ptr<Component>> _components = {};
|
||||
std::unordered_map<std::type_index, std::unique_ptr<Component>> _components = {};
|
||||
|
||||
//! @brief This ID will be the one of the next entity created.
|
||||
static unsigned nextID;
|
||||
|
||||
//! @brief Callback called when a component is added
|
||||
//! @param type The type of component
|
||||
void _componentAdded(const std::type_index &type);
|
||||
//! @brief Callback called when a component is removed
|
||||
//! @param type The type of component
|
||||
void _componentRemoved(const std::type_index &type);
|
||||
protected:
|
||||
//! @brief A reference to the ECS.
|
||||
Scene &_scene;
|
||||
public:
|
||||
//! @brief Get the ID of the entity.
|
||||
unsigned getUid() const;
|
||||
@@ -47,17 +60,55 @@ namespace WAL
|
||||
void scheduleDeletion();
|
||||
|
||||
//! @brief Get a component of a specific type
|
||||
//! @tparam The type of the component
|
||||
//! @throw NotFoundError if the component could not be found
|
||||
//! @return The component of the requested type.
|
||||
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;
|
||||
});
|
||||
T *ret = this->tryGetComponent<T>();
|
||||
if (ret == nullptr)
|
||||
throw NotFoundError("No component could be found with the type \"" + std::string(typeid(T).name()) + "\".");
|
||||
return *ret;
|
||||
}
|
||||
|
||||
//! @brief Get a component of a specific type or null if not found.
|
||||
//! @tparam The type of the component
|
||||
//! @return The component or nullptr if not found.
|
||||
template<typename T>
|
||||
T *tryGetComponent()
|
||||
{
|
||||
const std::type_index &type = typeid(T);
|
||||
auto existing = this->_components.find(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());
|
||||
return nullptr;
|
||||
return static_cast<T *>(existing->second.get());
|
||||
}
|
||||
|
||||
//! @brief Get a component of a specific type
|
||||
//! @tparam The type of the component
|
||||
//! @throw NotFoundError if the component could not be found
|
||||
//! @return The component of the requested type.
|
||||
template<typename T>
|
||||
const T &getComponent() const
|
||||
{
|
||||
const T *ret = this->tryGetComponent<T>();
|
||||
if (ret == nullptr)
|
||||
throw NotFoundError("No component could be found with the type \"" + std::string(typeid(T).name()) + "\".");
|
||||
return *ret;
|
||||
}
|
||||
|
||||
//! @brief Get a component of a specific type or null if not found.
|
||||
//! @tparam The type of the component
|
||||
//! @return The component or nullptr if not found.
|
||||
template<typename T>
|
||||
const T *tryGetComponent() const
|
||||
{
|
||||
const std::type_index &type = typeid(T);
|
||||
auto existing = this->_components.find(type);
|
||||
if (existing == this->_components.end())
|
||||
return nullptr;
|
||||
return static_cast<T *>(existing->second.get());
|
||||
}
|
||||
|
||||
//! @brief Check if this entity has a component.
|
||||
@@ -83,12 +134,14 @@ namespace WAL
|
||||
//! @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>
|
||||
template<typename T, typename ...TNested, typename ...Types>
|
||||
Entity &addComponent(Types &&...params)
|
||||
{
|
||||
if (this->hasComponent<T>(false))
|
||||
throw DuplicateError("A component of the type \"" + std::string(typeid(T).name()) + "\" already exists.");
|
||||
this->_components.push_back(std::make_unique<T>(*this, std::forward<Types>(params)...));
|
||||
const std::type_index &type = typeid(T);
|
||||
if (this->hasComponent(type))
|
||||
throw DuplicateError("A component of the type \"" + std::string(type.name()) + "\" already exists.");
|
||||
this->_components[type] = std::make_unique<T>(*this, TypeHolder<TNested>()..., std::forward<Types>(params)...);
|
||||
this->_componentAdded(type);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -103,17 +156,16 @@ namespace WAL
|
||||
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;
|
||||
});
|
||||
auto existing = this->_components.find(type);
|
||||
if (existing == this->_components.end())
|
||||
throw NotFoundError("No component could be found with the type \"" + std::string(type.name()) + "\".");
|
||||
this->_components.erase(existing);
|
||||
this->_componentRemoved(type);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! @brief A default constructor
|
||||
explicit Entity(std::string name);
|
||||
explicit Entity(Scene &wal, std::string name);
|
||||
//! @brief An entity is copyable
|
||||
Entity(const Entity &);
|
||||
//! @brief An entity is movable.
|
||||
|
||||
@@ -24,10 +24,14 @@ namespace WAL
|
||||
//! @brief Add a method to be called when this callback is invoked.
|
||||
//! @param callback The list of arguments of the callback method
|
||||
//! @return A unique ID for this callback. That can be used to remove the callback later.
|
||||
int addCallback(std::function<void (Types...)> callback)
|
||||
template<typename Func>
|
||||
int addCallback(Func callback)
|
||||
{
|
||||
int id = this->_nextID++;
|
||||
this->_functions[id] = std::move(callback);
|
||||
if constexpr(std::is_same_v<Func, std::function<void (Types...)>>)
|
||||
this->_functions[id] = std::move(callback);
|
||||
else
|
||||
this->_functions[id] = std::function<void (Types...)>(callback);
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -53,10 +57,14 @@ namespace WAL
|
||||
//! @brief A default assignment operator
|
||||
Callback &operator=(const Callback &) = default;
|
||||
|
||||
//! @brief Implicitly transform a function into a callback.
|
||||
Callback(std::function<void (Types...)> callback) // NOLINT(google-explicit-constructor)
|
||||
//! @brief Implicitly transform a callable into a callback.
|
||||
template<typename Func>
|
||||
Callback(Func callback) // NOLINT(google-explicit-constructor)
|
||||
{
|
||||
this->addCallback(callback);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ...Types>
|
||||
static constexpr Callback<Types...> EmptyCallback;
|
||||
} // namespace WAL
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// Created by Zoe Roux on 2021-06-02.
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace WAL
|
||||
{
|
||||
//! @brief A class only used to specify template arguments.
|
||||
template<typename T>
|
||||
class TypeHolder {};
|
||||
}
|
||||
@@ -3,15 +3,62 @@
|
||||
//
|
||||
|
||||
#include "Scene.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
namespace WAL
|
||||
{
|
||||
std::vector<Entity> &Scene::getEntities()
|
||||
int Scene::_nextID = 0;
|
||||
|
||||
std::list<Entity> &Scene::getEntities()
|
||||
{
|
||||
return this->_entities;
|
||||
}
|
||||
|
||||
Scene &Scene::operator=(const Scene &)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
Entity &Scene::addEntity(const std::string &name)
|
||||
{
|
||||
return this->_entities.emplace_back(*this, name);
|
||||
}
|
||||
|
||||
void Scene::_componentAdded(Entity &entity, const std::type_index &type)
|
||||
{
|
||||
for (auto &view : this->_views) {
|
||||
if (std::find(view->getTypes().begin(), view->getTypes().end(), type) == view->getTypes().end())
|
||||
continue;
|
||||
bool valid = std::all_of(view->getTypes().begin(), view->getTypes().end(), [&entity](const auto &type){
|
||||
return entity.hasComponent(type);
|
||||
});
|
||||
if (valid)
|
||||
view->emplace_back(entity);
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::_componentRemoved(const Entity &entity, const std::type_index &type)
|
||||
{
|
||||
for (auto &view : this->_views) {
|
||||
if (std::find(view->getTypes().begin(), view->getTypes().end(), type) == view->getTypes().end())
|
||||
continue;
|
||||
view->erase(entity);
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::_entityRemoved(const Entity &entity)
|
||||
{
|
||||
for (auto &view : this->_views)
|
||||
view->erase(entity);
|
||||
}
|
||||
|
||||
void Scene::deleteMarkedEntities()
|
||||
{
|
||||
this->_entities.remove_if([this](auto &entity) {
|
||||
if (!entity.shouldDelete())
|
||||
return false;
|
||||
this->_entityRemoved(entity);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
} // namespace WAL
|
||||
@@ -6,7 +6,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <functional>
|
||||
#include <View/View.hpp>
|
||||
#include "Entity/Entity.hpp"
|
||||
|
||||
namespace WAL
|
||||
@@ -15,20 +17,51 @@ namespace WAL
|
||||
class Scene
|
||||
{
|
||||
private:
|
||||
static int _nextID;
|
||||
//! @brief An ID representing this scene.
|
||||
int _id = _nextID++;
|
||||
|
||||
//! @brief The list of registered entities
|
||||
std::vector<Entity> _entities = {};
|
||||
std::list<Entity> _entities = {};
|
||||
//! @brief The list of cached views to update.
|
||||
std::vector<std::shared_ptr<IView>> _views = {};
|
||||
|
||||
//! @brief Notify this scene that a component has been added to the given entity.
|
||||
//! @param entity The entity with the new component
|
||||
//! @param type The type of the component added.
|
||||
void _componentAdded(Entity &entity, const std::type_index &type);
|
||||
//! @brief Notify this scene that a component has been removed to the given entity.
|
||||
//! @param entity The entity with the removed component
|
||||
//! @param type The type of the component removed.
|
||||
void _componentRemoved(const Entity &entity, const std::type_index &type);
|
||||
//! @brief Remove an entity from every views.
|
||||
//! @param entity The entity to remove.
|
||||
void _entityRemoved(const Entity &entity);
|
||||
public:
|
||||
//! @brief Get the list of entities.
|
||||
std::vector<Entity> &getEntities();
|
||||
std::list<Entity> &getEntities();
|
||||
|
||||
//! @brief Add a new entity to the scene, you can use this method with the same arguments as the entity's constructor.
|
||||
//! @return The current scene is returned to allow you to chain call.
|
||||
template <class ...Params>
|
||||
Entity &addEntity(Params &&...params)
|
||||
//! @brief Add a new entity to the scene.
|
||||
//! @param name The name of the created entity.
|
||||
//! @return The created entity is returned.
|
||||
Entity &addEntity(const std::string &name);
|
||||
|
||||
template<typename ...Components>
|
||||
View<Components...> &view()
|
||||
{
|
||||
return this->_entities.emplace_back(std::forward<Params>(params)...);
|
||||
static std::unordered_map<int, std::weak_ptr<View<Components...>>> cache;
|
||||
auto existing = cache.find(this->_id);
|
||||
if (existing != cache.end() && !existing->second.expired())
|
||||
return *existing->second.lock();
|
||||
auto view = std::make_shared<View<Components...>>(this->_entities);
|
||||
this->_views.emplace_back(view);
|
||||
cache.emplace(this->_id, view);
|
||||
return *view;
|
||||
}
|
||||
|
||||
//! @brief Delete entities marked as deleted.
|
||||
void deleteMarkedEntities();
|
||||
|
||||
//! @brief A default constructor
|
||||
Scene() = default;
|
||||
//! @brief A scene is copy constructable
|
||||
@@ -38,5 +71,7 @@ namespace WAL
|
||||
//! @brief A scene is assignable
|
||||
Scene &operator=(const Scene &);
|
||||
Scene(Scene &&) = default;
|
||||
|
||||
friend Entity;
|
||||
};
|
||||
} // namespace WAL
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// Created by Zoe Roux on 2021-06-04.
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Entity/Entity.hpp"
|
||||
#include "View/View.hpp"
|
||||
#include <chrono>
|
||||
|
||||
namespace WAL
|
||||
{
|
||||
//! @brief A base class that represent a system.
|
||||
class ISystem
|
||||
{
|
||||
public:
|
||||
//! @brief Update the whole system (every entities that this system is responsible can be updated.
|
||||
//! @param dtime The delta time since the last call to this method.
|
||||
virtual void update(std::chrono::nanoseconds dtime) = 0;
|
||||
|
||||
//! @brief An alternative of update 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.
|
||||
virtual void fixedUpdate() = 0;
|
||||
|
||||
//! @brief Get a view containing every entity this system should update.
|
||||
virtual IView &getView() = 0;
|
||||
|
||||
//! @brief A virtual default destructor.
|
||||
virtual ~ISystem() = default;
|
||||
};
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
//
|
||||
// Created by Zoe Roux on 5/17/21.
|
||||
//
|
||||
|
||||
#include "System.hpp"
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
namespace WAL
|
||||
{
|
||||
System::System(std::vector<std::type_index> dependencies)
|
||||
: _dependencies(std::move(dependencies))
|
||||
{}
|
||||
|
||||
void System::onUpdate(Entity &entity, std::chrono::nanoseconds dtime)
|
||||
{}
|
||||
|
||||
void System::onFixedUpdate(Entity &entity)
|
||||
{}
|
||||
|
||||
void System::onSelfUpdate()
|
||||
{}
|
||||
|
||||
const std::vector<std::type_index> &System::getDependencies() const
|
||||
{
|
||||
return this->_dependencies;
|
||||
}
|
||||
} // namespace WAL
|
||||
@@ -6,40 +6,71 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include <typeinfo>
|
||||
#include <typeindex>
|
||||
#include "Entity/Entity.hpp"
|
||||
#include "Wal.hpp"
|
||||
#include "View/View.hpp"
|
||||
#include "ISystem.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace WAL
|
||||
{
|
||||
//! @brief A base system of WAL
|
||||
class System
|
||||
//! @tparam Dependencies The list of dependencies this system has.
|
||||
template<typename ...Dependencies>
|
||||
class System : public ISystem
|
||||
{
|
||||
private:
|
||||
//! @brief The list of dependencies of this system
|
||||
std::vector<std::type_index> _dependencies = {};
|
||||
public:
|
||||
//! @brief A virtual, default, destructor
|
||||
virtual ~System() = default;
|
||||
~System() override = default;
|
||||
//! @brief A system can be moved
|
||||
System(System &&) = default;
|
||||
System(System &&) noexcept = default;
|
||||
|
||||
//! @brief Get the name of the component corresponding to this system.
|
||||
const std::vector<std::type_index> &getDependencies() const;
|
||||
//! @brief Get a view of all entities containing every dependencies of this system.
|
||||
View<Dependencies...> &getView() override
|
||||
{
|
||||
return this->_wal.scene->template view<Dependencies...>();
|
||||
}
|
||||
|
||||
//! @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);
|
||||
virtual void onUpdate(ViewEntity<Dependencies...> &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);
|
||||
virtual void onFixedUpdate(ViewEntity<Dependencies...> &entity) {}
|
||||
|
||||
//! @brief A method called after all entities that this system manage has been updated.
|
||||
virtual void onSelfUpdate();
|
||||
virtual void onSelfUpdate() {}
|
||||
|
||||
|
||||
//! @brief Update the whole system (every entities that this system is responsible can be updated.
|
||||
//! @param dtime The delta time since the last call to this method.
|
||||
void update(std::chrono::nanoseconds dtime) final
|
||||
{
|
||||
for (auto &entity : this->getView())
|
||||
this->onUpdate(entity, dtime);
|
||||
this->onSelfUpdate();
|
||||
}
|
||||
|
||||
//! @brief An alternative of update 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.
|
||||
void fixedUpdate() final
|
||||
{
|
||||
for (auto &entity : this->getView())
|
||||
this->onFixedUpdate(entity);
|
||||
}
|
||||
protected:
|
||||
//! @brief A reference to the ECS.
|
||||
Wal &_wal;
|
||||
|
||||
//! @brief A system can't be instantiated, it should be derived.
|
||||
explicit System(std::vector<std::type_index> dependencies);
|
||||
explicit System(Wal &wal)
|
||||
: _wal(wal)
|
||||
{}
|
||||
//! @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.
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
//
|
||||
// Created by Zoe Roux on 2021-06-03.
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <tuple>
|
||||
#include <typeindex>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <optional>
|
||||
#include "Entity/Entity.hpp"
|
||||
|
||||
namespace WAL
|
||||
{
|
||||
template<typename ...Components>
|
||||
class ViewEntity
|
||||
{
|
||||
private:
|
||||
std::tuple<std::reference_wrapper<Entity>, std::reference_wrapper<Components>...> &_value;
|
||||
public:
|
||||
explicit ViewEntity(std::tuple<std::reference_wrapper<Entity>, std::reference_wrapper<Components>...> &value)
|
||||
: _value(value)
|
||||
{}
|
||||
|
||||
Entity *operator->()
|
||||
{
|
||||
return &(std::get<0>(this->_value).get());
|
||||
}
|
||||
|
||||
Entity &operator*()
|
||||
{
|
||||
return std::get<0>(this->_value);
|
||||
}
|
||||
|
||||
operator Entity &()
|
||||
{
|
||||
return std::get<0>(this->_value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T &get()
|
||||
{
|
||||
return std::get<std::reference_wrapper<T>>(this->_value);
|
||||
}
|
||||
|
||||
template<std::size_t I>
|
||||
auto &get()
|
||||
{
|
||||
return std::get<I>(this->_value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename It, typename ...Components>
|
||||
class ViewIterator
|
||||
{
|
||||
private:
|
||||
It _it;
|
||||
std::optional<ViewEntity<Components...>> _entity;
|
||||
|
||||
public:
|
||||
ViewEntity<Components...> &operator*()
|
||||
{
|
||||
if (!this->_entity)
|
||||
this->_entity.emplace(*this->_it);
|
||||
return *this->_entity;
|
||||
}
|
||||
|
||||
ViewEntity<Components...> *operator->()
|
||||
{
|
||||
if (!this->_entity)
|
||||
this->_entity =(*this->_it);
|
||||
return &this->_entity;
|
||||
}
|
||||
|
||||
ViewIterator &operator++()
|
||||
{
|
||||
this->_it++;
|
||||
this->_entity = std::nullopt;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ViewIterator operator++(int)
|
||||
{
|
||||
ViewIterator copy = *this;
|
||||
this->_it++;
|
||||
this->_entity = std::nullopt;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const ViewIterator &other) const
|
||||
{
|
||||
return this->_it == other._it;
|
||||
}
|
||||
|
||||
bool operator!=(const ViewIterator &other) const
|
||||
{
|
||||
return !this->operator==(other);
|
||||
}
|
||||
|
||||
explicit ViewIterator(It current)
|
||||
: _it(current),
|
||||
_entity(std::nullopt)
|
||||
{}
|
||||
};
|
||||
|
||||
//! @brief A basic view used to manipulate view without knowing their type at compile time.
|
||||
class IView
|
||||
{
|
||||
public:
|
||||
//! @brief The list of types that every entity of the view has.
|
||||
virtual const std::vector<std::type_index> &getTypes() const = 0;
|
||||
|
||||
virtual void emplace_back(Entity &) = 0;
|
||||
|
||||
virtual void erase(const Entity &) = 0;
|
||||
|
||||
//! @brief A default destructor
|
||||
virtual ~IView() = default;
|
||||
};
|
||||
|
||||
//! @brief A view allowing one to easily access entities containing a set list of component.
|
||||
//! A view is always updated and only references to entities are kept.
|
||||
template<typename ...Components>
|
||||
class View : public IView
|
||||
{
|
||||
private:
|
||||
using entity_type = std::tuple<std::reference_wrapper<Entity>, std::reference_wrapper<Components>...>;
|
||||
|
||||
//! @brief The list of entities in the view.
|
||||
std::vector<entity_type> _entities = {};
|
||||
//! @brief The list of types that every entity of the view has.
|
||||
std::vector<std::type_index> _types = {};
|
||||
public:
|
||||
using iterator = ViewIterator<typename std::vector<entity_type>::iterator, Components...>;
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return iterator(this->_entities.begin());
|
||||
}
|
||||
|
||||
iterator end()
|
||||
{
|
||||
return iterator(this->_entities.end());
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return this->_entities.size();
|
||||
}
|
||||
|
||||
ViewEntity<Components...> front()
|
||||
{
|
||||
return *iterator(this->_entities.begin());
|
||||
}
|
||||
|
||||
ViewEntity<Components...> back()
|
||||
{
|
||||
return *iterator(--this->_entities.end());
|
||||
}
|
||||
|
||||
const std::vector<std::type_index> &getTypes() const override
|
||||
{
|
||||
return this->_types;
|
||||
}
|
||||
|
||||
void emplace_back(Entity &entity) override
|
||||
{
|
||||
auto tuple = std::make_tuple<Components *...>(entity.tryGetComponent<Components>()...);
|
||||
if (std::apply([](const auto *...component) {return ((component == nullptr) || ...);}, tuple))
|
||||
return;
|
||||
std::apply([&](auto *...component) {
|
||||
this->_entities.emplace_back(entity, *component...);
|
||||
}, tuple);
|
||||
}
|
||||
|
||||
void erase(const Entity &entity) override
|
||||
{
|
||||
this->_entities.erase(std::remove_if(this->_entities.begin(), this->_entities.end(), [&entity](const auto &ref){
|
||||
return &std::get<0>(ref).get() == &entity;
|
||||
}));
|
||||
}
|
||||
|
||||
//! @brief Construct a view from a list of entities.
|
||||
//! Those entities are never copied but references to them are kept internally.
|
||||
explicit View(std::list<Entity> &scene)
|
||||
{
|
||||
this->_types = {typeid(Components)...};
|
||||
for (auto &entity : scene)
|
||||
this->emplace_back(entity);
|
||||
}
|
||||
|
||||
//! @brief Copying a view is not possible since a view must be managed by a scene.
|
||||
View(const View &) = delete;
|
||||
//! @brief A default destructor
|
||||
~View() override = default;
|
||||
//! @brief A view is not assignable.
|
||||
View &operator=(const View &) = delete;
|
||||
};
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<typename ...Components>
|
||||
struct tuple_size<::WAL::ViewEntity<Components...>>
|
||||
: public std::integral_constant<std::size_t, 1 + sizeof...(Components)>
|
||||
{};
|
||||
|
||||
template<typename ...Components>
|
||||
struct tuple_element<0, ::WAL::ViewEntity<Components...>>
|
||||
{
|
||||
using type = WAL::Entity &;
|
||||
};
|
||||
|
||||
template<std::size_t N, typename ...Components>
|
||||
struct tuple_element<N, ::WAL::ViewEntity<Components...>>
|
||||
{
|
||||
using type = typename std::tuple_element<N - 1, std::tuple<Components...>>::type;
|
||||
};
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
//
|
||||
// Created by Zoe Roux on 2021-05-14.
|
||||
//
|
||||
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
#include "Wal.hpp"
|
||||
|
||||
namespace WAL
|
||||
{
|
||||
std::chrono::nanoseconds Wal::timestep = std::chrono::milliseconds(8);
|
||||
|
||||
void Wal::_update(std::chrono::nanoseconds dtime)
|
||||
{
|
||||
auto &entities = this->scene->getEntities();
|
||||
|
||||
for (auto &system : this->_systems) {
|
||||
for (auto &entity : entities) {
|
||||
if (!Wal::_hasDependencies(entity, *system))
|
||||
continue;
|
||||
system->onUpdate(entity, dtime);
|
||||
}
|
||||
system->onSelfUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void Wal::_fixedUpdate()
|
||||
{
|
||||
auto &entities = this->scene->getEntities();
|
||||
|
||||
for (auto &system : this->_systems) {
|
||||
for (auto &entity : entities) {
|
||||
if (!Wal::_hasDependencies(entity, *system))
|
||||
continue;
|
||||
system->onFixedUpdate(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Wal::_hasDependencies(const Entity &entity, const System &system)
|
||||
{
|
||||
// TODO use an hashmap to cache results.
|
||||
const auto &dependency = system.getDependencies();
|
||||
return std::ranges::all_of(dependency.begin(), dependency.end(), [&entity](const auto &dependency) {
|
||||
return entity.hasComponent(dependency);
|
||||
});
|
||||
}
|
||||
} // namespace WAL
|
||||
+67
-46
@@ -10,38 +10,84 @@
|
||||
#include <memory>
|
||||
#include <typeinfo>
|
||||
#include "Exception/WalError.hpp"
|
||||
#include "Scene/Scene.hpp"
|
||||
#include "Entity/Entity.hpp"
|
||||
#include "System/System.hpp"
|
||||
#include "System/ISystem.hpp"
|
||||
#include "Models/Callback.hpp"
|
||||
#include "Scene/Scene.hpp"
|
||||
|
||||
#if defined(PLATFORM_WEB)
|
||||
#include <emscripten/emscripten.h>
|
||||
#endif
|
||||
|
||||
namespace WAL
|
||||
{
|
||||
class Entity;
|
||||
|
||||
//! @brief The main WAL class, it is used to setup and run the ECS.
|
||||
class Wal
|
||||
{
|
||||
private:
|
||||
//! @brief The list of registered systems
|
||||
std::vector<std::unique_ptr<System>> _systems = {};
|
||||
std::vector<std::unique_ptr<ISystem>> _systems = {};
|
||||
|
||||
//! @brief Call the onUpdate of every system with every component
|
||||
void _update(std::chrono::nanoseconds dtime);
|
||||
//! @brief Start the game loop
|
||||
//! @param callback A callback called after each update of the game. It allow you to update the engine based on a specific game state. (you can also update the game state here)
|
||||
//! @param state An initial game state. If not specified, it will be defaulted.
|
||||
//! @tparam T A type used to track your game state. It must be default constructable.
|
||||
template<typename T>
|
||||
void _run(const Callback<Wal &, T &> &callback, T state = T())
|
||||
{
|
||||
auto lastTick = std::chrono::steady_clock::now();
|
||||
std::chrono::nanoseconds fBehind(0);
|
||||
|
||||
//! @brief Call the onFixedUpdate of every system with every component
|
||||
void _fixedUpdate();
|
||||
while (!this->shouldClose) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
std::chrono::nanoseconds dtime = now - lastTick;
|
||||
fBehind += dtime;
|
||||
lastTick = now;
|
||||
|
||||
//! @brief Check if an entity met a system's dependencies.
|
||||
//! @param entity The entity to check
|
||||
//! @param system The system that will list dependencies
|
||||
//! @return True if all dependencies are met, false otherwise.
|
||||
static bool _hasDependencies(const Entity &entity, const System &system);
|
||||
while (fBehind > Wal::timestep) {
|
||||
fBehind -= Wal::timestep;
|
||||
for (auto &system : this->_systems)
|
||||
system->fixedUpdate();
|
||||
}
|
||||
for (auto &system : this->_systems)
|
||||
system->update(dtime);
|
||||
auto &entities = this->scene->getEntities();
|
||||
this->scene->deleteMarkedEntities();
|
||||
callback(*this, state);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(PLATFORM_WEB)
|
||||
template<typename T>
|
||||
static void _runIteration(void *param)
|
||||
{
|
||||
static auto [wal, callback, state] = *reinterpret_cast<std::tuple<Wal &, const Callback<Wal &, T &> &, T &> *>(param);
|
||||
static auto lastTick = std::chrono::steady_clock::now();
|
||||
static std::chrono::nanoseconds fBehind(0);
|
||||
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
std::chrono::nanoseconds dtime = now - lastTick;
|
||||
fBehind += dtime;
|
||||
lastTick = now;
|
||||
while (fBehind > Wal::timestep) {
|
||||
fBehind -= Wal::timestep;
|
||||
for (auto &system : wal._systems)
|
||||
system->fixedUpdate();
|
||||
}
|
||||
for (auto &system : wal._systems)
|
||||
system->update(dtime);
|
||||
wal.scene->deleteMarkedEntities();
|
||||
callback(wal, state);
|
||||
}
|
||||
#endif
|
||||
public:
|
||||
//! @brief The scene that contains entities.
|
||||
std::shared_ptr<Scene> scene;
|
||||
//! @brief True if the engine should close after the end of the current tick.
|
||||
bool shouldClose = false;
|
||||
//! @brief The time between each fixed update.
|
||||
static std::chrono::nanoseconds timestep;
|
||||
static constexpr std::chrono::nanoseconds timestep = std::chrono::milliseconds(32);
|
||||
|
||||
//! @brief Create a new system in place.
|
||||
//! @return The wal instance used to call this function is returned. This allow method chaining.
|
||||
@@ -54,7 +100,7 @@ namespace WAL
|
||||
});
|
||||
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>(std::forward<Types>(params)...));
|
||||
this->_systems.push_back(std::make_unique<T>(*this, std::forward<Types>(params)...));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -101,17 +147,6 @@ namespace WAL
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! @brief Start the game loop
|
||||
//! @param callback A callback called after each update of the game. It allow you to update the engine based on a specific game state. (you can also update the game state here)
|
||||
//! @param state An initial game state. If not specified, it will be defaulted.
|
||||
//! @tparam T A type used to track your game state. It must be default constructable.
|
||||
template<typename T>
|
||||
void run(const std::function<void (Wal &, T &)> &callback, T state = T())
|
||||
{
|
||||
Callback<Wal &, T &> update(callback);
|
||||
return this->run(update, state);
|
||||
}
|
||||
|
||||
//! @brief Start the game loop
|
||||
//! @param callback A callback called after each update of the game. It allow you to update the engine based on a specific game state. (you can also update the game state here)
|
||||
//! @param state An initial game state. If not specified, it will be defaulted.
|
||||
@@ -119,26 +154,12 @@ namespace WAL
|
||||
template<typename T>
|
||||
void run(const Callback<Wal &, T &> &callback, T state = T())
|
||||
{
|
||||
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);
|
||||
auto &entities = this->scene->getEntities();
|
||||
entities.erase(std::remove_if(entities.begin(), entities.end(), [](auto &entity) {
|
||||
return entity.shouldDelete();
|
||||
}), entities.end());
|
||||
callback(*this, state);
|
||||
}
|
||||
#if defined(PLATFORM_WEB)
|
||||
std::tuple<Wal &, const Callback<Wal &, T &> &, T &> iterationParams(*this, callback, state);
|
||||
return emscripten_set_main_loop_arg((em_arg_callback_func)_runIteration<T>, (void *)&iterationParams, 0, 1);
|
||||
#else
|
||||
return this->_run(callback, state);
|
||||
#endif
|
||||
}
|
||||
|
||||
//! @brief A default constructor
|
||||
|
||||
Reference in New Issue
Block a user