Rust 中的 2D 游戏渲染器 — 让我们来制作一个迷你 RPG!

更新于 2026-01-18

Trish 2024-10-28

简介

欢迎来到 Rust 的游戏开发世界!今天,我们将构建一个简单的 2D RPG 游戏渲染器。我们将使用 ggez 来处理图形需求,专注于创建一个玩家可以在基于瓦片(tile-based)的地图上移动角色、与物体互动,甚至可能与一些基础敌人战斗的游戏。

前置要求

  • 具备中级 Rust 编程知识
  • 对 2D 图形有基本理解
  • 熟悉游戏循环(game loop)的概念

项目结构

让我们先组织好项目:

mkdir rust-2d-rpg
cd rust-2d-rpg
cargo init --bin

以下是我们的文件夹结构:

rust-2d-rpg/
│
├── src/
│   ├── main.rs
│   ├── game.rs
│   ├── map.rs
│   ├── player.rs
│   ├── enemy.rs
│   └── assets/
│       ├── player.png
│       ├── enemy.png
│       └── tile.png
│
├── Cargo.toml
└── README.md

第一步:配置 Cargo.toml

Cargo.toml 中添加以下依赖项:

[package]
name = "rust-2d-rpg"
version = "0.1.0"
edition = "2018"

[dependencies]
ggez = "0.6.1"
rand = "0.8.4"

第二步:main.rs —— 程序入口点

mod game;
mod map;
mod player;
mod enemy;

use ggez::{Context, ContextBuilder, GameResult};
use ggez::event::{self, EventHandler};

fn main() -> GameResult {
    let cb = ContextBuilder::new("rust_2d_rpg", "YourName")
        .window_setup(ggez::conf::WindowSetup::default().title("2D RPG in Rust"));
    let (mut ctx, event_loop) = cb.build()?;
    let game = game::Game::new(&mut ctx)?;
    event::run(ctx, event_loop, game)
}

第三步:game.rs —— 游戏逻辑

use ggez::{Context, GameResult};
use ggez::event::{self, EventHandler};
use ggez::graphics;

pub struct Game {
    player: player::Player,
    map: map::Map,
    enemies: Vec<enemy::Enemy>,
}

impl Game {
    pub fn new(ctx: &mut Context) -> GameResult<Game> {
        let player = player::Player::new(ctx)?;
        let map = map::Map::new();
        let enemies = vec![enemy::Enemy::new(ctx)?];
        Ok(Game { player, map, enemies })
    }
}

impl EventHandler for Game {
    fn update(&mut self, _ctx: &mut Context) -> GameResult {
        // 更新玩家移动、敌人行为等逻辑
        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult {
        graphics::clear(ctx, [0.1, 0.2, 0.3, 1.0].into());
        self.map.draw(ctx)?;
        self.player.draw(ctx)?;
        for enemy in &self.enemies {
            enemy.draw(ctx)?;
        }
        graphics::present(ctx)?;
        Ok(())
    }
}

第四步:player.rs —— 玩家管理

use ggez::{Context, GameResult};
use ggez::graphics::{self, Rect};
use ggez::nalgebra as na;

pub struct Player {
    position: na::Point2<f32>,
}

impl Player {
    pub fn new(_ctx: &mut Context) -> GameResult<Player> {
        Ok(Player { position: na::Point2::new(100.0, 100.0) })
    }

    pub fn draw(&self, ctx: &mut Context) -> GameResult {
        let rectangle = graphics::Mesh::new_rectangle(
            ctx,
            graphics::DrawMode::fill(),
            Rect::new(self.position.x, self.position.y, 32.0, 32.0),
            graphics::Color::new(0.0, 1.0, 0.0, 1.0),
        )?;
        graphics::draw(ctx, &rectangle, (na::Point2::new(0.0, 0.0),))
    }

    // 添加用于移动、碰撞检测等的方法
}

第五步:map.rs —— 世界生成

use ggez::{Context, GameResult};
use ggez::graphics;
use ggez::nalgebra as na;

pub struct Map {
    // 简化的地图表示
    tiles: Vec<Vec<bool>>,
}

impl Map {
    pub fn new() -> Map {
        let tiles = vec![vec![true; 20]; 15];
        Map { tiles }
    }

    pub fn draw(&self, ctx: &mut Context) -> GameResult {
        let tile_size = 32.0;
        for (y, row) in self.tiles.iter().enumerate() {
            for (x, &tile) in row.iter().enumerate() {
                if tile {
                    let rectangle = graphics::Mesh::new_rectangle(
                        ctx,
                        graphics::DrawMode::fill(),
                        graphics::Rect::new((x as f32) * tile_size, (y as f32) * tile_size, tile_size, tile_size),
                        [0.5, 0.5, 0.5, 1.0].into(),
                    )?;
                    graphics::draw(ctx, &rectangle, (na::Point2::new(0.0, 0.0),))?;
                }
            }
        }
        Ok(())
    }
}

第六步:enemy.rs —— 敌人管理

use ggez::{Context, GameResult};
use ggez::graphics;
use ggez::nalgebra as na;

pub struct Enemy {
    position: na::Point2<f32>,
}

impl Enemy {
    pub fn new(ctx: &mut Context) -> GameResult<Enemy> {
        Ok(Enemy { position: na::Point2::new(200.0, 200.0) })
    }

    pub fn draw(&self, ctx: &mut Context) -> GameResult {
        let rectangle = graphics::Mesh::new_rectangle(
            ctx,
            graphics::DrawMode::fill(),
            graphics::Rect::new(self.position.x, self.position.y, 32.0, 32.0),
            graphics::Color::new(1.0, 0.0, 0.0, 1.0),
        )?;
        graphics::draw(ctx, &rectangle, (na::Point2::new(0.0, 0.0),))
    }

    // 添加敌人移动、AI 等方法
}

第七步:运行游戏

要运行游戏,请执行:

cargo run

说明

  • 主循环(Main Loop):在 main.rs 中,我们设置了 ggez 上下文和游戏循环。
  • 游戏状态(Game State)game.rs 保存了整体的游戏状态,包括玩家、地图和敌人。
  • 图形渲染(Graphics):每个组件(Player、Map、Enemy)都有自己的 draw 方法来处理渲染。
  • 可扩展性(Expansion):这个基础框架可以进一步扩展,例如:
    • 使用键盘输入实现玩家移动。
    • 实现玩家与地图/敌人的碰撞检测。
    • 为敌人添加基础 AI 行为或移动逻辑。
    • 添加物品库存系统。
    • 实现战斗机制或环境交互功能。

结语

现在你已经用 Rust 构建了一个基础的 2D RPG 游戏渲染器!该项目向你介绍了游戏开发的基本概念、使用 ggez 进行图形处理,以及模块化的代码组织方式。

你可以在此基础上继续扩展,例如:

  • 实现玩家输入以控制角色移动。
  • 添加不同类型的瓦片或可交互对象。
  • 创建战斗系统或任务系统。
  • 加入音效或更复杂的图形效果。

这个项目是学习高级 Rust 编程和游戏开发原理的绝佳平台。不断探索,并享受创造属于你自己的游戏世界的乐趣吧!