Add audio, bug fix in map reader

This commit is contained in:
oss 2020-03-20 00:59:21 +03:00
parent 6896cf037d
commit f8060438da
11 changed files with 180 additions and 37 deletions

Binary file not shown.

View File

@ -2,9 +2,13 @@ size
7 7 7 7
map map
2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 0 0 0 0 0 2 2 0 0 0 0 6 2
2 0 0 1 0 0 2 2 0 0 1 0 0 2
2 0 0 1 1 0 2 2 0 0 1 1 0 2
2 1 1 2 2 2 2 2 1 1 2 2 2 2
2 0 0 0 0 0 2 2 0 0 0 0 5 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2
teleport
5 5 1 1
trigger
1 5 4 5 0

76
src/audio.cpp Normal file
View File

@ -0,0 +1,76 @@
#include "audio.h"
Audio::Audio(const std::string &background_path, std::array<std::string, N_SOUNDS> &&sounds_paths)
{
SfMusicPtr music = std::make_unique<sf::Music>();
if (music->openFromFile(background_path))
music->setLoop(true);
else
music = nullptr;
background_music = std::move(music);
SoundEffectPtr effect;
for (int i = 0; i < N_SOUNDS; ++i)
{
effect = std::make_unique<SoundEffect>();
if (effect->buffer.loadFromFile(sounds_paths[i]))
{
effect->sound.setBuffer(effect->buffer);
array_sounds[i] = std::move(effect);
}
else
{
array_sounds[i] = nullptr;
}
}
}
bool Audio::setSound(const SOUND_TYPE &type, const std::string &sound_file_path)
{
SoundEffectPtr effect = std::make_unique<SoundEffect>();
if (!effect->buffer.loadFromFile(sound_file_path))
return false;
effect->sound.setBuffer(effect->buffer);
array_sounds[type] = std::move(effect);
return true;
}
void Audio::playSound(const SOUND_TYPE &type)
{
array_sounds[type]->sound.play();
}
bool Audio::setBackground(const std::string &music_file_path)
{
SfMusicPtr music = std::make_unique<sf::Music>();
if (!music->openFromFile(music_file_path))
return false;
background_music = std::move(music);
return true;
}
void Audio::playBackground()
{
background_music->play();
}
void Audio::stopBackground()
{
background_music->stop();
}
void Audio::pauseBackground()
{
background_music->pause();
}
void Audio::setBackgroundVolume(const float &volume)
{
background_music->setVolume(volume);
}

47
src/audio.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef AUDIO_H
#define AUDIO_H
#include <memory>
#include <array>
#include <string>
#include <SFML/Audio.hpp>
enum SOUND_TYPE {
FOOTSTEP_SOUND = 0,
N_SOUNDS
};
class Audio
{
private:
// Struct for small sounds, like shots, foot steps, etc.
// As we always need to store SoundBuffer in the same scope as Sound, it's better to make struct.
struct SoundEffect {
sf::SoundBuffer buffer;
sf::Sound sound;
};
using SfMusicPtr = std::unique_ptr<sf::Music>;
using SoundEffectPtr = std::unique_ptr<SoundEffect>;
std::array<SoundEffectPtr, N_SOUNDS> array_sounds;
SfMusicPtr background_music;
public:
Audio(const std::string &background_file_name, std::array<std::string, N_SOUNDS> &&sounds_paths);
bool setSound(const SOUND_TYPE &type, const std::string &sound_file_path);
void playSound(const SOUND_TYPE &type);
bool setBackground(const std::string &music_file_path);
void playBackground();
void stopBackground();
void pauseBackground();
void setBackgroundVolume(const float &volume);
};
using AudioPtr = std::unique_ptr<Audio>;
#endif // AUDIO_H

View File

@ -195,8 +195,7 @@ TriggerCell::~TriggerCell()
void TriggerCell::addTarget(CellPtr &&cell) void TriggerCell::addTarget(CellPtr &&cell)
{ {
UNUSED(cell); vector_cells.emplace_back(std::move(cell));
//cells.emplace_back(cell);
} }
bool TriggerCell::onMovingTo(HeroPtr &hero, LevelPtr &level) bool TriggerCell::onMovingTo(HeroPtr &hero, LevelPtr &level)
@ -204,7 +203,7 @@ bool TriggerCell::onMovingTo(HeroPtr &hero, LevelPtr &level)
UNUSED(hero); UNUSED(hero);
// We replace needed cells with the ones that the trigger provides. // We replace needed cells with the ones that the trigger provides.
for (CellPtr &cell : cells) for (CellPtr &cell : vector_cells)
{ {
const coordinate &row = cell->row(); const coordinate &row = cell->row();
const coordinate &col = cell->col(); const coordinate &col = cell->col();
@ -213,6 +212,8 @@ bool TriggerCell::onMovingTo(HeroPtr &hero, LevelPtr &level)
level->getCellAt(row, col) = std::move(cell); level->getCellAt(row, col) = std::move(cell);
} }
vector_cells.clear();
// It's an impassable object, so player can't move to here. // It's an impassable object, so player can't move to here.
return false; return false;
} }

View File

@ -50,7 +50,7 @@ class Cell : public Entity
{ {
protected: protected:
sf::Color cell_color; sf::Color cell_color;
coordinate height_shift; coordinate height_shift;
public: public:
Cell(coordinate cell_row = 0, Cell(coordinate cell_row = 0,
@ -60,11 +60,11 @@ public:
virtual ~Cell() override; virtual ~Cell() override;
sf::Color color() const noexcept; sf::Color color() const noexcept;
/// "shift_by_y" indicates the height of current cell /// "shift_by_y" indicates the height of current cell
/// Height is a shift of y coordinate on the scene, relatively to the ground (which is 0) /// Height is a shift of y coordinate on the scene, relatively to the ground (which is 0)
void setHeightShift(coordinate shift_by_y); void setHeightShift(coordinate shift_by_y);
coordinate heightShift() const; coordinate heightShift() const;
/// Determine if Hero can move onto this cell or not /// Determine if Hero can move onto this cell or not
virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) = 0; virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) = 0;
@ -177,9 +177,9 @@ public:
const sf::Color &color = palette::Purple); const sf::Color &color = palette::Purple);
virtual ~TeleportCell() override; virtual ~TeleportCell() override;
/// Set the coordinates of this teleport destination /// Set the coordinates of this teleport destination
void setDestination(coordinate new_cell_row, coordinate new_cell_col); void setDestination(coordinate new_cell_row, coordinate new_cell_col);
virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override; virtual bool onMovingTo(HeroPtr &hero, LevelPtr &level) override;
@ -193,7 +193,7 @@ class TriggerCell final : public Cell
{ {
private: private:
// Vector of cells to place on map // Vector of cells to place on map
std::vector<CellPtr> cells; std::vector<CellPtr> vector_cells;
public: public:
/// Vector of cell types you can cast in to /// Vector of cell types you can cast in to

View File

@ -8,44 +8,51 @@ Game::Game()
// Generate level // Generate level
level = std::make_unique<Level>(); level = std::make_unique<Level>();
audio = std::make_unique<Audio>("background_music.ogg", std::array<std::string, N_SOUNDS> { "footstep_sound.wav" });
// Prepare level renderer // Prepare level renderer
renderer = std::make_unique<Renderer>(); renderer = std::make_unique<Renderer>();
main_window.create(sf::VideoMode(renderer->windowSize() * 5, renderer->windowSize() * 5), "SFML-Test Application", sf::Style::Default); main_window.create(sf::VideoMode(renderer->windowSize() * 3, renderer->windowSize() * 3), "SFML-Test Application", sf::Style::Default);
main_window.setActive(); main_window.setActive();
main_window.setFramerateLimit(60); main_window.setFramerateLimit(60);
current_level = 1; current_level = 1;
audio->setBackgroundVolume(15.f);
} }
int Game::run() int Game::run()
{ {
// Initial level rendering audio->playBackground();
renderer->render(level, hero, main_window);
// On the game loop // On the game loop
while (main_window.isOpen()) while (main_window.isOpen())
{ {
sf::Event event; sf::Event event;
while (main_window.pollEvent(event)) while (main_window.pollEvent(event))
{ {
if (event.type == sf::Event::Closed) switch (event.type)
main_window.close();
// Handling keyboard activity
if (event.type == sf::Event::KeyPressed)
{ {
// Move case sf::Event::Closed:
onMoving(event.key.code); main_window.close();
break;
// Probably something changed! Re-render case sf::Event::KeyPressed:
renderer->render(level, hero, main_window); audio->playSound(FOOTSTEP_SOUND);
onMoving(event.key.code);
break;
default:
break;
} }
} }
renderer->render(level, hero, main_window);
main_window.display(); main_window.display();
} }
audio->stopBackground();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -7,6 +7,7 @@
#include "hero.h" #include "hero.h"
#include "level.h" #include "level.h"
#include "audio.h"
#include "renderer.h" #include "renderer.h"
/// The main class where all the process happens /// The main class where all the process happens
@ -16,7 +17,8 @@ private:
// Game entities // Game entities
HeroPtr hero; HeroPtr hero;
LevelPtr level; LevelPtr level;
std::unique_ptr<Renderer> renderer; AudioPtr audio;
std::unique_ptr<Renderer> renderer; // wer is `using RendererPrt = ...` A?A?A?
int current_level; int current_level;

View File

@ -11,12 +11,12 @@ std::unique_ptr<D> static_unique_pointer_cast(std::unique_ptr<B>&& old)
return std::unique_ptr<D>{static_cast<D *>(old.release())}; return std::unique_ptr<D>{static_cast<D *>(old.release())};
} }
void Level::Map::init(const std::string &map_file_name) void Level::Map::init(const std::string &path)
{ {
prepareDefaultCells(); prepareDefaultCells();
std::ifstream file; std::ifstream file;
file.open(map_file_name); file.open(path);
std::string cur_line; std::string cur_line;
std::istringstream sstr; std::istringstream sstr;
@ -109,13 +109,15 @@ void Level::Map::readTrigger(std::istringstream &sstr)
return ; return ;
auto trigger_cell = static_unique_pointer_cast<TriggerCell>(std::move(data[src_row][src_col])); auto trigger_cell = static_unique_pointer_cast<TriggerCell>(std::move(data[src_row][src_col]));
trigger_cell->addTarget(default_cells[cell_type]->clone()); auto dest_cell = default_cells[cell_type]->clone();
dest_cell->setPosition(dest_row, dest_col);
trigger_cell->addTarget(std::move(dest_cell));
data[src_row][src_col] = std::move(trigger_cell); data[src_row][src_col] = std::move(trigger_cell);
} }
Level::Level(const std::string &map_file_name) Level::Level(const std::string &path)
{ {
map.init(map_file_name); map.init(path);
} }
size_t Level::rows() const size_t Level::rows() const

View File

@ -1,13 +1,15 @@
#ifndef LEVEL_H #ifndef LEVEL_H
#define LEVEL_H #define LEVEL_H
#include <memory>
#include <string>
#include <array> #include <array>
#include <map> #include <map>
#include "cell.h" #include "cell.h"
// Very desirable to create module for default values // Very desirable to create module for default values
const std::string default_map_file_name = "test_map"; const std::string default_file_path = "test_map";
/// Abstraction over 2D array to quickly get access to level cells /// Abstraction over 2D array to quickly get access to level cells
class Level class Level
@ -40,7 +42,7 @@ private:
size_t rows, cols; size_t rows, cols;
std::array<CellPtr, N_CELLS> default_cells; std::array<CellPtr, N_CELLS> default_cells;
void init(const std::string &map_file_name = default_map_file_name); void init(const std::string &path = default_file_path);
/// Prepare prototypes of default cells /// Prepare prototypes of default cells
void prepareDefaultCells(); void prepareDefaultCells();
@ -56,7 +58,7 @@ private:
sf::Color color_ground; sf::Color color_ground;
public: public:
Level(const std::string &map_file_name = default_map_file_name); Level(const std::string &path = default_file_path);
/// Number of map rows /// Number of map rows
size_t rows() const; size_t rows() const;

View File

@ -6,6 +6,7 @@ CONFIG -= qt
QMAKE_CXXFLAGS = -Wall -Werror -Wextra -Wpedantic -Wconversion -std=c++17 -O0 -g QMAKE_CXXFLAGS = -Wall -Werror -Wextra -Wpedantic -Wconversion -std=c++17 -O0 -g
SOURCES += \ SOURCES += \
audio.cpp \
cell.cpp \ cell.cpp \
entity.cpp \ entity.cpp \
game.cpp \ game.cpp \
@ -15,6 +16,7 @@ SOURCES += \
renderer.cpp renderer.cpp
HEADERS += \ HEADERS += \
audio.h \
cell.h \ cell.h \
entity.h \ entity.h \
game.h \ game.h \
@ -25,4 +27,4 @@ HEADERS += \
# Only to highlight syntax when I am on Windows # Only to highlight syntax when I am on Windows
win32:INCLUDEPATH += d:\SFML-2.5.1\include win32:INCLUDEPATH += d:\SFML-2.5.1\include
LIBS += -lsfml-graphics -lsfml-window -lsfml-system LIBS += -lsfml-graphics -lsfml-audio -lsfml-window -lsfml-system