Ncurses 终端小游戏(一)

10次阅读
没有评论

简介

一个基于 ncurses 的简单终端小游戏,包含玩家移动与射击(子弹上移)等核心机制。项目采用模块化设计:Gui 负责渲染与输入,Game 负责游戏状态与主循环,PlayerBullet 为游戏实体。

我们将会推出系列文章以实现最终的坦克大战小游戏。本篇为第一节。

Ncurses 终端小游戏(一)

运行环境与构建

  • 系统:macOS 或 Linux(需支持 ncurses)
  • 依赖:g++makencurses
  • 构建:
make
  • 运行:
./mygame
  • 清理:
make clean

提示:按 q 退出程序(也可使用 Ctrl+C 强制结束)。


控制方式

  • 方向键: 水平移动; 垂直移动(持续速度模型)
  • 空格键:发射子弹(^ 符号向上移动)
  • q:退出游戏

边界约束:横向列范围为 [4, 66];纵向行范围约为 [1, 20]。玩家以 M 字符显示。


目录结构

Bullet.C
Bullet.h
Game.C
Game.h
Gui.C
Gui.h
Player.C
Player.h
main.C
makefile
output/

模块设计与实现细节

Gui(终端图形 / 输入封装)

文件:Gui.h, Gui.C

  • 初始化与模式设置:
    • initscr(), noecho(), cbreak(), keypad(win, true), nodelay(win, true)
    • 关闭光标 curs_set(0),非阻塞输入(游戏循环无需卡顿等待按键)。
  • 边框绘制:
    • 在列 466 画竖线作为左右边界,高度 20 行。
    • clear() 会先擦除窗口再重画边界,确保每帧干净刷新。
  • 输入与绘制:
    • get() 使用 wgetch(win) 读取按键;flushinp() 清空输入缓存。
    • paintat(row, col, ch) 在指定位置绘制字符并立即 wrefresh(win)
  • 资源收尾:~Gui()end() 最终调用 endwin() 恢复终端状态。

Game(游戏状态 / 主循环调度)

文件:Game.h, Game.C

  • 成员:
    • Gui gui:渲染与输入。
    • Player* pl:玩家对象指针。
    • std::list<Bullet*> bullets:子弹容器。
  • 生命周期:
    • 构造时 gui.init() 并创建玩家:pl = new Player(this)
    • 析构时调用 gui.end()(终止 ncurses)。
  • 重要方法:
    • addBullet(r, c):在位置 (r, c) 生成子弹并加入 bullets
    • update()(每帧):
      1. gui.clear() 清屏并重画边界;
      2. 读取输入 int c = gui.get()
      3. pl->update(c) 更新玩家(处理输入、移动、发射、绘制);
      4. 遍历 bullets 调用 update(),并对“越界”子弹执行删除与 erase

注意:当前 update()erase 后仍做一次 bi++,可能导致跳过元素(详见“已知问题”)。

Player(玩家实体)

文件:Player.h, Player.C

  • 状态:
    • 位置:size_t row, size_t col,初始为 (18, 25)
    • 速度 / 方向:int direction(水平速度,初始 0)、int getrow(垂直速度,未显式初始化,详见“已知问题”)。
    • Game* game:回调到 Game 用于发射子弹与绘制。
  • 输入与运动模型(持续速度):
    • KEY_LEFT/KEY_RIGHT:将 direction 递减 / 递增(按一次改变一次速度)
    • KEY_UP/KEY_DOWN:将 getrow 递减 / 递增(按一次改变一次速度)
    • space:设置发射标志,调用 game->addBullet(row, col) 生成子弹。
  • 移动与边界:
    • 每帧 col += direction; row += getrow;
    • 触碰左右边界时夹紧并将 direction = 0;当 row > 20 时夹紧并将 getrow = 0
    • 绘制玩家:game->paintat(row, col, 'M')

Bullet(子弹实体)

文件:Bullet.h, Bullet.C

  • 状态:row, colGame*
  • 行为:
    • update():若 row > 1row-- 上移一行,并在当前位置绘制 '^'
    • out()row <= 0 视为越界(由 Game::update() 删除)。

main(时间步进驱动)

文件:main.C

  • 使用 gettimeofday 获取毫秒级时间,设定固定步长约 50ms(≈20 FPS)。
  • 主循环:
    1. 控制时间步进(若未到 50ms 就 usleep(26) 小休);
    2. 调用 game.update() 进行一帧更新;
    3. 更新“上一帧时间戳”。

makefile(构建脚本)

文件:makefile

  • 编译器与库:g++-lncurses 链接 ncurses。
  • 目标:mygame,依赖 main.o Gui.o Game.o Player.o Bullet.o
  • 常用命令:makemake clean

游戏循环与帧率

  • 固定时间步长(每帧 50ms)保证不同机器上表现一致。
  • Gui 采用非阻塞输入,避免在无按键时卡住主循环。
  • 每帧顺序:清屏 / 重画边界 → 读取输入 → 更新玩家 → 更新子弹 → 绘制刷新。

内存与资源管理

  • Game 在构造中 new Player,在运行时通过 addBullet new 子弹。
  • 子弹在 out()true 时由 Game::update() deleteerase
  • Gui 析构与 Game 析构中调用 endwin(),确保终端恢复。

扩展方向

  • 敌人实体与碰撞检测、得分系统、生命 / 血量。
  • 粒子 / 尾迹效果、不同子弹类型与射速。
  • 更丰富的 UI:帧率 / 得分显示、开始 / 暂停 / 结束界面。
  • 配置化边界与地图,或多关卡设计。

我们将在下一节讲解实现更多功能


快速开始

# 构建
make

# 运行
./mygame

# 清理
make clean

发生构建问题时,请确认已安装 Xcode Command Line Tools(macOS)或系统提供的 ncurses 开发包(Linux)。

源码

点击下载

正文完
 0
评论(没有评论)