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 系统上应该可以直接运行。 你可以根据需要扩展和完善这个游戏。

相关推荐
资源分享助手5 小时前
我!勇者?The Warrior免安装中文版下载与玩法体验
游戏
云起SAAS7 小时前
抖音小游戏源码 - 消消乐 | 含激励广告+成就系统 | 开箱即用商业级消除游戏模板
android·游戏·广告联盟·看激励广告联盟流量主·抖音小游戏源码 - 消消乐
津津有味道8 小时前
一键写入启动游戏NDEF复合记录NFC标签vb6源码
游戏·标签·nfc·ndef·复合记录
游乐码8 小时前
Unity基础(四)向量相关
游戏·unity·游戏引擎
阿阳微客10 小时前
网易Buff游戏搬砖,长期可做!
笔记·学习·游戏
Kurisu57510 小时前
探灵直播2026最新官方正版免费下载 一键转存 永久更新 (看到速转存 资源随时走丢)
游戏·游戏引擎·游戏程序·动画·关卡设计
STDD11 小时前
Abiotic Factor多人生存建筑游戏《非生物因素》 专用服务器搭建教程
服务器·数据库·游戏
开开心心就好12 小时前
带OCR识别的电子发票打印工具
运维·javascript·科技·游戏·青少年编程·ocr·powerpoint
经济元宇宙15 小时前
HOPE星火燎原不是希望工程,也不是游戏项目:项目名称与定位澄清
游戏
2601_9503160615 小时前
XBOX360 KINECT体感游戏合集109个
游戏