ADD: PlaySound when piece walks to its destination

This commit is contained in:
sebastian 2026-08-05 12:25:37 +02:00
parent a5643d794c
commit 64671363dc
15 changed files with 101 additions and 18 deletions

View File

@ -246,6 +246,7 @@ add_library(Engine STATIC
engine/src/core/events/EventBus.cpp engine/src/core/events/EventBus.cpp
engine/src/core/events/EventBus.h engine/src/core/events/EventBus.h
engine/src/core/audio/ecs/PlaySoundComponent.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_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) target_link_libraries(Engine PUBLIC OpenGL::GL glfw glad glm::glm imgui spdlog::spdlog tinyobjloader stb_image OpenAL::OpenAL)

View File

@ -15,6 +15,11 @@ namespace engine::animation {
for (auto& [entity, animation] : m_registry.getPool<TransformAnimationComponent>()) { for (auto& [entity, animation] : m_registry.getPool<TransformAnimationComponent>()) {
animation.time += deltaTime; 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<TransformComponent>(entity); auto& transform = m_registry.getComponent<TransformComponent>(entity);
if (!animation.position.keys.empty()) { if (!animation.position.keys.empty()) {
@ -30,6 +35,7 @@ namespace engine::animation {
if (animation.time >= animation.duration) { if (animation.time >= animation.duration) {
if (animation.loop) { if (animation.loop) {
animation.time = 0.f; animation.time = 0.f;
animation.nextMarkerIndex = 0;
} else { } else {
if (animation.onFinished) { if (animation.onFinished) {
animation.onFinished(); animation.onFinished();

View File

@ -21,6 +21,11 @@ namespace engine::animation {
}; };
}; };
struct AnimationMarker {
float time;
std::function<void()> callback;
};
template<typename T> template<typename T>
struct AnimationTrack { struct AnimationTrack {
@ -35,6 +40,9 @@ namespace engine::animation {
AnimationTrack<glm::vec3> rotation; AnimationTrack<glm::vec3> rotation;
AnimationTrack<glm::vec3> scale; AnimationTrack<glm::vec3> scale;
std::vector<AnimationMarker> markers;
size_t nextMarkerIndex = 0;
std::function<void()> onFinished; std::function<void()> onFinished;
bool loop = false; bool loop = false;
bool finished = false; bool finished = false;

View File

@ -0,0 +1,18 @@
//
// Created by sebastian on 05.08.26.
//
#ifndef COLORRACE_ANIMATIONMARKEREVENT_H
#define COLORRACE_ANIMATIONMARKEREVENT_H
#include <string>
#include "ecs/Entity.h"
namespace engine::animation {
struct AnimationMarkerEvent {
engine::Entity entity;
std::string tag;
};
}
#endif //COLORRACE_ANIMATIONMARKEREVENT_H

View File

@ -14,7 +14,6 @@ void engine::audio::AudioSystem::update(float deltaTime) const {
for (auto& [entity, playSound] : m_registry.getPool<PlaySoundComponent>()) { for (auto& [entity, playSound] : m_registry.getPool<PlaySoundComponent>()) {
auto& source = m_registry.getComponent<SoundSource>(entity); auto& source = m_registry.getComponent<SoundSource>(entity);
if (!playSound.started) { if (!playSound.started) {
source.setBuffer(*playSound.soundBuffer);
source.setGain(playSound.gain); source.setGain(playSound.gain);
source.play(); source.play();
playSound.started = true; playSound.started = true;

View File

@ -11,7 +11,6 @@
namespace engine::audio { namespace engine::audio {
struct PlaySoundComponent { struct PlaySoundComponent {
std::shared_ptr<SoundBuffer> soundBuffer;
float gain = 1.f; float gain = 1.f;
bool started = false; bool started = false;
bool blocking = false; bool blocking = false;

View File

@ -13,8 +13,8 @@ namespace engine {
class DebugLayer: public Layer { class DebugLayer: public Layer {
public: public:
DebugLayer(Camera& camera, engine::EntityRegistry& registry, Keyboard &keyboard, Mouse &mouse, std::shared_ptr<Model> sphereModel) DebugLayer(engine::EventBus<animation::AnimationMarkerEvent>& animationEventbus, Camera& camera, engine::EntityRegistry& registry, Keyboard &keyboard, Mouse &mouse, std::shared_ptr<Model> sphereModel)
: Layer(keyboard, mouse), m_entityRegistry(registry), m_sphereModel(std::move(sphereModel)), m_camera(camera) { : Layer(keyboard, mouse, animationEventbus), m_entityRegistry(registry), m_sphereModel(std::move(sphereModel)), m_camera(camera) {
} }
void onUpdate(float deltaTime) override; void onUpdate(float deltaTime) override;

View File

@ -4,6 +4,8 @@
#ifndef COLORRACE_LAYER_H #ifndef COLORRACE_LAYER_H
#define 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/context/InputContextStack.h"
#include "core/inputsOutputs/inputs/Keyboard.h" #include "core/inputsOutputs/inputs/Keyboard.h"
#include "core/inputsOutputs/inputs/Mouse.h" #include "core/inputsOutputs/inputs/Mouse.h"
@ -13,7 +15,7 @@
namespace engine { namespace engine {
class Layer { class Layer {
public: public:
Layer(Keyboard& keyboard, Mouse& mouse) : m_keyboard(keyboard), m_mouse(mouse){}; Layer(Keyboard& keyboard, Mouse& mouse, EventBus<animation::AnimationMarkerEvent>& animationEventBus) : m_keyboard(keyboard), m_mouse(mouse), m_animationEventBus(animationEventBus){};
virtual ~Layer() = default; virtual ~Layer() = default;
void onAttach(InputContextStack& inputStack); void onAttach(InputContextStack& inputStack);
void onDetach(InputContextStack& inputStack); void onDetach(InputContextStack& inputStack);
@ -26,12 +28,14 @@ namespace engine {
InputContextStack& getInputStack() { return *m_inputStack; } InputContextStack& getInputStack() { return *m_inputStack; }
Keyboard& getKeyboard() { return m_keyboard; } Keyboard& getKeyboard() { return m_keyboard; }
Mouse& getMouse() { return m_mouse; } Mouse& getMouse() { return m_mouse; }
EventBus<animation::AnimationMarkerEvent>& getAnimationEventBus() { return m_animationEventBus; }
private: private:
std::shared_ptr<InputContext> m_context; std::shared_ptr<InputContext> m_context;
InputContextStack* m_inputStack = nullptr; InputContextStack* m_inputStack = nullptr;
engine::Keyboard& m_keyboard; engine::Keyboard& m_keyboard;
engine::Mouse& m_mouse; engine::Mouse& m_mouse;
engine::EventBus<animation::AnimationMarkerEvent>& m_animationEventBus;
}; };
} }

View File

@ -8,6 +8,7 @@
#include <vector> #include <vector>
#include "Layer.h" #include "Layer.h"
#include "core/animation/events/AnimationMarkerEvent.h"
#include "core/events/EventBus.h" #include "core/events/EventBus.h"
#include "loader/assets/AssetManager.h" #include "loader/assets/AssetManager.h"
#include "loader/assets/AssetRequests.h" #include "loader/assets/AssetRequests.h"
@ -40,6 +41,7 @@ namespace engine {
virtual std::vector<AssetRequest> getRequiredAssets() const { return {}; } virtual std::vector<AssetRequest> getRequiredAssets() const { return {}; }
protected: protected:
std::vector<std::unique_ptr<Layer>> m_layers; std::vector<std::unique_ptr<Layer>> m_layers;
EventBus<animation::AnimationMarkerEvent>& getAnimationEventBus() { return m_animationMarkerEventBus; }
void addLayer(std::unique_ptr<Layer> layer); void addLayer(std::unique_ptr<Layer> layer);
@ -49,6 +51,9 @@ namespace engine {
engine::Keyboard& m_keyboard; engine::Keyboard& m_keyboard;
engine::Mouse& m_mouse; engine::Mouse& m_mouse;
private:
EventBus<animation::AnimationMarkerEvent> m_animationMarkerEventBus;
}; };
} }

View File

@ -6,7 +6,9 @@
#include "../../engine/src/core/audio/ecs/PlaySoundComponent.h" #include "../../engine/src/core/audio/ecs/PlaySoundComponent.h"
#include "core/audio/ecs/BlockingSoundComponent.h" #include "core/audio/ecs/BlockingSoundComponent.h"
#include "ecs/standardComponents/TransformComponent.h"
#include "GLFW/glfw3.h" #include "GLFW/glfw3.h"
#include "spdlog/spdlog.h"
void ludo::AudioLayer::onUpdate(float deltaTime) { void ludo::AudioLayer::onUpdate(float deltaTime) {
Layer::onUpdate(deltaTime); Layer::onUpdate(deltaTime);
@ -20,13 +22,14 @@ void ludo::AudioLayer::onAttachImpl() {
auto rollingDiceSoundBuffer = m_assetManager.getSound("rolling_dice"); auto rollingDiceSoundBuffer = m_assetManager.getSound("rolling_dice");
m_diceSoundEntity = m_registry.createEntity(); m_diceSoundEntity = m_registry.createEntity();
m_registry.addComponent<engine::audio::SoundSource>(m_diceSoundEntity, engine::audio::SoundSource()); auto diceSoundSource = engine::audio::SoundSource();
diceSoundSource.setBuffer(*rollingDiceSoundBuffer);
m_registry.addComponent<engine::audio::SoundSource>(m_diceSoundEntity, std::move(diceSoundSource));
m_eventBus.subscribe([this, rollingDiceSoundBuffer](const ludo::LudoEvent& event) { m_eventBus.subscribe([this, rollingDiceSoundBuffer](const ludo::LudoEvent& event) {
std::visit([this, rollingDiceSoundBuffer]<typename T>(const T& e) { std::visit([this, rollingDiceSoundBuffer]<typename T>(const T& e) {
if constexpr (std::is_same_v<T, ludo::DiceRolledEvent>) { if constexpr (std::is_same_v<T, ludo::DiceRolledEvent>) {
engine::audio::PlaySoundComponent playSound; engine::audio::PlaySoundComponent playSound;
playSound.soundBuffer = rollingDiceSoundBuffer;
playSound.gain = 1.0f; playSound.gain = 1.0f;
playSound.blocking = true; playSound.blocking = true;
m_registry.addComponent<engine::audio::PlaySoundComponent>(m_diceSoundEntity, playSound); m_registry.addComponent<engine::audio::PlaySoundComponent>(m_diceSoundEntity, playSound);
@ -34,4 +37,22 @@ void ludo::AudioLayer::onAttachImpl() {
} }
}, event); }, event);
}); });
getAnimationEventBus().subscribe([this](const engine::animation::AnimationMarkerEvent& event) {
if (event.tag == "step") {
engine::audio::PlaySoundComponent playSound;
auto& soundSource = this->m_registry.getComponent<engine::audio::SoundSource>(event.entity);
playSound.gain = 1.0f;
playSound.blocking = false;
auto& transform = this->m_registry.getComponent<engine::TransformComponent>(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<engine::audio::PlaySoundComponent>(event.entity, playSound);
}
});
} }

View File

@ -19,8 +19,8 @@ namespace ludo {
class AudioLayer :public engine::Layer { class AudioLayer :public engine::Layer {
public: public:
AudioLayer(engine::AssetManager& assetManager, engine::Keyboard &keyboard, engine::Mouse &mouse, std::shared_ptr<LudoGameMode> gameMode, AudioLayer(engine::AssetManager& assetManager, engine::Keyboard &keyboard, engine::Mouse &mouse, std::shared_ptr<LudoGameMode> gameMode,
engine::EntityRegistry& registry, engine::EventBus<LudoEvent>& eventBus) engine::EntityRegistry& registry, engine::EventBus<LudoEvent>& eventBus, engine::EventBus<engine::animation::AnimationMarkerEvent>& animationEventbus)
: Layer(keyboard, mouse), m_assetManager(assetManager), m_gameMode(std::move(gameMode)), m_registry(registry), : Layer(keyboard, mouse, animationEventbus), m_assetManager(assetManager), m_gameMode(std::move(gameMode)), m_registry(registry),
m_eventBus(eventBus), m_audioSystem(m_registry) { m_eventBus(eventBus), m_audioSystem(m_registry) {
} }

View File

@ -24,10 +24,10 @@
class GameLayer: public engine::Layer { class GameLayer: public engine::Layer {
public: public:
GameLayer(std::shared_ptr<ludo::LudoGameMode> gameMode, engine::EntityRegistry& entityRegistry, std::shared_ptr<engine::Camera> cam, std::shared_ptr<engine::PointLight> light, GameLayer(engine::EventBus<engine::animation::AnimationMarkerEvent>& animationEventbus, std::shared_ptr<ludo::LudoGameMode> gameMode, engine::EntityRegistry& entityRegistry, std::shared_ptr<engine::Camera> cam, std::shared_ptr<engine::PointLight> light,
engine::Keyboard& keyboard, engine::Mouse& mouse, std::shared_ptr<engine::Model> boardModel, std::unordered_map<std::string, std::shared_ptr<engine::Model>> pieceModdels) engine::Keyboard& keyboard, engine::Mouse& mouse, std::shared_ptr<engine::Model> boardModel, std::unordered_map<std::string, std::shared_ptr<engine::Model>> pieceModdels, std::unordered_map<std::string, std::shared_ptr<engine::audio::SoundBuffer>> pieceSounds)
: Layer(keyboard, mouse), m_gameMode(std::move(gameMode)), m_entityRegistry(entityRegistry), m_camera(std::move(cam)), m_renderer(std::make_unique<engine::MeshRenderer>()), : Layer(keyboard, mouse, animationEventbus), m_gameMode(std::move(gameMode)), m_entityRegistry(entityRegistry), m_camera(std::move(cam)), m_renderer(std::make_unique<engine::MeshRenderer>()),
m_pointLight(std::move(light)), m_boardLayout(std::move(boardModel)), m_piecePresenter(m_entityRegistry, std::move(pieceModdels), m_boardLayout), 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) { m_pickingSystem(*m_camera), m_highlightSystem(*m_camera), m_animationSystem(m_entityRegistry) {
for (auto playerID : m_gameMode->getPlayerOrder()) { for (auto playerID : m_gameMode->getPlayerOrder()) {
if (playerID != m_localPlayer) { if (playerID != m_localPlayer) {

View File

@ -39,10 +39,12 @@ void GameScene::onEnter() {
pieceModels["Green"] = assetManager->getModel("pawn_green"); pieceModels["Green"] = assetManager->getModel("pawn_green");
pieceModels["Yellow"] = assetManager->getModel("pawn_yellow"); pieceModels["Yellow"] = assetManager->getModel("pawn_yellow");
addLayer(std::make_unique<ludo::AudioLayer>(*assetManager, m_keyboard, m_mouse, m_gameMode, entityRegistry, m_eventBus)); std::unordered_map<std::string, std::shared_ptr<engine::audio::SoundBuffer>> pieceSounds;
addLayer(std::make_unique<engine::DebugLayer>(*m_camera, entityRegistry, m_keyboard, m_mouse, engine::SphereFactory::createUVSphere(1.0f))); pieceSounds["step"] = assetManager->getSound("pawn_step");
addLayer(std::make_unique<GameLayer>(m_gameMode, entityRegistry, m_camera, m_pointLight, m_keyboard, m_mouse, assetManager->getModel("gameboard"), pieceModels));
addLayer(std::make_unique<ludo::AudioLayer>(*assetManager, m_keyboard, m_mouse, m_gameMode, entityRegistry, m_eventBus, getAnimationEventBus()));
addLayer(std::make_unique<engine::DebugLayer>(getAnimationEventBus(), *m_camera, entityRegistry, m_keyboard, m_mouse, engine::SphereFactory::createUVSphere(1.0f)));
addLayer(std::make_unique<GameLayer>(getAnimationEventBus(), m_gameMode, entityRegistry, m_camera, m_pointLight, m_keyboard, m_mouse, assetManager->getModel("gameboard"), pieceModels, pieceSounds));
} }
void GameScene::onExit() { void GameScene::onExit() {

View File

@ -24,6 +24,13 @@ void ludo::PiecePresenter::spawnPieces(const ludo::LudoGameState &state) {
m_registry.addComponent<engine::TransformComponent>(entity, transform); m_registry.addComponent<engine::TransformComponent>(entity, transform);
m_registry.addComponent<engine::MeshComponent>(entity, engine::MeshComponent(m_pieceModels[std::string(ludo::toString(piece.color))])); m_registry.addComponent<engine::MeshComponent>(entity, engine::MeshComponent(m_pieceModels[std::string(ludo::toString(piece.color))]));
m_registry.addComponent<engine::PickableComponent>(entity, engine::PickableComponent()); m_registry.addComponent<engine::PickableComponent>(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<engine::audio::SoundSource>(entity, std::move(soundSource));
m_pieceEntities.push_back(entity); m_pieceEntities.push_back(entity);
} }
} }
@ -41,6 +48,7 @@ void ludo::PiecePresenter::onPieceMoved(const ludo::PieceMovedEvent &event, cons
} }
std::vector<engine::animation::Keyframe<glm::vec3>> keyframes; std::vector<engine::animation::Keyframe<glm::vec3>> keyframes;
std::vector<engine::animation::AnimationMarker> animationMarkers;
float time = 0; float time = 0;
constexpr int kHopSubdivisions = 8; // Punkte zwischen den beiden Endpunkten constexpr int kHopSubdivisions = 8; // Punkte zwischen den beiden Endpunkten
constexpr float kHopHeight = 0.3f; // Sprunghöhe in Weltkoordinaten 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); time += 0.5f / (kHopSubdivisions + 1);
} }
keyframes.emplace_back(time, to); 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); time += 0.5f / (kHopSubdivisions + 1);
} }
@ -80,6 +93,7 @@ void ludo::PiecePresenter::onPieceMoved(const ludo::PieceMovedEvent &event, cons
animationComponent.onFinished = [this, entity]() { animationComponent.onFinished = [this, entity]() {
m_registry.removeComponent<engine::animation::BlockingAnimationComponent>(entity); m_registry.removeComponent<engine::animation::BlockingAnimationComponent>(entity);
}; };
animationComponent.markers = animationMarkers;
m_registry.addComponent<engine::animation::TransformAnimationComponent>(entity, animationComponent); m_registry.addComponent<engine::animation::TransformAnimationComponent>(entity, animationComponent);
m_registry.addComponent<engine::animation::BlockingAnimationComponent>(entity, engine::animation::BlockingAnimationComponent()); m_registry.addComponent<engine::animation::BlockingAnimationComponent>(entity, engine::animation::BlockingAnimationComponent());

View File

@ -5,9 +5,13 @@
#ifndef COLORRACE_PIECEPRESENTER_H #ifndef COLORRACE_PIECEPRESENTER_H
#define COLORRACE_PIECEPRESENTER_H #define COLORRACE_PIECEPRESENTER_H
#include <optional> #include <optional>
#include <utility>
#include "BoardLayout.h" #include "BoardLayout.h"
#include "LudoGameState.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 "ecs/EntityRegistry.h"
#include "loader/assets/AssetManager.h" #include "loader/assets/AssetManager.h"
#include "loader/models/Model.h" #include "loader/models/Model.h"
@ -16,8 +20,8 @@
namespace ludo { namespace ludo {
class PiecePresenter { class PiecePresenter {
public: public:
PiecePresenter(engine::EntityRegistry& registry, std::unordered_map<std::string, std::shared_ptr<engine::Model>> pieceModels, BoardLayout& layout) PiecePresenter(engine::EventBus<engine::animation::AnimationMarkerEvent>& animationEventbus, engine::EntityRegistry& registry, std::unordered_map<std::string, std::shared_ptr<engine::Model>> pieceModels, std::unordered_map<std::string, std::shared_ptr<engine::audio::SoundBuffer>> pieceSounds, BoardLayout& layout)
: m_registry(registry), m_pieceModels(std::move(pieceModels)), m_layout(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 spawnPieces(const ludo::LudoGameState& state);
void onPieceMoved(const ludo::PieceMovedEvent& event, const ludo::LudoGameState& state); void onPieceMoved(const ludo::PieceMovedEvent& event, const ludo::LudoGameState& state);
@ -28,8 +32,10 @@ namespace ludo {
private: private:
engine::EntityRegistry& m_registry; engine::EntityRegistry& m_registry;
std::unordered_map<std::string, std::shared_ptr<engine::Model>> m_pieceModels; std::unordered_map<std::string, std::shared_ptr<engine::Model>> m_pieceModels;
std::unordered_map<std::string, std::shared_ptr<engine::audio::SoundBuffer>> m_pieceSounds;
BoardLayout& m_layout; BoardLayout& m_layout;
std::vector<engine::Entity> m_pieceEntities; std::vector<engine::Entity> m_pieceEntities;
engine::EventBus<engine::animation::AnimationMarkerEvent>& m_animationEventBus;
}; };
} }