Trying to merge with develop

This commit is contained in:
Zoe Roux
2021-06-07 14:40:24 +02:00
112 changed files with 2955 additions and 1081 deletions
+24 -20
View File
@@ -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
+68 -16
View File
@@ -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.
+12 -4
View File
@@ -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
+13
View File
@@ -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 {};
}
+48 -1
View File
@@ -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
+42 -7
View File
@@ -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
+32
View File
@@ -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;
};
}
-28
View File
@@ -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
+43 -12
View File
@@ -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.
+222
View File
@@ -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;
};
}
-48
View File
@@ -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
View File
@@ -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