欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~

由 QQ会员技术团队 发布在云+社区

1. Unity编辑器基础

从原理上讲,游戏开发就是将一系列变动的场景呈现在玩家面前,并根据玩家的输入修改游戏画面;而游戏画面则是通过调用目标操作系统上的图形图像库来绘制的。比较知名的图形图像库有Windows上的DirectX,*nix系统、macOS和iOS等系统上用到的OpenGL以及Android用到的Vulkan等。

一般来讲,底层的图形图像API只能进行最基本的三角形绘制,但是,因为是通过计算机的GPU进行的操作,具有并行计算的优势,在短短六十分之一秒时间内,也可以绘制出成千上万个三角形,而这么多小三角形堆叠起来看,视觉效果也就和真实场景差别不大了。

[ 图一:古墓丽影劳拉变化图 ]

现代游戏引擎一般都会把游戏人物的“建模”工作交给第三方,引擎本身只负责游戏场景和人物的绘制以及内部交互逻辑。第三方建模软件通过模拟人物的真实3D外观来将虚拟人物表面“三角形化”,附带上游戏人物在做出不同动作时的外观数据,最后生成游戏引擎可识别格式的文件,这个过程就是所谓的3D建模。

[ 图二:绘制流程 ]

3D模型制作完成后,会由游戏引擎进行绘制,这个过程一般称作“着色”(Shading)。着色的核心是叫做“着色器(Shader)”的GPU程序 - GPU通过输入一些参数信息,然后执行着色器程序就能生成最终的游戏图像。

GPU需要的参数信息主要有两种:一是纹理,二是材质。

纹理是指一个模型的表面,可以理解成一件衣服平铺起来的样子。如果是一个三维物体,其表面的纹理可以想象成是把它的表面拆开,然后压扁后的样子。什么是材质呢?材质(Material)从字面上理解的话就是材料,比如木头和大理石,看起来就是不一样的效果。同样的纹理,用不一样的材质来绘制,会得到不一样的效果图,因为材质有一些关键的参数,会影响着色器的绘制效果。

比较重要的一个参数是反射率(Albedo)。

光滑材质的反射率比较高,看起来就会亮一些。在自然白光的照射下,这样的材质看起来会偏白,如果沿着光照方向看过去,会出现光斑效果(太阳光照射下的湖面看起来会有一种很耀眼的效果)。粗糙材质的反射率比较低,看起来就比较柔和。典型的高反射率材质比如光滑的金属表面,典型的低反射率材质有布料、地面等。在3D场景中,反射率高的物体受周围物体的影响更大。譬如,一个平静的湖面会倒映出地面的建筑物。因此,高反射率的材质通常需要更多的绘制步骤。

[ 图三:一个金属球体在场景中的效果图 ]

材质的另一个重要参数是法向图(Normal Map)。

法向就是物体表面的方向。法向图表示的是材质的表面细节,比如凹槽、斑点、凸起或者空洞等,法向图通常以纹理图来表示。然而不同于一般的纹理图,法向图的每个像素点称作“纹素(texel)”,它表示的是纹理在此位置处的光照反射方向,纹素的RGB分量分别对应反射方向的XYZ分量。

[ 图四:法向图示例 ]

一个3D模型的表面纹理被分割成一个个小三角形,而法向图就表示此表面的每个像素点位置的光照反射方向。方向不同的三角形绘制出来和周围的三角形看起来颜色是不同的,从而产生了视觉上的凸起/凹陷效果。这种物体的表面细节,如果在3D建模阶段通过修改模型外观的方式来实现的话,会增加很多物体表面的细小的绘制操作。通过材质的法向图来实现,将物体“表面”和物体的实际皮肤剥离开了,可以实现同一个人物穿上不同衣服的效果。

[ 图五:绘制效果图 ]

如上图所示,右边的物体采用左边的法向图来绘制,注意看凸起位置的颜色

2. C#脚本语言

2.1 为什么需要脚本?

长久以来,游戏引擎开发都采用底层语言如C++来进行,这对于游戏上层开发来说,并不友好。很难想象如果使用一款引擎修改某个人物的动作,还需要直接调用C++底层的接口,这样既不安全,也不方便。因此,一般引擎从设计之初就会把封装好的绘制接口通过某些上层语言暴露出来,给游戏制作方使用。这些上层语言就叫做游戏脚本语言。

lua是脚本语言里面比较流行的一种,因其虚拟机小巧、API丰富、可灵活定制而深受游戏引擎开发商的喜爱。Unity使用了C#和Unity Script(现已废弃)来作为脚本语言。C#语言因为建立在.NET IL之上而具有跨平台扩展性。这样,游戏开发者只需要一套代码就可在多个平台运行。

[ 图六:.NET CIL和CLR ]

2.2 IL是什么?

IL(Intermediate Language,在.NET平台下是CIL,Common Intermediate Language)是一种中间语言格式,类似于Java的字节码(byte code),这种格式的代码需要一个虚拟机来“解释”执行。IL的所有指令都是基于虚拟堆栈的:调用函数前,先将参数push到虚拟堆栈里面;函数执行的时候,从虚拟堆栈里面取出参数,然后将结果压入虚拟堆栈。由于调用方式简单,IL语言的指令集也比较精简。

IL作为脚本语言的独到之处在于可以将C#上层语言的各种特性(如泛型、协程等)转换成基本的IL指令集,但是这样的转换也是有代价的 — 转换后的IL指令比普通的函数调用多出数倍。因此,在游戏开发中,不宜在每一帧中都进行这一类的调用。

另外,IL语言执行需要一个虚拟机翻译成目标平台的机器码,虽然.NET虚拟机已经比较高效了(可参考.NET与Java的对比),但是和平台原生代码比起来,依然有一些差距。在iOS平台上,由于苹果禁止使用JIT方式,IL指令需要预先编译成目标平台库文件,然后在最终二进制文件打包的时候作为第三方库链接进去。Unity游戏几乎所有的游戏逻辑都是通过脚本来实现的,一个大型游戏,成千上万个脚本,AOT方式打包造成的效率低下,是不得不考虑的问题。因此,Unity在5.3.4版本中引入了il2cpp技术。

2.3 il2cpp原理

顾名思义,il2cpp就是把中间语言转换成cpp代码的工具。上面我们讲到,在iOS平台上,由于无法使用JIT方式执行IL指令,所以需要先将游戏脚本打包成.NET Managed Assembly(这里的Managed是指二进制文件是在.NET层面打包的,可能会依赖.NET底层库,可以理解为“安全的”库文件。另外有些库文件是通过直接封装C/C++接口方式生成的,由于有如指针之类的底层内存操作,所以称作是Unmanaged Assembly),然后和.NET CLR的Assembly链接之后生成最终的平台二进制文件。il2cpp的作用是去掉链接.NET CLR的步骤,将C#脚本生成的Managed Assembly“翻译”成C++文件,最后用目标平台的编译器编译这些C++文件来生成最终的游戏可执行文件。

[ 图七:il2cpp工作原理示意图 ]

il2cpp会先读取.NET二进制文件,解析其中的符号,然后将其中C#方法转换成对应的C方法。虽然名为il2cpp,但其实它只用到了很少部分的C++特性,绝大多数转换后的代码都是C函数。

[ 图八:il2cpp转换后的代码示例 ]

在游戏运行前,il2cpp会启动一个小的虚拟机,用于动态解析C方法。其会将所有方法的签名放在一个叫做global-metadata.dat的文件里,方法调用的时候会先从此文件里读取C函数地址,然后再调用。

获取函数指针的方法是这个:

inline Il2CppMethodPointer il2cpp_codegen_resolve_icall (const char* name){
Il2CppMethodPointer method = il2cpp::vm::InternalCalls::Resolve (name); if (!method)
{
il2cpp::vm::Exception::Raise(il2cpp::vm::Exception::GetMissingMethodException(name));
} return method;
}

[ 图九:获取函数指针 ]

Unity确保了所有采用il2cpp平台实现的游戏,其metadata的格式都是一样的。metadata加载时采用了内存映射技术,上述函数实际上会从一张内存的数据表里查找方法名对应的键值,也即目标函数的地址。

为何Unity要采用文件来记录方法名?一是游戏有动态解析方法的需求;再者是这样可以隐藏掉游戏内部逻辑的实现,起到一部分混淆的作用;最后还有一个重要的原因是Unity编辑器里可以设置脚本执行时候的延迟时间,而这些信息可以很方便的放在文件里。

Unity C#层面的接口暴露给游戏开发者,开发者通过C#脚本编写游戏逻辑,然后通过il2cpp将脚本翻译成C++文件,接着链接上Unity C#接口的底层C++实现,最终生成游戏的二进制文件,这就是Unity游戏开发的大致过程。

按照Unity的说法,通过il2cpp方式打包有多种好处:

  1. 跨平台兼容性更好。基本上所有游戏平台都支持C++代码,而.NET/Mono运行时却不一定能在所有平台上运行;
  2. 效率更高。Unity给出的数据显示采用il2cpp打包之后,游戏的执行效率提升了1.5到2.0倍。

以上就是游戏开发的一些基本知识。

相关阅读

基于腾讯云的视频聊天研究

ios微信内存监控

2017年数据库技术盘点


此文已由作者授权腾讯云+社区发布,转载请注明文章出处

原文链接:https://cloud.tencent.com/developer/article/1047768

Unity引擎与C#脚本简介的更多相关文章

  1. Unity优化方向——优化Unity游戏中的脚本(译)

    原文地址:https://unity3d.com/cn/learn/tutorials/topics/performance-optimization/optimizing-scripts-unity ...

  2. Unity引擎入门——制作第一个2D游戏(2)角色移动与动画

    在上一节的内容里,我们已经创建出了一个主角,也搭建了一个简单的场景. 传送门:https://www.cnblogs.com/zny0222/p/12653088.html 既然有了主角,要怎样才能让 ...

  3. Linux Shell——bash shell 脚本简介

    bash shell 脚本简介 shell 运行环境 如果你运行的是 Unix 或 Linux 系统,例如 Ubuntu,Red Hat,SUSE Linux,还有macOS,都是内置了 bash s ...

  4. 又一次发现Oracle太美之awr相关脚本简介

    又一次发现Oracle太美之awr相关脚本简介 大家知道在$ORACLE_HOME/rdbms/admin下,有例如以下的相关脚本(我的环境为11.2.0.4.2): [oracle@rh64 ~]$ ...

  5. 脚本简介jQuery微信开放平台注册表单

    脚本简介jQuery微信开放平台注册表单是一款仿微信开放平台的选项卡带步骤的注册表单验证jQuery代码 分享自:http://www.huiyi8.com/jiaoben/ 下载地址:http:// ...

  6. 盛大游戏技术总监徐峥:Unity引擎使用的三种方式

    在5月13日Unite 2017 案例分享专场上,盛大游戏技术总监徐峥分享了使用Unity引擎的三种方式,以下为详细内容: 大家好,我先简单介绍一下我自己,我是盛大游戏的技术总监徐峥.我今天想分享的主 ...

  7. Unite 2017 | Unity引擎发展四大方向

    Unite 2017 Shanghai已落幕,今天为大家分享本次大会备受关注的Keynote主题演讲.本次大会Keynote主题演讲聚焦了Unity全球领导团队,包括Unity创始人David Hel ...

  8. Unity3D热更新之LuaFramework篇[07]--怎么让unity对象绑定Lua脚本

    前言 在上一篇文章 Unity3D热更新之LuaFramework篇[06]--Lua中是怎么实现脚本生命周期的 中,我分析了由LuaBehaviour来实现lua脚本生命周期的方法. 但在实际使用中 ...

  9. Unity引擎入门——制作第一个2D游戏(1)

    Unity作为当今最流行的游戏引擎之一,受到各大厂商的喜爱. 像是炉石传说,以及最近的逃离塔克夫,都是由unity引擎开发制作. 作为初学者的我们,虽然无法直接做出完成度那么高的作品,但每一个伟大的目 ...

随机推荐

  1. MySQL基于binlog主从复制

    MySQL复制介绍 默认情况 下复制是异步进行的,从库也不需要一直连接到主库来同步数据 MySQL复制的数据粒度可以是主实例上所有的数据库,也可以是指定的一个或多个数据库 ,也可以是一个数据库里的指定 ...

  2. 反向代理和HTTP重定向

    1.什么是正向代理(前向代理)? 在NAT技术(Network Address Translation)出现之前,所有主机无法直接与外网相连,要想上网,需要连接到一台能够访问外网的Web服务器,再通过 ...

  3. openstack一键安装脚本(转载)

    #!/bin/sh # openstack pike 单机 一键安装 # 环境 centos 7.4.1708 x86_64 # 更多内容 http://dwz.cn/openstack # Myde ...

  4. [UWP]合体姿势不对的HeaderedContentControl

    1. 前言 HeaderedContentControl是WPF中就存在的控件,这个控件的功能很简单:提供Header和Content两个属性,在UI上创建两个ContentPresenter并分别绑 ...

  5. 使用Django实现分页器功能

    要使用Django实现分页器,必须从Django中导入Paginator模块 from django.core.paginator import Paginator 假如现在有150条记录要显示,每页 ...

  6. sphinx的再创造coreseek的安装过程

    CoreSeek详细安装过程:coreseek-3.2.14.tar.gz下载链接: http://pan.baidu.com/s/1o6DNesE 解压缩安装mmseg分词程序: .tar.gz c ...

  7. 安装golang的mongodb驱动mgo速记

    这里介绍的方法只适用于Centos平台,测试版本为centos 6.5 下载源码安装实在麻烦,这里采用比较简单的方法给GO安装mongodb驱动 安装mgo之前,需要先安装bzr yum -y ins ...

  8. System.nanoTime

    System.currentTimeMillis()返回的毫秒,这个毫秒其实就是自1970年1月1日0时起的毫秒数. System.nanoTime()返回的是纳秒,nanoTime而返回的可能是任意 ...

  9. JDBC访问及操作SQLite数据库

    SQLite 是一个开源的嵌入式关系数据库,其特点是高度便携.使用方便.结构紧凑.高效.可靠. 与其他数据库管理系统不同,SQLite 的安装和运行非常简单,在大多数情况下,只要确保SQLite的二进 ...

  10. HDU 5996 dingyeye loves stone [阶梯Nim]

    dingyeye喜欢和你玩石子游戏. dingyeye有一棵nn个节点的有根树,节点编号为00到n−1n−1,根为00号节点.游戏开始时,第ii个节点上有a[i]a[i]个石子.两位玩家轮流操作,每次 ...