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

相关文章
相关标签/搜索