fix the slider ball not moving correctly

This commit is contained in:
Michael Zhang 2021-01-08 07:34:30 -06:00
parent 69fd5a452a
commit aef8e2e6c6
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
6 changed files with 105 additions and 32 deletions

2
Cargo.lock generated
View file

@ -1038,7 +1038,7 @@ checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
[[package]] [[package]]
name = "libosu" name = "libosu"
version = "0.0.12" version = "0.0.12"
source = "git+https://github.com/iptq/libosu?rev=fa82d6b167c648ad3f68efa3005aa8f7af12d943#fa82d6b167c648ad3f68efa3005aa8f7af12d943" source = "git+https://github.com/iptq/libosu?rev=d75fe384e95156b3b40dd9e5ca7af539e48af8fa#d75fe384e95156b3b40dd9e5ca7af539e48af8fa"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitflags", "bitflags",

View file

@ -20,4 +20,4 @@ ordered-float = "2.0.1"
[dependencies.libosu] [dependencies.libosu]
git = "https://github.com/iptq/libosu" git = "https://github.com/iptq/libosu"
rev = "fa82d6b167c648ad3f68efa3005aa8f7af12d943" rev = "d75fe384e95156b3b40dd9e5ca7af539e48af8fa"

View file

@ -10,11 +10,13 @@ use ggez::{
self, Color, DrawMode, DrawParam, FillOptions, FilterMode, Mesh, Rect, StrokeOptions, Text, self, Color, DrawMode, DrawParam, FillOptions, FilterMode, Mesh, Rect, StrokeOptions, Text,
WHITE, WHITE,
}, },
nalgebra::Point2,
Context, GameError, GameResult, Context, GameError, GameResult,
}; };
use libosu::{Beatmap, HitObject, HitObjectKind, Point, SpinnerInfo}; use libosu::{Beatmap, HitObject, HitObjectKind, Point, SpinnerInfo};
use crate::audio::{AudioEngine, Sound}; use crate::audio::{AudioEngine, Sound};
use crate::skin::Skin;
use crate::slider_render::{render_slider, Spline}; use crate::slider_render::{render_slider, Spline};
pub type SliderCache = HashMap<Vec<Point<i32>>, Spline>; pub type SliderCache = HashMap<Vec<Point<i32>>, Spline>;
@ -25,6 +27,7 @@ pub struct Game {
song: Option<Sound>, song: Option<Sound>,
beatmap: Beatmap, beatmap: Beatmap,
hit_objects: Vec<HitObject>, hit_objects: Vec<HitObject>,
skin: Skin,
slider_cache: SliderCache, slider_cache: SliderCache,
} }
@ -34,6 +37,7 @@ impl Game {
let audio_engine = AudioEngine::new()?; let audio_engine = AudioEngine::new()?;
let beatmap = Beatmap::default(); let beatmap = Beatmap::default();
let hit_objects = Vec::new(); let hit_objects = Vec::new();
let skin = Skin::new();
Ok(Game { Ok(Game {
is_playing: false, is_playing: false,
@ -41,6 +45,7 @@ impl Game {
beatmap, beatmap,
hit_objects, hit_objects,
song: None, song: None,
skin,
slider_cache: SliderCache::default(), slider_cache: SliderCache::default(),
}) })
} }
@ -56,7 +61,7 @@ impl Game {
let dir = path.parent().unwrap(); let dir = path.parent().unwrap();
let song = Sound::create(dir.join(&self.beatmap.audio_filename))?; let song = Sound::create(dir.join(&self.beatmap.audio_filename))?;
song.set_position(113.0)?; song.set_position(36.5)?;
self.song = Some(song); self.song = Some(song);
Ok(()) Ok(())
@ -74,14 +79,14 @@ impl Game {
fn priv_draw(&mut self, ctx: &mut Context) -> Result<()> { fn priv_draw(&mut self, ctx: &mut Context) -> Result<()> {
// TODO: lol // TODO: lol
const EDITOR_SCREEN: Rect = Rect::new(112.0, 84.0, 800.0, 600.0); const PLAYFIELD_BOUNDS: Rect = Rect::new(112.0, 112.0, 800.0, 600.0);
graphics::clear(ctx, [0.0, 0.0, 0.0, 1.0].into()); graphics::clear(ctx, [0.0, 0.0, 0.0, 1.0].into());
let playfield = Mesh::new_rectangle( let playfield = Mesh::new_rectangle(
ctx, ctx,
DrawMode::Stroke(StrokeOptions::default()), DrawMode::Stroke(StrokeOptions::default()),
EDITOR_SCREEN, PLAYFIELD_BOUNDS,
Color::new(1.0, 1.0, 1.0, 0.5), Color::new(1.0, 1.0, 1.0, 0.5),
)?; )?;
graphics::draw(ctx, &playfield, DrawParam::default())?; graphics::draw(ctx, &playfield, DrawParam::default())?;
@ -128,9 +133,9 @@ impl Game {
} }
} }
let cs_scale = EDITOR_SCREEN.w / 640.0; let cs_scale = PLAYFIELD_BOUNDS.w / 640.0;
let osupx_scale_x = EDITOR_SCREEN.w / 512.0; let osupx_scale_x = PLAYFIELD_BOUNDS.w / 512.0;
let osupx_scale_y = EDITOR_SCREEN.h / 384.0; let osupx_scale_y = PLAYFIELD_BOUNDS.h / 384.0;
let cs_osupx = self.beatmap.difficulty.circle_size_osupx(); let cs_osupx = self.beatmap.difficulty.circle_size_osupx();
let cs_real = cs_osupx * cs_scale; let cs_real = cs_osupx * cs_scale;
@ -138,8 +143,8 @@ impl Game {
let ho = draw_info.hit_object; let ho = draw_info.hit_object;
let ho_time = (ho.start_time.0 as f64) / 1000.0; let ho_time = (ho.start_time.0 as f64) / 1000.0;
let pos = [ let pos = [
EDITOR_SCREEN.x + osupx_scale_x * ho.pos.0 as f32, PLAYFIELD_BOUNDS.x + osupx_scale_x * ho.pos.0 as f32,
EDITOR_SCREEN.y + osupx_scale_y * ho.pos.1 as f32, PLAYFIELD_BOUNDS.y + osupx_scale_y * ho.pos.1 as f32,
]; ];
let color = graphics::Color::new(1.0, 1.0, 1.0, draw_info.opacity as f32); let color = graphics::Color::new(1.0, 1.0, 1.0, draw_info.opacity as f32);
@ -148,7 +153,7 @@ impl Game {
let spline = render_slider( let spline = render_slider(
&mut self.slider_cache, &mut self.slider_cache,
ctx, ctx,
EDITOR_SCREEN, PLAYFIELD_BOUNDS,
&self.beatmap, &self.beatmap,
ho, ho,
color, color,
@ -166,10 +171,11 @@ impl Game {
travel_percent = 1.0 - travel_percent; travel_percent = 1.0 - travel_percent;
} }
let travel_length = travel_percent * info.pixel_length; let travel_length = travel_percent * info.pixel_length;
print!("ho={:.3} ", ho_time);
let pos = spline.point_at_length(travel_length); let pos = spline.point_at_length(travel_length);
let ball_pos = [ let ball_pos = [
EDITOR_SCREEN.x + osupx_scale_x * pos.0 as f32, PLAYFIELD_BOUNDS.x + osupx_scale_x * pos.0 as f32,
EDITOR_SCREEN.y + osupx_scale_y * pos.1 as f32, PLAYFIELD_BOUNDS.y + osupx_scale_y * pos.1 as f32,
]; ];
let ball = Mesh::new_circle( let ball = Mesh::new_circle(
ctx, ctx,
@ -183,28 +189,26 @@ impl Game {
} }
} }
let circ = Mesh::new_circle( self.skin.hitcircle.draw(
ctx, ctx,
DrawMode::Fill(FillOptions::default()), (cs_real * 2.0, cs_real * 2.0),
pos, DrawParam::default().dest(pos).color(color),
cs_real, )?;
1.0,
color, self.skin.hitcircleoverlay.draw(
ctx,
(cs_real * 2.0, cs_real * 2.0),
DrawParam::default().dest(pos).color(color),
)?; )?;
graphics::draw(ctx, &circ, DrawParam::default())?;
if time < ho_time { if time < ho_time {
let time_diff = ho_time - time; let time_diff = ho_time - time;
let approach_r = cs_real * (1.0 + 2.0 * time_diff as f32 / 0.75); let approach_r = cs_real * (1.0 + 2.0 * time_diff as f32 / 0.75);
let approach = Mesh::new_circle( self.skin.approachcircle.draw(
ctx, ctx,
DrawMode::Stroke(StrokeOptions::default().with_line_width(2.0)), (approach_r * 2.0, approach_r * 2.0),
pos, DrawParam::default().dest(pos).color(color),
approach_r,
1.0,
WHITE,
)?; )?;
graphics::draw(ctx, &approach, DrawParam::default())?;
} }
} }

View file

@ -30,7 +30,8 @@ fn main() -> Result<()> {
.init() .init()
.unwrap(); .unwrap();
let cb = ContextBuilder::new("super_simple", "ggez") let cb = ContextBuilder::new("osu_editor", "ggez")
.add_resource_path("skin")
.window_setup(WindowSetup::default().title("OSU editor")) .window_setup(WindowSetup::default().title("OSU editor"))
.window_mode(WindowMode::default().dimensions(1024.0, 768.0)); .window_mode(WindowMode::default().dimensions(1024.0, 768.0));

View file

@ -1 +1,67 @@
pub struct Skin {} use std::path::{Path, PathBuf};
use anyhow::Result;
use ggez::{
graphics::{self, DrawParam, Image},
nalgebra::Point2,
Context,
};
macro_rules! create_skin {
($([$name:ident]),* $(,)?) => {
pub struct Skin {
$(
pub $name: Texture,
)*
}
impl Skin {
pub fn new() -> Self {
Skin {
$($name: Texture::with_path(concat!("/", stringify!($name), ".png")),)*
}
}
}
}
}
create_skin! {
[approachcircle],
[hitcircle],
[hitcircleoverlay],
}
pub struct Texture {
path: PathBuf,
image: Option<Image>,
}
impl Texture {
pub fn with_path(path: impl AsRef<Path>) -> Self {
Texture {
path: path.as_ref().to_path_buf(),
image: None,
}
}
pub fn draw(&mut self, ctx: &mut Context, size: (f32, f32), param: DrawParam) -> Result<()> {
let image = if self.image.is_some() {
self.image.as_ref().unwrap()
} else {
self.image = Some(Image::new(ctx, &self.path)?);
self.image.as_ref().unwrap()
};
let random_constant = 1.35;
let x_scale = random_constant * size.0 / image.width() as f32;
let y_scale = random_constant * size.1 / image.height() as f32;
graphics::draw(
ctx,
image,
param
.scale([x_scale, y_scale])
.offset(Point2::new(0.5, 0.5)),
)?;
Ok(())
}
}

View file

@ -230,8 +230,6 @@ impl Spline {
let n = self.spline_points.len() - 1; let n = self.spline_points.len() - 1;
if idx == 0 && self.spline_points.len() > 2 { if idx == 0 && self.spline_points.len() > 2 {
return self.spline_points[0]; return self.spline_points[0];
} else if idx >= n {
return self.spline_points[n];
} }
let (len1, len2) = ( let (len1, len2) = (
@ -240,7 +238,11 @@ impl Spline {
); );
let proportion = (length - len1) / (len2 - len1); let proportion = (length - len1) / (len2 - len1);
let (p1, p2) = (self.spline_points[idx], self.spline_points[idx + 1]); let (p1, p2) = (self.spline_points[idx - 1], self.spline_points[idx]);
// println!(
// "len={:.3} idx={} len1={:.3} len2={:.3} prop={:.3} p1={:.3} p2={:.3}",
// length, idx, len1, len2, proportion, p1, p2
// );
assert!(p1 != p2); assert!(p1 != p2);
(p2 - p1) * proportion + p1 (p2 - p1) * proportion + p1
} }