Skip to content

Commit cc8e559

Browse files
committed
Implemented proper frame limiting.
I removed the fixed SDL_Delay(75) calls and replaced them with a more dynamic frame limiting approach based on the actual frame time. The rendering issues are far from fixed but at least this means things should theoretically be more consistent across hardware. This will likely be my last commit for a while as I focus on other projects and entering open source. I will come back to Meowstro
1 parent 7ec304c commit cc8e559

File tree

7 files changed

+74
-8
lines changed

7 files changed

+74
-8
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ After building, run the executable:
9292

9393
> The output directory is always `build/bin/Debug` or `build/bin/Release` depending on the build type, on all platforms.
9494
95+
## Known Issues
96+
97+
**Audio Synchronization:** There are known synchronization issues between the audio, game logic, and rendering that can cause the game to feel slow or off. Due to time constraints and other priorities, this issue remains unresolved. I (Hugo) will get back to it as soon as I feel like I have time to do so. This project will be worked on in the future with the goal of making the game good enough.
98+
9599
## Additional Tools Used in GitHub Actions
96100

97101
- **cppcheck**

include/AnimationSystem.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class AnimationSystem {
4242

4343
// Update animation timing (call once per frame)
4444
void updateTiming();
45+
void updateTiming(Uint64 currentTime);
4546

4647
// Hook throwing animation
4748
void startHookThrow(Sprite& hook, int handX, int handY, int throwDuration);
@@ -69,6 +70,7 @@ class AnimationSystem {
6970

7071
private:
7172
float m_timeCounter;
73+
Uint64 m_animationStartTime; // For absolute time calculations
7274

7375
// Helper methods for sway calculations
7476
int calculateSway(float timeOffset = 0.0f) const;

include/Audio.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ class Audio {
1111
void playBackgroundMusic(const std::string& filePath);
1212
void stopBackgroundMusic();
1313
bool isValid() const { return m_valid; }
14+
15+
// Get precise audio position for beat synchronization
16+
double getMusicPositionMs() const;
1417

1518
private:
1619
Mix_Music* bgMusic;

include/RhythmGame.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ class RhythmGame {
5454
Uint32 m_songStartTime;
5555
std::vector<bool> m_noteHitFlags;
5656

57+
// Frame timing for consistent framerates
58+
Uint64 m_lastFrameTime;
59+
Uint64 m_targetFrameTime;
60+
5761
// Game entities
5862
Entity m_ocean;
5963
Entity m_scoreLabel;
@@ -100,4 +104,7 @@ class RhythmGame {
100104

101105
// Format score helper
102106
std::string formatScore(int score);
107+
108+
// Helper method for precise timing
109+
double getCurrentGameTimeMs() const;
103110
};

src/AnimationSystem.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,25 @@
44
#include <cmath>
55
#include <algorithm>
66

7-
AnimationSystem::AnimationSystem() : m_timeCounter(0.0f) {
7+
AnimationSystem::AnimationSystem() : m_timeCounter(0.0f), m_animationStartTime(0) {
88
}
99

1010
void AnimationSystem::initialize() {
1111
m_timeCounter = 0.0f;
12+
m_animationStartTime = SDL_GetPerformanceCounter();
1213
}
1314

1415
void AnimationSystem::updateTiming() {
15-
m_timeCounter += 0.05f;
16+
// Legacy method - calculate from absolute time
17+
Uint64 currentTime = SDL_GetPerformanceCounter();
18+
updateTiming(currentTime);
19+
}
20+
21+
void AnimationSystem::updateTiming(Uint64 currentTime) {
22+
// Use absolute time to avoid accumulating errors
23+
Uint64 elapsed = currentTime - m_animationStartTime;
24+
double elapsedSeconds = (double)elapsed / SDL_GetPerformanceFrequency();
25+
m_timeCounter = (float)elapsedSeconds;
1626
}
1727

1828
void AnimationSystem::startHookThrow(Sprite& hook, int handX, int handY, int throwDuration) {

src/Audio.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,21 @@ Audio::~Audio() {
2626
}
2727
Mix_CloseAudio();
2828
}
29+
30+
double Audio::getMusicPositionMs() const {
31+
if (!m_valid || !bgMusic || Mix_PlayingMusic() == 0) {
32+
return 0.0;
33+
}
34+
35+
// Try to get precise audio position from SDL_mixer 2.8.0+
36+
double positionSeconds = Mix_GetMusicPosition(bgMusic);
37+
if (positionSeconds >= 0.0) {
38+
return positionSeconds * 1000.0; // Convert to milliseconds
39+
}
40+
41+
// Fallback: return -1 to indicate SDL_GetTicks should be used
42+
return -1.0;
43+
}
2944
void Audio::playBackgroundMusic(const std::string& filePath) {
3045
if (!m_valid) {
3146
Logger::error("Audio::playBackgroundMusic called on invalid Audio system");

src/RhythmGame.cpp

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ RhythmGame::RhythmGame()
1212
: m_resourceManager(nullptr)
1313
, m_gameStats(nullptr)
1414
, m_songStartTime(0)
15+
, m_lastFrameTime(0)
16+
, m_targetFrameTime(0)
1517
, m_ocean(0, 0, nullptr)
1618
, m_scoreLabel(0, 0, nullptr)
1719
, m_scoreNumber(0, 0, nullptr)
@@ -47,6 +49,10 @@ void RhythmGame::initialize(RenderWindow& window, ResourceManager& resourceManag
4749
m_songStartTime = SDL_GetTicks();
4850
m_noteHitFlags.assign(gameplayConfig.numBeats * 2, false);
4951

52+
// Initialize frame timing (60 FPS target)
53+
m_targetFrameTime = SDL_GetPerformanceFrequency() / 20;
54+
m_lastFrameTime = SDL_GetPerformanceCounter();
55+
5056
// Initialize animation system
5157
m_animationSystem.initialize();
5258

@@ -142,10 +148,11 @@ bool RhythmGame::update(InputAction action, InputHandler& inputHandler) {
142148
return false; // Game should end (ESC or window close)
143149
}
144150

145-
// Update animation timing
146-
m_animationSystem.updateTiming();
151+
// Update animation timing with current performance counter
152+
Uint64 currentPerformanceTime = SDL_GetPerformanceCounter();
153+
m_animationSystem.updateTiming(currentPerformanceTime);
147154

148-
double currentTime = SDL_GetTicks() - m_songStartTime;
155+
double currentTime = getCurrentGameTimeMs();
149156

150157
// Handle rhythm input - simplified logic
151158
if (action == InputAction::Select) {
@@ -170,8 +177,15 @@ bool RhythmGame::update(InputAction action, InputHandler& inputHandler) {
170177
return false;
171178
}
172179

173-
// Frame delay (only when no input events)
174-
SDL_Delay(visualConfig.frameDelay);
180+
// Maintain consistent frame rate (only when no input events)
181+
Uint64 currentFrameTime = SDL_GetPerformanceCounter();
182+
Uint64 frameTime = currentFrameTime - m_lastFrameTime;
183+
184+
if (frameTime < m_targetFrameTime) {
185+
Uint32 delayMs = (Uint32)((m_targetFrameTime - frameTime) * 1000 / SDL_GetPerformanceFrequency());
186+
SDL_Delay(delayMs);
187+
}
188+
m_lastFrameTime = SDL_GetPerformanceCounter();
175189
}
176190

177191
return true; // Continue game
@@ -286,7 +300,7 @@ void RhythmGame::updateFishMovement() {
286300
}
287301

288302
// Move fish left (same as original)
289-
m_fish[i].moveLeft(15);
303+
m_fish[i].moveLeft(10);
290304

291305
// Update base position after movement (for sway effects)
292306
m_fishBasePositions[i].first = m_fish[i].getX();
@@ -369,4 +383,15 @@ std::string RhythmGame::formatScore(int score) {
369383
std::ostringstream ss;
370384
ss << std::setw(6) << std::setfill('0') << score;
371385
return ss.str();
386+
}
387+
388+
double RhythmGame::getCurrentGameTimeMs() const {
389+
// Try to get precise audio position first
390+
double audioTimeMs = m_audioPlayer.getMusicPositionMs();
391+
if (audioTimeMs >= 0.0) {
392+
return audioTimeMs;
393+
}
394+
395+
// Fallback to SDL_GetTicks timing
396+
return SDL_GetTicks() - m_songStartTime;
372397
}

0 commit comments

Comments
 (0)