zig语言初探:来写贪吃蛇游戏

Zig 语言简介

Zig 是一种系统编程语言,注重安全、性能和可维护性。具有简洁的语法,支持强大的编译时功能,旨在取代 C 语言,适合系统级编程。

本文使用 Zig 实现贪吃蛇游戏,利用 curses 库进行终端显示,包含蛇的移动、食物生成、碰撞检测等逻辑,通过枚举和结构体定义方向和蛇身,使用动态数组存储蛇身,以循环更新游戏状态。

用zig来写贪吃蛇游戏的优缺点

优点:利用 Zig 的特性保证一定性能和安全性,简洁的语法使代码清晰。

缺点:对 curses 库依赖在 Windows 需额外配置,代码中随机数生成方式较简单,可能不够随机。

以下是一个简单的贪吃蛇游戏的 Zig 代码示例:

cpp 复制代码
// 导入 Zig 的标准库
const std = @import("std");
// 导入 C 的 curses 库,用于在终端中进行图形绘制和输入处理
const c = @cImport({
    @cInclude("curses.h");
});

// 定义游戏窗口的宽度
const WIDTH = 80;
// 定义游戏窗口的高度
const HEIGHT = 24;
// 定义蛇的初始长度
const INITIAL_LENGTH = 4;

// 定义一个枚举类型,表示蛇的移动方向
const Direction = enum { UP, DOWN, LEFT, RIGHT };

// 定义一个结构体,表示蛇的一个身体部分,包含 x 和 y 坐标
const SnakeSegment = struct {
    x: i32,
    y: i32,
};

// 存储蛇的身体部分的动态数组
var snake: std.ArrayList(SnakeSegment) = undefined;
// 存储蛇的当前移动方向
var direction: Direction = Direction.RIGHT;
// 存储食物的 x 坐标
var food_x: i32 = 0;
// 存储食物的 y 坐标
var food_y: i32 = 0;
// 标记游戏是否结束
var game_over: bool = false;

// 初始化游戏,设置 curses 库的属性,初始化蛇和食物
fn init()!void {
    // 初始化 curses 库
    c.initscr();
    // 开启 cbreak 模式,字符输入直接可用,无需等待回车键
    c.cbreak();
    // 关闭输入字符的回显
    c.noecho();
    // 启用键盘特殊键处理
    c.keypad(c.stdscr, c.TRUE);
    // 隐藏光标
    c.curs_set(0);
    // 确保 snake 列表有足够容量存储初始长度的蛇身
    try snake.ensureTotalCapacity(INITIAL_LENGTH);
    // 初始化蛇身,从左到右依次添加
    for (0..INITIAL_LENGTH) |i| {
        try snake.append(SnakeSegment{.x = i + 1,.y = 1 });
    }
    // 生成第一个食物
    spawnFood();
}

// 关闭 curses 库,结束游戏时清理资源
fn deinit() void {
    c.endwin();
}

// 在随机位置生成食物
fn spawnFood() void {
    // 使用默认随机数生成器在指定范围内生成食物的 x 坐标
    food_x = std.random.intRangeAtMost(std.rand.DefaultPrng.init(0).random(), 1, WIDTH - 2);
    // 使用默认随机数生成器在指定范围内生成食物的 y 坐标
    food_y = std.random.intRangeAtMost(std.rand.DefaultPrng.init(0).random(), 1, HEIGHT - 2);
}

// 清除屏幕并绘制蛇和食物
fn draw() void {
    // 清除屏幕
    c.clear();
    // 绘制蛇的每个身体部分
    for (snake.items) |segment| {
        c.mvaddch(@intCast(c_int, segment.y), @intCast(c_int, segment.x), c.ACS_CKBOARD);
    }
    // 绘制食物
    c.mvaddch(@intCast(c_int, food_y), @intCast(c_int, food_x), c.ACS_DIAMOND);
    // 刷新屏幕显示
    c.refresh();
}

// 更新蛇的位置,处理碰撞检测等
fn update()!void {
    // 获取蛇的头部位置
    var head = snake.items[snake.items.len - 1];
    // 根据当前方向更新头部位置
    switch (direction) {
        Direction.UP => head.y -= 1,
        Direction.DOWN => head.y += 1,
        Direction.LEFT => head.x -= 1,
        Direction.RIGHT => head.x += 1,
    }
    // 检查蛇是否撞到边界
    if (head.x == 0 or head.x == WIDTH - 1 or head.y == 0 or head.y == HEIGHT - 1) {
        game_over = true;
        return;
    }
    // 检查蛇是否撞到自身
    for (snake.items[0..snake.items.len - 1]) |segment| {
        if (segment.x == head.x and segment.y == head.y) {
            game_over = true;
            return;
        }
    }
    // 在尾部添加新的头部位置
    try snake.append(head);
    // 如果蛇吃到食物,生成新食物,否则移除尾部
    if (head.x == food_x and head.y == food_y) {
        spawnFood();
    } else {
        _ = snake.orderedRemove(0);
    }
}

// 处理用户输入,更新蛇的移动方向
fn handleInput()!void {
    // 获取用户输入的字符
    const ch = c.getch();
    // 根据输入的字符更新蛇的方向
    switch (ch) {
        c.KEY_UP => direction = Direction.UP,
        c.KEY_DOWN => direction = Direction.DOWN,
        c.KEY_LEFT => direction = Direction.LEFT,
        c.KEY_RIGHT => direction = Direction.RIGHT,
        else => {},
    }
}

pub fn main()!void {
    // 创建一个通用目的分配器
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    // 获取分配器
    const allocator = gpa.allocator();
    // 初始化存储蛇身的动态数组
    snake = std.ArrayList(SnakeSegment).init(allocator);
    // 程序结束时释放资源
    defer snake.deinit();
    // 初始化游戏
    try init();
    // 程序结束时清理 curses 库
    defer deinit();
    // 游戏循环
    while (!game_over) {
        // 绘制游戏界面
        draw();
        // 处理用户输入
        try handleInput();
        // 更新游戏状态
        try update();
        // 睡眠一段时间,控制游戏速度
        std.time.sleep(100 * std.time.ns_per_ms);
    }
    // 显示游戏结束信息
    c.mvprintw(HEIGHT / 2, WIDTH / 2 - 5, "Game Over");
    c.refresh();
    // 暂停一段时间,让用户看到游戏结束信息
    std.time.sleep(3 * std.time.ns_per_s);
}

代码解释

  • 导入部分
    • const std = @import("std");:导入 Zig 的标准库。
    • const c = @cImport({ @cInclude("curses.h"); });:导入 C 的 curses 库,用于在终端中进行图形绘制和输入处理。
  • 常量部分
    • WIDTHHEIGHT:定义游戏窗口的宽度和高度。
    • INITIAL_LENGTH:蛇的初始长度。
    • Direction:定义一个枚举类型,表示蛇的移动方向。
    • SnakeSegment:定义一个结构体,表示蛇的一个身体部分,包含 xy 坐标。
  • 全局变量部分
    • snake:存储蛇的身体部分的动态数组。
    • direction:存储蛇的当前移动方向。
    • food_xfood_y:存储食物的位置。
    • game_over:标记游戏是否结束。
  • 函数部分
    • init():初始化 curses 库,设置一些 curses 的属性,初始化蛇的初始位置和长度,并生成第一个食物。
    • deinit():关闭 curses 库。
    • spawnFood():在随机位置生成食物。
    • draw():清除屏幕并绘制蛇和食物。
    • update():更新蛇的位置,处理碰撞检测,根据是否吃到食物来增长或缩短蛇,并检查游戏是否结束。
    • handleInput():根据用户输入更新蛇的移动方向。
    • main():主函数,创建 GeneralPurposeAllocator 分配内存,初始化 snake 列表,调用 init(),在游戏循环中调用 draw()handleInput()update(),并处理游戏结束状态。

使用说明

  • 编译代码:你可以使用 zig build-exe 命令编译这个程序,例如 zig build-exe snake.zig
  • 运行程序:编译成功后,在终端中运行生成的可执行文件。
  • 控制蛇:使用箭头键控制蛇的移动。

请注意,这只是一个非常简单的示例,还有很多可以改进的地方,例如增加分数、增加难度、添加边界检查、处理错误等。此外,这个程序使用了 curses 库进行终端绘制,因此在 Windows 系统上可能需要额外的配置,而在 Linux 或 macOS 系统上应该可以直接运行。 你可以根据需要扩展和完善这个游戏。

相关推荐
在忙碌的生活里,别忘了给自己一个微笑6 小时前
抢十八游戏
c++·游戏
我想学LINUX1 天前
【2024年华为OD机试】(C卷,100分)- 攀登者1 (Java & JS & Python&C/C++)
java·c语言·javascript·c++·python·游戏·华为od
程序猿阿伟1 天前
《AI赋能鸿蒙Next,打造极致沉浸感游戏》
人工智能·游戏·harmonyos
AC100AC1 天前
[NOIP2007 提高组] 矩阵取数游戏
算法·游戏·矩阵
私人珍藏库1 天前
《变形金刚-游戏》V1.0官方学习版
学习·游戏
网易智企2 天前
游戏语音的历史是什么样的?
大数据·人工智能·游戏·ai·音视频·语音识别·实时音视频
程序猿阿伟2 天前
《探索鸿蒙Next上开发人工智能游戏应用的技术难点》
人工智能·游戏·harmonyos
我想学LINUX2 天前
【2024年华为OD机试】 (C卷,100分)- 消消乐游戏(Java & JS & Python&C/C++)
java·c语言·javascript·c++·游戏·华为od
bingbingyihao3 天前
采用GPT生成的Python 的 2048 游戏
python·gpt·游戏