前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Hands-on Rust 学习之旅(3)—— 游戏基本流程

Hands-on Rust 学习之旅(3)—— 游戏基本流程

作者头像
玖柒的小窝
修改2021-10-20 10:14:24
6690
修改2021-10-20 10:14:24
举报
文章被收录于专栏:各类技术文章~各类技术文章~

第三章开始我们就进入真正的 Game 学习阶段,在整个第三章我们能学到的知识主要有:

  1. game loop,
  2. basic program flow with a state machine,
  3. add a player,
  4. simulate gravity,
  5. and make the player's dragon flap its wings.
  6. Finally, add obstaches and scorekeeping to the game.

这也是基本的游戏框架了。

Game Loop

关于这个 game loop,我们参考作者给的流程图,再结合自己的理解。

截图来自文中,版权作者所有

游戏主要流程在于:

  1. 初始化(Configure App, Window & Graphics),这个好理解,就是渲染游戏的最初状态和界面。
  2. Poll OS for Input State. 我理解的就是游戏都需要有一个触发机制去改变状态 (state)的,比如,一个事件、一个动作等等;
  3. Call Tick Function. 这个我的理解就好比每个游戏的游戏引擎,帮助我们把所有的事件、动作转化为游戏理解的逻辑,去修改状态,本文主要使用 bracket-lib 引擎。
  4. Update Screen. 状态的改变,就需要去实时更新界面了。

周而复始,就能把游戏运行起来了。

文中介绍了 bracket-lib 引擎。下面我们就来看看如何使用这个。

Bracket-Lib 使用

引入插件:

代码语言:javascript
复制
[dependencies]
bracket-lib="~0.8.1"
复制代码

关于插件的版本号说明,可以看第一章介绍的:

=0.8.1 只能使用该版本; ^0.8.1 可以使用大于等于该版本,但是只能局限在0.x 版本,比如:1.x 就不能使用; ~0.8.1 则可以使用任何大于等于该版本,即使未来的一些版本有 bug。

具体代码:

代码语言:javascript
复制
use bracket_lib::prelude::*;

struct State {}

impl GameState for State {
    fn tick(&mut self, ctx: &mut BTerm) {
        ctx.cls();
        ctx.print(1, 1, "Hello, Bracket Terminal!");
    }
}

fn main() ->BError {
    println!("Hello, world!");

    let context = BTermBuilder::simple80x50()
        .with_title("Flappy Dragon")
        .build()?;
    
    main_loop(context, State{})
}
复制代码

先看运行效果:

简简单单的就可以在终端屏幕打印出文字:Hello, Bracket Terminal!

Creating Different Game Modes

我觉得这个更像是游戏的状态描述,在这个游戏 demo 中,需要有三个 modes:

  1. Menu: The player is waiting at the main menu.
  2. Playing: Game play is in progress.
  3. End: The game is over.

显然这个 modes 可以用一个 enum 表示:

代码语言:javascript
复制
enum GameMode {
    Menu,
    Playing,
    End,
}
复制代码

这就需要在我们定义的 State 加上:

代码语言:javascript
复制
struct State {
    mode: GameMode,
}

// 并 new 时初始化
impl State {
    fn new() -> Self {
        State {
            mode: GameMode::Menu,
        }
    }
}

// main 函数同时修改
main_loop(context, State::new())
复制代码

有了状态描述了,接下来就是需要通过不同的状态下,执行不同的动作,以达到游戏的效果:

代码语言:javascript
复制
impl GameState for State {
    fn tick(&mut self, ctx: &mut BTerm) {
        // ctx.cls();
        // ctx.print(1, 1, "Hello, Bracket Terminal!");
        match self.mode {
            GameMode::Menu => self.main_menu(ctx),
            GameMode::End => self.dead(ctx),
            GameMode::Playing => self.play(ctx),
        }
    }
}
复制代码

在这个 tick 函数里,“实时”监听所有的状态变化,执行不同的动作,如:在 Playing 状态下,执行 self.play(ctx) 函数:

代码语言:javascript
复制
fn play(&mut self, ctx: &mut BTerm) {
    // TODO: Fill in this stub later
    ctx.cls();
    ctx.print_centered(5, "Playing Flappy Dragon");
    ctx.print_centered(8, "(E) Play End");
    ctx.print_centered(9, "(Q) Quit Game");

    if let Some(key) = ctx.key {
        match key {
            VirtualKeyCode::E => self.mode = GameMode::End,
            VirtualKeyCode::Q => ctx.quitting = true,
            _ => {}
        }
    }

    // 直接状态改变
    // self.mode = GameMode::End;
}
复制代码

play 函数里,根据监听不同的按键,决定状态的改变,这就把游戏运转起来了。

执行效果如下:

完整的代码:

代码语言:javascript
复制
use bracket_lib::prelude::*;

enum GameMode {
    Menu,
    Playing,
    End,
}

struct State {
    mode: GameMode,
}

impl State {
    fn new() -> Self {
        State {
            mode: GameMode::Menu,
        }
    }

    fn restart(&mut self) {
        self.mode = GameMode::Playing;
    }

    fn main_menu(&mut self, ctx: &mut BTerm) {
        ctx.cls();
        ctx.print_centered(5, "Welcome to Flappy Dragon");
        ctx.print_centered(8, "(P) Play Game");
        ctx.print_centered(9, "(Q) Quit Game");

        if let Some(key) = ctx.key {
            match key {
                VirtualKeyCode::P => self.restart(),
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }
    }

    fn dead(&mut self, ctx: &mut BTerm) {
        ctx.cls();
        ctx.print_centered(5, "You are dead!");
        ctx.print_centered(8, "(P) Play Again");
        ctx.print_centered(9, "(Q) Quit Game");

        if let Some(key) = ctx.key {
            match key {
                VirtualKeyCode::P => self.restart(),
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }
    }

    fn play(&mut self, ctx: &mut BTerm) {
        // TODO: Fill in this stub later
        ctx.cls();
        ctx.print_centered(5, "Playing Flappy Dragon");
        ctx.print_centered(8, "(E) Play End");
        ctx.print_centered(9, "(Q) Quit Game");

        if let Some(key) = ctx.key {
            match key {
                VirtualKeyCode::E => self.mode = GameMode::End,
                VirtualKeyCode::Q => ctx.quitting = true,
                _ => {}
            }
        }

        // 直接状态改变
        // self.mode = GameMode::End;
    }
}

impl GameState for State {
    fn tick(&mut self, ctx: &mut BTerm) {
        // ctx.cls();
        // ctx.print(1, 1, "Hello, Bracket Terminal!");
        match self.mode {
            GameMode::Menu => self.main_menu(ctx),
            GameMode::End => self.dead(ctx),
            GameMode::Playing => self.play(ctx),
        }
    }
}

fn main() ->BError {
    println!("Hello, world!");

    let context = BTermBuilder::simple80x50()
        .with_title("Flappy Dragon")
        .build()?;
    
    main_loop(context, State::new())
}
复制代码

今天临时安排其他工作,跟着书中流程,顺出代码,通过 bracket-lib 引擎实时监听 State 的变化,执行不同的游戏动作,虽然简单的利用虚拟按键,响应状态的变化来模拟游戏的推进,但基本也就构成了游戏的逻辑。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Game Loop
  • Bracket-Lib 使用
  • Creating Different Game Modes
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档