Visuel notes

This commit is contained in:
Crizomb 2025-06-13 21:31:16 +02:00
parent e2fb0d911a
commit 05f8b11dec
14 changed files with 137 additions and 50 deletions

View file

@ -23,6 +23,7 @@ add_executable(simpleGame
Source/AudioEmitter.cpp Include/AudioEmitter.hpp Source/AudioEmitter.cpp Include/AudioEmitter.hpp
Source/NoteSprite.cpp Include/NoteSprite.hpp Source/NoteSprite.cpp Include/NoteSprite.hpp
Source/NoteTile.cpp Include/NoteTile.hpp Include/NotePlaceEnum.hpp Source/NoteTile.cpp Include/NoteTile.hpp Include/NotePlaceEnum.hpp
Source/TilePattern.cpp Include/TilePattern.hpp
) )
target_include_directories(simpleGame PRIVATE target_include_directories(simpleGame PRIVATE

View file

@ -1,3 +1,5 @@
#pragma once
#include <fmod.hpp> #include <fmod.hpp>
#include <fmod_errors.h> #include <fmod_errors.h>
#include <memory> #include <memory>
@ -31,6 +33,7 @@ public:
int firstNote(); int firstNote();
int nextNote(int currentNote); int nextNote(int currentNote);
int noteSecondaire(int note); int noteSecondaire(int note);
float getTime(); float getTimeTempo() const;
float getTime() const;
float timeBeforeNewGeneration{0.2f}; float timeBeforeNewGeneration{0.2f};
}; };

View file

@ -1,3 +1,5 @@
#pragma once
#ifndef BOOK_GAME_HPP #ifndef BOOK_GAME_HPP
#define BOOK_GAME_HPP #define BOOK_GAME_HPP

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Sprite.hpp> #include <SFML/Graphics/Sprite.hpp>
#include <SFML/System/Vector2.hpp> #include <SFML/System/Vector2.hpp>
class NoteTile; class NoteTile;
@ -8,6 +9,7 @@ class NoteTile;
class NoteSprite { class NoteSprite {
public: public:
static sf::Texture texture; static sf::Texture texture;
static bool loadTexture(const std::string &filename);
friend class NoteTile; friend class NoteTile;
private: private:
@ -15,5 +17,5 @@ private:
float fall_speed; float fall_speed;
NoteSprite(); NoteSprite();
NoteSprite(sf::Vector2f start_pos, float fall_speed); NoteSprite(sf::Vector2f start_pos, float fall_speed);
void update(float dtime); void update(float dtime, sf::RenderWindow &window);
}; };

View file

@ -1,7 +1,9 @@
#include <NotePlaceEnum.hpp> #include <NotePlaceEnum.hpp>
#include <NoteSprite.hpp> #include <NoteSprite.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/StencilMode.hpp> #include <SFML/Graphics/StencilMode.hpp>
#include <SFML/System/Vector2.hpp> #include <SFML/System/Vector2.hpp>
#include <SFML/Window/Window.hpp>
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -16,21 +18,25 @@ private:
NoteSprite note_sprite; NoteSprite note_sprite;
// probably some tileSprite class here in the future <- // probably some tileSprite class here in the future <-
// Constructor is rivate, use create // Constructor is private, use create
NoteTile(float play_time, float good_interval, NotePlaceEnum place); NoteTile(float play_time, float good_interval, NotePlaceEnum place,
float current_time);
// For unit testing // For unit testing
public: public:
// Good key press for this note, is if player press good place, within // Good key press for this note, is if player press good place, within
// [play_time - good_interval/2, play_time + good_interval/2] // [play_time - good_interval/2, play_time + good_interval/2]
static void create(float play_time, float good_interval, NotePlaceEnum place); static void create(float play_time, float good_interval, NotePlaceEnum place,
float current_time);
// Usefull in test, but don't use it anywhere // Usefull in test, but don't use it anywhere
static void clear() { existing_tiles.clear(); }; static void clear() { existing_tiles.clear(); };
// Should be called when one of arrows key pressed, return True if it's a good // Should be called when one of arrows key pressed, return True if it's a good
// press // press
static bool checkPress(float press_time, NotePlaceEnum key_pressed); static bool checkPress(float press_time, NotePlaceEnum key_pressed);
static void update(float dtime, sf::RenderWindow &window);
// Some getters usefull in test : // Some getters usefull in test :
float getPlayTime() const { return play_time; }; float getPlayTime() const { return play_time; };
float getGoodInterval() const { return good_interval; }; float getGoodInterval() const { return good_interval; };

View file

@ -0,0 +1,7 @@
#pragma once
#include "AudioEmitter.hpp"
#include <vector>
void generateTilePattern(std::vector<std::pair<float, int>> new_notes,
const AudioEmitter &audio_emitter);

View file

@ -2,7 +2,6 @@
#include <iostream> #include <iostream>
#include <numeric> #include <numeric>
#include <random> #include <random>
#include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -285,7 +284,7 @@ int AudioEmitter::noteSecondaire(int note) {
*/ */
std::vector<std::pair<float, int>> AudioEmitter::generateMusic() { std::vector<std::pair<float, int>> AudioEmitter::generateMusic() {
std::vector<std::pair<float, int>> result; std::vector<std::pair<float, int>> result;
result.reserve(16 * AudioEmitter::nbr_melo_max); result.reserve(16 * nbr_melo_max);
float beatDuration = tempo / 60.f; float beatDuration = tempo / 60.f;
unsigned int sampleRate = 48000; unsigned int sampleRate = 48000;
int maxsize = 400; int maxsize = 400;
@ -354,9 +353,15 @@ void AudioEmitter::audioEnd() {
system->release(); system->release();
} }
float AudioEmitter::getTime() { float AudioEmitter::getTimeTempo() const {
float beatDuration = tempo / 60.f / 8.f; float beatDuration = tempo / 60.f / 8.f;
unsigned long long dspClock = 0; unsigned long long dspClock = 0;
ERRCHECK(timer->getDSPClock(&dspClock, nullptr)); ERRCHECK(timer->getDSPClock(&dspClock, nullptr));
return dspClock / 48000.f / beatDuration; return dspClock / 48000.f / beatDuration;
} }
float AudioEmitter::getTime() const {
unsigned long long dspClock = 0;
ERRCHECK(timer->getDSPClock(&dspClock, nullptr));
return dspClock / 48000.f;
}

View file

@ -1,4 +1,9 @@
#include "Game.hpp" #include "Game.hpp"
#include "AudioEmitter.hpp"
#include "NoteSprite.hpp"
#include "NoteTile.hpp"
#include "TilePattern.hpp"
#include <SFML/Graphics/Color.hpp>
#include <iostream> #include <iostream>
#include <math.h> #include <math.h>
#include <string> #include <string>
@ -7,41 +12,41 @@ const sf::Time Game::TimePerFrame = sf::seconds(1.f / 60.f);
Game::Game() { Game::Game() {
assert(mFont.openFromFile("media/Sansation.ttf")); assert(mFont.openFromFile("media/Sansation.ttf"));
// We do not need to do mStatisticsText.setFont(mFont); as mStatisticsText is
// initialized with a reference to mFont
mStatisticsText.setPosition({5.f, 5.f}); mStatisticsText.setPosition({5.f, 5.f});
mStatisticsText.setCharacterSize(10); mStatisticsText.setCharacterSize(10);
} }
void Game::run() { void generateTilePatternAndMusic(AudioEmitter &audio_emitter) {
sf::Clock clock; generateTilePattern(audio_emitter.generateMusic(), audio_emitter);
sf::Time timeSinceLastUpdate = sf::Time::Zero; }
AudioEmitter audioEmitter = AudioEmitter();
audioEmitter.generateMusic();
bool generated = false;
mWindow.setVerticalSyncEnabled(true);
while (mWindow.isOpen()) {
sf::Time elapsedTime = clock.restart();
timeSinceLastUpdate += elapsedTime;
while (timeSinceLastUpdate > TimePerFrame) {
if (32.f - fmod(audioEmitter.getTime(), 32.f) <
audioEmitter.timeBeforeNewGeneration) {
if (!generated) {
audioEmitter.generateMusic();
generated = true;
}
} else {
generated = false;
}
timeSinceLastUpdate -= TimePerFrame;
processEvents(audioEmitter); bool updateAudio(AudioEmitter &audioEmitter, bool generated) {
audioEmitter.audioUpdate(); if (32.f - fmod(audioEmitter.getTimeTempo(), 32.f) <
update(TimePerFrame); audioEmitter.timeBeforeNewGeneration) {
if (!generated) {
generateTilePatternAndMusic(audioEmitter);
generated = true;
} }
} else {
generated = false;
}
return generated;
}
void Game::run() {
AudioEmitter audioEmitter = AudioEmitter();
generateTilePatternAndMusic(audioEmitter);
bool generated = false;
mWindow.setFramerateLimit(60);
while (mWindow.isOpen()) {
updateStatistics(audioEmitter);
render(); render();
generated = updateAudio(audioEmitter, generated);
processEvents(audioEmitter);
audioEmitter.audioUpdate();
update(TimePerFrame);
updateStatistics(audioEmitter);
} }
} }
@ -62,7 +67,8 @@ void Game::processEvents(AudioEmitter &audioEmitter) {
void Game::update(const sf::Time elapsedTime) { mTarget.update(elapsedTime); } void Game::update(const sf::Time elapsedTime) { mTarget.update(elapsedTime); }
void Game::render() { void Game::render() {
mWindow.clear(); mWindow.clear(sf::Color::Yellow);
NoteTile::update(1.0 / 60, mWindow);
mTarget.drawCurrent(mWindow); mTarget.drawCurrent(mWindow);
mWindow.draw(mStatisticsText); mWindow.draw(mStatisticsText);
mWindow.display(); mWindow.display();
@ -70,8 +76,8 @@ void Game::render() {
void Game::updateStatistics(AudioEmitter &audioEmitter) { void Game::updateStatistics(AudioEmitter &audioEmitter) {
mStatisticsText.setString(std::format( mStatisticsText.setString(std::format(
"Mesure = {}\nBeat = {} us", ceil(audioEmitter.getTime() / 8.f), "Mesure = {}\nBeat = {} us", ceil(audioEmitter.getTimeTempo() / 8.f),
ceil(fmod(audioEmitter.getTime(), 8.f)))); ceil(fmod(audioEmitter.getTimeTempo(), 8.f))));
mStatisticsUpdateTime -= sf::seconds(1.0f); mStatisticsUpdateTime -= sf::seconds(1.0f);
mStatisticsNumFrames = 0; mStatisticsNumFrames = 0;

View file

@ -1,6 +1,13 @@
#include "Game.hpp" #include "Game.hpp"
#include "NoteSprite.hpp"
#include <iostream>
int main() { int main() {
// from out/build/src
if (!NoteSprite::loadTexture("../../../media/sprites/flower_tile.png")) {
std::cerr << "Failed to load texture!" << std::endl;
return -1;
}
Game game; Game game;
game.run(); game.run();
} }

View file

@ -1,16 +1,34 @@
#include "NoteSprite.hpp" #include "NoteSprite.hpp"
#include <SFML/Graphics/Color.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Sprite.hpp> #include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/Texture.hpp> #include <SFML/Graphics/Texture.hpp>
#include <SFML/System/Vector2.hpp> #include <SFML/System/Vector2.hpp>
#include <SFML/Window/Window.hpp>
#include <bits/types/cookie_io_functions_t.h>
#include <cstdio>
sf::Texture NoteSprite::texture; sf::Texture NoteSprite::texture;
bool NoteSprite::loadTexture(const std::string &filename) {
return NoteSprite::texture.loadFromFile(filename);
}
NoteSprite::NoteSprite() : sprite(sf::Sprite(texture)), fall_speed(0.f) {}; NoteSprite::NoteSprite() : sprite(sf::Sprite(texture)), fall_speed(0.f) {};
NoteSprite::NoteSprite(sf::Vector2f start_pos, float fall_speed) NoteSprite::NoteSprite(sf::Vector2f start_pos, float fall_speed)
: fall_speed(fall_speed), sprite(sf::Sprite(NoteSprite::texture)) { : fall_speed(fall_speed), sprite(sf::Sprite(NoteSprite::texture)) {
sprite.setPosition(start_pos); // sprite position c'est le left up corner sprite.setPosition(start_pos); // sprite position c'est le left up corner
sprite.setColor(sf::Color::Yellow);
}; };
void NoteSprite::update(float dtime) { void NoteSprite::update(float dtime, sf::RenderWindow &window) {
sprite.move(sf::Vector2f(0, fall_speed * dtime)); sprite.move(sf::Vector2f(0, fall_speed * dtime));
window.draw(sprite);
/*
printf("draw \nposition (%f, %f)\n size(%u, %u)\n", sprite.getPosition().x,
sprite.getPosition().y, sprite.getTexture().getSize().x,
sprite.getTexture().getSize().y);
printf("texture (%u, %u)\n", NoteSprite::texture.getSize().x,
NoteSprite::texture.getSize().y);*/
}; };

View file

@ -1,24 +1,29 @@
#include "NoteTile.hpp" #include "NoteTile.hpp"
#include "NoteSprite.hpp" #include "NoteSprite.hpp"
#include <SFML/System/Vector2.hpp> #include <SFML/System/Vector2.hpp>
#include <SFML/Window/Window.hpp>
#include <memory> #include <memory>
const int NotePlaceXPos[3] = {200, 400, 600};
std::vector<std::unique_ptr<NoteTile>> NoteTile::existing_tiles{}; std::vector<std::unique_ptr<NoteTile>> NoteTile::existing_tiles{};
// private // private
NoteTile::NoteTile(float play_time, float good_interval, NotePlaceEnum place) NoteTile::NoteTile(float play_time, float good_interval, NotePlaceEnum place,
float current_time)
: play_time(play_time), good_interval(good_interval), place(place), : play_time(play_time), good_interval(good_interval), place(place),
note_sprite() { note_sprite() {
// TODO do real note_sprite init, make it fall at good speed far enough... // TODO do real note_sprite init, make it fall at good speed far enough...
note_sprite.sprite.setPosition(sf::Vector2f(200, 0)); note_sprite.fall_speed = 100;
note_sprite.fall_speed = 1; note_sprite.sprite.setPosition(
sf::Vector2f(NotePlaceXPos[place],
480 - note_sprite.fall_speed * (play_time - current_time)));
} }
// public // public
void NoteTile::create(float play_time, float good_interval, void NoteTile::create(float play_time, float good_interval, NotePlaceEnum place,
NotePlaceEnum place) { float current_time) {
// Can't use make_unique because constructor is private // Can't use make_unique because constructor is private
existing_tiles.push_back( existing_tiles.push_back(std::unique_ptr<NoteTile>(
std::unique_ptr<NoteTile>(new NoteTile(play_time, good_interval, place))); new NoteTile(play_time, good_interval, place, current_time)));
} }
bool NoteTile::checkPress(float press_time, NotePlaceEnum key_pressed) { bool NoteTile::checkPress(float press_time, NotePlaceEnum key_pressed) {
@ -31,3 +36,9 @@ bool NoteTile::checkPress(float press_time, NotePlaceEnum key_pressed) {
} }
return false; return false;
} }
void NoteTile::update(float dtime, sf::RenderWindow &window) {
for (const auto &note_tile : NoteTile::existing_tiles) {
note_tile->note_sprite.update(dtime, window);
}
}

View file

@ -8,6 +8,8 @@ RoundTarget::RoundTarget(const float radius, const sf::Color color, float x,
} }
void RoundTarget::drawCurrent(sf::RenderWindow &window) const { void RoundTarget::drawCurrent(sf::RenderWindow &window) const {
/*printf("sphere pos (%f, %f)\n", mShape.getPosition().x,*/
/* mShape.getPosition().y);*/
window.draw(mShape); window.draw(mShape);
} }

View file

@ -0,0 +1,17 @@
#include "TilePattern.hpp"
#include "AudioEmitter.hpp"
#include "NoteSprite.hpp"
#include "NoteTile.hpp"
#include <utility>
#include <vector>
void generateTilePattern(std::vector<std::pair<float, int>> new_notes,
const AudioEmitter &audio_emitter) {
int i = 0;
for (auto note : new_notes) {
float start_time = note.first;
NotePlaceEnum notePlace = static_cast<NotePlaceEnum>(i % 3);
NoteTile::create(start_time, 1.0, notePlace, audio_emitter.getTime());
i++;
}
}

View file

@ -10,7 +10,7 @@ TEST(NoteFileTest, Yes) { EXPECT_EQ(0, 0); }
TEST(NoteTileTest, CreateTile) { TEST(NoteTileTest, CreateTile) {
NoteTile::clear(); NoteTile::clear();
NoteTile::create(2.0f, 0.5f, NotePlaceEnum::Right); NoteTile::create(2.0f, 0.5f, NotePlaceEnum::Right, 0.0);
EXPECT_FLOAT_EQ(NoteTile::getExistingTiles()[0]->getPlayTime(), 2.0f); EXPECT_FLOAT_EQ(NoteTile::getExistingTiles()[0]->getPlayTime(), 2.0f);
EXPECT_FLOAT_EQ(NoteTile::getExistingTiles()[0]->getGoodInterval(), 0.5f); EXPECT_FLOAT_EQ(NoteTile::getExistingTiles()[0]->getGoodInterval(), 0.5f);
EXPECT_EQ(NoteTile::getExistingTiles()[0]->getPlace(), NotePlaceEnum::Right); EXPECT_EQ(NoteTile::getExistingTiles()[0]->getPlace(), NotePlaceEnum::Right);
@ -21,9 +21,9 @@ TEST(NoteTileTest, CreateTile) {
// est negatif est divisé par 2, voir checkPress // est negatif est divisé par 2, voir checkPress
TEST(NoteTileTest, CheckGoodPress) { TEST(NoteTileTest, CheckGoodPress) {
NoteTile::clear(); NoteTile::clear();
NoteTile::create(2.0f, 0.5f, NotePlaceEnum::Right); NoteTile::create(2.0f, 0.5f, NotePlaceEnum::Right, 0.0);
NoteTile::create(3.0f, 0.5f, NotePlaceEnum::Middle); NoteTile::create(3.0f, 0.5f, NotePlaceEnum::Middle, 0.0);
NoteTile::create(0.5f, 0.2f, NotePlaceEnum::Left); NoteTile::create(0.5f, 0.2f, NotePlaceEnum::Left, 0.0);
EXPECT_FALSE(NoteTile::checkPress(1.0f, NotePlaceEnum::Left)); EXPECT_FALSE(NoteTile::checkPress(1.0f, NotePlaceEnum::Left));
EXPECT_FALSE(NoteTile::checkPress(0.7f, NotePlaceEnum::Left)); EXPECT_FALSE(NoteTile::checkPress(0.7f, NotePlaceEnum::Left));