0x00 前言

在日常的工作中,我偶尔能遇到这样的问题:“为何游戏脚本在现在的游戏开发中变得不可或缺?”。那么这周我就写篇文章从游戏脚本聊起,分析一下游戏脚本因何出现,而mono又能提供怎样的脚本基础。最后会通过模拟Unity3D游戏引擎中的脚本功能,将Mono运行时嵌入到一个非托管(C/C++)程序中,实现脚本语言和“引擎”之间的分离。

0x01 Why?从为何需要游戏脚本开始

首先聊聊为何现在的游戏开发需要使用游戏脚本这个话题。

为何需要有脚本系统呢?脚本系统又是因何而出现的呢?其实游戏脚本并非一个新的名词或者技术,早在暴雪的《魔兽世界》开始火爆的年代,人们便熟知了一个叫做Lua的脚本语言。而当时其实有很多网游都不约而同的使用了Lua作为脚本语言,比如网易的大话西游系列。
但是在单机游戏流行的年代,我们却很少听说有什么单机游戏使用了脚本技术。这又是为什么呢?因为当时的硬件水平不高,所以需要使用C/C++这样的语言来尽量压榨硬件的性能,同时,单机游戏的更新换代并不如网游那么迅速,所以开发时间、版本迭代速度并非其考虑的第一要素,因而可以使用C/C++这样开发效率不高的语言来开发游戏。

但是随着时间的推移,硬件水平逐年水涨船高,压榨硬件性能的需求已经不再迫切。相反,此时网游的兴起却对开发速度、版本更迭提出了更高的要求。所以开发效率并不高效,且投资巨大风险很高的C/C++便不再适应市场的需求了。而更加现实的问题是,随着java、.net甚至是javascript等语言的流行,程序员可以选择的语言越来越多,这更加导致了优秀的C/C++程序员所占比例越来越小。而网游市场的不断扩大,这种对人才的需求也同样越来越大,这就造成了大量的人才空缺,也就反过来提高了使用C/C++开发游戏的成本。而由于C/C++是门入门容易进阶难的语言,其高级特性和高度灵活性带来的高风险也是每个项目使用C/C++进行开发时,所不得不考虑的问题。

而一个可以解决这种困境的举措便是在游戏中使用脚本。可以说游戏脚本的出现,不仅解决了由于C/C++难以精通而带来的开发效率问题,而且还降低了使用C/C++进行开发的项目风险和成本。从此,脚本与游戏开发相得益彰,互相促进,逐渐成为了游戏开发中不可或缺的一个部分。

而到了如今手游兴起的年代,市场的需求变得更加庞大且变化更加频繁。这就更加要求需要有脚本语言来提高项目的开发效率、降低项目的成本。
而作为游戏脚本,它具体的优势都包括哪些呢?

  1. 易于学习,代码方便维护。适合快速开发。
  2. 开发成本低。由于上述第一点,因为易于学习,所以可以启用新人,同时开发速度快,这些都是降低成本的方法。

因此,包括Unity3D在内的众多游戏引擎,都提供了脚本接口,让开发者在开发项目时能够摆脱C/C++(注:Unity3D本身是用C/C++写的)的束缚,这其实是变相的降低了游戏开发的门槛,吸引了很多独立开发者和游戏制作爱好者。

0x02 What?Mono提供的脚本机制

首先一个问题:Mono是什么?

Mono是一个由Xamarin公司所赞助的开源项目。它基于通用语言架构(Common Language Infrastructure ,缩写为CLI)和C#的ECMA 标准(Ecma-335、Ecam-334),提供了微软的.Net框架的另一种实现。与微软的.Net框架不同的是,Mono具备了跨平台的能力,也就是说它不仅能运行在Windows系统上,而且还可以运行在Mac OSX、Linux甚至是一些游戏平台上。

所以把它作为跨平台的方案是像Unity3D这种开发跨平台游戏的游戏引擎的一个不错的选择。但Mono又是如何提供这种脚本的功能的呢?

如果需要利用Mono为应用开发提供脚本功能,那么其中一个前提就是需要将Mono的运行时嵌入到应用中,因为只有这样才有可能使得托管代码和脚本能够在原生应用中使用。所以,我们可以发现,将Mono运行时嵌入应用中是多么的重要。但在讨论如何将Mono运行时嵌入原生应用中去之前,我们首先要搞清楚Mono是如何提供脚本功能的,以及Mono提供的到底是怎样的脚本机制。

Mono和脚本

本小节将会讨论如何利用Mono来提高我们的开发效率以及拓展性而无需将已经写好的C/C++代码重新用C#写一遍,也就是Mono是如何提供脚本功能的。

常常使用一种编程语言开发游戏是比较常见的一种情况。因而游戏开发者往往需要在高效率的低级语言和低效率的高级语言之间抉择。例如一个用C/C++开发的应用的结构如下图:

可以看到低级语言和硬件打交道的方式更加直接,所以其效率更高。

可以看到高级语言并没有和硬件直接打交道,所以其效率较低。
如果以速度作为衡量语言的标准,那么语言从低级到高级的大体排名如下:

  • 汇编语言
  • C/C++,编译型静态不安全语言
  • C#、Java,编译型静态安全语言
  • Python, Perl, Javascript,解释型动态安全语言

开发者在选择适合自己的开发语言时,的确面临着很多现实的问题。

高级语言对开发者而言效率更高,也更加容易掌握,但高级语言也并不具备低级语言的那种运行速度、甚至对硬件的要求更高,这在某种程度上的确也决定了一个项目到底是成功还是失败。

因此,如何平衡两者,或者说如何融合两者的优点,便变得十分重要和迫切。脚本机制便在此时应运而生。游戏引擎由富有经验的开发人员使用C/C++开发,而一些具体项目中功能的实现,例如UI、交互等等则使用高级语言开发。

通过使用高级脚本语言,开发者便融合了低级语言和高级语言的优点。同时提高了开发效率,如同第一节中所讲的,引入脚本机制之后开发效率提升了,可以快速的开发原型,而不必把大量的时间浪费在C/C++上。

脚本语言同时提供了安全的开发沙盒模式,也就是说开发者无需担心C/C++开发的引擎中的具体实现细节,也无需关注例如资源管理和内存管理这些事情的细节,这在很大程度上简化了应用的开发流程。

而Mono则提供了这种脚本机制实现的可能性。即允许开发者使用JIT编译的代码作为脚本语言为他们的应用提供拓展。

目前很多脚本语言的选择趋向于解释型语言,例如cocos2d-js使用的javascript。因此效率无法与原生代码相比。而Mono则提供了一种将脚本语言通过JIT编译为原生代码的方式,提高了脚本语言的效率。例如,Mono提供了一个原生代码生成器,使你的应用的运行效率尽可能高。同时提供了很多方便的调用原生代码的接口。

而为一个应用提供脚本机制时,往往需要和低级语言交互。这便不得不提到将Mono的运行时嵌入到应用中的必要性了。那么接下来,我将会讨论一下如何将Mono运行时嵌入到应用中。

Mono运行时的嵌入

既然我们明确了Mono运行时嵌入应用的重要性,那么如何将它嵌入应用中就成为了下一个值得讨论的话题。

这个小节我会为大家分析一下Mono运行时究竟是如何被嵌入到应用中的,以及如何在原生代码中调用托管方法,相应的,如何在托管代码中调用原生方法。而众所周知的一点是,Unity3D游戏引擎本身是用C/C++写成的,所以本节就以Unity3D游戏引擎为例,假设此时我们已经有了一个用C/C++写好的应用(Unity3D)。

将你的Mono运行时嵌入到这个应用之后,我们的应用就获取了一个完整的虚拟机运行环境。而这一步需要将“libmono”和应用链接,一旦链接完成,你的C++应用的地址空间就会像下图一般:

而在C/C++代码中,我们需要将Mono运行时初始化,一旦Mono运行时初始化成功,那么下一步最重要的就是将CIL/.NET代码加载进来。加载之后的地址空间将会如下图所示:

那些C/C++代码,我们通常称之为非托管代码,而通过CIL编译器生成CIL代码我们通常称之为托管代码。
所以,将Mono运行时嵌入我们的应用,可以分为三个步骤:

  1. 编译C++程序和链接Mono运行时
  2. 初始化Mono运行时
  3. C/C++和C#/CIL的交互

让我们一步一步的进行。首先我们需要将C++程序进行编译并链接Mono运行时。此时我们会用到pkg-config工具。在Mac上使用homebrew来进行安装,在终端中输入命令“brew install pkgconfig”,可以看到终端会有如下的输出内容:

==> Downloading https://homebrew.bintray.com/bottles/pkg-config-0.28.mavericks.bottle.2.tar.gz
######################################################################## 100.0%
==> Pouring pkg-config-0.28.mavericks.bottle..tar.gz

从游戏脚本语言说起,剖析Mono所搭建的脚本基础的更多相关文章

  1. LUA脚本调用C场景,使用C API访问脚本构造的表

    LUA调用C lua解析中集成了一些系统服务, 故脚本中可以访问系统资源, 例如, lua脚本可以调用文件系统接口, 可以调用数学库, 但是总存在一些lua脚本中访问不到的系统服务或者扩展功能, 如果 ...

  2. Learning Lua Programming (3) iMac下搭建Lua脚本最好的编码环境(代码补全,编译运行)

    这篇文章参考自http://blog.sina.com.cn/s/blog_991afe570101rdgf.html,十分感谢原作者的伟大创造,本人亲测可行. 这篇文章记录一下如何在MAC系统环境下 ...

  3. Xamarin Mono 环境搭建

    Xamarin Mono 环境搭建(使用Visual Studio 2013 开发android 和 ios ) 本文主要介绍Xamarin结合VS2013来开发Android应用程序,主要会介绍Mo ...

  4. 《Linux命令行与shell脚本编程大全》第十六章 控制脚本

    一些控制脚本的方式:向脚本发送信号.修改脚本优先级,在脚本运行时切换到运行模式 16.1 处理信号 linux利用信号与运行在系统中的进程进行通信. 也可以通过对脚本进行编程,使其在收到特定信号时执行 ...

  5. appium-desktop录制脚本二次开发,生成我司自动化脚本

    目的 通过对appium-desktop脚本录制功能进行二次开发,使录制的java脚本符合我司自动化框架要求. 实现步骤 1.增加元素名称的输入框 由于ATK(我司自动化测试框架)脚本中元素是以“ap ...

  6. 浏览器环境下JavaScript脚本加载与执行探析之动态脚本与Ajax脚本注入

    在<浏览器环境下JavaScript脚本加载与执行探析之defer与async特性>中,我们研究了延迟脚本(defer)和异步脚本(async)的执行时机.浏览器支持情况.浏览器bug以及 ...

  7. 带你走进脚本世界,ijkplayer之【init-ios.sh】脚本分析

    前言 集成ijkplayer,需要执行脚本init-ios.sh,那么init-ios.sh脚本干嘛用的了,花了半天时间,学习了下shell脚本,感觉脚本语言学起来还是比较容易上手的,现在仅仅能看懂了 ...

  8. 解剖Nginx·自动脚本篇(3)源码相关变量脚本 auto/sources

    在configure脚本中,运行完auto/options和auto/init脚本后,接下来就运行auto/soures脚本.这个脚本是为编译做准备的. 目录 核心模块 事件模块 OpenSSL 模块 ...

  9. Shell脚本使用汇总整理——文件夹及子文件备份脚本

    Shell脚本使用汇总整理——文件夹及子文件备份脚本 Shell脚本使用的基本知识点汇总详情见连接: https://www.cnblogs.com/lsy-blogs/p/9223477.html ...

随机推荐

  1. .NET Core & ASP.NET Core 1.0在Redhat峰会上正式发布

    众所周知,Red Hat和微软正在努力使.NET Core成为Red Hat企业版Linux (RHEL)系统上的一流开发平台选项.这个团队已经一起工作好几个月了,RHEL对.NET有许多需求.今天在 ...

  2. console的高级使用

    1.console.table()用来表格化展示数据. var people = { zqz: { name: 'zhaoqize', age: 'guess?' }, wdx: { name: 'w ...

  3. SQL Server-聚焦计算列或计算列持久化查询性能(二十二)

    前言 上一节我们详细讲解了计算列以及计算列持久化的问题,本节我们依然如前面讲解来看看二者查询性能问题,简短的内容,深入的理解,Always to review the basics. 持久化计算列比非 ...

  4. javascript运动系列第一篇——匀速运动

    × 目录 [1]简单运动 [2]定时器管理 [3]分享到效果[4]移入移出[5]运动函数[6]透明度[7]多值[8]多物体[9]回调[10]函数完善[11]最终函数 前面的话 除了拖拽以外,运动也是j ...

  5. 解决VS2008在win7找不到输入序列号的地方

    1.VS2008在Windows7 打开维护界面看不到可以输序列号的地方. 因为微软把他隐藏了. 2.我们可以借用工具把他显示出来 下载地址:http://www.zlsoft.com/techbbs ...

  6. 2Sum

    用哈希表(unordered_map)使得时间复杂度从O(n*n)降到O(n),空间复杂度从O(1)增到O(n):一边找一边插入哈希表 注意 在C++11以前要使用unordered_map需要 #i ...

  7. 企业做数据缓存是使用Memcached还是选Redis?

    企业是使用Memcached还是选Redis? 在构建一款现代且由数据库驱动的Web应用程序并希望使其拥有更为出色的性能表现时,这个问题总会时不时出现.并给每一位开发人员带来困扰.在考虑对应用程序的性 ...

  8. 原生JS实现-星级评分系统

    今天我又写了个很酷的实例:星级评分系统(可自定义星星个数.显示信息) sufuStar.star();使用默认值5个星星,默认信息 var msg = [........]; sufuStar.sta ...

  9. ORA-00821: Specified value of sga_target 3072M is too small, needs to be at least 12896M

    在测试PlateSpine克隆的数据库服务器时,由于资源有限,克隆过来的数据库服务器只给了9G的内存,结果在测试时,老是会出现OOMkiller导致宕机,即out of memory killer,是 ...

  10. mysql 大表拆分成csv导出

    最近公司有一个几千万行的大表需要按照城市的id字段拆分成不同的csv文件. 写了一个自动化的shell脚本 在/home/hdh 下面 linux-xud0:/home/hdh # lltotal 1 ...