从零开始学编程玩游戏:一个文科生计划的14周
我没有计算机背景,也没有美术基础,但是随着中国游戏产业的大发展,我有幸成为了一名游戏策划。希望在日常工作之余,以一种方式锻炼自己对游戏系统设计开发流程的理解。所以我参加了Coursera上的几门课程,利用课程提供的便捷工具实现了设想的功能。
我把这种方法叫做“周巡”,就是每周快速开发一款游戏,持续几周。这是许多开发人员磨练想法和技能的一种方式。
刚开始成绩很基础很简单,后来挑战水平逐渐上升,最后独立完成了600行左右的程序。
接下来,我将向大家展示我在过去四个月中取得的成就,以及我从这些成就中学到了什么,体会到了什么。我尽量省略枯燥的实现细节,一来避免无聊,二来更有助于实践需要努力的东西。有兴趣可以来我的微博交流。
第一周:史伯克,一只剪羊毛蜥蜴。
谢尔顿最喜欢的游戏。
谢尔顿最喜欢的游戏,石头剪子布的升级版。内容是最基本的,只要你在控制台输入一个命令,命令就会通过if-elif-else转换成数字(0-4,分别代表五样东西)。
电脑会随机生成一个数字,转换成字符串。然后根据双方的数字,用if-else来判断胜负。
对我来说,这是第一个自己写的游戏。虽然简单,却包含了一个游戏的所有必备要素:有固定的开始和结束,有胜负规则。
第二周:猜数字
猜数字游戏是由计算机随机产生一个指定范围内的数字,你猜,计算机告诉你是高还是低的游戏。在一定次数后,如果你没有猜中,你就输了。
游戏中首次引入了全局变量的概念。在初始化时,上限和下限以及允许猜测的次数都是在读取全局变量。这样,我们就可以在游戏的核心方法之外,用其他方法修改全局变量,让玩家自己选择数字范围和猜测次数。游戏本身还是写成if-elif-else。
这是我个人写的第一个可以被玩家调整的游戏。
第3周:秒表游戏
秒表游戏是一个测试反应的游戏。单击开始,秒表开始向前移动。如果你按秒表时秒表停在一个整数(小数点后0),你会得到1分。游戏会记录你按停止键的总次数和你得到的分数。
这个游戏包括为每个函数编写单独的方法。比如播放器控制的按钮如开始()、停止()和复位();游戏本身的Tick()等等。同时,为了在屏幕上正确显示时间,还有另一种方法将时间转换成“公元前:D”的形式。
我们测量时间的方法是定义一个叫做时间的变量。由于这个游戏中最小的时间单位是0.1秒,所以我们每隔100毫秒就把这个数做成+1。同时编写一个format()方法,通过一系列的计算将这个数字转换成分、秒和0.1秒,并显示在屏幕上。仍然使用If-else结构来判断玩家是否得分。
这是第一次涉及到玩家复杂的操作,也是第一次意识到游戏画面的表象下应该是什么机制在运行。
第四周:乒乓。
终于从小孩子玩的游戏进入街机时代了!
传说Pong是世界上第一个电子游戏。在那个游戏机只有滚轮操作的年代,这款画面非常简单的游戏启发了无限后来者。看着它在他的指挥下形成,有点感动。
这个游戏也是我做的第一个不模拟现实中的“逻辑”,而是模拟“物理”的游戏。它的核心部分是球的速度变化,板的速度变化,球与边界和板的碰撞。
为了让游戏不至于无限期进行下去,我让球的速度随着每一次棋盘的碰撞而上升。但是上升公式写成指数函数,所以球越打越快,每一轮很快就结束了。如果换成对数函数,会慢慢逼近一个上限,使得每轮后期的双人游戏都非常紧张,充满变数。
这是我第一次意识到游戏的“感觉”到底是什么。每次稍微调整参数,手感都有变化,可以让设计师和游戏本身有更深层次的接触。这是目前全分工的网游公司在日常工作中无法体会到的感觉。
除此之外,你很快就会从一个简单的原型中看到未来变化的可能性。我能加入吗:
“球碰到板子的不同部位会向不同方向反弹”?
板子击球时,板子本身的速度会让球曲线飞出?
还是“球员连续几次击球后可以大招”?诸如此类。想到这里,这款游戏能成为游戏行业几十年的起点,也是有道理的。
第五周:记忆游戏
记忆游戏就是把多副牌乱放。玩家一次开两张牌。如果是一样的,就保持原样。如果他们不一样,他们就把他们退回去。玩家在所有牌都翻完之后获胜。
在这个游戏中,暂时用数字代替扑克牌。我们用的是list(我对list、array、tuple、set这几个词的中文翻译有点困惑,就不废话了...)用布尔值(真和假)记录每张牌是否开。当它设置为打开时,显示数字,否则在相应的位置抽回一张牌。
这个游戏的逻辑很复杂,因为整个游戏实际上有三种状态,需要分别处理:
新游戏,一张卡都没开。
翻开第一张牌(本回合),等待翻开第二张牌。
我开了第二张牌(这一轮)等着判断是否相同,所以用了一个变量叫state,用0,1,2分别代表三种状态。使用核心方法中的状态值来决定下一步做什么。
第6周:21(21点)
啊,21。我人生中的第一场扑克游戏。是的,在我会玩拖拉机之前,我7岁的时候在DOS下早期航海时代的一个酒馆里学了21点。这是我年轻时在那个游戏中唯一理解的体系...
这是一个赌博游戏。简单来说,规则是:庄家给自己和玩家发一张暗牌和一张亮牌,玩家决定是否继续打;玩家站起来后,庄家自己加牌,然后双方摊牌。分数最高的玩家获胜,他的分数必须等于或低于21分。
在这个游戏的编写过程中第一次引入了类的概念。因为许多对象会在游戏中重复出现,所以使用类可以很容易地复制它们:
每张卡都是类卡;方法get_suit()可以得到它的颜色;
方法get_rank()可以得到它的编号;
还有一种画法。
手是阶级的手;方法add_card()可以在手上加一张牌;
get_value()方法可以计算手牌的得分。
图书馆是班级甲板。方法shuffle()可以对库进行洗牌;
方法deal_card()用于发牌。
指定了这些基本方法后,再发牌、加牌、摊牌都可以通过这些函数的组合来实现。比如开局就是洗牌,向两边发牌;双手加上两张牌。等一下。
此外,这款游戏还首次涉及到如何在屏幕上绘制固定图形。整个牌桌就是一个大图,如何根据牌的价值定位对应的牌面也是一个很好的计算。
第7周:小行星
经典街机游戏的复制品!大制作来了!
这一次,游戏涉及的内容比以前更多。除了控制小飞船飞来飞去,游戏中还引入了动画、音效和UI。但是每个部分的实现都可以通过之前尝试的小功能叠加来实现。简单理解游戏图像和声音的工作原理后,就没有什么特别的困难了。只不过这次我学会了一次开发和测试一个模块,一个功能调整正确,然后继续下一个。
相反,在游戏设计方面,制作这款游戏的过程带给我很多思考。这个游戏需要调整的变量太多了:飞船需要推进和旋转;但是推进是给飞船一个向前的加速度,飞船本身会有一个其他方向的速度。太空中微小的摩擦力和陨石撞击后的力都要考虑并融入到游戏中。
这时候你会发现,同样的参数,经过调整,会让整个游戏完全不一样。该舰是重型慢转慢射远程战列舰,还是轻型快转快射近程战斗机?是要躲开一群从一个方向来的流星(陨石都是从一个方向来的,一个方向的阻力特别大),还是四面八方出现的岩石?每个选择似乎都很有趣...
这时候我才意识到一个游戏设计者头脑中清晰的指挥意图的重要性。你想玩什么样的游戏,给玩家带来什么样的情感?仅仅有一个“我要变酷”的粗略想法是不够的:无论是控制一艘巨大的战舰缓慢机动通过一颗流星。