0x04. 視圖渲染

以前咱們已經用循環讓程序持續運行, 使用宏來統一處理事件, 可是 main 函數知道的太多了, 咱們目前定一個小目標, 一步一步讓入口簡化.
照慣例 git checkout -b render-viewgit

使用 trait

trait 目前咱們先理解成 Java 的 interface, 就相似於一個接口, 接口定義的函數具體如何實現無論, 可是它對外開放的是一個肯定的行爲. 可是視圖除了渲染, 它的事件要如何處理. 先走一步算一步.canvas

咱們先看看以前主函數如何渲染視圖的.bash

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 跟事件處理包裝起來就行了.ide

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;
}
複製代碼

咱們來實現一下這個 traitoop

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 處理了, 可是入口函數仍是沒有多少精簡, 看起來更麻煩了.ui

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
        }
    }
}
複製代碼

生命週期問題

先無論那麼多, 執行一下看看效果, 這裏有個坑. 執行後發現跑不起來this

error[E0106]: missing lifetime specifier
  --> src/phi/mod.rs:19:17
   |
19 |     pub canvas: Renderer,
   |                 ^^^^^^^^ expected lifetime parameter

error: aborting due to previous error

For more information about this error, try `rustc --explain E0106`.
複製代碼

生命週期錯誤, canvas 好像活得不夠久誒. 咱們思考一下 canvas 從哪裏來的spa

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, 添加一個生命週期的標記.code

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

相關文章
相關標籤/搜索