diff --git a/CMakeLists.txt b/CMakeLists.txt index 116b7f4..79111a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -246,6 +246,7 @@ add_library(Engine STATIC engine/src/core/events/EventBus.cpp engine/src/core/events/EventBus.h engine/src/core/audio/ecs/PlaySoundComponent.h + engine/src/core/animation/events/AnimationMarkerEvent.h ) target_include_directories(Engine PUBLIC engine/src ${dr_libs_SOURCE_DIR}) target_link_libraries(Engine PUBLIC OpenGL::GL glfw glad glm::glm imgui spdlog::spdlog tinyobjloader stb_image OpenAL::OpenAL) diff --git a/engine/src/core/animation/AnimationSystem.cpp b/engine/src/core/animation/AnimationSystem.cpp index 21b16f4..dddd167 100644 --- a/engine/src/core/animation/AnimationSystem.cpp +++ b/engine/src/core/animation/AnimationSystem.cpp @@ -15,6 +15,11 @@ namespace engine::animation { for (auto& [entity, animation] : m_registry.getPool()) { animation.time += deltaTime; + while (animation.nextMarkerIndex < animation.markers.size() && animation.markers[animation.nextMarkerIndex].time <= animation.time) { + animation.markers[animation.nextMarkerIndex].callback(); + animation.nextMarkerIndex++; + } + auto& transform = m_registry.getComponent(entity); if (!animation.position.keys.empty()) { @@ -30,6 +35,7 @@ namespace engine::animation { if (animation.time >= animation.duration) { if (animation.loop) { animation.time = 0.f; + animation.nextMarkerIndex = 0; } else { if (animation.onFinished) { animation.onFinished(); diff --git a/engine/src/core/animation/components/TransformAnimationComponent.h b/engine/src/core/animation/components/TransformAnimationComponent.h index b2600c6..ed87674 100644 --- a/engine/src/core/animation/components/TransformAnimationComponent.h +++ b/engine/src/core/animation/components/TransformAnimationComponent.h @@ -21,6 +21,11 @@ namespace engine::animation { }; }; + struct AnimationMarker { + float time; + std::function callback; + }; + template struct AnimationTrack { @@ -35,6 +40,9 @@ namespace engine::animation { AnimationTrack rotation; AnimationTrack scale; + std::vector markers; + size_t nextMarkerIndex = 0; + std::function onFinished; bool loop = false; bool finished = false; diff --git a/engine/src/core/animation/events/AnimationMarkerEvent.h b/engine/src/core/animation/events/AnimationMarkerEvent.h new file mode 100644 index 0000000..1a47823 --- /dev/null +++ b/engine/src/core/animation/events/AnimationMarkerEvent.h @@ -0,0 +1,18 @@ +// +// Created by sebastian on 05.08.26. +// + +#ifndef COLORRACE_ANIMATIONMARKEREVENT_H +#define COLORRACE_ANIMATIONMARKEREVENT_H +#include + +#include "ecs/Entity.h" + +namespace engine::animation { + struct AnimationMarkerEvent { + engine::Entity entity; + std::string tag; + }; +} + +#endif //COLORRACE_ANIMATIONMARKEREVENT_H diff --git a/engine/src/core/audio/ecs/AudioSystem.cpp b/engine/src/core/audio/ecs/AudioSystem.cpp index 19ac5b0..4e22ee0 100644 --- a/engine/src/core/audio/ecs/AudioSystem.cpp +++ b/engine/src/core/audio/ecs/AudioSystem.cpp @@ -14,7 +14,6 @@ void engine::audio::AudioSystem::update(float deltaTime) const { for (auto& [entity, playSound] : m_registry.getPool()) { auto& source = m_registry.getComponent(entity); if (!playSound.started) { - source.setBuffer(*playSound.soundBuffer); source.setGain(playSound.gain); source.play(); playSound.started = true; diff --git a/engine/src/core/audio/ecs/PlaySoundComponent.h b/engine/src/core/audio/ecs/PlaySoundComponent.h index 8445d58..00c34ae 100644 --- a/engine/src/core/audio/ecs/PlaySoundComponent.h +++ b/engine/src/core/audio/ecs/PlaySoundComponent.h @@ -11,7 +11,6 @@ namespace engine::audio { struct PlaySoundComponent { - std::shared_ptr soundBuffer; float gain = 1.f; bool started = false; bool blocking = false; diff --git a/engine/src/layer/DebugLayer.h b/engine/src/layer/DebugLayer.h index e32f1fe..75aa83e 100644 --- a/engine/src/layer/DebugLayer.h +++ b/engine/src/layer/DebugLayer.h @@ -13,8 +13,8 @@ namespace engine { class DebugLayer: public Layer { public: - DebugLayer(Camera& camera, engine::EntityRegistry& registry, Keyboard &keyboard, Mouse &mouse, std::shared_ptr sphereModel) - : Layer(keyboard, mouse), m_entityRegistry(registry), m_sphereModel(std::move(sphereModel)), m_camera(camera) { + DebugLayer(engine::EventBus& animationEventbus, Camera& camera, engine::EntityRegistry& registry, Keyboard &keyboard, Mouse &mouse, std::shared_ptr sphereModel) + : Layer(keyboard, mouse, animationEventbus), m_entityRegistry(registry), m_sphereModel(std::move(sphereModel)), m_camera(camera) { } void onUpdate(float deltaTime) override; diff --git a/engine/src/layer/Layer.h b/engine/src/layer/Layer.h index 994ad0c..fdc08a5 100644 --- a/engine/src/layer/Layer.h +++ b/engine/src/layer/Layer.h @@ -4,6 +4,8 @@ #ifndef COLORRACE_LAYER_H #define COLORRACE_LAYER_H +#include "core/animation/events/AnimationMarkerEvent.h" +#include "core/events/EventBus.h" #include "core/inputsOutputs/context/InputContextStack.h" #include "core/inputsOutputs/inputs/Keyboard.h" #include "core/inputsOutputs/inputs/Mouse.h" @@ -13,7 +15,7 @@ namespace engine { class Layer { public: - Layer(Keyboard& keyboard, Mouse& mouse) : m_keyboard(keyboard), m_mouse(mouse){}; + Layer(Keyboard& keyboard, Mouse& mouse, EventBus& animationEventBus) : m_keyboard(keyboard), m_mouse(mouse), m_animationEventBus(animationEventBus){}; virtual ~Layer() = default; void onAttach(InputContextStack& inputStack); void onDetach(InputContextStack& inputStack); @@ -26,12 +28,14 @@ namespace engine { InputContextStack& getInputStack() { return *m_inputStack; } Keyboard& getKeyboard() { return m_keyboard; } Mouse& getMouse() { return m_mouse; } + EventBus& getAnimationEventBus() { return m_animationEventBus; } private: std::shared_ptr m_context; InputContextStack* m_inputStack = nullptr; engine::Keyboard& m_keyboard; engine::Mouse& m_mouse; + engine::EventBus& m_animationEventBus; }; } diff --git a/engine/src/layer/Scene.h b/engine/src/layer/Scene.h index 1cfcea3..237d8b5 100644 --- a/engine/src/layer/Scene.h +++ b/engine/src/layer/Scene.h @@ -8,6 +8,7 @@ #include #include "Layer.h" +#include "core/animation/events/AnimationMarkerEvent.h" #include "core/events/EventBus.h" #include "loader/assets/AssetManager.h" #include "loader/assets/AssetRequests.h" @@ -40,6 +41,7 @@ namespace engine { virtual std::vector getRequiredAssets() const { return {}; } protected: std::vector> m_layers; + EventBus& getAnimationEventBus() { return m_animationMarkerEventBus; } void addLayer(std::unique_ptr layer); @@ -49,6 +51,9 @@ namespace engine { engine::Keyboard& m_keyboard; engine::Mouse& m_mouse; + + private: + EventBus m_animationMarkerEventBus; }; } diff --git a/game/src/AudioLayer.cpp b/game/src/AudioLayer.cpp index 0134dee..23b47ab 100644 --- a/game/src/AudioLayer.cpp +++ b/game/src/AudioLayer.cpp @@ -6,7 +6,9 @@ #include "../../engine/src/core/audio/ecs/PlaySoundComponent.h" #include "core/audio/ecs/BlockingSoundComponent.h" +#include "ecs/standardComponents/TransformComponent.h" #include "GLFW/glfw3.h" +#include "spdlog/spdlog.h" void ludo::AudioLayer::onUpdate(float deltaTime) { Layer::onUpdate(deltaTime); @@ -20,13 +22,14 @@ void ludo::AudioLayer::onAttachImpl() { auto rollingDiceSoundBuffer = m_assetManager.getSound("rolling_dice"); m_diceSoundEntity = m_registry.createEntity(); - m_registry.addComponent(m_diceSoundEntity, engine::audio::SoundSource()); + auto diceSoundSource = engine::audio::SoundSource(); + diceSoundSource.setBuffer(*rollingDiceSoundBuffer); + m_registry.addComponent(m_diceSoundEntity, std::move(diceSoundSource)); m_eventBus.subscribe([this, rollingDiceSoundBuffer](const ludo::LudoEvent& event) { std::visit([this, rollingDiceSoundBuffer](const T& e) { if constexpr (std::is_same_v) { engine::audio::PlaySoundComponent playSound; - playSound.soundBuffer = rollingDiceSoundBuffer; playSound.gain = 1.0f; playSound.blocking = true; m_registry.addComponent(m_diceSoundEntity, playSound); @@ -34,4 +37,22 @@ void ludo::AudioLayer::onAttachImpl() { } }, event); }); + + + + getAnimationEventBus().subscribe([this](const engine::animation::AnimationMarkerEvent& event) { + if (event.tag == "step") { + engine::audio::PlaySoundComponent playSound; + auto& soundSource = this->m_registry.getComponent(event.entity); + playSound.gain = 1.0f; + playSound.blocking = false; + + auto& transform = this->m_registry.getComponent(event.entity); + + + soundSource.setPosition(transform.m_position.x, transform.m_position.y, transform.m_position.z); + spdlog::info("Insert PlaySoundComponent for Entity {}", event.entity.value()); + this->m_registry.addComponent(event.entity, playSound); + } + }); } diff --git a/game/src/AudioLayer.h b/game/src/AudioLayer.h index a02a56d..5a43a0e 100644 --- a/game/src/AudioLayer.h +++ b/game/src/AudioLayer.h @@ -19,8 +19,8 @@ namespace ludo { class AudioLayer :public engine::Layer { public: AudioLayer(engine::AssetManager& assetManager, engine::Keyboard &keyboard, engine::Mouse &mouse, std::shared_ptr gameMode, - engine::EntityRegistry& registry, engine::EventBus& eventBus) - : Layer(keyboard, mouse), m_assetManager(assetManager), m_gameMode(std::move(gameMode)), m_registry(registry), + engine::EntityRegistry& registry, engine::EventBus& eventBus, engine::EventBus& animationEventbus) + : Layer(keyboard, mouse, animationEventbus), m_assetManager(assetManager), m_gameMode(std::move(gameMode)), m_registry(registry), m_eventBus(eventBus), m_audioSystem(m_registry) { } diff --git a/game/src/GameLayer.h b/game/src/GameLayer.h index 5dd6984..c9b8ed3 100644 --- a/game/src/GameLayer.h +++ b/game/src/GameLayer.h @@ -24,10 +24,10 @@ class GameLayer: public engine::Layer { public: - GameLayer(std::shared_ptr gameMode, engine::EntityRegistry& entityRegistry, std::shared_ptr cam, std::shared_ptr light, - engine::Keyboard& keyboard, engine::Mouse& mouse, std::shared_ptr boardModel, std::unordered_map> pieceModdels) - : Layer(keyboard, mouse), m_gameMode(std::move(gameMode)), m_entityRegistry(entityRegistry), m_camera(std::move(cam)), m_renderer(std::make_unique()), - m_pointLight(std::move(light)), m_boardLayout(std::move(boardModel)), m_piecePresenter(m_entityRegistry, std::move(pieceModdels), m_boardLayout), + GameLayer(engine::EventBus& animationEventbus, std::shared_ptr gameMode, engine::EntityRegistry& entityRegistry, std::shared_ptr cam, std::shared_ptr light, + engine::Keyboard& keyboard, engine::Mouse& mouse, std::shared_ptr boardModel, std::unordered_map> pieceModdels, std::unordered_map> pieceSounds) + : Layer(keyboard, mouse, animationEventbus), m_gameMode(std::move(gameMode)), m_entityRegistry(entityRegistry), m_camera(std::move(cam)), m_renderer(std::make_unique()), + m_pointLight(std::move(light)), m_boardLayout(std::move(boardModel)), m_piecePresenter(animationEventbus, m_entityRegistry, std::move(pieceModdels), std::move(pieceSounds), m_boardLayout), m_pickingSystem(*m_camera), m_highlightSystem(*m_camera), m_animationSystem(m_entityRegistry) { for (auto playerID : m_gameMode->getPlayerOrder()) { if (playerID != m_localPlayer) { diff --git a/game/src/GameScene.cpp b/game/src/GameScene.cpp index a783b65..463b399 100644 --- a/game/src/GameScene.cpp +++ b/game/src/GameScene.cpp @@ -39,10 +39,12 @@ void GameScene::onEnter() { pieceModels["Green"] = assetManager->getModel("pawn_green"); pieceModels["Yellow"] = assetManager->getModel("pawn_yellow"); - addLayer(std::make_unique(*assetManager, m_keyboard, m_mouse, m_gameMode, entityRegistry, m_eventBus)); - addLayer(std::make_unique(*m_camera, entityRegistry, m_keyboard, m_mouse, engine::SphereFactory::createUVSphere(1.0f))); - addLayer(std::make_unique(m_gameMode, entityRegistry, m_camera, m_pointLight, m_keyboard, m_mouse, assetManager->getModel("gameboard"), pieceModels)); + std::unordered_map> pieceSounds; + pieceSounds["step"] = assetManager->getSound("pawn_step"); + addLayer(std::make_unique(*assetManager, m_keyboard, m_mouse, m_gameMode, entityRegistry, m_eventBus, getAnimationEventBus())); + addLayer(std::make_unique(getAnimationEventBus(), *m_camera, entityRegistry, m_keyboard, m_mouse, engine::SphereFactory::createUVSphere(1.0f))); + addLayer(std::make_unique(getAnimationEventBus(), m_gameMode, entityRegistry, m_camera, m_pointLight, m_keyboard, m_mouse, assetManager->getModel("gameboard"), pieceModels, pieceSounds)); } void GameScene::onExit() { diff --git a/game/src/ludo/PiecePresenter.cpp b/game/src/ludo/PiecePresenter.cpp index 27a495d..4372c4f 100644 --- a/game/src/ludo/PiecePresenter.cpp +++ b/game/src/ludo/PiecePresenter.cpp @@ -24,6 +24,13 @@ void ludo::PiecePresenter::spawnPieces(const ludo::LudoGameState &state) { m_registry.addComponent(entity, transform); m_registry.addComponent(entity, engine::MeshComponent(m_pieceModels[std::string(ludo::toString(piece.color))])); m_registry.addComponent(entity, engine::PickableComponent()); + + auto soundSource = engine::audio::SoundSource(); + soundSource.setBuffer(*m_pieceSounds["step"]); + soundSource.setPosition(transform.m_position.x, transform.m_position.y, transform.m_position.z); + soundSource.setGain(1.0f); + soundSource.setLooping(false); + m_registry.addComponent(entity, std::move(soundSource)); m_pieceEntities.push_back(entity); } } @@ -41,6 +48,7 @@ void ludo::PiecePresenter::onPieceMoved(const ludo::PieceMovedEvent &event, cons } std::vector> keyframes; + std::vector animationMarkers; float time = 0; constexpr int kHopSubdivisions = 8; // Punkte zwischen den beiden Endpunkten constexpr float kHopHeight = 0.3f; // Sprunghöhe in Weltkoordinaten @@ -64,6 +72,11 @@ void ludo::PiecePresenter::onPieceMoved(const ludo::PieceMovedEvent &event, cons time += 0.5f / (kHopSubdivisions + 1); } keyframes.emplace_back(time, to); + animationMarkers.emplace_back(time, [this, entity]() { + spdlog::info("Should play step"); + auto animationMarkerEvent = engine::animation::AnimationMarkerEvent(entity, "step"); + this->m_animationEventBus.publish(animationMarkerEvent); + }); time += 0.5f / (kHopSubdivisions + 1); } @@ -80,6 +93,7 @@ void ludo::PiecePresenter::onPieceMoved(const ludo::PieceMovedEvent &event, cons animationComponent.onFinished = [this, entity]() { m_registry.removeComponent(entity); }; + animationComponent.markers = animationMarkers; m_registry.addComponent(entity, animationComponent); m_registry.addComponent(entity, engine::animation::BlockingAnimationComponent()); diff --git a/game/src/ludo/PiecePresenter.h b/game/src/ludo/PiecePresenter.h index b620b35..fa230b5 100644 --- a/game/src/ludo/PiecePresenter.h +++ b/game/src/ludo/PiecePresenter.h @@ -5,9 +5,13 @@ #ifndef COLORRACE_PIECEPRESENTER_H #define COLORRACE_PIECEPRESENTER_H #include +#include #include "BoardLayout.h" #include "LudoGameState.h" +#include "core/animation/events/AnimationMarkerEvent.h" +#include "core/audio/SoundSource.h" +#include "core/events/EventBus.h" #include "ecs/EntityRegistry.h" #include "loader/assets/AssetManager.h" #include "loader/models/Model.h" @@ -16,8 +20,8 @@ namespace ludo { class PiecePresenter { public: - PiecePresenter(engine::EntityRegistry& registry, std::unordered_map> pieceModels, BoardLayout& layout) - : m_registry(registry), m_pieceModels(std::move(pieceModels)), m_layout(layout) {} + PiecePresenter(engine::EventBus& animationEventbus, engine::EntityRegistry& registry, std::unordered_map> pieceModels, std::unordered_map> pieceSounds, BoardLayout& layout) + : m_registry(registry), m_pieceModels(std::move(pieceModels)), m_layout(layout), m_animationEventBus(animationEventbus), m_pieceSounds(std::move(pieceSounds)) {} void spawnPieces(const ludo::LudoGameState& state); void onPieceMoved(const ludo::PieceMovedEvent& event, const ludo::LudoGameState& state); @@ -28,8 +32,10 @@ namespace ludo { private: engine::EntityRegistry& m_registry; std::unordered_map> m_pieceModels; + std::unordered_map> m_pieceSounds; BoardLayout& m_layout; std::vector m_pieceEntities; + engine::EventBus& m_animationEventBus; }; }