介绍
上一篇文章的延续。
这一次,我们将实现两个矩形的碰撞检测过程。
命中检测是导致诸如给予损坏或擦除对象等事件的条件。
逻辑在游戏编程中很重要。
矩形结构和命中检测过程
矩形是具有左上角坐标、宽度和高度的数据结构。
pub struct Rect {
pub x: i32,
pub y: i32,
pub width: u32,
pub height: u32,
}
判断两个矩形是否接触的函数如下。
判断条件可以说是“当x轴和y轴重叠时”。
impl Rect {
...
pub fn is_hit(&self, rect: &Rect) -> bool {
return (self.x <= rect.x + rect.width as i32 && rect.x <= self.x + self.width as i32)
&& (self.y <= rect.y + rect.height as i32 && rect.y <= self.y + rect.height as i32);
}
}
两个矩形之间的位置关系大致可以分为以下四种模式。
看下图可以看到,只命中了④的情况。
(1) 完全不重叠
② 只有 x 轴重叠时
(3) 只有y轴重叠时
④ 两者重叠时
另外,如果仔细观察Rect.is_hit函数的判断条件,可以发现x轴和y轴的判断处理逻辑是一样的。
两者都确定它们是否与起点的坐标和长度重叠。
因此,将通用逻辑切割成一个单独的函数,并进行如下重构。
impl Rect {
...
pub fn is_hit(&self, rect: &Rect) -> bool {
return is_hit(self.x, self.width, rect.x, rect.width)
&& is_hit(self.y, self.height, rect.y, rect.height);
}
}
fn is_hit(s1: i32, l1: u32, s2: i32, l2: u32) -> bool {
let e1 = s1 + l1 as i32;
let e2 = s2 + l2 as i32;
return s1 <= e2 && s2 <= e1;
}
测试实施
然后实现测试代码。
确认线段碰撞检测is_hit函数和矩形碰撞检测Rect.is_hit函数正常工作。
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_line_hit() {
assert_eq!(is_hit(50, 50, 19, 30), false);
assert_eq!(is_hit(50, 50, 101, 20), false);
assert_eq!(is_hit(50, 50, 40, 11), true);
assert_eq!(is_hit(50, 50, 60, 15), true);
assert_eq!(is_hit(50, 50, 90, 30), true);
}
#[test]
fn test_rect_hit() {
let rect = Rect::new(50, 50, 50, 50);
assert_eq!(rect.is_hit(&Rect::new(0, 0, 30, 30)), false);
assert_eq!(rect.is_hit(&Rect::new(0, 70, 40, 40)), false);
assert_eq!(rect.is_hit(&Rect::new(40, 0, 20, 20)), false);
assert_eq!(rect.is_hit(&Rect::new(40, 40, 15, 18)), true);
assert_eq!(rect.is_hit(&Rect::new(60, 60, 10, 10)), true);
}
}
在cargo test 上运行测试。
> cargo test
Finished test [unoptimized + debuginfo] target(s) in 0.06s
Running unittests src\main.rs (target\debug\deps\sample_sdl2-0e5b02a7694a67db.exe)
running 2 tests
test rect::tests::test_line_hit ... ok
test rect::tests::test_rect_hit ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
好像没问题。
如果有复杂的流程,把共同的部分提取出来或者分成小流程进行测试
如果出现问题,将更容易确定问题位置。
确认
通过实际移动它来检查它。
为了视觉清晰,我们在两个矩形碰撞时改变颜色。
源代码
这是此实现的源代码。
重新CT。 rs
pub struct Rect {
pub x: i32,
pub y: i32,
pub width: u32,
pub height: u32,
}
impl Rect {
pub fn new(x: i32, y: i32, width: u32, height: u32) -> Self {
return Rect {
x: x,
y: y,
width: width,
height: height,
};
}
pub fn is_hit(&self, rect: &Rect) -> bool {
return is_hit(self.x, self.width, rect.x, rect.width)
&& is_hit(self.y, self.height, rect.y, rect.height);
}
}
fn is_hit(s1: i32, l1: u32, s2: i32, l2: u32) -> bool {
let e1 = s1 + l1 as i32;
let e2 = s2 + l2 as i32;
return s1 <= e2 && s2 <= e1;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_line_hit() {
assert_eq!(is_hit(50, 50, 19, 30), false);
assert_eq!(is_hit(50, 50, 101, 20), false);
assert_eq!(is_hit(50, 50, 40, 11), true);
assert_eq!(is_hit(50, 50, 60, 15), true);
assert_eq!(is_hit(50, 50, 90, 30), true);
}
#[test]
fn test_rect_hit() {
let rect = Rect::new(50, 50, 50, 50);
assert_eq!(rect.is_hit(&Rect::new(0, 0, 30, 30)), false);
assert_eq!(rect.is_hit(&Rect::new(0, 70, 40, 40)), false);
assert_eq!(rect.is_hit(&Rect::new(40, 0, 20, 20)), false);
assert_eq!(rect.is_hit(&Rect::new(40, 40, 15, 18)), true);
assert_eq!(rect.is_hit(&Rect::new(60, 60, 10, 10)), true);
}
}
矿。 rs
use sdl2::event::Event;
use sdl2::keyboard::Scancode;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::Canvas;
use sdl2::video::Window;
use std::time::Duration;
mod rect;
fn draw_rect(canvas: &mut Canvas<Window>, rect: &rect::Rect, color: Color) {
canvas.set_draw_color(color);
// 画面描画用のRectを生成して渡す
canvas.fill_rect(Rect::new(rect.x, rect.y, rect.width, rect.height));
}
pub fn main() {
let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();
let window = video_subsystem
.window("Rust-SDL2", 800, 600)
.position_centered()
.build()
.unwrap();
let mut rect = rect::Rect::new(50, 100, 100, 100);
let rect2 = rect::Rect::new(350, 250, 100, 100);
let white = Color::RGB(255, 255, 255);
let green = Color::RGB(0, 255, 0);
let mut canvas = window.into_canvas().build().unwrap();
let mut event_pump = sdl_context.event_pump().unwrap();
'running: loop {
for event in event_pump.poll_iter() {
match event {
Event::Quit { .. } => break 'running,
_ => {}
}
}
let state = event_pump.keyboard_state();
if state.is_scancode_pressed(Scancode::Up) {
rect.y -= 5;
}
if state.is_scancode_pressed(Scancode::Down) {
rect.y += 5;
}
if state.is_scancode_pressed(Scancode::Left) {
rect.x -= 5;
}
if state.is_scancode_pressed(Scancode::Right) {
rect.x += 5;
}
canvas.set_draw_color(Color::RGB(0, 0, 0));
canvas.clear();
let is_hit = rect.is_hit(&rect2);
draw_rect(&mut canvas, &rect2, if is_hit { green } else { white });
draw_rect(&mut canvas, &rect, if is_hit { green } else { white });
canvas.present();
std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
}
}
参考
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://www.likecs.com/show-308628229.html