Skip to content

Commit

Permalink
HitBox/Collision Improvements (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
Cleptomania authored Mar 3, 2024
1 parent aa75d1c commit 1b1da04
Show file tree
Hide file tree
Showing 2 changed files with 279 additions and 178 deletions.
301 changes: 224 additions & 77 deletions src/hitbox.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
use pyo3::prelude::*;

pub trait NativeAdjustedPoints {
fn get_adjusted_points_native(&mut self) -> &Vec<(f32, f32)>;
}

#[derive(Clone)]
#[pyclass(subclass, module = "arcade.hitbox.base")]
#[pyclass(module = "arcade.hitbox.base")]
pub struct HitBox {
#[pyo3(get, set)]
pub points: Vec<(f32, f32)>,
#[pyo3(get, set)]
#[pyo3(get)]
pub position: (f32, f32),
#[pyo3(get, set)]
#[pyo3(get)]
pub scale: (f32, f32),
pub angle: f32,

pub adjusted_cache: Vec<(f32, f32)>,
pub cache_dirty: bool,
}

#[pymethods]
Expand All @@ -27,6 +34,8 @@ impl HitBox {
position: final_position,
scale: final_scale,
angle: 0.0,
adjusted_cache: vec![],
cache_dirty: true,
}
}

Expand All @@ -48,90 +57,93 @@ impl HitBox {
Ok(adjustable)
}

fn get_adjusted_points(self_: PyRef<'_, Self>) -> Vec<(f32, f32)> {
let mut new_points: Vec<(f32, f32)> = Vec::with_capacity(self_.points.len());

for point in self_.points.iter() {
let x = (point.0 * self_.scale.0) + self_.position.0;
let y = (point.1 * self_.scale.1) + self_.position.1;
new_points.push((x, y));
pub fn get_adjusted_points(&mut self) -> Vec<(f32, f32)> {
if self.cache_dirty {
self.adjusted_cache = Vec::with_capacity(self.points.len());
for point in self.points.iter() {
let x = (point.0 * self.scale.0) + self.position.0;
let y = (point.1 * self.scale.1) + self.position.1;
self.adjusted_cache.push((x, y));
}
self.cache_dirty = false;
}

new_points
self.adjusted_cache.to_vec()
}

#[setter]
pub fn set_position(&mut self, value: (f32, f32)) -> PyResult<()> {
self.position = value;
self.cache_dirty = true;
Ok(())
}

#[setter]
pub fn set_scale(&mut self, value: (f32, f32)) -> PyResult<()> {
self.scale = value;
self.cache_dirty = true;
Ok(())
}

#[getter]
pub fn left(self_: PyRef<'_, Self>) -> PyResult<f32> {
let mut converted = HitBox::get_adjusted_points(self_);
pub fn left(&mut self) -> PyResult<f32> {
let mut converted: Vec<(f32, f32)> = self.get_adjusted_points_native().to_vec();
converted.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
Ok(converted[0].0)
}

#[getter]
pub fn right(self_: PyRef<'_, Self>) -> PyResult<f32> {
let mut converted: Vec<(f32, f32)> = HitBox::get_adjusted_points(self_);
pub fn right(&mut self) -> PyResult<f32> {
let mut converted: Vec<(f32, f32)> = self.get_adjusted_points_native().to_vec();
converted.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
Ok(converted[0].0)
}

#[getter]
pub fn bottom(self_: PyRef<'_, Self>) -> PyResult<f32> {
let mut converted: Vec<(f32, f32)> = HitBox::get_adjusted_points(self_);
pub fn bottom(&mut self) -> PyResult<f32> {
let mut converted: Vec<(f32, f32)> = self.get_adjusted_points_native().to_vec();
converted.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
Ok(converted[0].1)
}

#[getter]
pub fn top(self_: PyRef<'_, Self>) -> PyResult<f32> {
let mut converted: Vec<(f32, f32)> = HitBox::get_adjusted_points(self_);
pub fn top(&mut self) -> PyResult<f32> {
let mut converted: Vec<(f32, f32)> = self.get_adjusted_points_native().to_vec();
converted.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
Ok(converted[0].1)
}
}

impl HitBox {
pub fn get_adjusted_points_native(&self) -> Vec<(f32, f32)> {
let mut new_points: Vec<(f32, f32)> = Vec::with_capacity(self.points.len());

for point in self.points.iter() {
let x = (point.0 * self.scale.0) + self.position.0;
let y = (point.1 * self.scale.1) + self.position.1;
new_points.push((x, y));
impl NativeAdjustedPoints for HitBox {
fn get_adjusted_points_native(&mut self) -> &Vec<(f32, f32)> {
if self.cache_dirty {
self.adjusted_cache = Vec::with_capacity(self.points.len());
for point in self.points.iter() {
let x = (point.0 * self.scale.0) + self.position.0;
let y = (point.1 * self.scale.1) + self.position.1;
self.adjusted_cache.push((x, y));
}
self.cache_dirty = false;
}

new_points
}

pub fn left_native(&self) -> f32 {
let mut converted: Vec<(f32, f32)> = self.get_adjusted_points_native();
converted.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
converted[0].0
}

pub fn right_native(&self) -> f32 {
let mut converted: Vec<(f32, f32)> = self.get_adjusted_points_native();
converted.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
converted[0].0
}

pub fn bottom_native(&self) -> f32 {
let mut converted: Vec<(f32, f32)> = self.get_adjusted_points_native();
converted.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
converted[0].1
}

pub fn top_native(&self) -> f32 {
let mut converted: Vec<(f32, f32)> = self.get_adjusted_points_native();
converted.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
converted[0].1
&self.adjusted_cache
}
}

#[derive(Clone)]
#[pyclass(extends=HitBox, module = "arcade.hitbox.base")]
#[pyclass(module = "arcade.hitbox.base")]
pub struct RotatableHitBox {
#[pyo3(get, set)]
angle: f32,
pub points: Vec<(f32, f32)>,
#[pyo3(get)]
pub position: (f32, f32),
#[pyo3(get)]
pub scale: (f32, f32),
#[pyo3(get)]
pub angle: f32,

pub adjusted_cache: Vec<(f32, f32)>,
pub cache_dirty: bool,
}

#[pymethods]
Expand All @@ -142,59 +154,194 @@ impl RotatableHitBox {
position: Option<(f32, f32)>,
scale: Option<(f32, f32)>,
angle: Option<f32>,
) -> (Self, HitBox) {
) -> RotatableHitBox {
let final_position = position.unwrap_or((0.0, 0.0));
let final_scale = scale.unwrap_or((1.0, 1.0));
let final_angle = angle.unwrap_or(0.0);
(
RotatableHitBox { angle: final_angle },
HitBox::new(points, position, scale),
RotatableHitBox {
points,
position: final_position,
scale: final_scale,
angle: final_angle,
adjusted_cache: vec![],
cache_dirty: true,
}
}

fn create_rotatable(
self_: PyRef<'_, Self>,
py: Python<'_>,
angle: Option<f32>,
) -> PyResult<Py<RotatableHitBox>> {
let adjustable: Py<RotatableHitBox> = Py::new(
py,
RotatableHitBox::new(
self_.points.to_vec(),
Some(self_.position),
Some(self_.scale),
angle,
),
)
.unwrap();
Ok(adjustable)
}

pub fn get_adjusted_points(self_: PyRef<'_, Self>) -> Vec<(f32, f32)> {
let super_: &HitBox = self_.as_ref();
let mut new_points: Vec<(f32, f32)> = Vec::with_capacity(super_.points.len());
pub fn get_adjusted_points(&mut self) -> Vec<(f32, f32)> {
if self.cache_dirty {
self.adjusted_cache = Vec::with_capacity(self.points.len());

let rad = self_.angle.to_radians();
let rad_cos = rad.cos();
let rad_sin = rad.sin();
for point in super_.points.iter() {
let x = ((point.0 * rad_cos + point.1 * rad_sin) * super_.scale.0) + super_.position.0;
let y = ((-point.0 * rad_sin + point.1 * rad_cos) * super_.scale.1) + super_.position.1;
new_points.push((x, y));
let rad = self.angle.to_radians();
let rad_cos = rad.cos();
let rad_sin = rad.sin();
for point in self.points.iter() {
let x = ((point.0 * rad_cos + point.1 * rad_sin) * self.scale.0) + self.position.0;
let y = ((-point.0 * rad_sin + point.1 * rad_cos) * self.scale.1) + self.position.1;
self.adjusted_cache.push((x, y));
}
self.cache_dirty = false;
}

new_points
self.adjusted_cache.to_vec()
}

#[setter]
pub fn set_position(&mut self, value: (f32, f32)) -> PyResult<()> {
self.position = value;
self.cache_dirty = true;
Ok(())
}

#[setter]
pub fn set_scale(&mut self, value: (f32, f32)) -> PyResult<()> {
self.scale = value;
self.cache_dirty = true;
Ok(())
}

#[setter]
pub fn set_angle(&mut self, value: f32) -> PyResult<()> {
self.angle = value;
self.cache_dirty = true;
Ok(())
}

#[getter]
fn left(self_: PyRef<'_, Self>) -> PyResult<f32> {
let mut converted: Vec<(f32, f32)> = RotatableHitBox::get_adjusted_points(self_);
pub fn left(&mut self) -> PyResult<f32> {
let mut converted: Vec<(f32, f32)> = self.get_adjusted_points_native().to_vec();
converted.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
Ok(converted[0].0)
}

#[getter]
fn right(self_: PyRef<'_, Self>) -> PyResult<f32> {
let mut converted: Vec<(f32, f32)> = RotatableHitBox::get_adjusted_points(self_);
pub fn right(&mut self) -> PyResult<f32> {
let mut converted: Vec<(f32, f32)> = self.get_adjusted_points_native().to_vec();
converted.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
Ok(converted[0].0)
}

#[getter]
fn bottom(self_: PyRef<'_, Self>) -> PyResult<f32> {
let mut converted: Vec<(f32, f32)> = RotatableHitBox::get_adjusted_points(self_);
pub fn bottom(&mut self) -> PyResult<f32> {
let mut converted: Vec<(f32, f32)> = self.get_adjusted_points_native().to_vec();
converted.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
Ok(converted[0].1)
}

#[getter]
fn top(self_: PyRef<'_, Self>) -> PyResult<f32> {
let mut converted: Vec<(f32, f32)> = RotatableHitBox::get_adjusted_points(self_);
pub fn top(&mut self) -> PyResult<f32> {
let mut converted: Vec<(f32, f32)> = self.get_adjusted_points_native().to_vec();
converted.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
Ok(converted[0].1)
}
}

impl NativeAdjustedPoints for RotatableHitBox {
fn get_adjusted_points_native(&mut self) -> &Vec<(f32, f32)> {
if self.cache_dirty {
self.adjusted_cache = Vec::with_capacity(self.points.len());

let rad = self.angle.to_radians();
let rad_cos = rad.cos();
let rad_sin = rad.sin();
for point in self.points.iter() {
let x = ((point.0 * rad_cos + point.1 * rad_sin) * self.scale.0) + self.position.0;
let y = ((-point.0 * rad_sin + point.1 * rad_cos) * self.scale.1) + self.position.1;
self.adjusted_cache.push((x, y));
}
self.cache_dirty = false;
}

&self.adjusted_cache
}
}

// #[derive(Clone)]
// #[pyclass(extends=HitBox, module = "arcade.hitbox.base")]
// pub struct RotatableHitBox {
// #[pyo3(get, set)]
// angle: f32,
// }

// #[pymethods]
// impl RotatableHitBox {
// #[new]
// fn new(
// points: Vec<(f32, f32)>,
// position: Option<(f32, f32)>,
// scale: Option<(f32, f32)>,
// angle: Option<f32>,
// ) -> (Self, HitBox) {
// let final_angle = angle.unwrap_or(0.0);
// (
// RotatableHitBox { angle: final_angle },
// HitBox::new(points, position, scale),
// )
// }

// pub fn get_adjusted_points(self_: PyRef<'_, Self>) -> Vec<(f32, f32)> {
// let super_: &HitBox = self_.as_ref();
// let mut new_points: Vec<(f32, f32)> = Vec::with_capacity(super_.points.len());

// let rad = self_.angle.to_radians();
// let rad_cos = rad.cos();
// let rad_sin = rad.sin();
// for point in super_.points.iter() {
// let x = ((point.0 * rad_cos + point.1 * rad_sin) * super_.scale.0) + super_.position.0;
// let y = ((-point.0 * rad_sin + point.1 * rad_cos) * super_.scale.1) + super_.position.1;
// new_points.push((x, y));
// }

// new_points
// }

// #[getter]
// fn left(self_: PyRef<'_, Self>) -> PyResult<f32> {
// let mut converted: Vec<(f32, f32)> = RotatableHitBox::get_adjusted_points(self_);
// converted.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
// Ok(converted[0].0)
// }

// #[getter]
// fn right(self_: PyRef<'_, Self>) -> PyResult<f32> {
// let mut converted: Vec<(f32, f32)> = RotatableHitBox::get_adjusted_points(self_);
// converted.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
// Ok(converted[0].0)
// }

// #[getter]
// fn bottom(self_: PyRef<'_, Self>) -> PyResult<f32> {
// let mut converted: Vec<(f32, f32)> = RotatableHitBox::get_adjusted_points(self_);
// converted.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
// Ok(converted[0].1)
// }

// #[getter]
// fn top(self_: PyRef<'_, Self>) -> PyResult<f32> {
// let mut converted: Vec<(f32, f32)> = RotatableHitBox::get_adjusted_points(self_);
// converted.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
// Ok(converted[0].1)
// }
// }

// impl RotatableHitBox {
// pub fn get_adjusted_points_native(self) -> Vec<(f32, f32)> {
// let mut new_points: Vec<(f32, f32)> = Vec::with_capacity(self.parent.points.len());
Expand Down
Loading

0 comments on commit 1b1da04

Please sign in to comment.