之前我們已經用迴圈讓程式持續執行, 使用巨集來統一處理事件, 但是 main
函式知道的太多了, 我們目前定一個小目標, 一步一步讓入口簡化.
照慣例 git checkout -b render-view
使用 trait
trait
目前我們先理解成 Java 的 interface
, 就類似於一個介面, 介面定義的函式具體如何實現不管, 但是它對外開放的是一個確定的行為. 但是檢視除了渲染, 它的事件要如何處理.先走一步算一步.
我們先看看之前主函式如何渲染檢視的.
let sdl2_context = sdl2::init().unwrap();
let video = sdl2_context.video().unwrap();
let window = video .window("Arcade Shooter", 800, 600) .position_centered() .opengl() .build() .unwrap();
let mut canvas = window.renderer().accelerated().build().unwrap();
canvas.set_draw_color(Color::RGB(0, 0, 0));
canvas.clear();
canvas.present();
複製程式碼
通過 SDL 的函式來建立一個渲染器, 這裡被命名成 canvas
的東西. 此外, 我們還要考慮如何處理事件. 我們把 canvas
跟事件處理包裝起來就好了.
use sdl2::render::Renderer;
struct Phi {
pub events: Events, pub canvas: Renderer,
}impl Phi {
pub fn new(events: Events, canvas: Renderer) ->
Phi {
Phi {
events, canvas,
}
}
}複製程式碼
定義一個結構體來拿到事件跟渲染器, 然後定一個渲染函式. 還要讓主函式曉得檢視的動作, 而且之前的事件觸發執行都涉及值的變化, 所以傳入的 Phi
結構體不僅需要借用, 還要考慮可變.
pub enum ViewAction {
Quit, None,
}pub trait View {
fn render(&
mut self, context: &
mut Phi) ->
ViewAction;
}複製程式碼
我們來實現一下這個 trait
pub struct DefaultView;
impl View for DefaultView {
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, 0, 0));
canvas.clear();
ViewAction::None
}
}複製程式碼
在入口使用
現在還是個半成品, 渲染器跟事件給 render
處理了, 但是入口函式還是沒有多少精簡, 看起來更麻煩了.
fn main() {
let sdl2_context = sdl2::init().unwrap();
let video = sdl2_context.video().unwrap();
let window = video .window("Arcade Shooter", 800, 600) .position_centered() .opengl() .build() .unwrap();
let canvas = window.renderer().accelerated().build().unwrap();
let events = Events::new(sdl2_context.event_pump().unwrap());
let mut context = Phi::new(events, canvas);
let mut current_view = views::DefaultView;
'running: loop {
context.events.pump();
match current_view.render(&
mut context) {
ViewAction::None =>
context.canvas.present(), ViewAction::Quit =>
break 'running
}
}
}複製程式碼
生命週期問題
先不管那麼多, 執行一下看看效果, 這裡有個坑.執行後發現跑不起來
error[E0106]: missing lifetime specifier -->
src/phi/mod.rs:19:17 |19 | pub canvas: Renderer, | ^^^^^^^^ expected lifetime parametererror: aborting due to previous errorFor more information about this error, try `rustc --explain E0106`.複製程式碼
生命週期錯誤, canvas
好像活得不夠久誒.我們思考一下 canvas
從哪裡來的
let canvas = window.renderer().accelerated().build().unwrap();
let events = Events::new(sdl2_context.event_pump().unwrap());
let mut context = Phi::new(events, canvas);
// 注意 Window 的 renderer 函式impl Window {
/// Initializes a new `RendererBuilder`;
a convenience method that calls `RendererBuilder::new()`. pub fn renderer(self) ->
RendererBuilder {
RendererBuilder::new(self)
}
}複製程式碼
我們可以看到 window
呼叫 renderer
時把 self
給 RendererBuilder
了, 等於講我們的 canvas
擁有 window
, 所以我們得保證 canvas
活得夠久. 改一下 Phi
, 新增一個生命週期的標記.
use sdl2::render::Renderer;
pub struct Phi<
'window>
{
pub events: Events, pub canvas: Renderer<
'window>
,
}impl<
'window>
Phi<
'window>
{
pub fn new(events: Events, canvas: Renderer<
'window>
) ->
Phi {
Phi {
events, canvas,
}
}
}複製程式碼
雖然現在還是個半成品, 但是後面還有用途, 我們當下的目的是一步一步讓 main
僅僅是作為一個入口存在.
這一節改的東西稍微多那麼一捏捏, 有問題看看程式碼.Coding