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