QCon北京「鸿蒙专场」火热来袭!即刻报名,与创新同行~ 了解详情
写点什么

Unity 引擎与 C#脚本简介

  • 2019-08-21
  • 本文字数:3380 字

    阅读完需:约 11 分钟

Unity引擎与C#脚本简介

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 方式打包有多种好处:


跨平台兼容性更好。基本上所有游戏平台都支持 C++代码,而 .NET/Mono 运行时却不一定能在所有平台上运行;


效率更高。Unity 给出的数据显示采用 il2cpp 打包之后,游戏的执行效率提升了 1.5 到 2.0 倍。


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


本文转载自公众号小时光茶舍(ID:gh_7322a0f167b5)。


原文链接:


Unity引擎与C#脚本简介


2019-08-21 10:1916183

评论

发布
暂无评论
发现更多内容

2022最新BATJ等一线互联网大厂秋招面试题汇总,速刷

程序知音

Java 程序员面试 后端技术 Java面试题 Java面试八股文

数据变更白屏化利器-推送轨迹上线

阿里巴巴云原生

zookeeper 阿里云 开源 微服务 云原生

最后 3 天|报名参加 OpenYurt+EdgeX 挑战赛 ,冲击最高 5 万元奖励!

阿里巴巴云原生

阿里云 云原生 openyurt EdgeX

设计模式的艺术 第二十二章观察者设计模式练习(开发一款实时在线股票软件。该软件需要提供如下功能:当股票购买者所购买的某只股票价格变化幅度达到5%时,系统将自动发送通知(包括新价格)给购买该股票的所有股民。试使用观察者模式设计并实现该系统)

代廉洁

软件测试 | 测试开发 | 基于Requests与mitmproxy打造迷你接口测试框架

测吧(北京)科技有限公司

测试 Request

牛客“基础-中级-高级”Java程序员面试八股文集结,熬夜挑灯刷

程序知音

Java java面试 后端技术 Java面试八股文 Java 面试题

国产操作系统应用小程序化:夯实技术底座,促进生态发展

Speedoooo

小程序 国产操作系统 小程序容器

区块链NFT网站开发:NFT数字藏品网站开发

开源直播系统源码

NFT 数字藏品 数字藏品系统

LED屏幕有色差要怎么办?

Dylan

LED显示屏 户外LED显示屏 led显示屏厂家

北京哪家WEB前端培训机构比较不错

小谷哥

软件测试 | 测试开发 | app自动化测试(Android)--显式等待机制

测吧(北京)科技有限公司

测试

软件测试 | 测试开发 | 抓包分析 TCP 协议

测吧(北京)科技有限公司

TCP 抓包分析

软件测试 | 测试开发 | app自动化测试(Android)-- 特殊控件 T识别oast

测吧(北京)科技有限公司

自动化测试 Android;

硅谷名企、国内大厂是如何度量研发效能的?|ONES 研发管理大师课

万事ONES

我用 极狐 Gitlab issue 来点菜 #JIHULAB 101

朱亚光

JIHULAB 101

2022年8月国产数据库大事记-墨天轮

墨天轮

数据库 opengauss 国产数据库 达梦 polarDB

软件测试 | 测试开发 | RPC接口测试技术-Tcp 协议的接口测试

测吧(北京)科技有限公司

软件测试 | 测试开发 | 文未有福利 | 接口自动化你不懂?听HttpRunner的作者怎么说

测吧(北京)科技有限公司

测试 接口调试

Serverless 架构下的 AI 应用开发:入门、实战与性能优化

阿里巴巴云原生

阿里云 Serverless 云原生

代码质量与安全 | 实践“边写边清理”,您需要做好这两件事:质量配置文件和质量门

龙智—DevSecOps解决方案

代码质量 代码安全 静态代码安全

GOPS现场 | 对话龙智技术顾问,分享DevOps观察与心得

龙智—DevSecOps解决方案

运维 DevOps工具链

设备健康管理在石化行业的探索与实践

PreMaint

预测性维护 设备健康管理

【荣耀开发者服务平台—百亿曝光扶持等你来】智慧服务内容接口卡片接入指南

荣耀开发者服务平台

手机 激励 卡片服务 厂商 honor

深圳web前端技术培训学习费用

小谷哥

软件测试 | 测试开发 | 一文搞懂测试左移和测试右移的 Why-How-What

测吧(北京)科技有限公司

测试 安全测试

软件测试 | 测试开发 | 接口管理工具YApi怎么用?颜值高、易管理、超好用

测吧(北京)科技有限公司

测试 Mock

版本管理 | 如何解决SVN的合并冲突与分支问题?

龙智—DevSecOps解决方案

svn 版本管理

CI/CD | 大型企业与开发团队如何进行持续集成与持续发布

龙智—DevSecOps解决方案

持续集成 CI/CD 持续发布

在Java培训机构中怎么学习?

小谷哥

leetcode 104. Maximum Depth of Binary Tree 二叉树的最大深度(简单)

okokabcd

LeetCode 算法与数据结构

测试管理 | 龙智获得Xray专家认证

龙智—DevSecOps解决方案

Jira插件

Unity引擎与C#脚本简介_语言 & 开发_jojokzhang_InfoQ精选文章