C64 Basic地牢探险游戏教程:哥布林攻击(第8部分)
速览
这是C64 Basic编程系列教程的第8部分,专注于开发一个地牢探险游戏(Dungeon Crawler)。本教程详细讲解了如何实现哥布林攻击(Goblin Attack)功能,包括敌人移动与碰撞检测。该系列面向复古编程爱好者,展示在8位计算机上使用Basic语言构建游戏的技术。
AI 深度解读
背景
这是《C64 Basic Dungeon Crawler》系列教程的第八部分,前作(第七部分)中,程序员构建了一个充满哥布林的地牢,但这些哥布林像花园小矮人一样毫无威胁——它们只会站在原地,玩家可以走过去戳一下,掉5点血然后走开,过一会儿回来它们还在原地。本部分的核心目标是让哥布林学会追逐玩家,并且引入真正的战斗系统、自定义按键,以及修复一个诡异的字符集内存损坏 bug。作者强调,这个 bug 的排查过程是整个教程中最有价值的教训。
核心内容
本部分实现的四个改变
- 自定义按键:玩家不再只能使用默认的 QAOP(向上/左/下/右 + 空格键),而是可以自由映射任何按键。
- 哥布林拥有位置、生命值和“大脑”:每个哥布林现在有独立的坐标、血量(默认10点),并具备简单的追逐逻辑(虽然很小)。
- 战斗系统:攻击使用真实的 d20 骰子(20面骰)加上力量修正值来计算伤害。
- 修复内存 bug:游戏不再“自食其肉”——作者在开发过程中遇到了一个字符集被损坏的奇怪 bug,本部分给出了解决方案。
自定义按键的实现细节
为了不让特定操作习惯的玩家不满(QAOP 是老派标准,WASD 是另一派的偏好),作者决定让玩家自己映射按键。关键在于:游戏代码不能再用硬编码的字母(如 Q),而必须比较当前存储的按键变量。
变量名的陷阱(Commodore BASIC 特有)
- 两字母限制:Commodore BASIC 的变量名只存储前两个字符。例如
PLAYERHEALTH和PLAYERX实际上是同一个变量PL,导致游戏行为诡异且难以调试。因此,作者特意让五个按键变量名在前两个字母上唯一:QKEY$(QK)、AKEY$(AK)、OKEY$(OK)、PKEY$(PK)、FKEY$(FK)。 - BASIC 关键字冲突:变量名中不能包含 BASIC 关键字,因为 tokenizer 会从左到右识别关键字并替换成 token。例如
GOLD中的GO会被识别为GO TO的关键字,导致语法错误。常见的陷阱包括TO、IF、ON、OR、AND等,所以SCORE(含OR)、COUNT(含ON)、TOTAL(含TO)都是危险变量名。
按键读取的实现
- 使用
GET而非INPUT。INPUT会等待用户输入并回车,且对逗号等字符敏感,不适合即时按键映射。GET从键盘缓冲区读取一个字符后立即返回,如果缓冲区为空则返回空字符串。 - 为了让游戏等待按键,需要循环检测直到
GET返回非空字符串:
CHANGETHISKEY:
GET K$: IF K$="" THEN GOTO CHANGETHISKEY
RETURN
- 映射流程:先显示当前默认键(QAOP 和空格),然后依次提示用户按下“上”、“下”、“左”、“右”、“发射”键,将结果存入对应变量。最后要求确认(Y/N),如果按 N 则跳回重新开始映射,避免误操作。
哥布林对象化:从像素到游戏对象
之前,哥布林仅仅是屏幕上的像素图案(屏幕代码 38),程序无法跟踪它们。现在需要为每个哥布林分配位置、血量,并能够移动和判断存亡。作者使用并行数组(parallel arrays)来模拟记录/结构体:
REM DECLARE ENEMY VARIABLES
GC=0: REM 哥布林计数器
DIM GH(10) : REM 哥布林血量数组
FOR GC=1 TO 10: GH(GC)=10: NEXT GC
GC=0
DIM GX(10) : DIM GY(10) : REM 哥布林坐标列表
GX、GY分别存储每个哥布林的列和行,GH存储血量,GC是实际放置的哥布林数量。- 循环中复用
GC作为计数器来初始化血量,之后再重置为 0。这种并行数组方式在 8 位机器上比替代方案(如字符串或复杂数据结构)更高效。
战斗系统
战斗使用真实的 d20 骰子(20面骰)加上力量修正值。原文未给出完整战斗代码,但明确提到“Combat happens with a real d20 roll and a strength modifier”。
内存 bug 的教训
作者在开发过程中遇到了一个字符集被损坏的 bug,表现为字符集在游戏运行时被覆盖。该 bug 的排查过程被作者认为是本部分最有价值的教训。原文未详细展示 bug 的代码,但提醒读者不要跳过“内存部分”(memory section),并指出“the game stops eating itself”是一个比听起来更长的故事。
关键要点
- 按键自定义的核心是抽象:游戏代码不再比较硬编码的字母,而是比较变量,从而支持动态映射。这是从“写死”到“可配置”的关键一步。
- Commodore BASIC 变量名陷阱:只取前两个字符,且不能包含 BASIC 关键字。这是现代开发者容易忽视的坑,会导致难以调试的 bug。
GETvsINPUT:在即时按键映射场景下,GET更合适,但需要循环等待直到缓冲区有数据。INPUT会引入回车和特殊字符问题。- 并行数组模拟对象:在无结构体的语言中,用三个数组(坐标X、Y、血量)和计数器来表示多个哥布林,是高效且经典的做法。
- 内存 bug 的教训:字符集损坏往往源于内存覆盖,例如变量数组越界或不当的 POKE 操作。排查需要逐行检查内存地址和变量分配。
- 验证机制的重要性:按键映射后要求确认(Y/N),避免误操作后无法恢复,提供了简单的“撤销”路径。
意义与影响
本部分将一个静态的装饰性游戏(地牢生成后只有固定哥布林)升级为具有基本 AI 和战斗系统的可玩原型。按键自定义功能显著提升了用户体验,反映了在资源受限的 8 位平台上设计交互细节的思考——从“我替用户选”到“让用户自己选”。对 Commodore BASIC 变量名陷阱的深入剖析,不仅帮助了该系列读者,也为所有学习 6502 平台 BASIC 编程的人提供了宝贵的避坑指南。内存
