Skip to content

Commit 1b1da04

Browse files
authored
HitBox/Collision Improvements (#38)
1 parent aa75d1c commit 1b1da04

2 files changed

Lines changed: 279 additions & 178 deletions

File tree

src/hitbox.rs

Lines changed: 224 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
use pyo3::prelude::*;
22

3+
pub trait NativeAdjustedPoints {
4+
fn get_adjusted_points_native(&mut self) -> &Vec<(f32, f32)>;
5+
}
6+
37
#[derive(Clone)]
4-
#[pyclass(subclass, module = "arcade.hitbox.base")]
8+
#[pyclass(module = "arcade.hitbox.base")]
59
pub struct HitBox {
610
#[pyo3(get, set)]
711
pub points: Vec<(f32, f32)>,
8-
#[pyo3(get, set)]
12+
#[pyo3(get)]
913
pub position: (f32, f32),
10-
#[pyo3(get, set)]
14+
#[pyo3(get)]
1115
pub scale: (f32, f32),
1216
pub angle: f32,
17+
18+
pub adjusted_cache: Vec<(f32, f32)>,
19+
pub cache_dirty: bool,
1320
}
1421

1522
#[pymethods]
@@ -27,6 +34,8 @@ impl HitBox {
2734
position: final_position,
2835
scale: final_scale,
2936
angle: 0.0,
37+
adjusted_cache: vec![],
38+
cache_dirty: true,
3039
}
3140
}
3241

@@ -48,90 +57,93 @@ impl HitBox {
4857
Ok(adjustable)
4958
}
5059

51-
fn get_adjusted_points(self_: PyRef<'_, Self>) -> Vec<(f32, f32)> {
52-
let mut new_points: Vec<(f32, f32)> = Vec::with_capacity(self_.points.len());
53-
54-
for point in self_.points.iter() {
55-
let x = (point.0 * self_.scale.0) + self_.position.0;
56-
let y = (point.1 * self_.scale.1) + self_.position.1;
57-
new_points.push((x, y));
60+
pub fn get_adjusted_points(&mut self) -> Vec<(f32, f32)> {
61+
if self.cache_dirty {
62+
self.adjusted_cache = Vec::with_capacity(self.points.len());
63+
for point in self.points.iter() {
64+
let x = (point.0 * self.scale.0) + self.position.0;
65+
let y = (point.1 * self.scale.1) + self.position.1;
66+
self.adjusted_cache.push((x, y));
67+
}
68+
self.cache_dirty = false;
5869
}
5970

60-
new_points
71+
self.adjusted_cache.to_vec()
72+
}
73+
74+
#[setter]
75+
pub fn set_position(&mut self, value: (f32, f32)) -> PyResult<()> {
76+
self.position = value;
77+
self.cache_dirty = true;
78+
Ok(())
79+
}
80+
81+
#[setter]
82+
pub fn set_scale(&mut self, value: (f32, f32)) -> PyResult<()> {
83+
self.scale = value;
84+
self.cache_dirty = true;
85+
Ok(())
6186
}
6287

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

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

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

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

92-
impl HitBox {
93-
pub fn get_adjusted_points_native(&self) -> Vec<(f32, f32)> {
94-
let mut new_points: Vec<(f32, f32)> = Vec::with_capacity(self.points.len());
95-
96-
for point in self.points.iter() {
97-
let x = (point.0 * self.scale.0) + self.position.0;
98-
let y = (point.1 * self.scale.1) + self.position.1;
99-
new_points.push((x, y));
117+
impl NativeAdjustedPoints for HitBox {
118+
fn get_adjusted_points_native(&mut self) -> &Vec<(f32, f32)> {
119+
if self.cache_dirty {
120+
self.adjusted_cache = Vec::with_capacity(self.points.len());
121+
for point in self.points.iter() {
122+
let x = (point.0 * self.scale.0) + self.position.0;
123+
let y = (point.1 * self.scale.1) + self.position.1;
124+
self.adjusted_cache.push((x, y));
125+
}
126+
self.cache_dirty = false;
100127
}
101128

102-
new_points
103-
}
104-
105-
pub fn left_native(&self) -> f32 {
106-
let mut converted: Vec<(f32, f32)> = self.get_adjusted_points_native();
107-
converted.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
108-
converted[0].0
109-
}
110-
111-
pub fn right_native(&self) -> f32 {
112-
let mut converted: Vec<(f32, f32)> = self.get_adjusted_points_native();
113-
converted.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
114-
converted[0].0
115-
}
116-
117-
pub fn bottom_native(&self) -> f32 {
118-
let mut converted: Vec<(f32, f32)> = self.get_adjusted_points_native();
119-
converted.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
120-
converted[0].1
121-
}
122-
123-
pub fn top_native(&self) -> f32 {
124-
let mut converted: Vec<(f32, f32)> = self.get_adjusted_points_native();
125-
converted.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
126-
converted[0].1
129+
&self.adjusted_cache
127130
}
128131
}
129132

130133
#[derive(Clone)]
131-
#[pyclass(extends=HitBox, module = "arcade.hitbox.base")]
134+
#[pyclass(module = "arcade.hitbox.base")]
132135
pub struct RotatableHitBox {
133136
#[pyo3(get, set)]
134-
angle: f32,
137+
pub points: Vec<(f32, f32)>,
138+
#[pyo3(get)]
139+
pub position: (f32, f32),
140+
#[pyo3(get)]
141+
pub scale: (f32, f32),
142+
#[pyo3(get)]
143+
pub angle: f32,
144+
145+
pub adjusted_cache: Vec<(f32, f32)>,
146+
pub cache_dirty: bool,
135147
}
136148

137149
#[pymethods]
@@ -142,59 +154,194 @@ impl RotatableHitBox {
142154
position: Option<(f32, f32)>,
143155
scale: Option<(f32, f32)>,
144156
angle: Option<f32>,
145-
) -> (Self, HitBox) {
157+
) -> RotatableHitBox {
158+
let final_position = position.unwrap_or((0.0, 0.0));
159+
let final_scale = scale.unwrap_or((1.0, 1.0));
146160
let final_angle = angle.unwrap_or(0.0);
147-
(
148-
RotatableHitBox { angle: final_angle },
149-
HitBox::new(points, position, scale),
161+
RotatableHitBox {
162+
points,
163+
position: final_position,
164+
scale: final_scale,
165+
angle: final_angle,
166+
adjusted_cache: vec![],
167+
cache_dirty: true,
168+
}
169+
}
170+
171+
fn create_rotatable(
172+
self_: PyRef<'_, Self>,
173+
py: Python<'_>,
174+
angle: Option<f32>,
175+
) -> PyResult<Py<RotatableHitBox>> {
176+
let adjustable: Py<RotatableHitBox> = Py::new(
177+
py,
178+
RotatableHitBox::new(
179+
self_.points.to_vec(),
180+
Some(self_.position),
181+
Some(self_.scale),
182+
angle,
183+
),
150184
)
185+
.unwrap();
186+
Ok(adjustable)
151187
}
152188

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

157-
let rad = self_.angle.to_radians();
158-
let rad_cos = rad.cos();
159-
let rad_sin = rad.sin();
160-
for point in super_.points.iter() {
161-
let x = ((point.0 * rad_cos + point.1 * rad_sin) * super_.scale.0) + super_.position.0;
162-
let y = ((-point.0 * rad_sin + point.1 * rad_cos) * super_.scale.1) + super_.position.1;
163-
new_points.push((x, y));
193+
let rad = self.angle.to_radians();
194+
let rad_cos = rad.cos();
195+
let rad_sin = rad.sin();
196+
for point in self.points.iter() {
197+
let x = ((point.0 * rad_cos + point.1 * rad_sin) * self.scale.0) + self.position.0;
198+
let y = ((-point.0 * rad_sin + point.1 * rad_cos) * self.scale.1) + self.position.1;
199+
self.adjusted_cache.push((x, y));
200+
}
201+
self.cache_dirty = false;
164202
}
165203

166-
new_points
204+
self.adjusted_cache.to_vec()
205+
}
206+
207+
#[setter]
208+
pub fn set_position(&mut self, value: (f32, f32)) -> PyResult<()> {
209+
self.position = value;
210+
self.cache_dirty = true;
211+
Ok(())
212+
}
213+
214+
#[setter]
215+
pub fn set_scale(&mut self, value: (f32, f32)) -> PyResult<()> {
216+
self.scale = value;
217+
self.cache_dirty = true;
218+
Ok(())
219+
}
220+
221+
#[setter]
222+
pub fn set_angle(&mut self, value: f32) -> PyResult<()> {
223+
self.angle = value;
224+
self.cache_dirty = true;
225+
Ok(())
167226
}
168227

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

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

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

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

257+
impl NativeAdjustedPoints for RotatableHitBox {
258+
fn get_adjusted_points_native(&mut self) -> &Vec<(f32, f32)> {
259+
if self.cache_dirty {
260+
self.adjusted_cache = Vec::with_capacity(self.points.len());
261+
262+
let rad = self.angle.to_radians();
263+
let rad_cos = rad.cos();
264+
let rad_sin = rad.sin();
265+
for point in self.points.iter() {
266+
let x = ((point.0 * rad_cos + point.1 * rad_sin) * self.scale.0) + self.position.0;
267+
let y = ((-point.0 * rad_sin + point.1 * rad_cos) * self.scale.1) + self.position.1;
268+
self.adjusted_cache.push((x, y));
269+
}
270+
self.cache_dirty = false;
271+
}
272+
273+
&self.adjusted_cache
274+
}
275+
}
276+
277+
// #[derive(Clone)]
278+
// #[pyclass(extends=HitBox, module = "arcade.hitbox.base")]
279+
// pub struct RotatableHitBox {
280+
// #[pyo3(get, set)]
281+
// angle: f32,
282+
// }
283+
284+
// #[pymethods]
285+
// impl RotatableHitBox {
286+
// #[new]
287+
// fn new(
288+
// points: Vec<(f32, f32)>,
289+
// position: Option<(f32, f32)>,
290+
// scale: Option<(f32, f32)>,
291+
// angle: Option<f32>,
292+
// ) -> (Self, HitBox) {
293+
// let final_angle = angle.unwrap_or(0.0);
294+
// (
295+
// RotatableHitBox { angle: final_angle },
296+
// HitBox::new(points, position, scale),
297+
// )
298+
// }
299+
300+
// pub fn get_adjusted_points(self_: PyRef<'_, Self>) -> Vec<(f32, f32)> {
301+
// let super_: &HitBox = self_.as_ref();
302+
// let mut new_points: Vec<(f32, f32)> = Vec::with_capacity(super_.points.len());
303+
304+
// let rad = self_.angle.to_radians();
305+
// let rad_cos = rad.cos();
306+
// let rad_sin = rad.sin();
307+
// for point in super_.points.iter() {
308+
// let x = ((point.0 * rad_cos + point.1 * rad_sin) * super_.scale.0) + super_.position.0;
309+
// let y = ((-point.0 * rad_sin + point.1 * rad_cos) * super_.scale.1) + super_.position.1;
310+
// new_points.push((x, y));
311+
// }
312+
313+
// new_points
314+
// }
315+
316+
// #[getter]
317+
// fn left(self_: PyRef<'_, Self>) -> PyResult<f32> {
318+
// let mut converted: Vec<(f32, f32)> = RotatableHitBox::get_adjusted_points(self_);
319+
// converted.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
320+
// Ok(converted[0].0)
321+
// }
322+
323+
// #[getter]
324+
// fn right(self_: PyRef<'_, Self>) -> PyResult<f32> {
325+
// let mut converted: Vec<(f32, f32)> = RotatableHitBox::get_adjusted_points(self_);
326+
// converted.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
327+
// Ok(converted[0].0)
328+
// }
329+
330+
// #[getter]
331+
// fn bottom(self_: PyRef<'_, Self>) -> PyResult<f32> {
332+
// let mut converted: Vec<(f32, f32)> = RotatableHitBox::get_adjusted_points(self_);
333+
// converted.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
334+
// Ok(converted[0].1)
335+
// }
336+
337+
// #[getter]
338+
// fn top(self_: PyRef<'_, Self>) -> PyResult<f32> {
339+
// let mut converted: Vec<(f32, f32)> = RotatableHitBox::get_adjusted_points(self_);
340+
// converted.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
341+
// Ok(converted[0].1)
342+
// }
343+
// }
344+
198345
// impl RotatableHitBox {
199346
// pub fn get_adjusted_points_native(self) -> Vec<(f32, f32)> {
200347
// let mut new_points: Vec<(f32, f32)> = Vec::with_capacity(self.parent.points.len());

0 commit comments

Comments
 (0)