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/NoteSprite.cpp Include/NoteSprite.hpp
Source/NoteTile.cpp Include/NoteTile.hpp Include/NotePlaceEnum.hpp
Source/TilePattern.cpp Include/TilePattern.hpp
)
target_include_directories(simpleGame PRIVATE

View file

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

View file

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

View file

@ -1,5 +1,6 @@
#pragma once
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/System/Vector2.hpp>
class NoteTile;
@ -8,6 +9,7 @@ class NoteTile;
class NoteSprite {
public:
static sf::Texture texture;
static bool loadTexture(const std::string &filename);
friend class NoteTile;
private:
@ -15,5 +17,5 @@ private:
float fall_speed;
NoteSprite();
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 <NoteSprite.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/StencilMode.hpp>
#include <SFML/System/Vector2.hpp>
#include <SFML/Window/Window.hpp>
#include <memory>
#include <vector>
@ -16,21 +18,25 @@ private:
NoteSprite note_sprite;
// probably some tileSprite class here in the future <-
// Constructor is rivate, use create
NoteTile(float play_time, float good_interval, NotePlaceEnum place);
// Constructor is private, use create
NoteTile(float play_time, float good_interval, NotePlaceEnum place,
float current_time);
// For unit testing
public:
// Good key press for this note, is if player press good place, within
// [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
static void clear() { existing_tiles.clear(); };
// Should be called when one of arrows key pressed, return True if it's a good
// press
static bool checkPress(float press_time, NotePlaceEnum key_pressed);
static void update(float dtime, sf::RenderWindow &window);
// Some getters usefull in test :
float getPlayTime() const { return play_time; };
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 <numeric>
#include <random>
#include <string>
#include <utility>
#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>> result;
result.reserve(16 * AudioEmitter::nbr_melo_max);
result.reserve(16 * nbr_melo_max);
float beatDuration = tempo / 60.f;
unsigned int sampleRate = 48000;
int maxsize = 400;
@ -354,9 +353,15 @@ void AudioEmitter::audioEnd() {
system->release();
}
float AudioEmitter::getTime() {
float AudioEmitter::getTimeTempo() const {
float beatDuration = tempo / 60.f / 8.f;
unsigned long long dspClock = 0;
ERRCHECK(timer->getDSPClock(&dspClock, nullptr));
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 "AudioEmitter.hpp"
#include "NoteSprite.hpp"
#include "NoteTile.hpp"
#include "TilePattern.hpp"
#include <SFML/Graphics/Color.hpp>
#include <iostream>
#include <math.h>
#include <string>
@ -7,41 +12,41 @@ const sf::Time Game::TimePerFrame = sf::seconds(1.f / 60.f);
Game::Game() {
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.setCharacterSize(10);
}
void Game::run() {
sf::Clock clock;
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;
void generateTilePatternAndMusic(AudioEmitter &audio_emitter) {
generateTilePattern(audio_emitter.generateMusic(), audio_emitter);
}
processEvents(audioEmitter);
audioEmitter.audioUpdate();
update(TimePerFrame);
bool updateAudio(AudioEmitter &audioEmitter, bool generated) {
if (32.f - fmod(audioEmitter.getTimeTempo(), 32.f) <
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();
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::render() {
mWindow.clear();
mWindow.clear(sf::Color::Yellow);
NoteTile::update(1.0 / 60, mWindow);
mTarget.drawCurrent(mWindow);
mWindow.draw(mStatisticsText);
mWindow.display();
@ -70,8 +76,8 @@ void Game::render() {
void Game::updateStatistics(AudioEmitter &audioEmitter) {
mStatisticsText.setString(std::format(
"Mesure = {}\nBeat = {} us", ceil(audioEmitter.getTime() / 8.f),
ceil(fmod(audioEmitter.getTime(), 8.f))));
"Mesure = {}\nBeat = {} us", ceil(audioEmitter.getTimeTempo() / 8.f),
ceil(fmod(audioEmitter.getTimeTempo(), 8.f))));
mStatisticsUpdateTime -= sf::seconds(1.0f);
mStatisticsNumFrames = 0;

View file

@ -1,6 +1,13 @@
#include "Game.hpp"
#include "NoteSprite.hpp"
#include <iostream>
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.run();
}

View file

@ -1,16 +1,34 @@
#include "NoteSprite.hpp"
#include <SFML/Graphics/Color.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/Texture.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;
bool NoteSprite::loadTexture(const std::string &filename) {
return NoteSprite::texture.loadFromFile(filename);
}
NoteSprite::NoteSprite() : sprite(sf::Sprite(texture)), fall_speed(0.f) {};
NoteSprite::NoteSprite(sf::Vector2f start_pos, float fall_speed)
: fall_speed(fall_speed), sprite(sf::Sprite(NoteSprite::texture)) {
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));
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 "NoteSprite.hpp"
#include <SFML/System/Vector2.hpp>
#include <SFML/Window/Window.hpp>
#include <memory>
const int NotePlaceXPos[3] = {200, 400, 600};
std::vector<std::unique_ptr<NoteTile>> NoteTile::existing_tiles{};
// 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),
note_sprite() {
// 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 = 1;
note_sprite.fall_speed = 100;
note_sprite.sprite.setPosition(
sf::Vector2f(NotePlaceXPos[place],
480 - note_sprite.fall_speed * (play_time - current_time)));
}
// public
void NoteTile::create(float play_time, float good_interval,
NotePlaceEnum place) {
void NoteTile::create(float play_time, float good_interval, NotePlaceEnum place,
float current_time) {
// Can't use make_unique because constructor is private
existing_tiles.push_back(
std::unique_ptr<NoteTile>(new NoteTile(play_time, good_interval, place)));
existing_tiles.push_back(std::unique_ptr<NoteTile>(
new NoteTile(play_time, good_interval, place, current_time)));
}
bool NoteTile::checkPress(float press_time, NotePlaceEnum key_pressed) {
@ -31,3 +36,9 @@ bool NoteTile::checkPress(float press_time, NotePlaceEnum key_pressed) {
}
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 {
/*printf("sphere pos (%f, %f)\n", mShape.getPosition().x,*/
/* mShape.getPosition().y);*/
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) {
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]->getGoodInterval(), 0.5f);
EXPECT_EQ(NoteTile::getExistingTiles()[0]->getPlace(), NotePlaceEnum::Right);
@ -21,9 +21,9 @@ TEST(NoteTileTest, CreateTile) {
// est negatif est divisé par 2, voir checkPress
TEST(NoteTileTest, CheckGoodPress) {
NoteTile::clear();
NoteTile::create(2.0f, 0.5f, NotePlaceEnum::Right);
NoteTile::create(3.0f, 0.5f, NotePlaceEnum::Middle);
NoteTile::create(0.5f, 0.2f, NotePlaceEnum::Left);
NoteTile::create(2.0f, 0.5f, NotePlaceEnum::Right, 0.0);
NoteTile::create(3.0f, 0.5f, NotePlaceEnum::Middle, 0.0);
NoteTile::create(0.5f, 0.2f, NotePlaceEnum::Left, 0.0);
EXPECT_FALSE(NoteTile::checkPress(1.0f, NotePlaceEnum::Left));
EXPECT_FALSE(NoteTile::checkPress(0.7f, NotePlaceEnum::Left));