Rust游戏开发宏库macroquad_macro的使用,简化2D游戏渲染与跨平台开发

Rust游戏开发宏库macroquad_macro的使用,简化2D游戏渲染与跨平台开发

简介

macroquad_macro是macroquad游戏引擎的内部宏库,用于简化2D游戏开发和跨平台部署。它提供了便捷的宏来定义游戏主循环和窗口配置。

安装

在项目目录中运行以下Cargo命令:

cargo add macroquad_macro

或者在Cargo.toml中添加:

macroquad_macro = "0.1.8"

基本用法

示例代码

// 使用macroquad_macro创建简单的2D游戏
use macroquad::prelude::*;

#[macroquad::main("MyGame")]
async fn main() {
    loop {
        clear_background(BLACK);
        
        // 绘制一个简单的红色方块
        draw_rectangle(screen_width() / 2.0 - 30.0, 
                      screen_height() / 2.0 - 30.0, 
                      60.0, 60.0, 
                      RED);
        
        next_frame().await;
    }
}

完整示例

下面是一个完整的2D游戏示例,展示了如何使用macroquad_macro简化游戏开发:

use macroquad::prelude::*;

// 定义一个简单的游戏状态
struct GameState {
    player_pos: Vec2,
    player_speed: f32,
    score: u32,
}

#[macroquad::main("Simple Game")]
async fn main() {
    // 初始化游戏状态
    let mut game = GameState {
        player_pos: vec2(screen_width() / 2.0, screen_height() / 2.0),
        player_speed: 5.0,
        score: 0,
    };

    // 加载纹理
    let texture: Texture2D = load_texture("assets/player.png").await.unwrap();

    loop {
        // 处理输入
        if is_key_down(KeyCode::Right) {
            game.player_pos.x += game.player_speed;
        }
        if is_key_down(KeyCode::Left) {
            game.player_pos.x -= game.player_speed;
        }
        if is_key_down(KeyCode::Up) {
            game.player_pos.y -= game.player_speed;
        }
        if is_key_down(KeyCode::Down) {
            game.player_pos.y += game.player_speed;
        }

        // 边界检查
        game.player_pos.x = game.player_pos.x.clamp(0.0, screen_width());
        game.player_pos.y = game.player_pos.y.clamp(0.0, screen_height());

        // 渲染
        clear_background(BLACK);

        // 绘制玩家
        draw_texture(texture, game.player_pos.x, game.player_pos.y, WHITE);

        // 绘制分数
        draw_text(
            &format!("Score: {}", game.score),
            20.0,
            20.0,
            30.0,
            WHITE,
        );

        // 每帧增加分数
        game.score += 1;

        next_frame().await;
    }
}

特性

  1. 简化窗口创建:通过#[macroquad::main]宏自动处理窗口创建和事件循环
  2. 跨平台支持:支持Windows、macOS、Linux、WebAssembly和移动平台
  3. 异步支持:内置async/await支持,简化资源加载和游戏逻辑
  4. 2D渲染:提供简单的2D渲染API,包括精灵、形状和文本绘制

许可证

本项目采用MIT或Apache-2.0许可证。


1 回复

macroquad_macro: 简化Rust 2D游戏开发与跨平台渲染的宏库

介绍

macroquad_macro是macroquad游戏引擎的配套宏库,它提供了一系列过程宏来简化2D游戏开发流程,特别适合快速原型开发和跨平台项目。macroquad本身是一个轻量级的、即时模式的游戏框架,灵感来自Raylib,而macroquad_macro则通过宏进一步简化了常见模式。

主要特性

  • 简化窗口和游戏循环配置
  • 自动处理跨平台渲染
  • 提供便捷的着色器和资源加载宏
  • 减少样板代码
  • 支持WebAssembly目标

基本使用方法

1. 添加依赖

首先在Cargo.toml中添加依赖:

[dependencies]
macroquad = "0.4"
macroquad_macro = "0.4"

2. 基本游戏结构

使用main!宏简化入口点和游戏循环:

use macroquad::prelude::*;
use macroquad_macro::main;

#[main]
async fn main() {
    loop {
        clear_background(BLACK);
        draw_text("Hello macroquad!", 20.0, 20.0, 30.0, WHITE);
        next_frame().await;
    }
}

3. 配置窗口

使用window!宏配置窗口属性:

#[window]
struct WindowConfig {
    title: String,
    width: i32,
    height: i32,
    resizable: bool,
}

#[main]
async fn main() {
    // 游戏代码...
}

4. 着色器宏

shader!宏简化了着色器的加载:

use macroquad_macro::shader;

let shader = shader! {
    vertex: "
        #version 100
        attribute vec3 position;
        void main() {
            gl_Position = vec4(position, 1.0);
        }
    ",
    fragment: "
        #version 100
        void main() {
            gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
        }
    ",
}.unwrap();

5. 资源加载宏

resource!宏简化了资源路径处理:

use macroquad_macro::resource;

let texture = load_texture(resource!("assets/player.png")).await.unwrap();

完整示例:简单2D游戏

use macroquad::prelude::*;
use macroquad_macro::{main, window, resource};

#[window]
struct WindowConfig {
    title: String,
    width: i32,
    height: i32,
}

struct Player {
    position: Vec2,
    texture: Texture2D,
}

impl Player {
    async fn new() -> Self {
        Self {
            position: vec2(100.0, 100.0),
            texture: load_texture(resource!("assets/player.png")).await.unwrap(),
        }
    }

    fn update(&mut self) {
        if is_key_down(KeyCode::Right) {
            self.position.x += 2.0;
        }
        if is_key_down(KeyCode::Left) {
            self.position.x -= 2.0;
        }
    }

    fn draw(&self) {
        draw_texture(self.texture, self.position.x, self.position.y, WHITE);
    }
}

#[main]
async fn main() {
    let mut player = Player::new().await;

    loop {
        player.update();

        clear_background(BLUE);
        player.draw();

        next_frame().await;
    }
}

跨平台开发

macroquad_macro的一个主要优势是它简化了跨平台开发。相同的代码可以编译为:

  • Windows/macOS/Linux原生应用
  • WebAssembly (通过cargo build --target wasm32-unknown-unknown)
  • Android/iOS (需要额外配置)

高级用法

自定义渲染管道

use macroquad_macro::pipeline;

pipeline! {
    name: "custom_pipeline",
    params: {
        time: f32,
    },
    vertex: "
        #version 100
        attribute vec3 position;
        void main() {
            gl_Position = vec4(position, 1.0);
        }
    ",
    fragment: "
        #version 100
        uniform float time;
        void main() {
            gl_FragColor = vec4(sin(time), 0.0, 0.0, 1.0);
        }
    ",
}

场景管理

虽然macroquad是即时模式的,但可以结合宏实现简单的场景管理:

use macroquad_macro::scene;

scene! {
    enum GameScene {
        Menu,
        Game,
        Pause,
    }
}

#[main]
async fn main() {
    let mut current_scene = GameScene::Menu;

    loop {
        match current_scene {
            GameScene::Menu => {
                if is_key_pressed(KeyCode::Enter) {
                    current_scene = GameScene::Game;
                }
                // 绘制菜单...
            }
            GameScene::Game => {
                // 游戏逻辑...
                if is_key_pressed(KeyCode::Escape) {
                    current_scene = GameScene::Pause;
                }
            }
            GameScene::Pause => {
                // 暂停逻辑...
            }
        }

        next_frame().await;
    }
}

完整示例Demo

下面是一个完整的2D游戏示例,包含场景管理、角色控制和简单的碰撞检测:

use macroquad::prelude::*;
use macroquad_macro::{main, window, resource, scene};

// 定义游戏场景
scene! {
    enum GameScene {
        MainMenu,
        GamePlay,
        GameOver,
    }
}

// 窗口配置
#[window]
struct WindowConfig {
    title: String,
    width: i32,
    height: i32,
    fullscreen: bool,
}

// 玩家角色
struct Player {
    rect: Rect,
    speed: f32,
    texture: Texture2D,
}

impl Player {
    async fn new() -> Self {
        Self {
            rect: Rect::new(100.0, 100.0, 50.0, 50.0),
            speed: 5.0,
            texture: load_texture(resource!("assets/player.png")).await.unwrap(),
        }
    }

    fn update(&mut self) {
        // 键盘控制移动
        if is_key_down(KeyCode::Right) {
            self.rect.x += self.speed;
        }
        if is_key_down(KeyCode::Left) {
            self.rect.x -= self.speed;
        }
        if is_key_down(KeyCode::Up) {
            self.rect.y -= self.speed;
        }
        if is_key_down(KeyCode::Down) {
            self.rect.y += self.speed;
        }

        // 简单的边界检查
        self.rect.x = self.rect.x.clamp(0.0, screen_width() - self.rect.w);
        self.rect.y = self.rect.y.clamp(0.0, screen_height() - self.rect.h);
    }

    fn draw(&self) {
        draw_texture_ex(
            self.texture,
            self.rect.x,
            self.rect.y,
            WHITE,
            DrawTextureParams {
                dest_size: Some(vec2(self.rect.w, self.rect.h)),
                ..Default::default()
            },
        );
    }
}

// 敌人角色
struct Enemy {
    rect: Rect,
    speed: f32,
    texture: Texture2D,
}

impl Enemy {
    async fn new(x: f32, y: f32) -> Self {
        Self {
            rect: Rect::new(x, y, 40.0, 40.0),
            speed: 2.0,
            texture: load_texture(resource!("assets/enemy.png")).await.unwrap(),
        }
    }

    fn update(&mut self, player_rect: &Rect) {
        // 简单AI:向玩家移动
        if self.rect.x < player_rect.x {
            self.rect.x += self.speed;
        } else {
            self.rect.x -= self.speed;
        }
        
        if self.rect.y < player_rect.y {
            self.rect.y += self.speed;
        } else {
            self.rect.y -= self.speed;
        }
    }

    fn draw(&self) {
        draw_texture_ex(
            self.texture,
            self.rect.x,
            self.rect.y,
            WHITE,
            DrawTextureParams {
                dest_size: Some(vec2(self.rect.w, self.rect.h)),
                ..Default::default()
            },
        );
    }
}

#[main]
async fn main() {
    // 初始化游戏状态
    let mut current_scene = GameScene::MainMenu;
    let mut player = Player::new().await;
    let mut enemies = vec![
        Enemy::new(200.0, 200.0).await,
        Enemy::new(400.0, 300.0).await,
        Enemy::new(100.0, 400.0).await,
    ];
    let mut score = 0;
    let font = load_ttf_font(resource!("assets/font.ttf")).await.unwrap();

    loop {
        match current_scene {
            GameScene::MainMenu => {
                // 绘制主菜单
                clear_background(BLACK);
                draw_text_ex(
                    "RUST GAME DEMO",
                    screen_width() / 2. - 150.,
                    screen_height() / 2. - 50.,
                    TextParams {
                        font,
                        font_size: 48,
                        color: WHITE,
                        ..Default::default()
                    },
                );
                draw_text_ex(
                    "按ENTER键开始游戏",
                    screen_width() / 2. - 120.,
                    screen_height() / 2. + 50.,
                    TextParams {
                        font,
                        font_size: 24,
                        color: WHITE,
                        ..Default::default()
                    },
                );

                if is_key_pressed(KeyCode::Enter) {
                    current_scene = GameScene::GamePlay;
                }
            }
            
            GameScene::GamePlay => {
                // 更新游戏逻辑
                player.update();
                
                for enemy in &mut enemies {
                    enemy.update(&player.rect);
                    
                    // 碰撞检测
                    if player.rect.overlaps(&enemy.rect) {
                        current_scene = GameScene::GameOver;
                    }
                }
                
                // 绘制游戏场景
                clear_background(Color::from_rgba(30, 30, 40, 255));
                player.draw();
                
                for enemy in &enemies {
                    enemy.draw();
                }
                
                // 绘制分数
                draw_text_ex(
                    &format!("分数: {}", score),
                    20.0,
                    40.0,
                    TextParams {
                        font,
                        font_size: 32,
                        color: WHITE,
                        ..Default::default()
                    },
                );
                
                score += 1;
                
                if is_key_pressed(KeyCode::Escape) {
                    current_scene = GameScene::MainMenu;
                }
            }
            
            GameScene::GameOver => {
                // 绘制游戏结束画面
                clear_background(Color::from_rgba(40, 10, 10, 255));
                draw_text_ex(
                    "游戏结束!",
                    screen_width() / 2. - 80.,
                    screen_height() / 2. - 50.,
                    TextParams {
                        font,
                        font_size: 48,
                        color: WHITE,
                        ..Default::default()
                    },
                );
                draw_text_ex(
                    &format!("最终分数: {}", score),
                    screen_width() / 2. - 100.,
                    screen_height() / 2. + 20.,
                    TextParams {
                        font,
                        font_size: 32,
                        color: WHITE,
                        ..Default::default()
                    },
                );
                draw_text_ex(
                    "按R键重新开始",
                    screen_width() / 2. - 100.,
                    screen_height() / 2. + 80.,
                    TextParams {
                        font,
                        font_size: 24,
                        color: WHITE,
                        ..Default::default()
                    },
                );
                
                if is_key_pressed(KeyCode::R) {
                    // 重置游戏状态
                    player = Player::new().await;
                    enemies = vec![
                        Enemy::new(200.0, 200.0).await,
                        Enemy::new(400.0, 300.0).await,
                        Enemy::new(100.0, 400.0).await,
                    ];
                    score = 0;
                    current_scene = GameScene::GamePlay;
                }
                
                if is_key_pressed(KeyCode::Escape) {
                    current_scene = GameScene::MainMenu;
                }
            }
        }

        next_frame().await;
    }
}

总结

macroquad_macro通过提供一系列便利宏,显著简化了Rust中2D游戏开发的流程,特别是:

  1. 减少了初始化代码
  2. 统一了跨平台资源管理
  3. 简化了着色器使用
  4. 提供了更简洁的游戏循环结构

对于想要快速开发2D游戏原型或小型游戏的Rust开发者来说,macroquad_macro是一个值得考虑的选择。

回到顶部