Added bottom bar labels. Delayed start of MIDI glyphs.

This commit is contained in:
Gordon Weeks 2026-01-31 03:28:02 -08:00
parent 0133fc9f14
commit a092de9d35

View File

@ -14,23 +14,27 @@ namespace
constexpr int LANE_COUNT = 12;
constexpr int MAX_INSTRUMENT_TYPES = 8;
constexpr int MAX_GAMEPADS = 4;
constexpr float HIT_WINDOW_PX = 80.0f;
constexpr float RECEPTOR_HEIGHT = 100.0f;
constexpr float RECEPTOR_HEIGHT = 150.0f;
constexpr float SCROLL_PX_PER_SEC = 350.0f;
constexpr float LEAD_OFFSET_SECONDS = 3.0f;
const int GAMEPAD_BUTTONS[LANE_COUNT] = {
GAMEPAD_BUTTON_LEFT_FACE_LEFT,
GAMEPAD_BUTTON_LEFT_FACE_UP,
GAMEPAD_BUTTON_LEFT_FACE_RIGHT,
GAMEPAD_BUTTON_LEFT_FACE_DOWN,
GAMEPAD_BUTTON_RIGHT_FACE_LEFT,
GAMEPAD_BUTTON_RIGHT_FACE_UP,
GAMEPAD_BUTTON_RIGHT_FACE_RIGHT,
GAMEPAD_BUTTON_RIGHT_FACE_DOWN,
GAMEPAD_BUTTON_LEFT_TRIGGER_2,
GAMEPAD_BUTTON_LEFT_TRIGGER_1,
GAMEPAD_BUTTON_RIGHT_TRIGGER_1,
GAMEPAD_BUTTON_RIGHT_TRIGGER_2,
GAMEPAD_BUTTON_LEFT_FACE_LEFT, // Left
GAMEPAD_BUTTON_LEFT_FACE_UP, // Up
GAMEPAD_BUTTON_LEFT_FACE_RIGHT, // Right
GAMEPAD_BUTTON_LEFT_FACE_DOWN, // Down
GAMEPAD_BUTTON_RIGHT_FACE_LEFT, // X
GAMEPAD_BUTTON_RIGHT_FACE_UP, // Y
GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, // B
GAMEPAD_BUTTON_RIGHT_FACE_DOWN, // A
GAMEPAD_BUTTON_LEFT_TRIGGER_2, // LT
GAMEPAD_BUTTON_LEFT_TRIGGER_1, // LB
GAMEPAD_BUTTON_RIGHT_TRIGGER_1, // RB
GAMEPAD_BUTTON_RIGHT_TRIGGER_2, // RT
};
const char* const GAMEPAD_BUTTON_LABELS[LANE_COUNT] = {
"<", "^", ">", "v", "X", "Y", "B", "A", "LT", "LB", "RB", "RT",
};
const int KEY_KEYS[LANE_COUNT] = {
@ -187,8 +191,10 @@ public:
std::vector<Glyph*> spawned;
std::unordered_set<Glyph*> completed_notes;
float song_time = 0.0f;
float chart_time_offset = 0.0f;
int score = 0;
int combo = 0;
float upper_bar_y = 0.0f;
float hit_line_y = 0.0f;
float lane_width = 0.0f;
float screen_width = 0.0f;
@ -222,6 +228,10 @@ public:
{
StopMusicStream(music);
PlayMusicStream(music);
if (chart_time_offset < 0.0f)
{
SeekMusicStream(music, -chart_time_offset);
}
}
}
@ -262,6 +272,7 @@ public:
font = font_manager->get_font("Roboto");
screen_width = static_cast<float>(GetScreenWidth());
screen_height = static_cast<float>(GetScreenHeight());
upper_bar_y = screen_height - RECEPTOR_HEIGHT;
hit_line_y = screen_height - RECEPTOR_HEIGHT / 2.0f;
lane_width = screen_width / LANE_COUNT;
for (int slot = 0; slot < MAX_INSTRUMENT_TYPES; slot++)
@ -277,6 +288,13 @@ public:
}
}
chart = load_chart(GHHB_CHART_PATH);
float first_note_time = 0.0f;
if (!chart.empty())
{
first_note_time = chart.front().time;
float lead_seconds = (hit_line_y - 60.0f) / SCROLL_PX_PER_SEC;
chart_time_offset = lead_seconds + LEAD_OFFSET_SECONDS - first_note_time;
}
background = add_game_object<Background>();
background->add_tag("background");
}
@ -318,10 +336,15 @@ public:
return false;
}
float glyph_y(const Glyph& n) const
{
return hit_line_y - (n.time + chart_time_offset - song_time) * SCROLL_PX_PER_SEC;
}
bool is_note_hittable(const Glyph& n) const
{
float y = n.y_position(song_time, hit_line_y);
return fabsf(y - hit_line_y) <= HIT_WINDOW_PX;
float y = glyph_y(n);
return y >= upper_bar_y && y <= hit_line_y;
}
void consume_note(Glyph* n)
@ -363,12 +386,13 @@ public:
float last_note_time = 0.0f;
for (const auto& n : chart)
{
if (n.time > last_note_time)
float t = n.time + chart_time_offset;
if (t > last_note_time)
{
last_note_time = n.time;
last_note_time = t;
}
}
float note_exit_seconds = HIT_WINDOW_PX / SCROLL_PX_PER_SEC;
float note_exit_seconds = (hit_line_y - upper_bar_y) / SCROLL_PX_PER_SEC;
if (song_time >= last_note_time + note_exit_seconds + RESULTS_DELAY_AFTER_LAST_NOTE)
{
game_ended = true;
@ -384,7 +408,7 @@ public:
}
bool already = std::find_if(spawned.begin(), spawned.end(),
[&n](Glyph* p) { return p == &n; }) != spawned.end();
if (!already && song_time >= n.time - lead_seconds)
if (!already && song_time >= n.time + chart_time_offset - lead_seconds)
{
spawned.push_back(&n);
}
@ -393,7 +417,7 @@ public:
for (auto it = spawned.begin(); it != spawned.end();)
{
Glyph* n = *it;
float y = n->y_position(song_time, hit_line_y);
float y = glyph_y(*n);
if (y > hit_line_y)
{
miss_flash_timer[n->lane] = MISS_FLASH_DURATION;
@ -443,7 +467,7 @@ public:
{
continue;
}
float y = n->y_position(song_time, hit_line_y);
float y = glyph_y(*n);
float d = fabsf(y - hit_line_y);
if (d < best_dist)
{
@ -483,9 +507,8 @@ public:
DrawLineEx(Vector2{cx, 0}, Vector2{cx, screen_height}, 2.0f, Color{70, 70, 90, 255});
}
float receptor_top = screen_height - RECEPTOR_HEIGHT;
DrawRectangle(0,
static_cast<int>(receptor_top),
static_cast<int>(upper_bar_y),
static_cast<int>(screen_width),
static_cast<int>(RECEPTOR_HEIGHT),
Color{60, 60, 100, 255});
@ -495,7 +518,7 @@ public:
{
float alpha = 180.0f * (hit_flash_timer[lane] / PRESS_FLASH_DURATION);
DrawRectangle(static_cast<int>(lane * lane_width),
static_cast<int>(receptor_top),
static_cast<int>(upper_bar_y),
static_cast<int>(lane_width),
static_cast<int>(RECEPTOR_HEIGHT),
Color{80, 255, 120, static_cast<unsigned char>(alpha)});
@ -504,7 +527,7 @@ public:
{
float alpha = 180.0f * (press_flash_timer[lane] / PRESS_FLASH_DURATION);
DrawRectangle(static_cast<int>(lane * lane_width),
static_cast<int>(receptor_top),
static_cast<int>(upper_bar_y),
static_cast<int>(lane_width),
static_cast<int>(RECEPTOR_HEIGHT),
Color{255, 255, 255, static_cast<unsigned char>(alpha)});
@ -513,17 +536,30 @@ public:
{
float alpha = 200.0f * (miss_flash_timer[lane] / MISS_FLASH_DURATION);
DrawRectangle(static_cast<int>(lane * lane_width),
static_cast<int>(receptor_top),
static_cast<int>(upper_bar_y),
static_cast<int>(lane_width),
static_cast<int>(RECEPTOR_HEIGHT),
Color{255, 80, 80, static_cast<unsigned char>(alpha)});
}
}
DrawLineEx(Vector2{0, upper_bar_y}, Vector2{screen_width, upper_bar_y}, 3.0f, WHITE);
DrawLineEx(Vector2{0, hit_line_y}, Vector2{screen_width, hit_line_y}, 3.0f, WHITE);
const float button_label_font_size = 28.0f;
const float button_label_y = hit_line_y + 18.0f;
for (int lane = 0; lane < LANE_COUNT; lane++)
{
const char* label = GAMEPAD_BUTTON_LABELS[lane];
float label_w = MeasureTextEx(font, label, button_label_font_size, 1).x;
float cx = lane_center_x(lane);
DrawTextEx(font, label,
{cx - label_w / 2.0f, button_label_y},
button_label_font_size, 1, Color{220, 220, 240, 255});
}
for (Glyph* n : spawned)
{
float y = n->y_position(song_time, hit_line_y);
float y = glyph_y(*n);
if (y < -40.0f || y > screen_height + 40.0f)
{
continue;
@ -539,8 +575,8 @@ public:
std::string score_text = "Score: " + std::to_string(score) + " Combo: " + std::to_string(combo);
DrawTextEx(font, score_text.c_str(), {20, 16}, 28, 1, WHITE);
DrawTextEx(font, "Arrows / D-pad / X Y A B / LB LT RB RT: hit when note reaches white line",
{20, receptor_top - 28}, 18, 1, Color{200, 200, 200, 255});
DrawTextEx(font, "Arrows / D-pad / X Y A B / LB LT RB RT: hit when note is between the two white lines",
{20, upper_bar_y - 28}, 18, 1, Color{200, 200, 200, 255});
if (game_ended)
{