游戏编程模式|有趣的字节码

在读书的时候看到了这个东西,觉得很有趣,就记录下来。

为什么选择字节码


制作游戏是一项很有趣的工作,但同时也是非常复杂的,为了保证游戏的稳定和高效,使用c++是一个很好的选择。

我们会为此感到骄傲,但这也是有代价的。成为一个精通c++的程序员需要多年的专业训练,随后你又必须面对庞大的代码库。大型游戏的编译时间说短不过“喝杯咖啡”的时间,说长够你把“自己烘焙咖啡豆、磨咖啡豆、倒咖啡、打奶泡、练练拿铁的拉花”统统做一遍。——《游戏编程模式》

玩家需要新奇的体验,游戏也需要不断迭代,这会让我们不断添加新的道具、技能等等。如果每次修改都要修改c++代码并且编译,那么整个游戏创作的流程就无法顺畅的运行。并且,在游戏发布以后,我们仍然要进行更新,现在的游戏基本上都可以进行“热更新”,不去下载整个游戏而只是下载需要修改的部分,一般使用lua这样的脚本语言就可以实现“热更新”了,不过,如果你c++部分的核心逻辑需要修改,那就无能为力了,更新大包意味着你将失去一部分用户!

当然,你也可以用配置文件等方式增加游戏的多样性。不过,这种方式可以定义属性,却不能定义行为,使用字节码的方式更为灵活,功能更加强大。

“通过将行为编码成虚拟机指令,而使其具备数据的灵活性”。

字节码模式


字节码模式实际上是将一系列操作编码成字节序列,然后在虚拟机逐条执行这些指令,指令的组合可以完成很多高级的行为。这和我们使用编程语言感觉起来有点像,通过一系列简单的语法,我们可以完成很复杂的程序,这很cool,不是吗?

使用情景

字节码模式是一个很复杂的模式,并不是所有的游戏都适用。对于游戏来说,如果需要定义大量的行为,并且游戏语言是这样的才应该使用:

1.编程语言太底层了,编写起来繁琐易出错。
2.因编译时间太长或工具问题,导致迭代缓慢。
3.它的安全性太依赖编码者。你想确保定义的行为不会让程序崩溃,就得把它们从代码库转移至安全沙箱中。

如果游戏完全是脚本写的,那就不用在意这些细节了。不过在使用c++这样的语言时,还是可以根据游戏具体的内容来考虑使用的。

如何使用

想要使用字节码模式,首先我们要设计一个指令集,这个指令集可以让我们对游戏的行为进行一些基础操作。这个指令集的设计要根据游戏实际情况确定。

比如说游戏中操作的玩家有一个治疗的技能,那么我们就需要这样的操作:选取对象、修改血量,甚至可以播放声音特效等。

设计完你需要的指令以后,为了让这些指令编码成数据,我们在数组中存储一系列枚举值代表这些操作。实际上,基本操作的数量不会很多,所以枚举值取一个字节即可。最后我们将某个行为转化为一个字节序列(即字节码)。

enum Instruction
{
    INST_SET_CHARACTER        = 0x00,        //选择角色
    INST_SET_HEALTH           = 0x01,        //设置血量
    INST_PUSH_VALUE           = 0x02,        //添加数据
    INST_CAL_ADD              = 0x03,        //加法操作
    INST_PLAY_SOUND           = 0x04,        //播放声音
}

这样,当我们得到一条指令的时候,就可以根据对应属性,执行正确的操作:

class VM {
public:
    void interpret(char bytecode[], int size) {
        for (int i = 0; i < size; ++i) {
            char instruction = bytecode[i];
            switch(instruction) {
                case INST_SET_CHARACTER :
                    setCharacter(0);
                    break;
                case INST_SET_HEALTH :
                    setHealth(100);
                    break;
                //other case
            }
        }
    }
};

到这里位置,我们的字节码已经初步完成。但是它还只能做一些简单的操作,只是将字节映射了对应的函数,它还不够灵活,我们要在执行指令的时候添加我们需要的数据才行,也就是要为函数添加参数。

我们想要更容易地获得数据,计算表达式并正确的传值,并且通过指令的顺序去控制,这就可以使用和CPU同样处理数据的方式——堆栈。

我们可以添加一条指令用来在堆栈中添加数据,并且在需要的时候从堆栈中获得数据。如下图所示。

字节码模式运行图

这样,我们的字节码模式就可以运行起来了,通过添加对应的指令,我们可以创建一些表达式,让不同的值组合起来,比如说我们想要治疗的血量是个百分比的值而不是一个固定的值。那么我们就可以:

1.获取治疗对象
2.获取对象血量
3.获取百分比值
4.计算血量乘以百分比,得到需要增加的血量
5.执行加血指令

通过添加更多基础指令,并进行组合,我们可以实现许多多样化的操作。

其他问题


文本语言or图形界面

虽然经过上面的步骤,我们的字节码已经基本可用了,但它实在是不怎么好用,你不能指望使用它的人看着一堆指令列表来和对应的字节数字来工作,那么我们就需要为字节码提供一种可靠方便的使用形式。使用形式可以是一种文本语言,也可以做一个图形界面。
实际上,如果你的用户是策划,那么你别指望他们会喜欢使用文本语言,这种反复无常并且缺乏耐心的生物在你告诉他你创造了一个文本语言来定义游戏行为的时候,会表示:“oh,你这个东西确实很厉害,但是我看不懂,能不能把它弄得简单一点,最好有个界面能够操作!”。可怜的程序员还没有感受到完成新的模式带来的喜悦,就得继续开始拼UI了。
其实,添加一个图形界面比想象中的要好一些,因为使用文本语言,你很难保证你的用户能够正确书写语法,并且在遇到各种错误的时候能够自己修复。做一些简单的UI,来帮助用户翻译,可以保证他们很难创造非法的程序,使得创建出来的东西总是合法的,这也可以减少自己的麻烦,提高效率。

应有的指令类型

  • 外部基本操作。即游戏相关的一些api。
  • 内部基本操作。操作虚拟机内部的值,如添加数值、算术运算符等。
  • 控制流。相当于跳转指令,可以实现循环等操作。
  • 抽象化。为了重用定义内容,可以通过构造一个类似函数的形式。比如,遇到“call”指令,就将当前指令入栈,跳转到被调用字节码。遇到“return”指令,就返回,从栈中获取索引继续执行。

值的表现形式

如果你需要的虚拟机只需要支持一种类型,那么很简单,和上面一样用就好了。但是如果想要支持不同的数据类型,你就要决定如何保存这些值。

一种方法是,在数据前面再添加一个标签,这样,每次要读数据的时候,先去查看标签,再决定读取数据的长度。这种方式的好处是:有自身类型信息,可以在调用前进行类型检查。当然,缺点就是会占用更多内存。

还有一种方式是,不为数据添加额外类型标签,在使用的时候,根据需要获取对应的值,让使用的地方保证能得到正确的解析。这样做的好处是数据紧凑,速度快。缺点是不安全,如果解析出了问题,很有可能就导致游戏崩溃。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,701评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,649评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,037评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,994评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,018评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,796评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,481评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,370评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,868评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,014评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,153评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,832评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,494评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,039评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,437评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,131评论 2 356

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,198评论 25 707
  • 8086汇编 本笔记是笔者观看小甲鱼老师(鱼C论坛)《零基础入门学习汇编语言》系列视频的笔记,在此感谢他和像他一样...
    Gibbs基阅读 37,222评论 8 114
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,673评论 18 139
  • 参考书目 《联盟》参考电影 《战狼2》 《联盟》的作者是硅谷著名的“人脉王”、领英创始人里德·霍夫曼。他在书中和我...
    萌小Q在路上阅读 235评论 9 10
  • 晚上开会时,旁边一同事把他的烟放在了桌子上,因为就在我斜前方,我无聊的就顺手拿在了手中把玩着。 不知出于何种想法,...
    浪里周小白阅读 238评论 0 0