Skip to content

Commit

Permalink
merge: add beat guidelines
Browse files Browse the repository at this point in the history
  • Loading branch information
LiteHell authored Jun 10, 2024
2 parents f9a28dc + 7ebac75 commit fb50f95
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 8 deletions.
1 change: 0 additions & 1 deletion game/src/game/display_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use std::path::Path;
use sdl2::{
event::Event,
keyboard::Keycode,
pixels::Color,
rect::Rect,
render::{Canvas, TextureQuery},
video::Window,
Expand Down
49 changes: 48 additions & 1 deletion game/src/game/game_player/chart_player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use bidrum_data_struct_lib::{
janggu::JangguFace,
song::{GameChart, GameNote},
};
use num_rational::Rational64;
use sdl2::{render::Canvas, video::Window};

use crate::constants::{ACCURACY_DISPLAY_DURATION, DEFAULT_BPM};
Expand All @@ -11,7 +12,9 @@ use crate::game::game_player::{
};

use super::{
chart_player_ui::{disappearing_note_effect::DisapearingNoteEffect, ChartPlayerUI},
chart_player_ui::{
disappearing_note_effect::DisapearingNoteEffect, BeatGuideline, ChartPlayerUI,
},
game_result::GameResult,
janggu_state_with_tick::JangguStateWithTick,
timing_judge::TimingJudge,
Expand Down Expand Up @@ -152,6 +155,49 @@ impl ChartPlayer<'_> {
display_notes
}

fn beat_guideline(&self, tick: i128) -> Option<BeatGuideline> {
if tick < 0 {
return None;
}

// bpm = beat / minute
// minute-per-beat = 1 / bpm
// timing-in-minute = beat * minute-per-beat
// timing-in-millisecond = timing-in-minute (minute) * ( 60000(millisecond) / 1(minute) )
// timing = timing-in-millisecond
let timing_of_one_beat = Rational64::new(60000, self.chart.bpm as i64);

// beat_per_millisecond = (display_bpm / 60000)
// millisecond_per_beat = 1/ beat_per_millisecond
// speed = 1 / millisecond_per_beat
let speed_ratio = Rational64::new((self.chart.bpm * DEFAULT_BPM) as i64, 60000);

let length_ratio = timing_of_one_beat * speed_ratio;
let length = *length_ratio.numer() as f64 / *length_ratio.denom() as f64;

let even_beat =
((*timing_of_one_beat.denom() * tick as i64) / *timing_of_one_beat.numer()) % 2 == 0;

let position = {
//position_ratio = tick % timing_of_one_beat as i128;
let mut position_ratio = Rational64::new(
(*timing_of_one_beat.denom() * tick as i64) % *timing_of_one_beat.numer(),
*timing_of_one_beat.denom(),
);

position_ratio = length_ratio - position_ratio;
position_ratio = position_ratio / timing_of_one_beat;
position_ratio *= length_ratio;
*position_ratio.numer() as f64 / *position_ratio.denom() as f64
};

Some(BeatGuideline {
length,
position,
even_beat,
})
}

pub fn draw(
&mut self,
tick: i128,
Expand Down Expand Up @@ -180,6 +226,7 @@ impl ChartPlayer<'_> {
self.ui.disappearing_note_effects.update_base_tick(tick);
self.ui.input_effect.update(janggu_state_with_tick, tick);
self.ui.notes = self.get_display_notes(tick as u64);
self.ui.beat_guideline = self.beat_guideline(tick);
}
self.ui.overall_effect_tick = overall_tick;
self.ui.draw(canvas);
Expand Down
66 changes: 60 additions & 6 deletions game/src/game/game_player/chart_player_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ use self::{

use super::timing_judge::NoteAccuracy;

pub struct BeatGuideline {
pub position: f64,
pub length: f64,
pub even_beat: bool,
}

pub struct ChartPlayerUI<'a> {
pub notes: Vec<DisplayedSongNote>,
pub accuracy: Option<NoteAccuracy>,
Expand All @@ -37,6 +43,7 @@ pub struct ChartPlayerUI<'a> {
pub input_effect: InputEffect,
pub overall_effect_tick: u128,
pub disappearing_note_effects: DisapearingNoteEffect,
pub beat_guideline: Option<BeatGuideline>,
resources: ChartPlayerUIResources<'a>,
}

Expand All @@ -56,6 +63,7 @@ impl ChartPlayerUI<'_> {
input_effect: InputEffect::new(),
overall_effect_tick: 0,
disappearing_note_effects: DisapearingNoteEffect::new(),
beat_guideline: None,
resources: resources,
};
}
Expand Down Expand Up @@ -140,10 +148,11 @@ impl ChartPlayerUI<'_> {
let background_width = (viewport.width() - janggu_width_min) / 2;
let background_y =
(canvas.viewport().height() as i32 - (background_height_without_border as i32)) / 2;
for background_x in [
let background_x = [
0, /* x coordinate of left background */
background_width as i32 + janggu_width_min as i32, /* x coordinate of right background */
] {
];
for background_x in background_x {
let background_alpha = {
// is the face hitted?
let hitting = if background_x == 0 {
Expand Down Expand Up @@ -203,7 +212,7 @@ impl ChartPlayerUI<'_> {
.unwrap();
}

// draw judgement line
// judgement line info / max note width is required for beat guideline
let judgement_line_height = max_stick_note_height;
let judgement_line_padding_px = 20;
let judgement_line_width = ((judgement_line_texture.query().width as f32
Expand All @@ -214,6 +223,54 @@ impl ChartPlayerUI<'_> {
background_width as i32 - judgement_line_width as i32 - judgement_line_padding_px, /* left judgement line */
background_width as i32 + janggu_width_min as i32 + judgement_line_padding_px, /* right judgement line */
];
let note_width_max = std::cmp::max(left_stick_note_width, right_stick_note_width);

// draw beat guideline
if let Some(beat_guideline) = &self.beat_guideline {
let mut position = beat_guideline.position % beat_guideline.length;
while position < 0.0 {
position += beat_guideline.length;
}
let thicknesses: [u32; 2] = [4, 4];
let mut white = beat_guideline.even_beat;
loop {
let distance_between_centers = (position * note_width_max as f64) as i32;
let thickness = thicknesses[if white { 0 } else { 1 }];
let color = if white {
Color::RGBA(255, 255, 255, 60)
} else {
Color::RGBA(255, 255, 255, 30)
};

if distance_between_centers
> (background_width + (judgement_line_width + thickness) / 2) as i32
{
break;
}
for line_x in [
judgement_line_xposes[0] - distance_between_centers
+ (judgement_line_width / 2 + thickness / 2) as i32,
judgement_line_xposes[1]
+ distance_between_centers
+ (judgement_line_width / 2 - thickness / 2) as i32,
] {
canvas.set_draw_color(color);
canvas
.fill_rect(Rect::new(
line_x,
background_y,
thickness,
background_height_without_border,
))
.unwrap();
}

position += beat_guideline.length;
white = !white;
}
}

// draw judgement line
for judgement_line_xpos in judgement_line_xposes {
canvas
.copy(
Expand All @@ -229,9 +286,6 @@ impl ChartPlayerUI<'_> {
.unwrap();
}

// load textures for the notes and accuracy
let note_width_max = std::cmp::max(left_stick_note_width, right_stick_note_width);

// draw note
let mut draw_note = |i: &DisplayedSongNote, disappearing_effect: Option<f32>| {
let note_texture = match i.stick {
Expand Down

0 comments on commit fb50f95

Please sign in to comment.