Fix animation objects and note state machine

This commit is contained in:
NaiJi ✨ 2021-06-24 21:04:09 +03:00
parent 06d099c11f
commit 77a9d15caa
17 changed files with 127 additions and 78 deletions

View File

@ -1,10 +1,8 @@
#ifndef INPUTTYPE_H
#define INPUTTYPE_H
#include <SFML/System/Clock.hpp>
#include <SFML/Window/Event.hpp>
using microsec = sf::Int64;
#include "mathutils.h"
struct PlayerInput
{

38
include/mathutils.h Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include <SFML/System/Clock.hpp>
using microsec = sf::Int64;
struct Coordinates
{
float x;
float y;
constexpr inline Coordinates operator+(const Coordinates& right) const noexcept
{
return {right.x + x, right.y + y};
}
constexpr inline Coordinates operator-(const Coordinates& right) const noexcept
{
return {right.x - x, right.y - y};
}
inline void moveBy(float x, float y) noexcept
{
this->x += x;
this->y += y;
}
inline void moveBy(const Coordinates& right) noexcept
{
this->x += right.x;
this->y += right.y;
}
inline void scale(float factor) noexcept
{
x *= factor;
y *= factor;
}
};

View File

@ -1,13 +1,7 @@
#pragma once
#include <vector>
#include <SFML/System/Clock.hpp>
#include <SFML/Graphics/Drawable.hpp>
#include "inputtype.h"
using microsec = sf::Int64;
class Note
{
public:
@ -15,12 +9,13 @@ public:
_perfect_offset(perfect_offset) {}
virtual ~Note() = default;
virtual bool isActive(const microsec& music_offset) const = 0;
virtual bool isActive() const = 0;
virtual void update(const microsec& music_offset) = 0;
virtual void input(PlayerInput&& inputdata) = 0;
virtual void draw() const = 0;
virtual void putToGame(const microsec &offset) = 0;
virtual bool isInGame() const = 0;
virtual bool isExpired() const = 0;
microsec offset() const

View File

@ -1,16 +1,13 @@
#ifndef PRECISIONEVALUATOR_H
#define PRECISIONEVALUATOR_H
#include "mathutils.h"
#include <numeric>
#include <type_traits>
#include <vector>
#include <cmath>
#include <iostream>
#include <SFML/System/Clock.hpp>
using microsec = sf::Int64;
template<typename Grade, typename = std::enable_if_t<std::is_enum<Grade>::value>>
class PrecisionEvaluator
{

View File

@ -1,11 +1,9 @@
#ifndef TIMELINE_H
#define TIMELINE_H
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Config.hpp>
#include "mathutils.h"
#include <memory>
using microsec = sf::Int64;
#include <vector>
class Note;

View File

@ -4,6 +4,8 @@
#include "classicgame/classicgame.h"
#include "classicgame/classicgraphicsmanager.h"
#include <iostream>
const sf::Time TIME_PER_FRAME = sf::seconds(1.f / 90.f);
Application::Application() :
@ -27,7 +29,6 @@ void Application::run()
void Application::exec()
{
sf::Clock timer;
sf::Clock game_timer;
sf::Time time_since_last_update = sf::Time::Zero;
while (_game_window.isOpen())
@ -41,7 +42,6 @@ void Application::exec()
if (isOneFramePassed)
{
time_since_last_update -= TIME_PER_FRAME;
game_timer.restart();
update();
draw();
}

View File

@ -1,10 +1,8 @@
#pragma once
#include <SFML/Config.hpp>
#include "mathutils.h"
#include <memory>
using microsec = sf::Int64;
class ClassicSprite;
class ClassicAnimationScenario

View File

@ -24,10 +24,10 @@ void ClassicFlyingAnimationScenario::update(const microsec& music_offset)
auto update_time = music_offset - _time_begin;
i = update_time / _percentage * 0.01;
float xa = getPoint( crd.first + 20. , crd.first + 90. , i );
float ya = getPoint( crd.second - 600. , crd.second - 150. , i );
float xb = getPoint( crd.first + 90. , crd.first , i );
float yb = getPoint( crd.second - 150. , crd.second , i );
float xa = getPoint( crd.x + 20. , crd.x + 90. , i );
float ya = getPoint( crd.y - 600. , crd.y - 150. , i );
float xb = getPoint( crd.x + 90. , crd.x , i );
float yb = getPoint( crd.y - 150. , crd.y , i );
_sprite->update(getPoint( xa , xb , i ), getPoint( ya , yb , i ));
if (i >= 1)

View File

@ -11,12 +11,13 @@ ClassicGraphicsManager::ClassicGraphicsManager(sf::RenderTarget& target) :
void ClassicGraphicsManager::initGraphics(ClassicNote* note)
{
note->setSprite(_sprite_container.getSprite(note->type()));
note->sprite()->setCoordinates(note->getCoordinates().x, note->getCoordinates().y, 0, 0);
note->sprite()->setCoordinates(note->getCoordinates(), 0, 0);
}
void ClassicGraphicsManager::resetGraphics(ClassicNote* note)
{
_sprite_container.resetSprite(note->sprite(), note->type());
note->setSprite(nullptr);
}
void ClassicGraphicsManager::draw(const ClassicNote *note)

View File

@ -12,7 +12,7 @@ Beatmap ClassicMapCreator::createBeatmap(const std::string& filepath) const
microsec starting_beat_offset = 352162;
int amount_of_beats = 209;
microsec interval = 1412162;
microsec tempo_interval = interval / 2;
microsec tempo_interval = interval / 4;
microsec note_input_offset = 412162 / 3;
microsec bpm_iterator = starting_beat_offset;
microsec bpm_end = starting_beat_offset + (interval * amount_of_beats);

View File

@ -3,7 +3,6 @@
#include "classicgraphicsmanager.h"
#include "classicflyinganimationscenario.h"
#include "classicdyinganimationscenario.h"
#include <iostream>
ClassicNote::ClassicNote(const std::vector<microsec>& intervals, microsec perfect_offset,
Type type, const Coordinates& coord, const std::unique_ptr<ClassicGraphicsManager> &manager) :
@ -12,48 +11,66 @@ ClassicNote::ClassicNote(const std::vector<microsec>& intervals, microsec perfec
_evaluator(intervals, _perfect_offset),
_keys({sf::Keyboard::W, sf::Keyboard::Up}),
_graphics_manager(manager),
_is_expired(true),
_type(type),
_state(State::NONE)
{
_animations[State::NONE] = nullptr;
_animations[State::FLYING] = std::make_shared<ClassicFlyingAnimationScenario>();
_animations[State::ACTIVE] = _animations[State::FLYING];
_animations[State::DYING] = std::make_shared<ClassicDyingAnimationScenario>();
_animations[State::DEAD] = nullptr;
}
bool ClassicNote::isActive(const microsec &music_offset) const
bool ClassicNote::isActive() const
{
return _evaluator.isActive(music_offset);
return _state == State::ACTIVE;
}
void ClassicNote::putToGame(const microsec &music_offset)
{
_is_expired = false;
_graphics_manager->initGraphics(this);
_state = State::FLYING;
_animations[_state]->launch(_sprite, music_offset, offset());
}
bool ClassicNote::isInGame() const
{
return _state == State::FLYING || _state == State::ACTIVE || _state == State::DYING;
}
bool ClassicNote::isExpired() const
{
return _is_expired;
return _state == State::DEAD;
}
void ClassicNote::update(const microsec& music_offset)
{
if (!_animations[_state])
return;
bool is_not_active_anymore = (!_is_expired && !isActive(music_offset));
if (is_not_active_anymore)
switch (_state)
{
_state = State::DYING;
_animations[_state]->launch(_sprite, music_offset, offset());
default: return;
break;
case State::FLYING:
if (_evaluator.isActive(music_offset))
_state = State::ACTIVE;
break;
case State::DYING:
if (_animations[_state]->isDone())
_state = State::DEAD;
break;
case State::ACTIVE:
if (!_evaluator.isActive(music_offset))
{
_state = State::DYING;
_animations[_state]->launch(_sprite, music_offset, offset());
}
break;
}
_animations[_state]->update(music_offset);
_is_expired = _animations[_state]->isDone();
if (_animations[_state])
_animations[_state]->update(music_offset);
}
void ClassicNote::input(PlayerInput&& inputdata)

View File

@ -7,17 +7,6 @@
#include <memory>
#include <array>
struct Coordinates
{
float x;
float y;
inline Coordinates operator+(const Coordinates& right) noexcept
{
return {right.x + x, right.y - y};
}
}; // MOVE TO OWN HEADER ^
class ClassicSprite;
class ClassicGraphicsManager;
class ClassicAnimationScenario;
@ -38,7 +27,9 @@ public:
NONE,
FLYING,
DYING
ACTIVE,
DYING,
DEAD
};
explicit ClassicNote(const std::vector<microsec>& intervals, microsec perfect_offset,
@ -46,10 +37,11 @@ public:
const std::unique_ptr<ClassicGraphicsManager>& manager);
virtual ~ClassicNote() = default;
virtual bool isActive(const microsec &music_offset) const override;
virtual bool isActive() const override;
virtual void update(const microsec &music_offset) override;
virtual void input(PlayerInput&& inputdata) override;
virtual void putToGame(const microsec &music_offset) override;
virtual bool isInGame() const override;
virtual bool isExpired() const override;
virtual void draw() const override;
@ -67,9 +59,8 @@ private:
const std::unique_ptr<ClassicGraphicsManager>& _graphics_manager;
std::shared_ptr<ClassicSprite> _sprite;
bool _is_expired;
const Type _type;
State _state;
std::array<std::shared_ptr<ClassicAnimationScenario>, 3> _animations;
std::array<std::shared_ptr<ClassicAnimationScenario>, 5> _animations;
};

View File

@ -29,16 +29,16 @@ void ClassicSprite::reset()
_trail_fade = false;
}
void ClassicSprite::setCoordinates(float x, float y, float trail_x, float trail_y) noexcept
void ClassicSprite::setCoordinates(const Coordinates& coordinates, float trail_x, float trail_y) noexcept
{
_shape.setPosition(x, y);
_shape.setPosition(coordinates.x, coordinates.y);
_trail.setPosition(trail_x, trail_y);
_grade_text.setPosition(x + _shape.getSize().x/2, y + 10);
_grade_text.setPosition(coordinates.x + _shape.getSize().x/2, coordinates.y + 10);
}
std::pair<float, float> ClassicSprite::coordinates() const
Coordinates ClassicSprite::coordinates() const
{
return std::make_pair(_shape.getPosition().x, _shape.getPosition().y);
return {_shape.getPosition().x, _shape.getPosition().y};
}
void ClassicSprite::update(float trail_x, float trail_y) noexcept
@ -76,8 +76,12 @@ void ClassicSprite::fade()
{
auto fill_color = _grade_text.getFillColor();
if (fill_color.a == 0)
if (fill_color.a <= 15)
{
fill_color.a = 0;
_grade_text.setFillColor(fill_color);
return;
}
auto new_alpha = fill_color.a - 15;
fill_color.a = new_alpha < 0 ? 0 : new_alpha;
@ -87,7 +91,11 @@ void ClassicSprite::fade()
fill_color = _shape.getFillColor();
if (fill_color.a == 0)
{
fill_color.a = 0;
_shape.setFillColor(fill_color);
return;
}
new_alpha = fill_color.a - 15;
fill_color.a = new_alpha < 0 ? 0 : new_alpha;

View File

@ -1,5 +1,6 @@
#pragma once
#include "mathutils.h"
#include "sprite.h"
#include "SFML/Graphics/RectangleShape.hpp"
#include "SFML/Graphics/Text.hpp"
@ -11,8 +12,8 @@ public:
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
virtual void reset() override;
void setCoordinates(float x, float y, float trail_x, float trail_y) noexcept;
std::pair<float, float> coordinates() const;
void setCoordinates(const Coordinates &coordinates, float trail_x, float trail_y) noexcept;
Coordinates coordinates() const;
void update(float trail_x, float trail_y) noexcept;
void update() noexcept;

View File

@ -12,6 +12,7 @@ ClassicTimeline::ClassicTimeline()
_music.openFromFile(song_filename);
_music.setVolume(10);
_last_timestamp = -1;
}
void ClassicTimeline::run(std::vector<Note*>&& notes, const microsec& visibility)
@ -44,32 +45,37 @@ void ClassicTimeline::clear()
void ClassicTimeline::update()
{
const auto& music_offset = currentMusicOffset();
checkCurrentActiveNote(music_offset);
checkForNextActiveNote(music_offset);
updateVisibleSprites(music_offset);
if (music_offset != _last_timestamp)
{
checkCurrentActiveNote();
checkForNextActiveNote();
updateVisibleSprites(music_offset);
}
_last_timestamp = music_offset;
}
void ClassicTimeline::checkCurrentActiveNote(const microsec& music_offset)
void ClassicTimeline::checkCurrentActiveNote()
{
if (isExpired(_active_note))
return;
auto note = *_active_note;
if (!note->isActive(music_offset))
if (!note->isActive())
{
expire(_active_note);
++_top_note;
}
}
void ClassicTimeline::checkForNextActiveNote(const microsec& music_offset)
void ClassicTimeline::checkForNextActiveNote()
{
if (!isExpired(_active_note))
return;
auto top_note = *_top_note;
if (top_note->isActive(music_offset))
if (top_note->isActive())
_active_note = _top_note;
}
@ -127,7 +133,7 @@ void ClassicTimeline::findLastVisibleNote(const microsec &music_offset)
auto note = *note_iterator;
if (note->isExpired())
if (!note->isInGame())
note->putToGame(music_offset);
++note_iterator;

View File

@ -33,12 +33,13 @@ private:
std::vector<microsec> _input_intervals;
std::vector<Note*> _timeline;
microsec _visibility_offset;
microsec _last_timestamp;
sf::Music _music;
void updateVisibleSprites(const microsec& music_offset);
void checkCurrentActiveNote(const microsec& music_offset);
void checkForNextActiveNote(const microsec& music_offset);
void checkCurrentActiveNote();
void checkForNextActiveNote();
bool isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const;
inline bool nothingToDraw() const noexcept;