Commit fbaeac21 authored by Florian Oetke's avatar Florian Oetke

Merge branch 'feature/16-sound-effects' into 'develop'

Resolve "Sound-Effects"

Closes #16

See merge request !19
parents 6e24ab00 4a27b29b
Pipeline #3360 passed with stage
in 8 minutes and 31 seconds
audio: = audio/
audio: = audio/*.opus
cfg:sound_effects = settings/sound_effects.json
{
"effects": {
"dash": ["dash3"],
"attack": ["stationary_attack"],
"e_att_h": ["enemy_attack_heavy"],
"e_att_s": ["enemy_attack_small"],
"enemy_hit": ["enemy_hit_1", "enemy_hit_2", "enemy_hit_3", "enemy_hit_4", "enemy_hit_5"],
"gameover": ["gameover"],
"damaged": ["player_damaged"],
"win": ["win", "win2"]
}
}
#include "sound_manager.hpp"
#include "audio_asset.hpp"
#include "../messages.hpp"
#include <mirrage/utils/random.hpp>
#include <context.hpp>
namespace phase_shifter::audio {
struct Effect_file {
std::unordered_map<std::string, std::vector<std::string>> effects;
};
sf2_structDef(Effect_file, effects);
Sound_manager::Sound_manager(eam::audio& audio,
mirrage::util::Message_bus& bus,
mirrage::asset::Asset_manager& assets)
: _audio(audio), _mailbox(bus), _assets(assets), _random_gen(mirrage::util::construct_random_engine())
{
_mailbox.subscribe_to([&](Play_sound_msg& e) { play_effect(e.id); });
}
void Sound_manager::load_effects_file(const mirrage::asset::AID& aid)
{
auto file = _assets.load<Effect_file>(aid, false);
for(auto&& [id, paths] : file.get_blocking().effects) {
for(auto&& path : paths)
register_effect(Sound_effect_id(id), mirrage::asset::AID("audio"_strid, path));
}
}
void Sound_manager::effect_volume(float v) { _effect_volume = v; }
void Sound_manager::pause()
{
// TODO
}
void Sound_manager::resume()
{
// TODO
}
void Sound_manager::update(mirrage::util::Time dt) { _mailbox.update_subscriptions(); }
void Sound_manager::register_effect(Sound_effect_id id, const mirrage::asset::AID& aid)
{
LOG(plog::info) << "Loading sound " << aid.str();
auto effect = _assets.load<std::shared_ptr<eam::opus>>(aid).get_blocking();
auto entry = _sound_effects.find(id);
if(entry != _sound_effects.end())
entry->second.raw_vector().emplace_back(std::move(effect));
else {
auto effects = Sound_effect_set();
effects.raw_vector().emplace_back(std::move(effect));
_sound_effects.emplace(id, std::move(effects));
}
}
void Sound_manager::unregister_effect(Sound_effect_id id) { _sound_effects.erase(id); }
void Sound_manager::play_effect(Sound_effect_id id)
{
auto effect = _sound_effects.find(id);
if(effect != _sound_effects.end()) {
auto ctx = _audio.generate_context(effect->second.get_random(_random_gen));
ctx.get_object()->set_loops(0);
ctx.get_object()->set_volume(_effect_volume * 100);
ctx.play();
}
}
} // namespace phase_shifter::audio
#pragma once
#include "../util/random_vector.hpp"
#include <mirrage/asset/asset_manager.hpp>
#include <mirrage/utils/messagebus.hpp>
#include <mirrage/utils/str_id.hpp>
#include <mirrage/utils/units.hpp>
#include <audio.hpp>
#include <sources/opus.hpp>
#include <memory>
#include <random>
#include <string>
#include <unordered_map>
namespace phase_shifter::audio {
using Sound_effect_id = mirrage::util::Str_id;
class Sound_manager {
public:
Sound_manager(eam::audio& audio, mirrage::util::Message_bus&, mirrage::asset::Asset_manager&);
void load_effects_file(const mirrage::asset::AID& aid);
void effect_volume(float);
auto effect_volume() const -> float { return _effect_volume; }
void pause();
void resume();
void update(mirrage::util::Time dt);
void register_effect(Sound_effect_id id, const mirrage::asset::AID& aid);
void unregister_effect(Sound_effect_id id);
void play_effect(Sound_effect_id id);
private:
using Sound_effect = std::shared_ptr<eam::opus>;
using Sound_effect_set = utils::Random_vector<Sound_effect>;
eam::audio& _audio;
mirrage::util::Mailbox_collection _mailbox;
mirrage::asset::Asset_manager& _assets;
float _effect_volume = 1.f;
std::mt19937_64 _random_gen;
std::unordered_map<Sound_effect_id, Sound_effect_set> _sound_effects;
};
} // namespace phase_shifter::audio
......@@ -3,6 +3,7 @@
#include "beat_system.hpp"
#include "combat_system.hpp"
#include "movement_comp.hpp"
#include "player_comp.hpp"
#include "rigid_body_comp.hpp"
#include "../level/level_system.hpp"
......@@ -67,12 +68,20 @@ namespace phase_shifter::gameplay {
if(aim_len < 0.0001f) {
// TODO: attack
move.attack = true;
if(entity.has<Player_comp>()) {
_bus.send<Play_sound_msg>("attack"_strid);
}
} else {
// start movement
move.step_time_left = beat.avg_beat_time * move.step_time_percentage;
move.step_time = move.step_time_left;
move.last_step =
(aim_len > 1.f ? move.aim / aim_len : move.aim) * move.distance_per_step;
if(entity.has<Player_comp>()) {
_bus.send<Play_sound_msg>("dash"_strid);
}
}
} else {
......
#pragma once
#include <mirrage/ecs/types.hpp>
#include <mirrage/utils/str_id.hpp>
#include <glm/vec3.hpp>
......@@ -20,4 +21,8 @@ namespace phase_shifter {
glm::vec3 attack_direction;
};
struct Play_sound_msg {
mirrage::util::Str_id id;
};
} // namespace phase_shifter
#include "meta_system.hpp"
#include "audio/audio_asset.hpp"
#include "audio/sound_manager.hpp"
#include "game_engine.hpp"
#include "gameplay/beat_system.hpp"
#include "gameplay/camera_system.hpp"
#include "gameplay/combat_system.hpp"
#include "gameplay/dash_system.hpp"
#include "gameplay/stationary_attack_system.hpp"
#include "gameplay/enemy_system.hpp"
#include "gameplay/killable_comp.hpp"
#include "gameplay/movement_system.hpp"
#include "gameplay/stationary_attack_system.hpp"
#include "helper/attachment_system.hpp"
#include "input/input_system.hpp"
#include "level/level_system.hpp"
......@@ -34,6 +35,7 @@ namespace phase_shifter {
, _audio()
, _renderer(engine.renderer_factory().create_renderer(_entities, engine.render_pass_mask()))
, _model_loading(std::make_unique<renderer::Loading_system>(_entities, engine.assets()))
, _sound_manager(std::make_unique<audio::Sound_manager>(_audio, engine.bus(), engine.assets()))
, _beat_system(std::make_unique<gameplay::Beat_system>(engine.bus(), engine.assets()))
, _level_system(std::make_unique<level::Level_system>(_entities, engine.assets()))
, _movement_system(std::make_unique<gameplay::Movement_system>(
......@@ -45,9 +47,12 @@ namespace phase_shifter {
, _camera_system(std::make_unique<gameplay::Camera_system>(_entities))
, _enemy_system(std::make_unique<gameplay::Enemy_system>(engine.bus(), _entities, *_beat_system))
, _dash_system(std::make_unique<gameplay::Dash_system>(engine.bus(), _entities))
, _stationary_attack_system(std::make_unique<gameplay::Stationary_attack_system>(engine.bus(), _entities))
, _stationary_attack_system(
std::make_unique<gameplay::Stationary_attack_system>(engine.bus(), _entities))
, _effect_system(std::make_unique<ui::Effect_system>(engine.bus(), _entities))
{
_sound_manager->load_effects_file("cfg:sound_effects"_aid);
_entities.register_component_type<ecs::components::Transform_comp>();
//auto s = _renderer->settings();
......@@ -130,6 +135,7 @@ namespace phase_shifter {
_model_loading->update(dt);
_renderer->update(dt * _beat_system->graphic_time_scale());
_sound_manager->update(dt);
}
void Meta_system::draw()
{
......@@ -138,8 +144,16 @@ namespace phase_shifter {
}
void Meta_system::pause() { _input_system->disable(); }
void Meta_system::resume() { _input_system->enable(); }
void Meta_system::pause()
{
_input_system->disable();
_sound_manager->pause();
}
void Meta_system::resume()
{
_input_system->enable();
_sound_manager->resume();
}
void Meta_system::shrink_to_fit() { _renderer->shrink_to_fit(); }
......
......@@ -19,6 +19,9 @@ namespace mirrage {
namespace phase_shifter {
class Game_engine;
namespace audio {
class Sound_manager;
}
namespace gameplay {
class Beat_system;
class Movement_system;
......@@ -64,6 +67,7 @@ namespace phase_shifter {
eam::audio _audio;
std::unique_ptr<mirrage::renderer::Deferred_renderer> _renderer;
std::unique_ptr<mirrage::renderer::Loading_system> _model_loading;
std::unique_ptr<audio::Sound_manager> _sound_manager;
std::unique_ptr<gameplay::Beat_system> _beat_system;
std::unique_ptr<level::Level_system> _level_system;
......
#include "effect_system.hpp"
#include "../gameplay/combat_system.hpp"
#include "../gameplay/player_comp.hpp"
#include "../messages.hpp"
#include <mirrage/ecs/entity_manager.hpp>
......@@ -13,6 +15,8 @@ namespace phase_shifter::ui {
: _bus(bus), _mailbox(bus), _ecs(ecs)
{
_mailbox.subscribe_to([&](Enemy_killed_msg& e) {
_bus.send<Play_sound_msg>("enemy_hit"_strid);
auto entity = _ecs.entity_builder("enemy_tombstone")
.rotation(glm::rotation(
glm::vec3{1.f, 0.f, 0.f},
......@@ -20,17 +24,22 @@ namespace phase_shifter::ui {
.position(e.position)
.create();
_actions.defer(5_s, [&, entity] {
_ecs.erase(entity);
LOG(plog::debug) << "erased";
_actions.defer(5_s, [&, entity] { _ecs.erase(entity); });
});
_mailbox.subscribe_to([&](gameplay::Damaged_msg& e) {
_ecs.get(e.entity).process([&](auto& entity) {
entity.process([&](gameplay::Player_comp&) { _bus.send<Play_sound_msg>("damaged"_strid); });
});
});
_mailbox.subscribe_to([&](Win_msg& e) {
_bus.send<Play_sound_msg>("win"_strid);
// TODO
});
_mailbox.subscribe_to([&](Lose_msg& e) {
_bus.send<Play_sound_msg>("gameover"_strid);
// TODO
});
}
......
#pragma once
#include <algorithm>
#include <random>
#include <vector>
namespace phase_shifter::utils {
template <class T>
class Random_vector {
public:
Random_vector(std::vector<T> data = {}) : _data(std::move(data)) {}
auto raw_vector() -> std::vector<T>& { return _data; }
auto empty() const { return _data.empty(); }
template <class RNG>
auto get_random(RNG& gen) -> T&
{
if(_data.size() <= 1)
return _data.front();
if(_next_index < _data.size())
return _data[_next_index++];
if(_data.size() > 2) {
auto low = static_cast<std::size_t>(std::ceil(_data.size() / 4.f));
auto high = static_cast<std::size_t>(std::floor(_data.size() / 4.f * 3.f));
std::shuffle(_data.begin(), _data.begin() + high, gen);
std::shuffle(_data.begin() + low, _data.end(), gen);
}
_next_index = 1;
return _data[0];
}
private:
std::vector<T> _data;
std::size_t _next_index = 0;
};
} // namespace phase_shifter::utils
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment