Unity基础知识汇总
2.Unity相关知识
2.1 Unity介绍
Unity成为一款可以轻松创建游戏和三维互动的开发工具,是一个专业跨平台游戏引擎
Unity操作快捷键
Ctrl |
| N | New Scene 新建场景 |
Ctrl |
| O | Open Scene 打开场景 |
Ctrl |
| S | Save Scene 保存 |
Ctrl | Shift | S | Save Scene as 保存场景为 |
Ctrl | Shift | B | Build Settings... 编译设置... |
Ctrl |
| B | Build and run 编译并运行 |
Edit 编辑 | |||
Ctrl |
| Z | Undo 撤消 |
Ctrl |
| Y | Redo 重做 |
Ctrl |
| X | Cut 剪切 |
Ctrl |
| C | Copy 拷贝 |
Ctrl |
| V | Paste 粘贴 |
Ctrl |
| D | Duplicate 复制 |
Shift |
| Del | Delete 删除 |
|
| F | Frame selected 选择的帧 |
Ctrl |
| F | Find 查找 |
Ctrl |
| A | Select All 全选 |
Ctrl |
| P | Play 播放 |
Ctrl | Shift | P | Pause 暂停 |
Ctrl | Alt | P | Step 停止 |
2.2 脚本基础开发
Unity面向组件开发,游戏物体想要实现什么样的功能,只需要添加相对应的组件就可以了,此时会在属性面板上显示出来。可以像添加组件一样把脚本添加到游戏物体上。
脚本生命周期:
1.Awake
最早执行,在GameObejct对象的数据和状态的初始化后执行,只会触发一次。
tips:GameObject实例化到场景中后在激活的状态下才会初始化,如果GameObject从来未激活,它的脚本上的Awake永远不会被调用。
即时触发,GameObject第一次active的时候调用。
2.Start
在脚本第一次调用Update之前调用,只会触发一次。
3.OnEnable
在脚本状态由disabled变为enabled状态是触发一次,Unity内部可能将脚本初始置为diabled,所以如果我们实例化一个激活的GameObject,并且在挂上一个enabled了的脚本后把它加入场景会调用一次OnEnable,并且早于Start。
4.OnDisable
在脚本由enabled状态变为disabled状态的时候会调用。
tips:OnEnable和OnDisable事件的触发是即时的,并且这两个事件是综合考虑GameObject的active状态和脚本的enabled状态的。所以不要在OnEnable里面把脚本设置为disabled同时在OnDisable中把脚本设置为enabled,会导致死循环!!!
5.Update
在每一帧调用,用于实现各种行为逻辑
6.LateUpdate
在每一帧调用,但是是在Update之后触发。常用的一种情况是相机跟随移动角色:角色移动逻辑在Update里面实现,相机跟随逻辑在LateUpdate里面实现。
7.OnDestroy
在组件被销毁时调用,只有GameObject被激活过才会触发。
脚本常用类:
1.Component类
2.GameObject类
3.Transform类
4. Vector3类
1. Quaternion
Identity:该四元素无旋转。
LookRotation:创建一个旋转,沿着forward(Z轴)并且头部沿着Y轴的约束注释
2. Time
3. Mathf
预设体
组件的集合体 , 预制物体可以实例化成游戏对象.
创建预设体的作用: 可以重复的创建具有相同结构的游戏对象。
在脚本中,使用instantiate函数来进行预设体的实例化
Object tmpObj = Instantiate(prefabs, pos , Quaternion.identity);
2.3 物理引擎
1.键盘输入常用方法
GetKey:按键按下期间返回true
GetKeyDown:按键按下的第一帧返回true
GetKeyUp:按键松开的第一帧返回True
Input类简介
虚拟按键
具体代码和实现方式如下:
Float hor = Input.GetAxis(“Horizontal”);
鼠标事件
常用的鼠标属性和方法
碰撞器组件:
当我们在场景当中创建出来一个游戏物体的方式后,引擎会自动为这个游戏物体添加碰撞器组件,其主要功能是进行碰撞检测。
发生碰撞的条件:1、相对运动,2、两个碰撞器,2、一个刚体
刚体组件
刚体组件能受到你的作用,比如:重力,反作用力,阻力,外加力,爆炸力等等
以下是刚体组件的一些属性和常用方法
触发器:
发生触发的条件:1、两个碰撞器 2、一个触发器 3、一个刚体
射线
Unity提供了涉嫌集资:通过虚拟射线检测所碰撞到的物体
Ray ray = new Ray();
Bool Raycast(Ray ray,out RaycastHit hitInfo);
Rayray=Camera.main.ScreenPointToRay(Input.mousePostion);
从主摄像机到鼠标点击位置创建一条射线。
2.4 Unity2D和精灵
精灵:
图片精灵的意思(Flash引擎里也有图片精灵对象),将导入的图片类型设置为Sprite即可拖到场景面板里,带有随屏幕大小自动缩放功能,还可以加Collider进行射线检测,简单实用。图片精灵是用来绘制图集的控件,精灵可以在一张大图中去截取一部分(大图就是整体图像集合(Atlas),而截取的小图就是一个精灵),然后给精灵命名,使用时通过精灵的名称就能直接绘制,并且精灵还可以用来制作动画。
图层排序:2D游戏当中的图片之键没有深度关系,都处于同一个平面上,但在实际游戏当中需要为他们排序,按视觉上的空间先后顺序显示。
2D物理组件与3D物理引擎在功能上基本相似,当我们给精灵添加了个刚体之后,他就会受到2D物理引擎的控制。
2D碰撞和触发回调方法:
2D开发常用类:
创建序列帧动画,按Ctrl+F6打开Animation的编辑窗口,点击后可以添加属性,如果要更改动画中某一帧的位置、旋转和缩放,只需要点击+号即可,在Animation编辑窗口,在关键帧点击天津爱时间,此时可以选择脚本中的方法,但是该方法必须是public ,可访问状态
2.6 UGUI
UGUI的特点:灵活,快速和可视化,对于开发者而言,效率高,易于使用和扩展以及对Unity的兼容性高。
UGUI与GUI插件NGUI相比:
1. 由NGUI创始人参与开发
2. 与Unity结合更加密切
3. 自适应系统更加完善
4. 更方便的深度处理
5. 省去Altas,直接使用Sprite Pa
UGUI画布:是所有UI控件的根类,所有的UI控件补习都在画布上面。EventSystem是事件系统,负责监听用户的输入,创建UI控件是,当层级试图当中没有CANVAS和EventSystem,系统会帮我们自动创建。
画布的三种渲染模式模式:
1、Screen Space-Overlay:不需要UI摄像机,画布会一直出现在摄像机最前面
2、Screen Space-Camera:需要一个UICamera,支持U前方显示3D模型和粒子系统
3、World Space:UI控件成为3D场景中的一部分
LayoutGroup组件:对子控件进行布局,上面只能有一种布局组件,布局组件有以下三种:
1.Horizontal Layout Group (水平布局)
2.Ver tical Layout Group(垂直布局)
3.Grid Layout Group(网格布局)
1. UGUI画布也称为Canvas,UGUI是所有控件的父类
2. 所有UGUI控件都必须绘制在画布上面
3. 当创建UGUI控件工程当中没有Canvas的时候会自动创建
Canvas与EventSystem
Text控件是用来显示文本的文本控件
Text的属性可以在Inspector当中通过Text组件进行设置,也可以在代码当中进行动态设置。给Canvas挂载脚本UGUISetting
Image控件主要是用来显示图片,显示图片的格式是Sprite
当我们给Image选择一张贴图之后会出现ImageType选项,如下图所示。ImageType总共有四种选项:simple显示单个会拉伸;Tilled平铺显示,图片按照原始显示;Sliced按照九宫格显示,拉伸区域只会在九宫格中间;Filled填充显示,可以根据不同的填充方式模拟技能冷却的
按钮添加监听事件:
Button btn;
void Star t () {
//获取到按钮
btn = GameObject.Find(“Button”).GetComponent<Button> ();
//给按钮添加监听事件
btn.onClick .AddListener (BtnClick);
}
void BtnClick(){//按钮响应事件
Debug.Log (“btn.onClick .AddListener ()”);
}
Slider是滑动条,Slider的属性如图所示:
Slider slider ;
void Star t () {
//获取到Slider组件
slider = GameObject.Find(“Slider ”).GetComponent<Slider> ();
//添加监听事件
slider.onValueChanged.AddListener(SliderValueChange);
}
// 事件响应
public voidSliderValueChange (float value){
Debug.Log (“value = “+value);
}
InputField创建出来如下图所示,InputField层级视图当中包含Placeholder与Text,Placeholder用于显示占位符,即输入框没有输入文本时显示的文本,例如下图的“Enter text”,Text用于显示输入的内容。
RectTransform的作用用来计算UI的位置和大小,RectTransform继承于Transform,具有Transform的所有特诊,通过RectTransform能够实现基本的布局和层级控制。
如图所示,箭头所指即为锚点,锚点表示的是相对于父级矩形的子矩形区域。如图所示锚点为四边形,锚点有多种摆放方式,可以为矩形,点状或是为线状。锚点移动范围仅限于父级视图当中。
按下T键选中某一个UI控件即可看到UI控件的中心点,中心点也叫中心轴,当鼠标拖动UI控件进行旋转的时候会围绕中心点旋转。中心点为矩形的一部分。0对应左下角,1对应右上角
UGUI回调方法
输入模块StandaloneModule和TouchInputModule两个组件会检测到用户的一些输入事件,并且以事件的方式通知目标对象。实现这些回调方法需要实现相应的接口。常用的回调事件如下所示
CanvasGroup的作用:当一个控件盖到另外一个控件上的时候,下面的控件默认是检测不的,为了可以透过当前控件检测到下面的控件,可以给该组件添CanvasGroup组件,其属性blocksRaycasts设置为false时表示可以穿透该控件检测到下面的控件,如果为true表示不能穿透,下方的控件检测不到.
在Unity中,所有与应用程序相关的方法都写在Application 类中。
主要功能:获取或设置当前应用程序的一些属性
• 加载游戏关卡场景
• 获取资源文件路径
• 退出当前游戏程序
• 获取当前游戏平台
• 获取数据文件夹路径
同步加载场景的方式分为两种:
读取新关卡后立即切换,其参数为所读取新关卡的名称或索引
SceneManager.LoadScene("Scene2");
加载一个新的场景,当前场景不会被销毁。
SceneManager.LoadScene("Scene2",LoadSceneMode.Additive);
异步加载新游戏场景,当新场景加载完成后进入新场景并且销毁之前的场景。
SceneManager.LoadSceneAsync("Scene2");
同样异步加载新场景,新场景加载完毕后,保留之前场景并且进入新场景。
SceneManager.LoadSceneAsync("Scene2",LoadSceneMode.Additive);
2.7 Unity 动画系统基础
Mecanim 功能强大的动画系统
方便的实现人型动画的设置和重用方便的实现动画剪辑的设置
可视化的动画控制界面
对动画播放的精确控制
动画类型分为四种
None:无任何动画
Legacy:旧版Animation动画
Generic:一般动画(非人形动画)
Humanoid:人形动画
设置Avatat,Avatat是Mecanim系统中极为重要的模块,因此为模型资源正确的设置Avatar也至关重要,不管Avatar自动创建过程是否成功,用户都需要进入
根动作设置包括旋转、Y 轴水平位置和 XZ 平面位置Bake Into Pose:烘焙进姿势
loop match:当需要让一个动作重复时,后面的圆点颜色就代表重复质量。绿色表示该动画的第一帧和最
后一帧角色的姿势相似,黄色次之,红色质量最差。Based Upon:定根动作的参考点
Offset:偏移量用来设置指定根动作,在运动时的偏移量
动画间的过渡需要使用状态机和状态参数实现,目前支持的参数类型有4种。
Animator常用方法:
改变 Float 类型状态参数的值
animator.SetFloat(speedFloat, 2.0f );
改变 Bool 类型状态参数的值
animator.SetFloat(deadBool, true);
BlendTree: 融合树的功能是将两个或多个相似的动作进行融合,可以使用多个或者一个参数来控制融合状态。
在 Unity 中可以使用动画层来管理不同类型的动画分层处理。动画层遮罩能够在本层动画中,身体的特定部分选择性的激活或禁用在 Project面板下创建一个 Avatar Mask。身体遮罩包括头部、左手臂、右手臂、左手、右手、左腿、右腿和根部,还可以选择给手和腿添加 IK,这将决定 IK 曲线是否会包含在动画融合中,点击身体部分可以激活或禁用对应部位的动画。
大多数动画是由旋转关节角度的骨架来预先确定的值。子关节的位置变化根据父节点的旋转,因此关节链的最终位置会根据角度和它所包含的各个关节的相对位置来确定。这种方法构成的骨架被称为正运动学。
IK动画全名是Inverse Kinematics 意思是反向动力学,就是子骨骼节点带动父骨骼节点运动。比如跳街舞的少年用手撑着身体在地上转圈,手就是子骨骼,胳膊身体就是它的父骨骼,这时运动手就需要带动胳膊身体来移动。我们需要从一个正确配置的Avatar的人形Mecanim角色开始,接下来创建一个动画控制器,包含至少一个动画人物。然后在动画窗口的图层面板中,单击图层的齿轮设置图标,并在弹出的菜单中选中IK复选框。
animator.SetIKPositionWeight(AvatarIKGoal.RightHand,1);
animator.SetIKRotationWeight(AvatarIKGoal.RightHand,1);
if (ikActive) {
animator.SetIKPosition(AvatarIKGoal.RightHand,rightHandObj.position);animator.SetIKRotation(AvatarIKGoal.RightHand,rightHandObj.rotation);
} else{
animator.SetIKPositionWeight(AvatarIKGoal.RightHand,0);
animator.SetIKRotationWeight(AvatarIKGoal.RightHand,0);
}
当你需要在角色动画播放到某一帧的时候需要做其他处理时,这时可以使用动画事件
2.8 Unity 导航系统
导航在游戏当中的概念就是从一点走到另外一点的过程,在该过程中需要考虑:阻挡,路径选择,可走地形,地形特点以及拟人化等多方面因素。在游戏当中导航分为两种:
(1)ai角色:基于计算机本身控制的纯导航
(2)主角:基于UI交互下的导航
导航实现方式
(1) A*算法
导航当中常用的算法是A*算法,它是一种从起点到终点探测代价最小路径的广度优先搜索方式,广泛应用于2D格子型地图。
(2)NavigationMesh
在Unity当中采用的是基于Navigation Mesh的寻路算法原理, 是A*算法的变种,将A*算法当中的格子变成三角形或多边形, 可以方便的从二维扩展到三维空间,是3D游戏当中的主流寻路算法。
NavMesh(导航网格)是3D游戏世界中用于实现动态物体自动寻路的一种技术,将游戏中复杂的结构组织关系简化为带有一定信息的网格,在这些网格的基础上通过一系列的计算来实现自动寻路。
NavMeshAgent常见的属性和方法:
分离路面导航:
在两个不连通的寻路网格间,如果角色想在这两个网格间行走就需要添
加跳跃的链接Off MeshLink
分层烘焙路面:
实现分层路面导航主要分为以下步骤:
(1)自定义Area层
(2)设置场景中不同区域烘焙为不同的层
(3)设置角色可以在导航网格中哪些层移动
设置不同区域在导航网格中为不同的层主要是控制角色有选择的行走某些区域。设置好烘焙出来不同区域在场景当中呈现不同颜色,接下来通过角色自身挂载的NavMeshAgent组件里的AreaMask属性设置代理可以在导航网格中的哪些区域移动。默认为Everything
动态障碍:
Nav Mesh Obstacle有两种模式,一种是普通的模式,通过设置半径与高度来确定一个范围,阻挡代理的移动。另一种是Car ve模式,会根据模型大小界定该区域无法参与导航网格计算
2.9 Unity 特效渲染
粒子特效:
Unity中一个典型的粒子系统是一个对象,它包含了一个粒子发射器,一个粒子动画和一个粒子渲染器。粒子发射器产生粒子,粒子动画器则随时间移动粒子,粒子渲染器则将它们渲染到屏幕中
粒子系统相关参数:
拖尾渲染:
TrailRenderer
拖尾渲染组件属于特效当中的一种,给一个物体添加拖尾渲染组件的方式
线性渲染:
LineRenderer常用方法
2.10 Unity 数据库与存储
PlayerPrefs存储数据
适用范围
1. 适用设备:Mac OSX、Linux、Windows、
Windows Store Apps、Windows Phone 8、Web players
2. 存储机制:Key-Value
3. 可存储变量类型:int、float、string
PlayerPrefs数据存储路径
1. Mac OSX:~/Library/Preferences
2.Windows:HKCU\Software\[company name]\[product name]
3.Linux:~/.config/unity3d/[CompanyName]/[ProductName]
4. Windows StoreApps:%userprofile%\AppData\Local\Packages\
[ProductPackageId]>\LocalState\playerprefs.dat
5. WebPlayer
6. Mac OS X:~/Library/Preferences/Unity/WebPlayerPrefs
7. Windows:%APPDATA%\Unity\WebPlayerPrefs
PlayerPrefs常用方法;
void Example() {
PlayerPrefs.SetFloat("PlayerScore", 10.0F);
print(PlayerPrefs.GetFloat("PlayerScore"));
XML数据生成和解析
1. XML 指可扩展标记语言(EXtensible Markup Language)
2. XML 是一种标记语言,很类似 HTML
3. XML 的设计宗旨是传输数据,而非显示数据
XML结构
每个标签内部可以有多个属性。标签可以层层嵌套,形成一个树形
结构。
例如:
<position name=“player”>
<x>18</x>
<y>5</y>
<z>30</z>
</position>
以上Alarm(元素节点),lock(属性节点),Time(元素节点), StringValue(文本节点)都是节点(Node),
但是只有<Alarm>……</Alarm>
和<Time>StringValue</Time>是元素(Element)
XML常用的类
1. XmlDocument——XML文件类2. XmlNode——XML节点类
3. XmlAttribute——XML属性类4. XmlElement——XML元素类
XmlNode
XmlDocument
XmlElement
Xml数据生成步骤
在Unity引擎中如何生成本地XML数据?
第一步:引用C#的命名空间 System.Xml
第二步:生成XML文档(XmlDocument 类)
第三步:生成根元素(XmlElement类)添加给文档对象第四步:循环生成子元素添加给父元素
第五步:将生成的XML文档保存
Xml数据生成示例
//创建xml文件对象
XmlDocument doc = newXmlDocument();
//创建xml头
XmlNode xmldct =doc.CreateXmlDeclaration ("1.0", "utf-8", null);
//添加xml头
doc.AppendChild (xmldct);
//创建xml根节点(元素属于节点)users
XmlNode root =doc.CreateElement("users");
//添加xml根节点
doc.AppendChild (root);
//创建子节点
XmlNode xn_element =doc.CreateNode (XmlNodeType.Element, "name", null); //设置子节点的值
xn element.InnerText ="Albert";
//创建属性
XmlAttribute xa =doc.CreateAttribute ("no");
//设置属性值
xa.Value ="1234";
//获取元素的docment
XmlDocument xd = xnelement.OwnerDocument;
_
//设置元素属性
xnelement.Attributes.SetNamedItem (xa);
_
//添加子节点到root节点
root.AppendChild (xn_element);
//保存xml
doc.Save(Application.dataPath+ "/test.xml");
Xml序列化
序列化是将对象状态转换为可保持或传输的格式的过程。
我们可以把对象序列化为不同的格式,比如说,Json序列化、XML序列化、二进制序列化等,以上这些不同的格式也都是为了适应具体的业务需求。
public class BaseInfo {
//BaseInfo对象中保存Person对象
List<Person> perList = newList<Person>(); //创建元素节点
[XmlElement(ElementName="Perosn")]publicList<Person> PerList
{
get
{
return perList;
}
set
{
perList = value;
}
}
}
//用于将信息写入字符串
StringWriter sw = newStringWriter();
//指定 Xml序列化名字空间
XmlSerializerNamespacesns = new XmlSerializerNamespaces();ns.Add("", "");
//声明Xml序列化对象实例serializer,对BaseInfo类 进行序列化
XmlSerializer serializer= new XmlSerializer(typeof(BaseInfo));
//使用 StringWriter 和指定的名字空间 将BaseInfo 对象写入Xml文件
serializer.Serialize(sw,baseInfo, ns);
sw.Close();
反序列化示例
//根据指定路径读取,实例化FileStream对象
FileStream fs = newFileStream(Application.dataPath +
"/Practise5/test.xml",FileMode.Open,FileAccess.Read);
//指定反序列化的类型
XmlSerializer serializer= new XmlSerializer(typeof(BaseInfo));BaseInfo baseInfo =(BaseInfo)serializer.Deserialize(fs);
fs.Close();
//遍历baseinfo对象中的信息,输出到控制台
for (int i = 0; i <baseInfo.PerList.Count; i++)
{
Person per =baseInfo.PerList[i];
Debug.Log("名字: "+ per.Name +", 年龄:"+per.Age );
for (int j = 0; j <per.BooksList.Count; j++)
{
Books books =per.BooksList[ j];
for (int k = 0; k <books.BookList.Count; k++)
{
Book book =books.BookList[k];
Debug.Log("书名: "+book.Title+", 价格:"+book.Price);
}
}
}
JSON数据生成和解析
System.Json
1. JSON 是纯文本
2. JSON 是一种轻量级的数据交换格式3. JSON 具有层级结构(值中存在值)
1. 数据在键值对
2. 数据由逗号分隔3. 花括号保存对象4. 方括号保存数组
LitJson.JsonMapper
1. 把对象转化成 JSON格式字符串: JsonMapper.ToJson
2. 把JSON格式字符串转化成对象: JsonMapper.ToObject
什么是SQLite
1. SQLite是一款轻型的数据库
2. SQLite的设计目标是嵌入式的
3. SQLite占用资源非常的低
4. SQLite能够支持Windows/Linux/Unix等等主流的操作系统
作用:INSERT INTO 语句用于向表格中插入新的行。
语法1:INSERT INTO 表名称 VALUES (值1, 值2,....)
语法2:INSERT INTO table name (列1, 列2,...) VALUES (值1, 值2,....)
作用:DELETE 语句用于删除表中的行。
语法:DELETE FROMPerson WHERE LastName = 'Wilson'
作用:Update 语句用于修改表中的数据。
语法:UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值
作用:SELECT 语句用于从表中选取数据。
语法:SELECT 列名称 FROM 表名称
Unity当中使用SQLite:
导入mono.data.sqlite.dll到Assets文件夹代码添加库:using Mono.Data.Sqlite;
使用SQLiteConnection对象,进行数据库连接,此操作可以创建空的数据库
具体代码如下:
//数据库连接路径
string path = "data source="+ Application.streamingAssetsPath +
"/UserDatabase.sqlite";
void OpenDataBase(stringconnectionString)
{
try
{
conn = newSqliteConnection(connectionString);
conn.Open();
}
catch (System.Exceptionexc)
{
Debug.Log(exc);
}
}
使用SqliteCommand数据指令,对象进行数据库操作
//判断数据库中是否有UserTable这个表SqliteCommand cmd =conn.CreateCommand();
cmd.CommandText ="select count(*) from sqlite master where
_
type = 'table' and name ='UserTable'";
SqliteDataReader reader =cmd.ExecuteReader();
使用SqliteDataReader 数据读取对象,进行数据库内容读取
//判断数据库中是否存在这张表
bool isExit = false;
while (reader.Read())
{
for (int i = 0; i <reader.FieldCount; i++)
{
if(reader.GetValue(i).ToString() == "1")
{
isExit = true;
}
}
}
数据库操作完成之后要将数据库关闭
//如果表不存在则建表
reader.Dispose();
reader.Close();
reader = null;
if (!isExit)
{
Debug.Log("表不存在,建表");
cmd.CommandText ="Create Table UserTable(uname text,pwd
text)";
cmd.ExecuteNonQuery();
}
cmd.Dispose();cmd =null;CloseDataBase();
执行SQL语句的三种方式
1、int ExecuteNonQuery()
返回受影响的行数(常用于执行增删改操作)
2、object ExecuteScalar()
返回查询到的第一个值(常用于只查询一个结果时)3、SqliteDataReader ExecuteReader()
返回所有查询的结果(SqliteDataReader对象)
数据库封装:
为何要封装?
1. 方便项目管理
2. 方便开发人员的快捷的使用
3. 防止高度保密数据外泄
1. 连接数据库
2. 通过Sql语句查询数据
3. 通过表名查询全表数据
…
4. 关闭数据库连接,释放资源
平台选择不同的存储路径:
在直接使用Application.dataPath来读取文件进行操作,移动端是没有访问权
限的。
Application.streamingAssetsPath
直接使用Application.streamingAssetsPath来读取文件进行操作,此方法在pc/Mac电脑中可实现对文件实施“增删查改”等操作,但在移动端只支持读取操作。
使用Application.persistentDataPath来操作文件,该文件存在手机沙盒中,因为不能直接存放文件。
1.通过服务器直接下载保存到该位置,也可以通过Md5码比对下载更新新的资源
2.没有服务器的,只有间接通过文件流的方式从本地读取并写入
Application.persistentDataPath文件下,然后再通过
Application.persistentDataPath来读取操作。
注:
在Pc/Mac电脑以及android跟Ipad、ipone都可对文件进行任意操作,
另外在IOS上该目录下的东西可以被iCloud自动备份。
发布到安卓端需要经过特殊处理
同样需要上述的三个类库和libsqlite.so文件,但不需要重新建表和重新
插入数据内容。当Android端安装应用程序时,需要一个*.apk的安装文件,此文件内保存着我们从Unity开发平台导入的*.sqlite文件,所以我们可以通过www来下载该sqlite文件,从而通过IO流写入到Android本地的persistentDataPath沙盒路径。该文件保存着所有表格和数据,无需再次创建和插入,通常使用的都是这种方式,较为便捷。
Android端连接本地数据库:
注意:
二进制文件需要放在Plugins->Android->assets中,然后根据下面的路径就可以在Android中读取。
string Path =jar:file://” + Application.dataPath + “!/assets/” +
“你的文件“;
2.11 WWW类与协程
什么是协程?
1. Unity的协程系统是基于C#的一个简单而强大的接口
2. 简单讲,协程就是可以把一个方法拆分成多次执行的一种接口
IEnumerator ShowTime()
{
Debug.Log("FirstFrame");//第一帧执行
yield return 0;//等待下一帧
Debug.Log("SecondFrame");//第二帧执行
yield return 0;//等待下一帧
Debug.Log("ThirdFrame");//第三帧执行
}
开启协程
//通过传入方法开启协程
StartCoroutine(ShowTime());
//通过传入字符串类型的方法名称开启协程
StartCoroutine("ShowTime");
停止协程
StartCoroutine(“ShowTime");
//停止协程
StopCoroutine(“ShowTime”);
注意:StopCoroutine只能停止以字符串方式开启的协程
当你“yield”一个方法时,你相当于说,“现在停止这个方法,然后在下一帧中从这里继续开始!”。
2. 用0或者null来yield的意思是告诉协程
等待下一帧,直到继续执行为止。
协程注意事项
1. 在程序中调用StopCoroutine()方法只能终止以字符串形式启动(开始)的协程。
2. 多个协程可以同时运行,它们会根据各自的启动顺序来更新。
3. 协程可以嵌套任意多层。
4. 协程不是多线程(尽管它们看上去是这样的),它们运行在同一线程中,跟普通的脚本一样。
5. IEnumerator类型的方法不能带ref或者out型的参数,但可以带被传递的引用。
协程,线程的区别
1. 线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是的)。
2. 协程和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。
3. 协程避免了无意义的调度,由此可以提高性能,但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力。
协程优点:
1. 跨平台
2. 跨体系架构
3. 无需线程上下文切换的开销
4. 无需原子操作锁定及同步的开销
5. 方便切换控制流,简化编程模型
6. 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。
缺点:
1. 无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
2. 进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序:这一点和事件驱动一样,可以使用异步IO操作来解决。
什么是AssetBundle。
AssetBundle是从unity项目中打包出来的资源文件,可用于资源的更新等。AssetBundle支持3中格式的压缩选择,分别是LZMA,LZ4,无压缩。默认是LZMA格式的压缩,但是这样虽然可以使资源文件大小大大缩小,利于下载,但是也有不利的一面,在使用时会先解压再使用,所以会造成加载时间过长。
不压缩格式资源包会比较大,但是加载时不需要解压,所以加载时会更快。
WWW类:
1. 可以简单的访问web页面;
2. 这是一个小工具模块检索url的内容;
3. 你开始在后台下载通过调用WWW(url),返回一个新的WWW对象;
4. 你可以检查isDone属性来查看是否已经下载完成,或者yield自动等待下载物体,直到它被下载完成(不会影响游戏的其余部分)。
WWW类常用属性:
2.12 网络基础
网络协议即网络中传递、管理信息的一些规范,在计算机之间的相互通信需要共同遵守一定的规则,这些规则就称为网络协议。
TCP/IP不是一个协议,而是一个协议簇的统称,里面包含TCP协议IP协议, UDP协议,及http、FTP等。之所以命名为TCP/IP协议,因为TCP,IP协议是两个很重要的协议,所以以TCP/IP命名。
TCP/IP协议的四层参考模型
应用层:
应用程序间沟通的层,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议
(Telnet),以及超文本传输协议(http) 等。
传输层:
在此层中,它提供了节点间的数据传送服务,如传输控制协议(TCP)、用户数据报协议(UDP)等,TCP和UDP给数据包加入传输数据并把它传输到下一层中,这一层负责传送数据,并且确定数据已被送达并接收。
互连网络层:
负责提供基本的数据封包传送功能,让每一块数据包都能够到达目的主机(但不检查是否被正确接收),如网际协议(IP)。
网络接口层:
对实际的网络媒体的管理,定义如何使用实际网络来传送数据。
长连接:
是客户端与服务器建立连接后,进行业务报文的收发,始终保持连接状态,在没有报文收发的情况下,可以使用心跳包来确认连接状态的保持,很多移动端游戏类型都采用长连接,例如RPG,SLG, ACT等需要即时交互的游戏类型,使用TCP长连接协议。
短连接:
是客户端与服务器需要进行报文交互时进行连接,交互完毕后断开连接。例如在某种推图游戏对战回合中,进行不联网的单机计算,当在本关卡结束时再联机交互结算报文,所以例如卡牌,回合制还有例如页游等使用http短连接协议。
TCP协议和UDP协议对比:
TCP(Transmission Control Protocol,传输控制协议)是面向连接的协议, 在收发数据前,必须和
对方建立可靠的连接。
客户端主机和服务器经过三次握手进行链接简单描述如下:
第一次,客户端向服务器发送连接请求。
第二次 服务器做出收到请求和允许发送数据的应答。
第三次客户端再次送一个确认应答,表示现在开始传输数据了。
UDP协议使用IP层提供的服务把从应用层得到的数据从一台主机的某个应用程序传给网络上另一台主机上的某一个应用程序UDP协议有如下的特点:
1、UDP传送数据前并不与对方建立连接,即UDP是无连接的,在传输数据前,发送方和接收方相互交换信息使双方同步。
2、UDP不对收到的数据进行排序,在UDP报文的头中并没有关于数据顺序的信息(如TCP所采用的
序号),而且报文不一定按顺序到达的,所以接收端无从排起。
3、UDP对接收到的数据报不发送确认信号,发送端不知道数据是否被正确接收,也不会重发数据。
4、UDP传送数据较TCP快速,系统开销也少。
总结:
从以上特点可知,UDP提供的是无连接的、不可靠的数据传送方式,是一种尽力而为的数据交互服务,而TCP是长连接,较可靠的网络协议。
Socket本质是对TCP/IP封装的编程接口(API),使得程序员更方便地使用TCP/IP协议。Socket类提供了各种网络连接,接收数据,发送数据等相关方法给程序开发人员使用。
Socket类常用方法
BeginConnect() 开始一个对远程主机连接的异步请求
BeginReceive() 开始异步接收数据
BeginSend() 开始异步发送数据
Connect() 建立与远程主机的连接
Send() 将数据发送到连接
Receive() 接收数据到缓冲区
EndConnect() 结束异步连接请求
EndReceive() 结束异步接收
EndSeed() 结束异步发送
Close() 关闭socket连接和释放所有关联
protocolbuffer也叫Googlebuffer,protobuf,它是谷歌的数据交换格式,独立于语言,原生支持Java,c++ ,python 等语言,一种高效率和优秀兼容性的二进制数据传输格式,使用第三方工具可以良好支持C#语言,由于它独立于语言和平台,可以在unity客户端和c++服务器,或python服务器之间进行良好交互,故而是目前unity移动端网络游戏开发的主流数据交互协议。
基本语法
定义消息 message 关键字:message c2s login game request
{
required string account =1;required string password = 2;
}
1,2是分配标识号
基本语法
字段关键字:
Package:包定义,名空间。
required:表示该值是必须要设置的。
optional:可选字段,消息格式中该字段可以有0个或1个值(不超过1个)。
repeated:重复的值的顺序会被保留,表示该值是一个集合。
注释:
Proto文件可以使用//注释不需要编译的内容。
序列化proto数据参考代码:
public static byte[]Serialize(IExtensible msg)
{
byte[] result;
using (var stream = new MemoryStream())
{
Serializer.Serialize(stream,msg);result = stream.ToArray();
}
return result;
}
反序列化参考代码:
public static TDeserialize<T>(byte[] message)
{
T result;
using (var stream = newMemoryStream(message))
{
result =Serializer.Deserialize<T>(stream);
}
return result;
}
2.13 性能优化
程序性能的分析
程序性能的分析主要是对Profiler的讲解。Profiler工具是Unity3D提供的一套用于实时监控资源消耗的工具,通过使用该工具,我们可以直观地查看程序运行时各个方面资源的占用情况,并匀速找到影响程序性能的线程和函数,再针对性的优化
我们可以使用快捷键Ctrl+7快捷键或依次点击“Window->Profiler”命令来调出Profiler窗口。
下方是Profiler工具中各项参数的意义:
CPUUsage
GC Alloc - 记录了游戏运行时代码产生的堆内存分配。这会导致ManagedHeap增大,加速GC的到来。我们要尽可能避免不必要的堆内存分配,同时注意:1、检测任何一次性内存分配大于2KB的选项;2、检测每帧都具有20B以上内存分配的选项。
WaitForTargetFPS -VSync功能所致,即显示的是当前帧的CPU等待时间。
Overhead -表示Profiler总体时间,即所有单项的记录时间总和。用于记录尚不明确的时间消耗,以帮助进一步完善Profiler的统计。(一般出现在移动设备,锯齿状为Vsync所致)
Physics.Simulate -当前帧物理模拟的CPU占用量。
Camera.Render -相机渲染准备工作的CPU占用量。
RenderTexture.SetActive -设置RenderTexture操作。比对当前帧与前一帧的ColorSurface和DepthSurface,如果一致则不生成新的RT,否则生成新的RT,并设置与之对应的Viewport和空间转换矩阵。
Monobehaviour.OnMouse_ -用于检测鼠标的输入消息接收和反馈,主要包括 SendMouseEvents和DoSendMouseEvents。
HandleUtility.SetViewInfo -仅用于Editor中,作用是将GUI在Editor中的显示看起来与发布版本上的显示一致。
GUI.Repaint - GUI的重绘(尽可能避免使用Unity内建GUI)。
Event.Internal_MakeMasterEventCurrent -负责GUI的消息传送。
CleanupUnused Cached Data - 清空无用的缓存数据,主要包括RenderBuffer 的垃圾回收和TextRendering的垃圾回收。
RenderTexture.GarbageCollectTemporary -存在于RenderBuffer的垃圾回收中,清除临时的FreeTexture。
TextRendering.Cleanup -TextMesh的垃圾回收操作。
Application.IntegrateAssets in Background - 遍历预加载的线程队列并完成加载,同时完成纹理的加载、Substance的Update等。
Application.LoadLevelAsyncIntegrate - 加载场景的CPU占用。
UnloadScene -卸载场景中的GameObjects、Component和GameManager,一般用在切换场景时。
CollectGameObjects -将场景中的GameObject和Component聚集到一个Array 中。
Destroy -删除GameObject或Component的CPU占用。
AssetBundle.LoadAsyncIntegrate - 多线程加载AwakeQueue中的内容,即多线程执行资源的AwakeFormLoad函数。
Loading.AwakeFormLoad -在资源被加载后调用,对每种资源进行与其对应的处理。
StackTraceUtility.PostprocessStacktrace()和StackTraceUtility.ExtractStackTrace() - 一般是由Debug.Log或类似API造成,游戏发布后需将Debug API进行屏蔽。
GC.Collect -系统启动的垃圾回收操作。当代码分配内存过量或一定时间间隔后触发,与现有的Garbage size及剩余内存使用粒度相关。
GarbageCollectAssetsProfile -引擎在执行UnloadUnusedAssets操作。
GPUUsage
Device.Present -device.PresentFrame的耗时显示,该选项出现在发布版本中。关于该参数有如下几个常见问题:1、GPU的presentdevice确实非常耗时,一般出现在使用了非常复杂的Shader等;2、GPU运行是非常快的,而由于Vsync的原因,使得它需要等待较长时间;3、同样是Vsync的原因,若其他线程非常耗时,会导致该项等待时间很长,比如过量的AssetBundle加载时容易出现该问题。
Graphics.PresentAndSync -GPU上的显示和垂直同步耗时,该选项出现在发布版本中。
Mesh.DrawVBO -GPU中关于Mesh的Vertex Buffer Object的渲染耗时。
Shader.Parse - 资源加入后引擎对Shader的解析过程。
Shader.CreateGPUProgram -根据当前设备支持的图形库信息来建立GPU工程。
Memory
GameObjects in Scene - 当前帧场景中的GameObject数量。
otalObjects in Scene - 当前帧场景中的Object数量(除了GameObject外,还有Component等)。
TotalObject Count - Object数量 + Asset数量。
SceneMemory - 记录当前帧场景中各方面的内存占用情况,包括GameObject、所有资源、各种组件及GameManager等。
帧调试器(FrameDebugger)的应用
一个针对渲染的调试器。与其他的调试工具的复杂性相比,Unity原生的帧调试器非常的简单便捷。我们可以使用它来看到游戏图像的某一帧是如何一步步渲染出来的
需要使用帧调试器,我们首先需要在Window->Frame Debugger中打开帧调试器窗口。
GPU优化分为四个部分:
1、DrawCall:Unity每次再准备数据并通知GPU渲染的过程被称为一次DrawCall。
优化方案:批处理(接下来我们会做详细的讲解)
2、物理组件的使用
优化方案:
<1>设置一个合适的FixedTimestep
<2>不要使用MeshCollider(从优化的角度上来说,我们尽量减少使用物理组建)
3、GC(Garbage Collection垃圾回收)
优化方案:减少对CPU的调用(稍后做讲解)
4、代码质量
Unity中有两种批处理方式:
动态批处理
好处:一切处理都是自动的,不需要我们自己做任何操作,而且物体是可以移动的。
坏处:限制很多,可能一不小心我们就会破坏了这种机制,导致Unity无法批处理一些使用了相同材质的物体。
静态批处理
好处:自由度很高,限制很少。
坏处:可能会占用更多的内存,而且经过静态批处理后的所有物体都不可以再移动了。
GPU优化
1、减少绘制的数目
解决方案:模型的LOD技术、遮挡剔除技术
2、优化显存的带宽
模型的LOD技术
LOD(Level of Detail)技术。这种技术的原理是,当另一个物体离摄像机很远时,模型上的很多细节是无法被察觉到的。因此,LOD允许当前对象逐渐原理摄像机时,减少模型上的面片数量,从而提高性能。
在Unity中,我们可以使用LODGrounp组件来为一个物体构建一个LOD。我们需要为同一个对象准备多个包含不同细节程序的模型,然后把它们赋给LODGroup组件中的不同等级,Unity就会自动判断当前位置上需要使用哪个等级的模型。
同样它的缺点同样是需要占用更多的内存,而且如果没有调整好距离的话,可能会造成模拟的突变。
遮挡剔除技术
实际开发过程中,每一个场景往往伴随着大量的对象,其中相当一部分对象是不在摄像机拍摄范围内的,进行着一部分对象的绘制是完全没有必要的。强大的Unity3D引擎提供了非常实用的遮挡剔除技术,使不被拍摄到的点或面不送入渲染管线绘制。
1、Resource资源不用的要删除,纹理、网格、音频等等;
2、在CPU压力不是特别大的时候,重置GameObject、组件等占用的内存;
3 、在打包AssetBundle的时候,可以考虑bundle的压缩。