0x08. 添加游戏背景

游戏背景

Posted on 2019-08-09

虽然带点小效果的飞船已经搞定了,但是现在背景一片黑,完全没看头,其实加上纹理就能搞出背景,岂不美哉。

ship-bg

做之前先总结一下思路,在 Viewrender 函数中,渲染飞船之前先把背景渲染出来,渲染背景其实就是加载纹理图片,然后为了制造视差效果,把不同背景向左移动,速度单独设置,再加个前景在飞船之后加载渲染,同时设置移动速度,总体就是给一个飞船在太空中飞行的感觉。

现在理清思路,实现一下逻辑,就是把行为做抽象,既然要处理背景那就 Background 结构体走起,结构体除了要显示的纹理,还有自身的位置,以及平移速度,但是因为循环一直在执行,可以采用时间来计算。 先把 Background 搞定

impl Sprite {
    pub fn size(&self) -> (f64, f64) {
        (self.src.w, self.src.h)
    }
}

#[derive(Clone)]
struct Background {
    pos: f64,
    vel: f64,
    sprite: Sprite,
}
impl Background {
    fn render(&mut self, canvas: &mut Renderer, elapsed: f64) {
        let (w, h) = self.sprite.size();
        self.pos += self.vel * elapsed;

        if self.pos > w {
            self.pos -= w;
        }

        let (win_w, win_h) = canvas.output_size().unwrap();
        let scale = win_h as f64 / h;
        let mut physical_left = -self.pos * scale;
        while physical_left < win_w as f64 {
            canvas.copy_sprite(&self.sprite,
                               Rectangle {
                                   x: physical_left,
                                   y: 0.0,
                                   w: w * scale,
                                   h: win_h as f64,
            });
            physical_left += w * scale;
        }
    }
}

elapsed 这个形参是通过计算后传入的,所以在 spawn 函数内 loop 处算好传进去,同时 Viewrender 函数也要改一下,ShipView 也要改一下,原因很简单,ShipView 需要引入 Background

pub struct ShipView {
    player: Ship,
    bg_back: Background,
    bg_middle: Background,
    bg_front: Background,
}
impl ShipView {
    pub fn new(phi: &mut Phi) -> Self {
        // ...
        Self {
            player: Ship {
                rect: Rectangle {
                    x: 64.0,
                    y: 64.0,
                    w: 32.0,
                    h: 32.0,
                },
                sprites,
                current: ShipFrame::MidNorm,
            },
            bg_front: Background {
                pos: 0.0,
                vel: 80.0,
                sprite: Sprite::load(&mut phi.canvas,
                "assets/star_fg.png").unwrap(),
            },
            bg_middle: Background{
                pos: 0.0,
                vel: 40.0,
                sprite: Sprite::load(&mut phi.canvas,
                "assets/star_mg.png").unwrap(),
            },
            bg_back: Background{
                pos: 0.0,
                vel: 20.0,
                sprite: Sprite::load(&mut phi.canvas,
                "assets/star_bg.png").unwrap(),
            }
        }
    }
}

pub trait View {
    fn render(&mut self, context: &mut Phi, elapsed: f64) -> ViewAction;
}

pub fn spawn(title: &str) {
    // ...
    let mut timer = sdl2_context.timer().unwrap();
    let interval = 1000 / 60;
    let mut before = timer.ticks();
    'running: loop {
        let now = timer.ticks();
        let dt = now - before;
        let elapsed = dt  as f64 / 1000.0;
        if dt < interval {
            timer.delay(interval - dt);
            continue;
        }
        before = now;
        context.events.pump(&mut context.canvas);
        match current_view.render(&mut context, elapsed) {
            ViewAction::None => context.canvas.present(),
            ViewAction::Quit => break 'running,
        }
    }
}

impl View for ShipView {
    fn render(&mut self, context: &mut Phi, elapsed: f64) -> ViewAction {
        // ...
        canvas.set_draw_color(Color::RGB(0, 0, 0));
        canvas.clear();

        self.bg_back.render(canvas, elapsed);
        self.bg_middle.render(canvas, elapsed);
        // ...
        canvas.copy_sprite(
            &self.player.sprites[self.player.current as usize],
            self.player.rect);

        self.bg_front.render(canvas, elapsed);
        ViewAction::None
    }
}

现在就可以做到背景变化,由于视差效果,我们看起来就像飞船在不停往前移动。