Unity脚本编程
2019-10-28 18:15:57 1 举报
AI智能生成
《Unity 3D脚本编程——使用C#语言开发跨平台游戏》作者使用准确清晰易于理解的语言向读者讲解了Unity3D脚本开发中的一些原理,方法和技巧。干货满满。
作者其他创作
大纲/内容
第13章 Unity 3D优化
13.1 看看Unity 3D优化需要从哪里着手
DrawCall
对底层图形程序接口的调用以在屏幕上画出东西
CPU调用
Fragment
GPU
Batching
将批处理之前需要很多次调用DrawCall的物体合并,之后只需调用一次底层图形程序的接口就行
内存的分配
Unity3D内存损耗,Mono内存管理,托管,项目dll等
13.2 CPU方面的优化
DrawCall
总体思路:每个物体尽量减少渲染次数,多个物体最好一起渲染
三种方案
使用Draw Call Batching,也就是描绘调用批处理。
通过把纹理打包成图集尽量减少材质的使用
尽量少的使用反光、阴影之类的效果,因为那会使物体多次渲染
使用Draw Call Batching批处理
Static Batching静态批处理
明确静止不动的游戏对象
检测器Inspector中勾选Static复选框即可
Dynamic Batching 动态批处理
只支持小于900顶点的网格物体
默认
只支持小于300顶点的网格物体
着色器使用顶点位置、法线和UV值3种属性
只支持小于180顶点的网格物体
着色器使用定点位置、法线、UV0、UV1和切向量
不要使用缩放,统一缩放的物体不会与非统一缩放的物体进行批处理
使用(1,1,1)和(1,2,1)的两个物体不会进行批处理
使用(1,2,1)和(1,3,1)的两个物体进行批处理
使用不同材质的实例化物体instance将会导致批处理失败
拥有lightmap的物体含有额外(隐藏)的材质属性,例如lightmap的偏移和缩放系数等。所以,拥有lightmap的物体将不会进行批处理
多通道的shader会妨碍批处理操作。比如几乎Unity 3D中所有的着色器在前向渲染中都支持多个光源,并为它们有效地开辟多个通道
预设体的实例会自动地使用相同的网格模型和材质
物体组件
设置合适的Fixed Timestep
尽量不要使用网格碰撞器
GC
字符串连接
尽量不要使用foreach语句
不要直接访问tag属性,用CompareTag来比较
不要用LINQ
代码优化
缓存Transform而不是每次都访问
GetComponent不要频繁调用
善于使用OnBecameVisible
使用内建数组,比如Vector3.zero
善于使用ref关键字
13.3 对GPU的优化
减少顶点数量,简化计算复杂度
保持材质的数目尽可能少。
使用纹理图集
如果使用了纹理图集和贡相材质,使用Renderer.sharedMaterial代替Renderer.material
使用光照纹理lightmap而非实时灯光
使用LOD
遮挡剔除
使用mobile版的shader
压缩图片,以适应显存带宽
OpenGL ES2.0 使用ETC1格式压缩
使用MipMap
13.4 内存的优化
Unity 3D的内存分配
Unity 3D的内部内存
资源:纹理、网格、音频
GameObject和各种组件
引擎内部逻辑需要的内存:渲染器、物理系统、粒子系统等
Mono的托管内存
游戏引擎各种引用类型控件的内存分配
项目dll需要的内存
第14章 Unity 3D的脚本编译
14.1 Unity 3D脚本编译流程概览
特殊文件夹
Standard Assert文件夹
Pro Standard Asset文件夹
Plugins文件夹
Editor文件夹
编译过程
第一阶段
编译运行时的脚本
Standard Assert文件夹
Pro Standard Asset文件夹
Plugins文件夹
第二阶段
编译Editor的脚本
第三阶段
所有不在Editor中的脚本(游戏逻辑脚本)
第四阶段
剩余的脚本
14.2 JIT即时编译
源代码生成CIL代码
运行时将CIL代码编译为原生CPU代码
跨平台为最重要的好处,性能损失不大
14.3 AOT提前编译
优势
减少启动时间
有利于内存共享以节约内存
生成的原生代码的性能可能更好
14.4 谁偷了我的热更新?Mono、JIT还是IOS
14.5 Unity 3D项目的编译与发布
Android
IOS
第三章 Unity 3D脚本语言的类型系统
3.1 C#的类型系统
静态
编译阶段确定变量类型
显式
编译阶段确定变量类型
安全
编译阶段确定变量类型
3.2 值类型和引用类型
值类型
一般在线程栈分配内存
个别分配在托管堆
数组中的元素
引用类型中的值类型字段
迭代器块中的局部变量
闭包情况下匿名函数中的局部变量
Which?
数字型结构
System.Int32
System.Float
System.Decimal
布尔型结构
System.Boolean
用户自定义结构
枚举
System.IO.FileAttributes
System.Drawing.FontStyle
特点
不派生出其他任何类型
不需要从其他类型派生
以值方式传递
值类型派生自System.ValueType
引用类型
托管堆分配内存
GC负责垃圾回收
Which?
Class
Interface
Delegate
Dynamic
System.Object
System.String
System.Collections.Generic.List
System.Text.Decoder
3.3 脚本语言中的引用类型
UnityEngine.Object
UnityEngine.Component
UnityEngine.MonoBehaviour
Editor
Reset
Initialization
Awake
OnEnable
Start
Physics
FixedUpdate
yield WaitForFixedUpdate
OnTriggerXXX
OnCoilisionXXX
InputEvents
OnMouseXXX
GameLogic
Update
yield null
yield WWW
yield StartCorbuline
SceneRendering
OnWillRenderObject
OnPreCull
OnBecameVisible
OnBecameInvisible
OnPreRender
OnRenderObject
OnPostRender
OnRenderImage
GizmoRendering
OnDrawGizmos
GUIRendering
OnGUI
EndOfFrame
yield WaitForEndOfFrame
Pausing
OnApplicationPause
Disable/Enable
OnDisable
Decommissioning
OnDestory
OnApplicationQuit
3.4 Unity 3D 游戏脚本中的值类型
Vector2、Vector3、Vector4
静态变量
down,left,one,right,up,zero,back,forward
点乘积
计算两个向量的夹角
(a面向b)OR(a背向b)
交叉乘积
计算两个向量的相对位置
向量b是处于向量a的顺时针方向还是逆时针方向
其他常见的值类型
Color
取值0~1
Color32
取值0~255
Ray
射线
Touch
手指触摸屏幕的状态
RaycastHit
光线投射碰撞返回的数据
Bounds
AABB
Rect
x,y,width,height
Plane
平面
3.5 装箱和拆箱
装箱
1.在托管堆中分配内存
2.将值类型的字段复制到新分配的堆内存中
3.返回对象地址,即对象的引用。值类型成了引用类型
拆箱
1.获取已经装箱对象中各个字段的地址
2.将托管堆上各个字段的值复制到线程栈的新的值类型实例中
第4章 Unity 3D中常用的数据结构
4.1 Array 数组
不推荐
4.2 ArrayList 数组
不推荐
4.3 List<T> 数组
推荐使用
4.4 C#中的链表LinkedList<T>
推荐使用
4.5 队列(Queue<T>)和栈(Stack<T>)
推荐使用
4.6 HashTable(哈希表)和Dictionary<K,T>(字典)
HashTable(哈希表)
Dictionary(字典)
第6章 在Unity中使用委托
6.1 向Unity 3D中的SendMessage和BroadcastMessage说拜拜
反射
性能
容易误删除
6.2 认识回调函数机制——委托
委托类型声明
delegate void MyDelegate(int num)
6.3 委托是如何实现的
使用委托调用回调函数,实际上是用委托invoke这个回调函数
6.4 委托是如何调用多个方法的
Delegate.Combine(+=)
这个方法可以将委托加入委托链,从而连续调用多个方法
Delegate.Remove(-=)
这个方法可以移除一个加入委托链的匹配委托
6.5 用事件实现消息系统
定义委托类型,确定回调方法原型
public delegate void SubHpHandler()
定义事件成员
public event SubHpHandler OnSubHp
定义触发事件的方法
OnSubHp();
6.6 事件是如何工作的
定义一个事件成员public event SubHpHandler OnSubHp
private SubHpHandler OnSubHp = null;
public void add_OnSubHp(SubHpHandler value)
public void remove_OnSubHp(SubHpHandler value)
6.7 定义事件的观察者,实现观察者模式
6.8 委托的简化语法
Action<T>
无返回值
Func<T>
有返回值
Predicate<T>
返回值是布尔值
Comparison<T>
排序
闭包
一个方法除了能和传递给它的参数交互之外,还可以同上下文进行更大程度的互动
外部变量,匿名方法的外部变量
被捕获的外部变量,匿名方法内部使用的外部变量
Lambda表达式
=>左边是参数
=>右边是函数体
第7章 Unity 3D 中的定制特性
7.1 初识特性——Attribute
DllImport特性
DLL为非托管代码时
C/C++
Serializable特性
使目标可以序列化和反序列化
多个特性可以同时应用于一个目标元素
逗号分隔
7.2 Unity 3D 中提供的常用定制特性
AddComponentMenu特性
Component菜单中加入一个脚本
MenuItem特性
Assets/Editor
7.3 定义自己的定制特性类
从System.Attribute继承
AttributeUsage
决定特性的目标
类
结构体
枚举
……
7.4 检测定制特性
this.GetType().IsDefined(typeof(CustomAttribute))
判断该类是否应用了某一个特性
7.5 新手拓展Unity 3D的编辑器
修改属性窗口
继承Editor
创建新窗口
继承EditorWindow
编辑场景窗口
继承Editor,重载OnSceneGUI
第8章 Unity 3D 协程背后的迭代器
8.1 初识Unity3D中的协程
StartCoroutine
StopCoroutine
8.2 使用协程实现延时效果
WaitForSeconds
WaitForFixedUpdate
WaitForEndOfFrame
8.3 Unity 3D 协程背后的秘密——迭代器
IEnumerator
IEnumerable
包含GetEnumerator方法
内部的实现是状态机
MoveNext
yield return
goto来实现
8.4 WWW和协程
第一章 Hello Unity 3D
1.1 Unity 3D游戏引擎进化史
1.2 Unity 3D编辑器初印象
Project视图
Inspector视图
Hierarchy视图
Game视图
Scene视图
绘图模式
渲染模式
场景视图控制
Effects 菜单和Gizmos菜单
1.3 Unity 3D的组成
图形模块
物理模块
音频模块
动作模块
导航模块
UI模块
脚本模块
1.4 为何需要游戏脚本
第二章 Mono所搭建的脚本核心基础
2.1 Mono是什么
开源项目
基于通用语言架构
C#的ECMA标准
跨平台
Windows
Mac OS X
Linux
一些游戏平台
Mono的组成
C#编译器(mcs)
Mono运行时
3种转译方式
即时编译(Just-in-time,JIT)
提前编译(Ahead-of-time,AOT)
完全静止编译(Full-ahead-of-time,Full-AOT)
垃圾回收器(garbage collection,GC)
类库加载器
线程系统
基础类库
Mono类库
2.2 Mono如何扮演脚本的角色
将脚本语言通过JIT编译为原生代码
效率
Mono运行时嵌入应用
1.编译C++程序和链接Mono运行时
2.初始化Mono运行时
3.C/C++和C#/CIL的交互
2.3 Unity 3D为何能跨平台?聊聊CIL
使用了CIL(Common Intermediate Language)代码指令集
可读性
CIL是什么?
CIL指令
以“.”开头
.class
.method
……
CIL特性
private
public
……
CIL操作码
ldstr
call
……
基于堆栈
面向对象
静态方法
操作参数从ldarg.0开始
实例方法
操作参数从ldarg.1开始,ldarg.0是this占用
CIL的跨平台
从CIL到NativeCode
JIT即时编译
AOT静态编译
Full AOT
2.4 脚本的选择,C#还是JavaScript
用C#就对了
第5章 在Unity 3D中使用泛型
5.1 为什么需要泛型机制
object需要强制类型转换,装箱拆箱
效率
泛型是在编译时检查
效率
泛型逻辑复用
Unity 3D创建泛型类型
泛型引用类型
泛型值类型
泛型枚举类型
类型安全
5.2 Unity 3D中常见的泛型
System.Collections.Generic
System.Collections.ObjectModel
System.Array
提供静态泛型方法
5.3 泛型机制的基础
泛型类型和类型参数
类型参数
放到一对尖括号<>中
逗号分隔
开放类型
不指定类型的泛型。如List<>
编译报错
封闭类型
制定具体类型的泛型。如List<int>
编译正常
泛型类型和继承
可以继承和派生
泛型接口和泛型委托
泛型接口
IEnumerator<T>等
可以继承
可以不指定类型参数
如:internal class Test<T>:IEnumerator<T>
泛型委托的参数三选一
不变量
泛型类型参数无法更改
逆变量
泛型类型参数可以从一个类更换为该类的某个派生类
in标识
协变量
泛型类型参数可以从一个类更换为该类的某个基类
out标识
泛型方法
例如:public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter)
5.4 泛型中的类型约束和类型推断
类型约束
引用类型约束
where T:class
必须是类型参数的第一个约束
T必须是引用类型
例如:struct Example<T> where T:class
合法样例
Example<string>
Example<IComparable>
Example<int[]>
非法样例
Example<int>
Example<float>
值类型约束
where T:struct
T必须是值类型
例如:class Example<T> where T:struct
合法样例
Example<int>
Example<float>
Example<bool>
非法样例
Example<Object>
Example<string>
构造函数类型约束
where T:new()
必须是类型参数的最后一个约束
工厂模式用的比较多
转换类型约束
一致性转换
class Example<T> where T:Stream
引用转换
class Example<T> where T:IDisposable
装箱转换
class Example<T> where T:IComparable<T>
只能指定一个类,但可以指定多个接口
class Example where T:Stream, IComparable<T>,IDisposable
类型约束的组合
主要约束
引用类型约束、值类型约束、一致性转换约束
可选但唯一
次要约束
接口或其他类型参数的转换类型约束
可多个
构造函数约束
可选,主要约束是值类型约束就不需要了
类型推断
泛型方法支持隐藏函数名后的<>
第9章 在Unity 3D中使用可空型
9.1 如果没有值
数据库空,但取值int的情况
9.2 表示空值的一些方案
魔值
牺牲一个值,如-1000,表示空
bool变量
需要维护,易出错
值和bool变量组成一个结构体
引用类型
频繁装箱拆箱
引用类型套用值类型
代码量增加
9.3 使用可空值类型
Nullable<T>
9.4 可空值类型的简化语法
?
??
9.5 可空值类型的装箱和拆箱
第10章 从序列化和反序列化看Unity 3D的存储机制
10.1 初识序列化和反序列化
MemoryStream
内存流
FileStream
文件流
SoapFormatter
处理soap格式的序列化和反序列化
被淘汰,无法序列化泛型
BinaryFormatter
处理二进制序列化和反序列化
XmlSerializer
xml序列化和反序列化
10.2 控制类型的序列化和反序列化
如何使类型可以序列化
[Serializable]
基类必须序列化,子类才可序列化
如何选择序列化的字段和控制反序列化的流程
[NonSerialized]
不进行序列化
StreamingContext参数必须有
[OnSerializing]
序列化时做某事
[OnSerialized]
序列化完成后做某事
[OnDeserializing]
反序列化时做某事
[OnDeserialized]
反序列化完成时做某事
序列化、反序列化中流的上下文介绍及应用
StreamingContextStates
10.3 Unity 3D中的序列化和反序列化
概览
属性监视板(Inspector)
预制体Prefab
YAML文本格式
二进制格式
实例化
Instantiate
存储场景
载入场景
重载编辑器代码
Resource.GarbageCollectSharedAssets
注意事项
要被序列化的字段最好是什么样?
public
[SerializeField]
static
const
readyonly
字段类型可以被序列化
什么样的类型可以被序列化?
自定义非抽象类
[SerializeField]
自定义结构体
[SerializeField]
派生自UnityEngine.Object
基元类型(int、float、double、bool、string)
基元类型Array
基元类型List<T>
10.4 Profab和实例化之谜——序列化和反序列化的过程
认识预制体Prefab
可放入多场景,同场景多次放入
同场景增加一个Prefab就实例化一个Prefab实例
所有Prefab实例都是Prefab的clone
原型改变,实例都改变
实例化游戏对象
Object.Instantiate
序列化和反序列化之谜
序列化
GetSerializableMembers找到需要序列化的字段类型
GetObjectData找到字段类型对应的值
标识程序集和类型的完整名称
将所有信息写入流中
反序列化
GetTypeFromAssembly,获得程序集和类型的完整名称
GetUninitializedObject,开辟内存空间
GetSerializableMembers,获得字段数组
获得字段值
PopulateObjectMembers,通过遍历数组将对象的每个字段和属性初始化为对应的值
第11章 移动平台动态读取外部文件
移动平台的资源路径问题
Application.dataPath
数据文件路径
Application.streamingAssetsPath
流数据缓存路径
Application.persistentDataPath
持久化数据存储路径
Application.temporaryCachePath
临时数据缓存路径
资源处理种类
Resources
只读
存放Prefab
主线程加载
Resources.Load()
压缩加密
StreamingAssets
只读
存放二进制文件
WWW来读取
不压缩不加密
AssetBundle
WWW来读取
Unity 3D定义的二进制类型
PersistentDataPath
内容可独写
存储无限制
第12章 在Unity 3D中使用AssetBundle
12.1 初识AssetBundle
经过了压缩的Unity3D导出的资源
模型
贴图
音频剪辑
游戏场景
自定义二进制文件
后缀必须是bytes
游戏容量
动态拓展
12.2 使用AssetBundle的工作流程
创建AssetBundle文件
BuildPipeline.BuildAssetBundle
任意资源打包
BuildPipeline.BuildStreamedSceneAssetBundle
流场景资源包
BuildPipeline.BuildAssetBundleExplicitAssetNames
任意资源打包(可自定义资源名称)
下载AssetBundle文件
WWW.LoadFromCacheOrDownload
加载AssetBundle文件
AssetBundle.LoadAsset
占据主线程
AssetBundle.LoadAssetAsync
不占据主线程
AssetBundle.LoadAllAssets
加载所有资源
卸载AssetBundle文件
AssetBundle.Unload
12.3 如何使用本地磁盘中的AssetBundle文件
AssetBundle.CreateFromFile
StreamingAssets文件夹下未经压缩的AssetBundle文件
WWW.LoadFromCacheOrDownload
压缩的AssetBundle文件
12.4 AssetBundle文件的平台兼容性
开发平台和运行平台要一致
12.5 AssetBundle如何识别资源
默认文件名识别
BuildPipeline.BuildAssetBundleExplicitAssetNames
自定义标识
0 条评论
下一页