放個矩形
現在要在視窗上放一個矩形, 順便學習一下 Rust 的其他語法.
我們在 views
模組內寫關於 rect
的結構體, 有一個不幸的訊息, 之前寫的 ViewA
ViewB
目前是用不上了, 之後檢視切換之類的操作肯定有用的, 現在我們先把它們處理掉.
來定義一個矩形的結構體, 一個矩形在視窗上有平面直角座標系的 X 軸和 Y 軸的座標和還有矩形本身的長寬.
如果想自己寫矩形的渲染, 可以通過 OpenGL 來繪製兩個三角形來組成一個矩形, 而且如果還要做矩形的移動等操作, 還要用線性代數的矩陣來做處理, 幸好 SDL 提供了相關的 API 讓我們可以方便地繪製矩形
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Rectangle {
pub x: f64,
pub y: f64,
pub w: f64,
pub h: f64,
}
impl Rectangle {
pub fn to_sdl(&self) -> Option<SdlRect> {
assert!(self.w >= 0.0 && self.h >= 0.0);
Some(SdlRect::new(self.x as i32,
self.y as i32,
self.w as u32,
self.h as u32))
}
}
複製程式碼
關於 #[derive(Clone, Copy, Debug, PartialEq)]
這塊, 一般的結構體通過 let
繫結到變數會發生所有權的 move
行為, 加了這 Clone
Copy
屬性之後, 這個結構體進行變數繫結時就變得跟整型浮點型之類的型別一樣的 copy
行為, 舉例說明
let a: i32 = 10;
let b = a; // copy, a 還能被使用
struct Test {}
impl Test {
pub fn new() {
...
}
}
let c = Test::new();
let d = c; // move, c 所有權轉移了
複製程式碼
關於 Debug
屬性就是使結構體可以讓 println!
這些巨集列印, PartialEq
就是讓結構體可比較.
來定義個 RectView
結構體來實現一下 View
trait
struct Rect {
rect: Rectangle,
}
pub struct RectView {
player: Rect,
}
impl RectView {
pub fn new() -> Self {
Self {
player: Rect {
rect: Rectangle {
x: 64.0,
y: 64.0,
w: 32.0,
h: 32.0,
}
}
}
}
}
impl View for RectView {
fn render(&mut self, context: &mut Phi) -> ViewAction {
let canvas = &mut context.canvas;
let events = &mut context.events;
if events.now.quit || events.now.key_escape == Some(true) {
return ViewAction::Quit;
}
canvas.set_draw_color(Color::RGB(0, 50, 0));
canvas.clear();
canvas.set_draw_color(Color::RGB(200, 200, 50));
canvas.fill_rect(self.player.rect.to_sdl().unwrap())
.expect("fill rect fail");
ViewAction::None
}
}
複製程式碼
定義好這些東西之後, 再在 spawn
函式內把之前的 ViewA
改成 RectView
, 此外上面的程式碼值得注意的是要先 clear()
背景色, 再填充 rect
顏色
...
let mut current_view: Box<View> = box views::RectView::new();
...
複製程式碼
現在執行一下, 矩形已經渲染出來了.
通過方向鍵控制矩形移動
我們發覺到, Rectangle
儲存了矩形的寬高還有位置, 我們現在只要修改這個結構體的值就可以改變矩形的寬高跟位置, 通過鍵盤的方向鍵來重新計算 x
, y
的值就可以達到讓矩形移動的效果.
按照慣例改一下事件巨集的呼叫傳入
events_macro! {
keyboard: {
key_escape: Escape,
key_up: Up,
key_down: Down,
key_left: Left,
key_right: Right,
key_space: Space
},
else: {
quit: Quit { .. }
}
}
複製程式碼
然後在 render
函式中寫重新計算值的邏輯, 現在方向鍵就四個, 然後我們要來考慮所有的情況, 我之前的想法是一個一個用 if
去判斷按鍵的觸發, 但是明顯這個方式寫出來的程式碼太 ugly 了, 幸好 Rust 模式匹配非常棒. 先來列一下會有的東西, 上下左右跟斜角移動, 這裡定義個常量來作為移動的速度
const PLAYER_SPEED: f64 = 0.35;
impl View for RectView {
fn render(&mut self, context: &mut Phi) -> ViewAction {
let canvas = &mut context.canvas;
let events = &mut context.events;
if events.now.quit || events.now.key_escape == Some(true) {
return ViewAction::Quit;
}
let diagonal: bool = (events.key_up ^ events.key_down) &&
(events.key_left ^ events.key_right);
let moved = if diagonal { 1.0 / 2.0f64.sqrt() } else { 1.0 } *
PLAYER_SPEED;
let dx = match (events.key_left, events.key_right) {
(true, true) | (false, false) => 0.0,
(true, false) => -moved,
(false, true) => moved,
};
let dy = match (events.key_up, events.key_down) {
(true, true) | (false, false) => 0.0,
(true, false) => -moved,
(false, true) => moved,
};
self.player.rect.x += dx;
self.player.rect.y += dy;
canvas.set_draw_color(Color::RGB(0, 50, 0));
canvas.clear();
canvas.set_draw_color(Color::RGB(200, 200, 50));
canvas.fill_rect(self.player.rect.to_sdl().unwrap())
.expect("fill rect fail");
ViewAction::None
}
}
複製程式碼
至此已經可以在視窗上顯示了矩形, 而且還能根據鍵盤方向鍵控制矩形移動, 瞭解到了 Rust 模式匹配的靈活運用, 結構體 copy
行為的定義, 就這樣吧, 之後再處理其他的事體.