用ggez游戏框架学Rust:游戏开发实战指南
2. draw 方法:绘制画面
fn draw(&mut self, ctx: &mut Context) -> GameResult {
let mut canvas = graphics::Canvas::from_frame(ctx, Color::new(0.1, 0.1, 0.2, 1.0));
- 解释:创建一个画布,背景色是深蓝色(模拟太空)。
// 绘制背景星星
for star in &self.stars {
let star_mesh = Mesh::new_circle(ctx, DrawMode::fill(), *star, 1.5, 0.1, Color::WHITE)?;
canvas.draw(&star_mesh, graphics::DrawParam::default());
}
- 解释:画出白色的圆点作为背景。 *star:你需要用
*解引用操作符来获取具体的坐标值传给Mesh::new_circle
// ===== 绘制暂停提示 =====
if self.paused {
let paused_text = Text::new(TextFragment {
text: "PAUSED".to_string(),
color: Some(Color::YELLOW),
font: Some("LiberationMono-Regular".into()),
scale: Some(graphics::PxScale::from(50.0)),
..Default::default()
});
canvas.draw(
&paused_text,
Vec2::new(SCREEN_WIDTH / 2.0 - 100.0, SCREEN_HEIGHT / 2.0 - 30.0),
);
}
if self.game_over {
// ===== 游戏结束画面 =====
let game_over_text = Text::new(TextFragment {
text: "GAME OVER".to_string(),
color: Some(Color::RED),
font: Some("LiberationMono-Regular".into()),
scale: Some(graphics::PxScale::from(60.0)),
..Default::default()
});
let restart_text = Text::new(TextFragment {
text: "Press R to restart".to_string(),
color: Some(Color::WHITE),
font: Some("LiberationMono-Regular".into()),
scale: Some(graphics::PxScale::from(30.0)),
..Default::default()
});
canvas.draw(
&game_over_text,
Vec2::new(SCREEN_WIDTH / 2.0 - 180.0, SCREEN_HEIGHT / 2.0 - 80.0),
);
canvas.draw(
&restart_text,
Vec2::new(SCREEN_WIDTH / 2.0 - 160.0, SCREEN_HEIGHT / 2.0 + 80.0),
);
} else {
// ===== 绘制玩家(多边形-绿色三角形)=====
let player_mesh = Mesh::new_polygon(
ctx,
DrawMode::fill(),
&[
Vec2::new(self.player.pos.x, self.player.pos.y - self.player.size), // 顶点
Vec2::new(self.player.pos.x - self.player.size / 2.0,
self.player.pos.y + self.player.size / 2.0,
), // 左下
Vec2::new(self.player.pos.x + self.player.size / 2.0,
self.player.pos.y + self.player.size / 2.0,
), // 右下
],
Color::GREEN,
)?;
canvas.draw(&player_mesh, graphics::DrawParam::default());
// 绘制血条(红色矩形)
for i in 0..self.player.hp {
let hp_mesh = Mesh::new_rectangle(
ctx,
DrawMode::fill(),
Rect::new(10.0 + i as f32 * 25.0, 10.0, 20.0, 5.0),
Color::RED,
)?;
canvas.draw(&hp_mesh, graphics::DrawParam::default());
}
// ===== 绘制子弹(黄色小矩形)=====
for bullet in &self.bullets {
let bullet_mesh = Mesh::new_rectangle(
ctx,
DrawMode::fill(),
Rect::new(bullet.pos.x - 3.0, bullet.pos.y - 8.0, 6.0, 16.0),
Color::YELLOW,
)?;
canvas.draw(&bullet_mesh, graphics::DrawParam::default());
}
for enemy in &self.enemies {
// ===== 绘制敌人(红色方块)=====
// 敌人血条
}
// 绘制分数等
}
canvas.finish(ctx)?; // 提交绘制指令
timer::yield_now(); // 让出 CPU 时间片
Ok(())
}//fn draw结束
}//impl EventHandler for MainState 结束
- 解释:根据游戏状态绘制不同的 UI。正常游戏中绘制飞船、血条和分数;游戏结束时绘制大大的 GAME OVER 提示。
- 代码中 ? 是 Rust 中一个非常强大和常用的错误传播操作符。 它的作用可以理解为:“如果前面的操作成功了,就拿到结果;如果失败了,就立刻把错误返回给调用者。”
- 调用
yield_now()可以告诉操作系统:“我这帧没事干了,你可以去调度其他程序,或者让我的 CPU 核心休息一下。” 这可以显著降低程序的功耗和发热。
第五部分:程序入口 (main)
fn main() -> GameResult {
let cb = ContextBuilder::new("space_shooter", "微信公众号:会编码")
.window_setup(ggez::conf::WindowSetup::default().title("Space Shooter - 微信公众号:会编码"))
.window_mode(
ggez::conf::WindowMode::default()
.dimensions(SCREEN_WIDTH, SCREEN_HEIGHT)
.resizable(false),
);
let (ctx, event_loop) = cb.build()?;
let state = MainState::new()?;
event::run(ctx, event_loop, state)
}
- 解释:这是程序的起点。它创建了游戏窗口,设置了标题和大小,初始化了
MainState,最后启动了ggez的事件循环。游戏从此刻开始正式运行。
总结
这段代码虽然不长,但麻雀虽小五脏俱全。它展示了 ggez 框架最标准的项目结构:
- 定义数据 (Structs)
- 实现逻辑 (update - 处理输入和移动)
- 实现渲染 (draw - 绘制图形)
- 启动循环 (main)
这是一个非常完美的 2D 游戏开发入门范例,这种结构很清爽。