0x04. 檢視渲染

劉子殊發表於2018-12-24

之前我們已經用迴圈讓程式持續執行, 使用巨集來統一處理事件, 但是 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 時把 selfRendererBuilder 了, 等於講我們的 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

來源:https://juejin.im/post/5c1b12866fb9a049b221d63a

相關文章