第三章开始我们就进入真正的 Game 学习阶段,在整个第三章我们能学到的知识主要有:
- game loop,
- basic program flow with a state machine,
- add a player,
- simulate gravity,
- and make the player's dragon flap its wings.
- Finally, add obstaches and scorekeeping to the game.
这也是基本的游戏框架了。
Game Loop
关于这个 game loop,我们参考作者给的流程图,再结合自己的理解。
截图来自文中,版权作者所有
游戏主要流程在于:
- 初始化(Configure App, Window & Graphics),这个好理解,就是渲染游戏的最初状态和界面。
- Poll OS for Input State. 我理解的就是游戏都需要有一个触发机制去改变状态 (state)的,比如,一个事件、一个动作等等;
- Call Tick Function. 这个我的理解就好比每个游戏的游戏引擎,帮助我们把所有的事件、动作转化为游戏理解的逻辑,去修改状态,本文主要使用
bracket-lib
引擎。 - Update Screen. 状态的改变,就需要去实时更新界面了。
周而复始,就能把游戏运行起来了。
文中介绍了 bracket-lib
引擎。下面我们就来看看如何使用这个。
Bracket-Lib 使用
引入插件:
[dependencies]
bracket-lib="~0.8.1"
复制代码
关于插件的版本号说明,可以看第一章介绍的:
=0.8.1 只能使用该版本;
^0.8.1 可以使用大于等于该版本,但是只能局限在0.x 版本,比如:1.x 就不能使用;
~0.8.1 则可以使用任何大于等于该版本,即使未来的一些版本有 bug。
具体代码:
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:
- Menu: The player is waiting at the main menu.
- Playing: Game play is in progress.
- End: The game is over.
显然这个 modes 可以用一个 enum 表示:
enum GameMode {
Menu,
Playing,
End,
}
复制代码
这就需要在我们定义的 State
加上:
struct State {
mode: GameMode,
}
// 并 new 时初始化
impl State {
fn new() -> Self {
State {
mode: GameMode::Menu,
}
}
}
// main 函数同时修改
main_loop(context, State::new())
复制代码
有了状态描述了,接下来就是需要通过不同的状态下,执行不同的动作,以达到游戏的效果:
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)
函数:
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
函数里,根据监听不同的按键,决定状态的改变,这就把游戏运转起来了。
执行效果如下:
完整的代码:
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 的变化,执行不同的游戏动作,虽然简单的利用虚拟按键,响应状态的变化来模拟游戏的推进,但基本也就构成了游戏的逻辑。
明天继续第三章的学习!
近期评论