Add collision detection methods

This commit is contained in:
Yash Karandikar 2022-04-12 13:09:28 -05:00
parent 3eb772cbd5
commit 49619bd141
3 changed files with 109 additions and 38 deletions

View file

@ -6,22 +6,22 @@
//!
//! fn main() {
//! let game = Game::new("catbox demo", 1000, 800);
//!
//!
//! let mut i = 0u8;
//! let mut s = Sprite::new("duck.png", 500, 400).unwrap();
//! let mut s2 = Sprite::new("duck.png", 400, 500).unwrap();
//!
//!
//! let mut coll = SpriteCollection::new();
//! for n in 0..10 {
//! for o in 0..8 {
//! let x = Sprite::new("duck.png", n * 100, o * 100).unwrap();
//! coll.push(x);
//! }
//! }
//! }
//! game.run(|ctx| {
//! i = (i + 1) % 255;
//! ctx.set_background_colour(i as u8, 64, 255);
//!
//!
//! draw_text(
//! ctx,
//! format!("i is {}", i),
@ -34,27 +34,27 @@
//! },
//! )
//! .unwrap();
//!
//!
//! let (start_x, start_y) = s.position();
//! let m = get_mouse_state(ctx);
//! let x_diff = m.x - start_x;
//! let y_diff = m.y - start_y;
//!
//!
//! let angle = (y_diff as f64).atan2(x_diff as f64);
//! s.set_angle(angle.to_degrees());
//!
//!
//! for spr in coll.iter() {
//! let (start_x, start_y) = spr.position();
//! let m = get_mouse_state(ctx);
//! let x_diff = m.x - start_x;
//! let y_diff = m.y - start_y;
//!
//!
//! let angle = (y_diff as f64).atan2(x_diff as f64);
//! spr.set_angle(angle.to_degrees());
//! }
//!
//!
//! let keys = get_keyboard_state(ctx).keys;
//!
//!
//! for key in keys {
//! let offset = match key {
//! Scancode::Escape => {
@ -67,14 +67,14 @@
//! Scancode::D | Scancode::Right => (5, 0),
//! _ => (0, 0),
//! };
//!
//!
//! s.translate(offset);
//!
//!
//! for spr in coll.iter() {
//! spr.translate(offset);
//! }
//! }
//!
//!
//! s2.draw(ctx).unwrap();
//! s.draw(ctx).unwrap();
//! coll.draw(ctx).unwrap();
@ -83,17 +83,26 @@
//! }
//! ```
use std::{cell::Cell, path::Path, ops::{DerefMut, Deref}, slice::IterMut};
pub mod physics;
use std::{
cell::Cell,
ops::{Deref, DerefMut},
path::Path,
slice::IterMut,
};
use sdl2::{
image::ImageRWops,
keyboard::Scancode,
mouse::MouseButton,
rect::Rect,
render::{Canvas, TextureCreator, TextureValueError},
rwops::RWops,
surface::Surface,
ttf::{FontError, Sdl2TtfContext},
video::{Window, WindowBuildError, WindowContext},
EventPump, IntegerOrSdlError, mouse::MouseButton, keyboard::Scancode,
EventPump, IntegerOrSdlError,
};
#[doc(no_inline)]
@ -275,8 +284,8 @@ impl Sprite {
}
}
/// Manages a collection of [`Sprite`]s.
///
/// Manages a collection of [`Sprite`]s.
///
/// Technically, this is a thin wrapper around a simple [`Vec`] of sprites,
/// although with some convenience methods.
pub struct SpriteCollection {
@ -285,20 +294,18 @@ pub struct SpriteCollection {
impl SpriteCollection {
/// Creates a new [`SpriteCollection`].
///
///
/// See [`Vec::new()`] for more information.
/// ```
/// # use cat_box::*;
/// let sprites = SpriteCollection::new();
/// ```
pub fn new() -> Self {
Self {
v: Vec::new()
}
Self { v: Vec::new() }
}
/// Creates a new [`SpriteCollection`] with the specified capacity.
///
///
/// The collection will be able to hold exactly `capacity` items without reallocating.
/// ```
/// # use cat_box::*;
@ -306,7 +313,7 @@ impl SpriteCollection {
/// ```
pub fn with_capacity(cap: usize) -> Self {
Self {
v: Vec::with_capacity(cap)
v: Vec::with_capacity(cap),
}
}
@ -318,7 +325,7 @@ impl SpriteCollection {
/// # let mut game = Game::new("asjdfhalksjdf", 1, 1);
/// # game.run(|ctx| {
/// sprites.draw(ctx);
/// # });
/// # });
/// ```
pub fn draw(&mut self, ctx: &mut Context) -> Result<()> {
for s in self.v.iter_mut() {
@ -327,7 +334,7 @@ impl SpriteCollection {
Ok(())
}
/// Add a new [`Sprite`] to the end of this collection.
/// ```
/// # use cat_box::*;
@ -336,7 +343,7 @@ impl SpriteCollection {
/// sprites.push(s);
/// ```
pub fn push(&mut self, s: Sprite) {
self.v.push(s);
self.v.push(s);
}
/// Inserts an element at position `index` within the collection.
@ -350,7 +357,7 @@ impl SpriteCollection {
pub fn insert(&mut self, s: Sprite, index: usize) {
self.v.insert(index, s);
}
/// Removes and returns the last element, or `None` if the collection is empty.
/// ```
/// # use cat_box::*;
@ -424,6 +431,11 @@ impl SpriteCollection {
pub fn get(&self, index: usize) -> Option<&Sprite> {
self.v.get(index)
}
/// Return the inner Vec. Only use this method if you know what you're doing.
pub fn inner(&self) -> &Vec<Sprite> {
&self.v
}
}
impl Deref for SpriteCollection {
@ -464,8 +476,18 @@ impl Context {
/// Get the inner [`Canvas`](sdl2::render::Canvas) and [`TextureCreator`](sdl2::render::TextureCreator).
///
/// Only use this method if you know what you're doing.
pub fn inner(&mut self) -> (&TextureCreator<WindowContext>, &mut Canvas<Window>, &mut EventPump) {
(&self.texture_creator, &mut self.canvas, &mut self.event_pump)
pub fn inner(
&mut self,
) -> (
&TextureCreator<WindowContext>,
&mut Canvas<Window>,
&mut EventPump,
) {
(
&self.texture_creator,
&mut self.canvas,
&mut self.event_pump,
)
}
fn update(&mut self) {
@ -483,7 +505,7 @@ impl Context {
if let Event::Quit { .. } = event {
return true;
}
}
}
false
}
@ -560,12 +582,12 @@ pub fn draw_text<S: AsRef<str>>(
pub struct MouseRepr {
pub buttons: Vec<MouseButton>,
pub x: i32,
pub y: i32
pub y: i32,
}
/// Representation of the keyboard state.
pub struct KeyboardRepr {
pub keys: Vec<Scancode>
pub keys: Vec<Scancode>,
}
/// Get the mouse state.
@ -602,9 +624,9 @@ pub fn get_keyboard_state(ctx: &mut Context) -> KeyboardRepr {
let (_, _, pump) = ctx.inner();
let keyboard = pump.keyboard_state();
KeyboardRepr {
keys: keyboard.pressed_scancodes().collect()
keys: keyboard.pressed_scancodes().collect(),
}
}
@ -662,7 +684,7 @@ impl Game {
let s = sdl2::ttf::init().unwrap();
let event_pump = sdl_context.event_pump()?;
let mut ctx = Context::new(canvas, event_pump, s);
loop {

View file

@ -1,4 +1,4 @@
use cat_box::{draw_text, Game, Sprite, SpriteCollection, get_mouse_state, get_keyboard_state};
use cat_box::{draw_text, get_keyboard_state, get_mouse_state, Game, Sprite, SpriteCollection};
use sdl2::keyboard::Scancode;
fn main() {
@ -14,7 +14,7 @@ fn main() {
let x = Sprite::new("duck.png", n * 100, o * 100).unwrap();
coll.push(x);
}
}
}
game.run(|ctx| {
i = (i + 1) % 255;
ctx.set_background_colour(i as u8, 64, 255);
@ -57,7 +57,7 @@ fn main() {
Scancode::Escape => {
game.terminate();
(0, 0)
},
}
Scancode::W | Scancode::Up => (0, 5),
Scancode::S | Scancode::Down => (0, -5),
Scancode::A | Scancode::Left => (-5, 0),
@ -72,6 +72,10 @@ fn main() {
}
}
if !cat_box::physics::check_for_collision_with_collection(&s2, &coll).is_empty() {
println!("Sprites collided! {}", i);
}
s2.draw(ctx).unwrap();
s.draw(ctx).unwrap();
coll.draw(ctx).unwrap();

45
src/physics.rs Normal file
View file

@ -0,0 +1,45 @@
use crate::{Sprite, SpriteCollection};
use std::cmp::max;
// https://github.com/pythonarcade/arcade/blob/d2ce45a9b965020cde57a2a88536311e04504e6e/arcade/sprite_list/spatial_hash.py#L356
fn collided(sprite1: &Sprite, sprite2: &Sprite) -> bool {
let coll_rad1 = max(sprite1.rect.width(), sprite1.rect.height()) as i32;
let coll_rad2 = max(sprite2.rect.width(), sprite2.rect.height()) as i32;
let collision_radius = coll_rad1 + coll_rad2;
let collision_diameter = collision_radius * collision_radius;
let diff_x = sprite1.position().0 - sprite2.position().0;
let diff_x2 = diff_x * diff_x;
if diff_x2 > collision_diameter {
return false;
}
let diff_y = sprite1.position().1 - sprite2.position().1;
let diff_y2 = diff_y * diff_y;
if diff_y2 > collision_diameter {
return false;
}
return sprite1.rect.has_intersection(sprite2.rect);
}
/// Check if two sprites are touching or overlapping.
pub fn check_for_collision(sprite1: &Sprite, sprite2: &Sprite) -> bool {
collided(sprite1, sprite2)
}
/// Check if the sprite is colliding with any sprite in the collection, and return a list of
/// references to the sprites which are colliding
pub fn check_for_collision_with_collection<'a>(
sprite: &Sprite,
list: &'a SpriteCollection,
) -> Vec<&'a Sprite> {
list.inner()
.iter()
.filter(|s| check_for_collision(sprite, s))
.collect()
}