[控场AI]
· 9 分钟阅读· 4,900 字

24小时不用AI从零学Rust并写出游戏:全程实录

24小时不用AI从零学Rust并写出游戏:全程实录

一场没有AI辅助的编程挑战

在AI编程助手遍地开花的今天,一位YouTube博主给自己立下了一个反潮流的挑战:在24小时内,从对Rust一无所知出发,全程不借助任何AI工具,纯手动敲键盘,最终做出一款可运行的游戏。如果失败,他就要让视频置顶评论来决定下一次学习的编程语言。

这个挑战的看点有两重:一方面,Rust连续多年被开发者评为最受喜爱的编程语言,热度居高不下;另一方面,Rust以「难学」著称。它不会容忍任何草率的代码,编译器会逐一检查每个细节,稍有不妥就直接拒绝运行。用博主的话说,「它非常固执,就像我一样。」

整个挑战分三个阶段:从零学习Rust语法、从零掌握一款基于Rust的游戏引擎,再用这个引擎独立做出一款完整游戏——全部压缩在24小时内完成。

恶补Rust语法:熟悉又陌生的基础构件

为了赶进度,博主选了一门时长三小时的YouTube Rust入门课程,开2倍速推进。他此前主要使用Python,有约半年编程经验,但他坦言每种语言都有自己的语法体系,「就像重新学写句子,但每个词的拼写和语法规则都全变了」。

课程从经典的Hello World开始。与Python写完直接运行不同,Rust需要先编译——把代码转换成计算机可直接执行的独立程序。这一步骤背后涉及一个重要的语言分类:Python属于解释型语言,代码由解释器逐行翻译执行,开发体验灵活但运行效率相对较低;Rust则是编译型语言,源代码在运行前被整体转换为机器码,执行速度接近C/C++,这也是Rust在系统编程和游戏开发领域受到青睐的核心原因之一。

这一性能差距在现实中往往令人震惊。在Computer Language Benchmarks Game等基准测试中,Rust的执行速度通常比Python快50到100倍,内存占用仅为Python的数分之一。差距来自多个层面:Python每行代码执行前都需要解释器进行类型检查和字节码解释,而Rust代码在编译时已确定所有类型,直接生成针对目标CPU指令集优化的原生机器码;Python的整数在内部是堆上分配的对象(为支持任意精度大整数),而Rust的i32就是4字节的栈内存;Python的GIL(全局解释器锁)限制了真正的多线程并行,而Rust的所有权系统天然保证多线程安全,可充分利用现代多核CPU。对于游戏开发而言,稳定维持60帧意味着每帧预算仅16毫秒,任何GC停顿或解释器开销都会直接破坏游戏体验。

随后他依次学习了变量与数据类型、结构体(struct)、循环、函数等各语言通用的基础构件。

Rust基础构件学习

一个关键差异是:Rust没有面向对象编程。作为偏底层的语言,Rust用更简单的构件组织代码,好处是程序运行更快、开销更低,代价是许多高级语言自动处理的事情,你都得自己动手实现。值得注意的是,Rust并非完全放弃了面向对象的思想,而是通过「特征(Trait)」系统实现了类似接口和多态的能力——这是一种更灵活、更零成本抽象的设计哲学,被称为「组合优于继承」。

卡壳于所有权与借用

真正让博主头疼的是Rust独有的两个概念:堆(heap)与栈(stack)内存管理,以及所有权(ownership)和借用(borrowing)机制。

理解堆与栈的工程含义: 栈(Stack)内存遵循后进先出原则,大小在编译时确定,访问速度极快;堆(Heap)内存则在运行时动态分配,大小可变但访问需要通过指针间接寻址,速度相对较慢且容易碎片化。高级语言通常对开发者隐藏这一区别,而Rust要求开发者明确意识到数据存储在哪里——String类型存堆,基础类型如i32存栈——这直接影响所有权转移的语义,也是博主感到「掉进兔子洞」的根源。

所有权系统的底层逻辑: 理解Rust所有权系统,需要先了解它试图解决的历史困境。在Rust诞生之前(2010年前后),系统编程领域长期面临两难局面:C/C++给予程序员完全的内存控制权,但use-after-free、buffer overflow等内存安全漏洞是业界头号安全威胁,Chrome浏览器历史上约70%的高危漏洞均源于此类问题;而带GC的语言虽然安全,却因运行时开销和不可预测的停顿(Stop-The-World GC暂停有时长达数百毫秒)而无法用于操作系统内核、游戏引擎等对延迟敏感的场景。

Rust选择了第三条路:在编译阶段通过所有权规则静态分析内存使用,完全不需要运行时GC,也无需手动管理内存,实现了「零运行时开销的内存安全」——这在编程语言史上是真正的范式突破。简单来说,Rust中每份数据有且仅有一个「所有者」。数据从一个变量传给另一个变量时,原变量就放弃了所有权。绕开这一限制的方式是「借用」——不转移所有权,而是临时把访问权借出去。正是这套机制强制程序高效运行,几乎杜绝了内存泄漏。博主感叹道:「我觉得在Rust里就算故意想制造内存泄漏都做不到。」

虽然没能完全吃透,但他学到足够程度后便继续前进。四小时后,Rust语法学习阶段宣告完成。

选错引擎的代价:孤军奋战的Rusty Engine

要渲染游戏窗口、绘制精灵、检测碰撞,就需要一款游戏引擎。博主找到了名为 Rusty Engine 的2D引擎,专为Rust初学者设计,看起来十分合适。

他借此机会练习「直接阅读官方文档」的能力,没做太多调研就直接上手。直到许多小时后他才发现一个致命问题:关于这款引擎,网上几乎没有任何第三方资料——没有论坛讨论、没有Stack Overflow帖子、没有YouTube视频,只有作者本人撰写的文档页面。

Rust游戏开发生态的现实: 这一困境折射出Rust游戏生态的整体现状。目前社区最活跃的纯Rust引擎是Bevy,采用ECS(实体-组件-系统)架构——与传统面向对象游戏开发中「玩家是继承自GameObject的类」不同,ECS将游戏对象分解为三个正交概念:实体(Entity)只是一个数字ID,组件(Component)是纯数据结构(如Position、Velocity、Health),系统(System)是处理特定组件组合的函数。这种架构数据局部性极佳,CPU缓存命中率高,处理数万个游戏对象时性能远超传统OOP方式。然而Bevy陡峭的学习曲线和频繁的破坏性API变动(每次大版本升级都可能需要大规模重构代码)使其对初学者并不友好。Macroquad则以简洁著称,更适合快速原型。与Unity、Godot等拥有数百万开发者和海量教程的成熟引擎相比,Rust系引擎普遍存在文档不完善、第三方资源匮乏的问题——这意味着一旦遇到问题,就彻底孤立无援,Google也帮不上忙。这个决策后来给他带来了不小的麻烦。

从精灵到物理:打砖块游戏的完整实现

博主决定做经典的打砖块(Brick Breaker)游戏——用弹球砸掉所有砖块即可获胜。这个选题能让他接触物理模拟、实时状态更新和多种游戏事件,是一个扎实的练手项目。

他用Photoshop设计了全部精灵:玩家角色选用Rust社区吉祥物——一只小螃蟹,让它举着挡板;砖块设计了五种不同样式;再配上一个弹球。图像完成后,他用Rusty Engine内置工具在精灵上绘制自定义多边形,定义碰撞区域。

用Photoshop设计游戏精灵

移动逻辑与弹球物理

玩家移动的逻辑很直接:每帧检查键盘状态,按右箭头就给玩家X坐标加10,按左箭头就减10,渲染函数据此摆放挡板位置。弹球同理——每帧将X、Y速度叠加到当前位置产生移动;撞到屏幕顶部或两侧时,把对应方向的速度乘以-1实现反弹。

这种「速度向量翻转」的反弹逻辑背后有严谨的物理基础:在理想完全弹性碰撞中,物体碰撞法线方向的速度分量符号取反,切线方向分量保持不变。对于轴对齐边界(水平墙壁或垂直墙壁),法线方向恰好与坐标轴平行,因此只需翻转对应轴的速度分量——即vx *= -1或vy *= -1——这在数学上对于无限质量平面与质点碰撞是精确的,而非简单近似。真正被忽略的是摩擦力、能量损耗和球体积(质点近似),但在打砖块这类轻度物理游戏中表现令人满意。

一个巧妙的设计是:弹球水平速度与挡板移动方向挂钩——球与挡板同向运动就加速,反向则减速。这实际上是对「移动挡板向球传递动量」这一真实物理效应的巧妙近似,最早可追溯到1976年雅达利原版Breakout的硬件电路实现,在Breakout类游戏中几乎已成标配。这样既能避免弹球陷入完美循环路径导致关卡无解,又让玩家始终对反弹角度保有一定控制权。

用二维数组构建关卡

砖块布局通过二维数组(5行8列的数字网格)定义,非零值代表砖块,零代表空位,修改数字即可轻松设计不同关卡布局。程序遍历每行每列,为非零单元格计算屏幕坐标,创建并放置对应精灵。

数据驱动设计的经典实践: 用二维数组定义游戏关卡是一种历史悠久的数据驱动设计模式,早在8位主机时代就被广泛使用——《超级马里奥》的关卡本质上就是一张瓦片索引矩阵。这种方式将游戏逻辑与关卡数据完全解耦:程序员只需修改数字矩阵,无需改动任何渲染或碰撞代码即可生成全新关卡。现代游戏引擎中的「瓦片地图(Tilemap)」编辑器,本质上是这一思路的可视化扩展,将数字矩阵转化为图形界面供关卡设计师操作,而底层存储格式往往仍是结构化的数值数据。

用二维数组构建砖块网格

每块砖被程序化地分配一个基于网格位置的唯一ID,以及随机选中的外观样式。所有数据存入由砖块结构体组成的向量(vector),每个结构体保存砖块ID、生命值和精灵副本。碰撞发生时,引擎返回被击中砖块的ID,程序据此从引擎和向量中移除该砖块。

引擎局限逼出的设计妥协

就在这里,选错引擎的后果显现出来。博主原本计划给每块砖设置生命值,被击中后切换纹理显示受损状态。然而Rusty Engine不支持精灵创建后修改纹理——纹理在添加时就固定了,没有内置的更新方法。他翻遍文档、搜遍网络都找不到解法,尝试用整体替换精灵的方式手动实现也耗费了数小时未果。最终只能简化设计:一击即碎。

这一困境深刻体现了技术选型中「生态成熟度」的重要性。Stack Overflow上的一个答案、GitHub上的一个Issue讨论,有时能节省数小时的debug时间。在快速原型场景中,选择一个拥有活跃社区(即便技术上并非最优)的工具,往往比选择功能完美但形单影只的工具更明智。

核心玩法跑通后,他补上了开始界面(按S键开始)、角落计分器,以及砖块全部清空时触发的胜利界面。

15小时23分完成挑战

博主最终在 15小时23分 内完成了游戏,比预定的24小时提前将近9小时。他表示非常享受学习Rust的过程,即便只覆盖了最基础的部分,也力荐感兴趣的人动手尝试。

这次挑战的价值不只在于「不用AI也能学会」,更在于它真实还原了自学一门陌生语言的完整心路:反复咀嚼语法细节、艰难消化所有权与借用等底层概念,以及在技术选型失误后独自debug的煎熬。对于习惯了AI一键补全的开发者来说,这种「纯手动」的过程反而更能暴露知识盲区,切实锻炼阅读文档和独立解决问题的能力。事实上,理解所有权系统这类底层机制,恰恰需要反复失败和亲手排错——而这正是AI补全最容易让人跳过的环节。

值得反思的是:选择Rusty Engine这种社区资源匮乏的引擎,虽然锻炼了硬啃文档的能力,却在纹理切换问题上被迫牺牲了设计目标。这提醒我们——在快速原型开发中,生态成熟度往往与技术本身同等重要。

核心要点

#Rust学习#Rust入门教程#不用AI编程#Rust游戏开发#Rust所有权借用#24小时学编程#Rusty Engine#打砖块游戏Rust
分享:

相关推荐