Timeout and Interval events
This commit is contained in:
@ -27,13 +27,45 @@ void AssetManager::update() {
|
||||
}
|
||||
}
|
||||
|
||||
void AssetManager::removeExisting(const std::string filename) {
|
||||
auto existing = std::find_if(
|
||||
pendingAssetLoaders.begin(), pendingAssetLoaders.end(),
|
||||
[&](auto &loader) {
|
||||
return loader->name == filename;
|
||||
}
|
||||
);
|
||||
if(existing != pendingAssetLoaders.end()) {
|
||||
pendingAssetLoaders.erase(existing);
|
||||
}
|
||||
|
||||
existing = std::find_if(
|
||||
finishedAssetLoaders.begin(), finishedAssetLoaders.end(),
|
||||
[&](auto &loader) {
|
||||
return loader->name == filename;
|
||||
}
|
||||
);
|
||||
if(existing != finishedAssetLoaders.end()) {
|
||||
finishedAssetLoaders.erase(existing);
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
std::shared_ptr<Texture> AssetManager::get<Texture>(
|
||||
const std::string filename
|
||||
) {
|
||||
// TODO: Check for existing loader
|
||||
|
||||
auto existing = this->getExisting<TextureLoader>(filename);
|
||||
if(existing) {
|
||||
std::cout << "Found existing texture loader for " << filename << std::endl;
|
||||
|
||||
// Check pointer hasn't gone stale, if it has remove it and create new.
|
||||
auto texture = existing->weakTexture.lock();
|
||||
if(texture) return texture;
|
||||
|
||||
std::cout << "Texture loader for " << filename << " has gone stale." << std::endl;
|
||||
this->removeExisting(filename);
|
||||
}
|
||||
|
||||
// Create new loader
|
||||
std::shared_ptr<TextureLoader> loader = std::make_shared<TextureLoader>(
|
||||
filename
|
||||
);
|
||||
|
@ -13,6 +13,42 @@ namespace Dawn {
|
||||
std::vector<std::shared_ptr<AssetLoader>> pendingAssetLoaders;
|
||||
std::vector<std::shared_ptr<AssetLoader>> finishedAssetLoaders;
|
||||
|
||||
/**
|
||||
* Returns an existing asset loader if it exists.
|
||||
*
|
||||
* @param filename The filename of the asset to get.
|
||||
* @return The asset loader if it exists, otherwise nullptr.
|
||||
*/
|
||||
template<class T>
|
||||
std::shared_ptr<T> getExisting(const std::string filename) {
|
||||
auto existing = std::find_if(
|
||||
pendingAssetLoaders.begin(), pendingAssetLoaders.end(),
|
||||
[&](auto &loader) {
|
||||
return loader->name == filename;
|
||||
}
|
||||
);
|
||||
|
||||
if(existing == finishedAssetLoaders.end()) {
|
||||
existing = std::find_if(
|
||||
finishedAssetLoaders.begin(), finishedAssetLoaders.end(),
|
||||
[&](auto &loader) {
|
||||
return loader->name == filename;
|
||||
}
|
||||
);
|
||||
|
||||
if(existing == finishedAssetLoaders.end()) return nullptr;
|
||||
}
|
||||
|
||||
return std::static_pointer_cast<T>(*existing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an existing asset loader if it exists.
|
||||
*
|
||||
* @param filename The filename of the asset to remove.
|
||||
*/
|
||||
void removeExisting(const std::string filename);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Initializes this asset manager so it can begin accepting assets.
|
||||
|
141
src/dawn/event/CustomEvent.hpp
Normal file
141
src/dawn/event/CustomEvent.hpp
Normal file
@ -0,0 +1,141 @@
|
||||
// Copyright (c) 2023 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "dawnlibs.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
enum class CustomEventResult {
|
||||
NOTHING,
|
||||
REMOVE,
|
||||
INVOKE,
|
||||
INVOKE_AND_REMOVE
|
||||
};
|
||||
|
||||
template<
|
||||
typename InternalData,
|
||||
typename InternalListenerData,
|
||||
typename ListenerArgument,
|
||||
typename ...InvokeArgs
|
||||
>
|
||||
class CustomEvent {
|
||||
private:
|
||||
int32_t nextId = 0;
|
||||
std::unordered_map<
|
||||
int32_t,
|
||||
std::pair<InternalListenerData, std::function<void(InvokeArgs...)>>
|
||||
> listeners;
|
||||
|
||||
protected:
|
||||
InternalData internalData;
|
||||
|
||||
/**
|
||||
* Custom event filter. Decides whether or not the event should be emitted
|
||||
* to the listener.
|
||||
*
|
||||
* @param listenerData Data for this listener.
|
||||
* @param args The arguments to pass to the listeners.
|
||||
* @return The result of the filter.
|
||||
*/
|
||||
virtual enum CustomEventResult shouldEmit(
|
||||
const InternalListenerData &listenerData,
|
||||
const InvokeArgs... args
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Transform the arguments for listener data when the listener is first
|
||||
* subscribed.
|
||||
*
|
||||
* @param argument The argument to transform.
|
||||
* @return The transformed argument into an internal data format.
|
||||
*/
|
||||
virtual InternalListenerData transformData(
|
||||
const ListenerArgument &argument
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* Transform the data for listener data after the event has been emitted.
|
||||
*
|
||||
* @param internalData The internal data to transform.
|
||||
* @return Updated/Transformed internal data.
|
||||
*/
|
||||
virtual InternalListenerData transformDataAfterEmit(
|
||||
const InternalListenerData &internalData
|
||||
) {
|
||||
return internalData;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Emits the event.
|
||||
* @param args The arguments to pass to the listeners.
|
||||
*/
|
||||
void emit(InvokeArgs... args) {
|
||||
auto copy = listeners;
|
||||
for(auto &pair : copy) {
|
||||
// Check emit test.
|
||||
auto result = this->shouldEmit(
|
||||
pair.second.first,
|
||||
args...
|
||||
);
|
||||
if(
|
||||
result == CustomEventResult::INVOKE ||
|
||||
result == CustomEventResult::INVOKE_AND_REMOVE
|
||||
) {
|
||||
pair.second.second(args...);
|
||||
}
|
||||
|
||||
if(
|
||||
result == CustomEventResult::REMOVE ||
|
||||
result == CustomEventResult::INVOKE_AND_REMOVE
|
||||
) {
|
||||
listeners.erase(pair.first);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(
|
||||
result == CustomEventResult::INVOKE ||
|
||||
result == CustomEventResult::INVOKE_AND_REMOVE
|
||||
) {
|
||||
// Update the internal data.
|
||||
listeners[pair.first].first = transformDataAfterEmit(
|
||||
pair.second.first
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens to the event.
|
||||
*
|
||||
* @param data Listener data to use.
|
||||
* @param listener The listener to add.
|
||||
* @returns A function that can be called to remove the listener.
|
||||
*/
|
||||
std::function<void()> listen(
|
||||
const ListenerArgument &data,
|
||||
const std::function<void(InvokeArgs...)> listener
|
||||
) {
|
||||
int32_t id = nextId++;
|
||||
|
||||
auto pair = std::make_pair(
|
||||
transformData(data),
|
||||
listener
|
||||
);
|
||||
|
||||
listeners[id] = pair;
|
||||
return [this, id]() {
|
||||
listeners.erase(id);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the custom event.
|
||||
*/
|
||||
virtual ~CustomEvent() {
|
||||
listeners.clear();
|
||||
}
|
||||
};
|
||||
}
|
@ -37,7 +37,9 @@ namespace Dawn {
|
||||
* @param listener The listener to add.
|
||||
* @returns A function that can be called to remove the listener.
|
||||
*/
|
||||
std::function<void()> listen(const std::function<void(A...)> listener) {
|
||||
std::function<void()> listen(
|
||||
const std::function<void(A...)> listener
|
||||
) {
|
||||
int32_t id = nextId++;
|
||||
listeners[id] = listener;
|
||||
return [this, id]() {
|
||||
@ -46,8 +48,7 @@ namespace Dawn {
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener from the event.
|
||||
* @param id The id of the listener to remove.
|
||||
* Destroys the event.
|
||||
*/
|
||||
virtual ~Event() {
|
||||
listeners.clear();
|
||||
|
@ -14,7 +14,6 @@ Scene::Scene(
|
||||
game(game),
|
||||
sceneInitializer(sceneInitializer)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Scene::stage() {
|
||||
@ -23,18 +22,33 @@ void Scene::stage() {
|
||||
}
|
||||
|
||||
void Scene::update() {
|
||||
// Initialize new scene items
|
||||
if(!hasInitialized) {
|
||||
hasInitialized = true;
|
||||
for(auto &item : sceneItems) {
|
||||
item->init();
|
||||
}
|
||||
}
|
||||
|
||||
// Remove stale scene items.
|
||||
auto itRemove = sceneItemsToRemove.begin();
|
||||
while(itRemove != sceneItemsToRemove.end()) {
|
||||
auto item = *itRemove;
|
||||
auto it = std::find(sceneItems.begin(), sceneItems.end(), item);
|
||||
if(it != sceneItems.end()) sceneItems.erase(it);
|
||||
itRemove++;
|
||||
}
|
||||
sceneItemsToRemove.clear();
|
||||
|
||||
// Tick scene items.
|
||||
float_t delta = getGame()->timeManager.delta;
|
||||
if(paused) {
|
||||
onPausedUpdate.emit(delta);
|
||||
} else {
|
||||
onUnpausedUpdate.emit(delta);
|
||||
|
||||
onTimeout.tick(delta);
|
||||
onInterval.tick(delta);
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,5 +62,9 @@ std::shared_ptr<SceneItem> Scene::createSceneItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
void Scene::removeItem(const std::shared_ptr<SceneItem> item) {
|
||||
sceneItemsToRemove.push_back(item);
|
||||
}
|
||||
|
||||
Scene::~Scene() {
|
||||
}
|
@ -6,19 +6,30 @@
|
||||
#pragma once
|
||||
#include "game/Game.hpp"
|
||||
#include "scene/SceneItem.hpp"
|
||||
#include "event/Event.hpp"
|
||||
#include "time/event/IntervalEvent.hpp"
|
||||
#include "time/event/TimeoutEvent.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
struct IntervalData {
|
||||
float_t frequency;
|
||||
float_t nextInvoke;
|
||||
};
|
||||
|
||||
class Scene final : public std::enable_shared_from_this<Scene> {
|
||||
private:
|
||||
std::weak_ptr<Game> game;
|
||||
const std::function<void(Scene&)> sceneInitializer;
|
||||
std::vector<std::shared_ptr<SceneItem>> sceneItems;
|
||||
std::vector<std::shared_ptr<SceneItem>> sceneItemsToRemove;
|
||||
bool_t paused = false;
|
||||
bool_t hasInitialized = false;
|
||||
|
||||
public:
|
||||
Event<float_t> onUnpausedUpdate;
|
||||
Event<float_t> onPausedUpdate;
|
||||
TimeoutEvent onTimeout;
|
||||
IntervalEvent onInterval;
|
||||
|
||||
/**
|
||||
* Constructs a scene object.
|
||||
@ -57,6 +68,14 @@ namespace Dawn {
|
||||
*/
|
||||
std::shared_ptr<SceneItem> createSceneItem();
|
||||
|
||||
/**
|
||||
* Removes a scene item from the scene. This remove will happen at the
|
||||
* start of the next scene update.
|
||||
*
|
||||
* @param item Scene item to remove.
|
||||
*/
|
||||
void removeItem(const std::shared_ptr<SceneItem> item);
|
||||
|
||||
/**
|
||||
* Returns a list of scene components that match the given type.
|
||||
*
|
||||
|
@ -30,6 +30,12 @@ void SceneItem::init() {
|
||||
}
|
||||
}
|
||||
|
||||
void SceneItem::remove() {
|
||||
auto scene = getScene();
|
||||
if(!scene) return;
|
||||
scene->removeItem(shared_from_this());
|
||||
}
|
||||
|
||||
SceneItem::~SceneItem() {
|
||||
std::for_each(
|
||||
components.begin(),
|
||||
|
@ -42,6 +42,12 @@ namespace Dawn {
|
||||
*/
|
||||
std::shared_ptr<Scene> getScene();
|
||||
|
||||
/**
|
||||
* Queues this scene item to be removed from the scene (on the next scene
|
||||
* update).
|
||||
*/
|
||||
void remove();
|
||||
|
||||
/**
|
||||
* Returns the scene that this item belongs to.
|
||||
*
|
||||
|
@ -6,4 +6,7 @@
|
||||
target_sources(${DAWN_TARGET_NAME}
|
||||
PRIVATE
|
||||
ITimeManager.cpp
|
||||
)
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(event)
|
10
src/dawn/time/event/CMakeLists.txt
Normal file
10
src/dawn/time/event/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2023 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DAWN_TARGET_NAME}
|
||||
PRIVATE
|
||||
IntervalEvent.cpp
|
||||
TimeoutEvent.cpp
|
||||
)
|
41
src/dawn/time/event/IntervalEvent.cpp
Normal file
41
src/dawn/time/event/IntervalEvent.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2023 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "IntervalEvent.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
IntervalEvent::IntervalEvent() : CustomEvent() {
|
||||
internalData = 0.0f;
|
||||
}
|
||||
|
||||
enum CustomEventResult IntervalEvent::shouldEmit(
|
||||
const struct IntervalEventData &listener
|
||||
) {
|
||||
if(listener.nextInvoke > internalData) return CustomEventResult::NOTHING;
|
||||
return CustomEventResult::INVOKE;
|
||||
}
|
||||
|
||||
struct IntervalEventData IntervalEvent::transformData(const float_t &interval) {
|
||||
|
||||
return {
|
||||
.interval = interval,
|
||||
.nextInvoke = internalData + interval
|
||||
};
|
||||
}
|
||||
|
||||
struct IntervalEventData IntervalEvent::transformDataAfterEmit(
|
||||
const struct IntervalEventData &listenerData
|
||||
) {
|
||||
return {
|
||||
.interval = listenerData.interval,
|
||||
.nextInvoke = listenerData.nextInvoke + listenerData.interval
|
||||
};
|
||||
}
|
||||
|
||||
void IntervalEvent::tick(const float_t delta) {
|
||||
internalData += delta;
|
||||
emit();
|
||||
}
|
38
src/dawn/time/event/IntervalEvent.hpp
Normal file
38
src/dawn/time/event/IntervalEvent.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2023 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "event/CustomEvent.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
struct IntervalEventData {
|
||||
float_t interval;
|
||||
float_t nextInvoke;
|
||||
};
|
||||
|
||||
class IntervalEvent : public CustomEvent<
|
||||
float_t, struct IntervalEventData, float_t
|
||||
> {
|
||||
protected:
|
||||
enum CustomEventResult shouldEmit(
|
||||
const struct IntervalEventData &listener
|
||||
) override;
|
||||
struct IntervalEventData transformData(const float_t &interval) override;
|
||||
struct IntervalEventData transformDataAfterEmit(
|
||||
const struct IntervalEventData &listenerData
|
||||
) override;
|
||||
|
||||
public:
|
||||
IntervalEvent();
|
||||
|
||||
/**
|
||||
* Ticks this interval event by a time delta amount. Will also emit all
|
||||
* events that are ready to be emitted.
|
||||
*
|
||||
* @param delta Delta time.
|
||||
*/
|
||||
void tick(const float_t delta);
|
||||
};
|
||||
}
|
26
src/dawn/time/event/TimeoutEvent.cpp
Normal file
26
src/dawn/time/event/TimeoutEvent.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2023 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "TimeoutEvent.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
TimeoutEvent::TimeoutEvent() : CustomEvent() {
|
||||
internalData = 0.0f;
|
||||
}
|
||||
|
||||
enum CustomEventResult TimeoutEvent::shouldEmit(const float_t &listener) {
|
||||
if(listener > internalData) return CustomEventResult::NOTHING;
|
||||
return CustomEventResult::INVOKE_AND_REMOVE;
|
||||
}
|
||||
|
||||
float_t TimeoutEvent::transformData(const float_t &interval) {
|
||||
return interval;
|
||||
}
|
||||
|
||||
void TimeoutEvent::tick(const float_t delta) {
|
||||
internalData += delta;
|
||||
emit();
|
||||
}
|
26
src/dawn/time/event/TimeoutEvent.hpp
Normal file
26
src/dawn/time/event/TimeoutEvent.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2023 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "event/CustomEvent.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
class TimeoutEvent : public CustomEvent<float_t, float_t, float_t> {
|
||||
protected:
|
||||
enum CustomEventResult shouldEmit(const float_t &listener) override;
|
||||
float_t transformData(const float_t &timeout) override;
|
||||
|
||||
public:
|
||||
TimeoutEvent();
|
||||
|
||||
/**
|
||||
* Ticks this timeout event by a time delta amount. Will also emit all
|
||||
* events that are ready to be emitted.
|
||||
*
|
||||
* @param delta Delta time.
|
||||
*/
|
||||
void tick(const float_t delta);
|
||||
};
|
||||
}
|
@ -23,13 +23,11 @@ void Dawn::helloWorldScene(Scene &s) {
|
||||
cubeMesh->createBuffers(CUBE_VERTICE_COUNT, CUBE_INDICE_COUNT);
|
||||
CubeMesh::buffer(cubeMesh, glm::vec3(-1, -1, -1), glm::vec3(1, 1, 1), 0, 0);
|
||||
|
||||
auto texture = s.getGame()->assetManager.get<Texture>("texture_border");
|
||||
|
||||
auto cubeItem = s.createSceneItem();
|
||||
auto cubeMeshRenderer = cubeItem->addComponent<MeshRenderer>();
|
||||
cubeMeshRenderer->mesh = cubeMesh;
|
||||
auto cubeMaterial = cubeItem->addComponent<SimpleTexturedMaterial>();
|
||||
cubeMaterial->setTexture(texture);
|
||||
cubeMaterial->setTexture(s.getGame()->assetManager.get<Texture>("texture_border"));
|
||||
|
||||
addSimpleComponent(cubeItem, [](auto &cmp, auto &events) {
|
||||
events.push_back(cmp.getScene()->onUnpausedUpdate.listen([&](float_t delta) {
|
||||
@ -37,4 +35,11 @@ void Dawn::helloWorldScene(Scene &s) {
|
||||
item->setLocalRotation(item->getLocalRotation() * glm::quat(glm::vec3(1, 1, 0) * delta));
|
||||
}));
|
||||
});
|
||||
|
||||
addSimpleComponent(cubeItem, [](auto &cmp, auto &events) {
|
||||
events.push_back(cmp.getScene()->onTimeout.listen(5.0f, [&]{
|
||||
auto item = cmp.getItem();
|
||||
item->setLocalPosition(item->getLocalPosition() + glm::vec3(0, 1, 0));
|
||||
}));
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user