Unity学习汇总
一、简述
1.1. 渲染管线
渲染管线是指将虚拟3D世界场景中的各个要素转换成2D平面图像的过程。主要功能包括一是将物体3D坐标转变为屏幕空间2D坐标,二是为屏幕每个像素点进行着色。
在这个过程中,CPU与GPU并行协调进行工作,要经历以下几个过程:
1)CPU将场景中的物体进行计算,变成场景数据,此过程又称为数据准备阶段,数据准备完毕后,发送Draw Call命令给GPU,通知GPU执行渲染任务,其中包括了物体的顶点数据,坐标数据等。
注意:Draw Call次数即为CPU准备数据发送命令的次数,CPU工作是根据物体数量进行分批准备以及通知的,当物体数量过多,Draw Call过多,CPU就会很多额外开销用于准备工作,影响性能。优化方案:(1)合批,就是把能合并的都合并起来,尽量减少Draw Call。(肯定有不能合并的,比如场景中的一个大树和主角,还有好多渲染状态啊透明啊之类的,篇幅有限不展开讲了,真感兴趣应该可以自己搜得到)。(2)instance,GPU硬件算法。使用与大量需要重复绘制的模型,比如草地。但是instance是有上限的,并且只能减少Draw Call,对于增加的面数来说是无解的。
2)GPU接收数据后进行顶点处理,包括坐标的转换、图元的组装(三角形)。
3)进行光栅化,也就是计算图元的像素以及为像素着色。。
4)混合测试后进行缓存。
、
1.2. 遮挡剔除(occlusion Culling)
当物体被送进渲染流水线之前,将摄像机视角内看不到的物体进行剔除,从而减少每帧渲染量,提高渲染性能。
实际中,遮挡剔除的实现方法多种多样,关于instanceOC插件的方法是发射射线进行检测。
1.3. 多细节层次(Levels of Detail)
LOD技术指根据物体模型的节点在显示环境中所处的位置和重要度,决定了物体渲染的资源分配,降低非重要物体的面数和细节度,从而获得高效率的渲染运算。
量化渲染运算速度的指标可以参考三角数以及顶点数。
1.4. 性能指标参考
路径:Windows-Analysis-profiler
1.5. GI
Global Illuminnation,简称GI,即全局光照。能够计算直接光,间接光,环境光以及反射光的光照系统。
直接光照,通过组件Light发出的光照就是直接光照。
—Directional Light :方向光,与光源位置无关,只与方向有关的光照
—Point Light:点光源,类似灯泡
—spot: 方向射灯。
—Area Light :区域光,注意:及其消耗性能,只能烘焙使用
1.6. RealTime GI
是指场景在运行期间,可以实时修改任意光源,变化会立即更新。
GI Cache 实时光照的缓存
1.7. 烘焙Lightmap
当场景中包含大量物体时,实时光照和阴影对游戏的性能有极大的影响,使用烘焙技术将光线效果预渲染成贴图再作用到物体上模拟光照效果,从而提高性能。适用于性能较低的设备。
.NET dotnet 是Microsoft的新一代多语言的开发平台,用于应用程序的开发以及运行。
C#是专门为.NET推出的高级编程语言,从语言技术风格,严谨性上来说,C#是诸多语言中最为优秀的一款,甚至由它引发的计算机语言界的多种新规范和新特征。
Mono支持在其他操作系统下开发.NET程序的框架。Unity借助Mono实现了跨平台,核心是.NET Framework。
二、Unity脚本
1、生命周期
注意:
1)Update与FixedUpdate之间的区别,update与FPS(即每秒渲染帧数相关):
Update是每次渲染新的的一帧的时候会进行调用,因此,调用频率受不同机器显卡的性能影响,同时每帧调用的时间间隔也不定,在实际中这个因素会导致FPS高的update固定时间内运行次数多,FPS低的运行次数少,因此在做移动、旋转效果的时候可以添加Time.deltatime来保证不同机器不同FPS下的游戏效果一致。
FixedUpdate是物理固定频率执行,FixedUpdate的时间间隔可以在项目设置中更改,点击 Edit - Project Setting - time - Fixed timestep,默认为0.02s
2)输入事件是用于获取玩家的输入的,OnMouseXXX系列。
3)yieldWWW,关于协程的后续进入,注意是在update之后进入的。
Unity官方API:https://docs.unity3d.com/Manual/ExecutionOrder.html。
2、序列化
转自:https://www.cnblogs.com/fzuljz/p/11168131.html
unity的序列化在unity的开发中起着举重足轻的地位,许多核心的功能都是基于序列化和反序列化来实现的。序列化简单来讲就是就是将我们所要保存的数据进行二进制存储,然后当我们需要的时候,在读取二进制文件,反序列化回来。下面是一些常用的序列化的例子:
- 存储脚本化的数据。在我们的c#代码中,可以将我们所要存储的数据进行序列化,进行存储
- prefab与初始化。在unity开发过程中我们会制作很多的预制体(prefab),这些prefab都是序列化,以二进制的形式保存的。当你要对预制体进行初始化的时候,unity根据之前序列化的信息新建出一个物体,然后将物体上的信息反序列化回去,实现类似于clone的效果。
- 在unity编辑器的拓展功能,AssetBundle等等,都可以看到序列化的身影。
可序列化对象:
- 公有的,或者有[SerializeField]属性,非静态,非常量,非只读
- 自定义非抽象类,并且有Serializable属性
- 继承自unity.object的类
- 数组和List
注意:
- 字典不能通过添加Serializable属性进行序列化
- 如果一个类基类不能被序列化,那它即便添加了序列化特性也无法被序列化
- 序列化不能保存另一个要反序列化的对象指针,因为反序列化是new一个新的对象,指针指向的内存将不会是原对象
可采用unity编辑器的序列化类ScriptableObject,当我们继承这个基类,我们就可以调用unity给我们的接口进行序列化了。具体的序列化接口可以到unity的官方接口文档上查看使用方法,这里不具体介绍。
3、常用的Attribute
举两个例子,在变量上使用[SerializeFiled]属性,可以强制让变量进行序列化,可以在Unity的Editor上进行赋值。
在Class上使用[RequireComponent]属性,就会在Class的GameObject上自动追加所需的Component。
以下是Unity官网文档中找到的所有Attribute,下面将按照顺序,逐个对这些Attribute进行说明和小的测试。
部分例子使用了Unity官方的示例。
UnityEngine
AddComponentMenu
可以在UnityEditor的Component的Menu中增加自定义的项目。菜单可以设置多级,使用斜线/分隔即可。在Hierarchy中选中GameObject的时候,点击该菜单项,就可以在GameObject上追加该Component。
例如如下代码可以完成下图的效果。
[AddComponentMenu("TestMenu/TestComponet")] public class TestMenu : MonoBehaviour { }
汇编级属性,使用该属性的Class会被认为是EditorClass。具体用法不明。
可以在Inspector的ContextMenu中增加选项。
例如,如下代码的效果
public class TestMenu : MonoBehaviour { [ContextMenu ("Do Something")] void DoSomething () { Debug.Log ("Perform operation"); } }
这个属性是Unity4.5之后提供的新功能,可以在Inspector上面对变量追加一个右键菜单,并执行指定的函数。
例子:
public class Sample : MonoBehaviour { [ContextMenuItem("Reset", "ResetName")] public string name = "Default"; void ResetName() { name = "Default"; } }
对一个MonoBehaviour的子类使用这个属性,那么在同一个GameObject上面,最多只能添加一个该Class的实例。
尝试添加多个的时候,会出现下面的提示。
默认状态下,MonoBehavior中的Start,Update,OnGUI等方法,需要在Play的状态下才会被执行。
这个属性让Class在Editor模式(非Play模式)下也能执行。
但是与Play模式也有一些区别。
例如:
Update方法只在Scene编辑器中有物体产生变化时,才会被调用。
OnGUI方法只在GameView接收到事件时,才会被调用。
这个属性可以在Inspector中变量的上面增加Header。
例子:
public class ExampleClass : MonoBehaviour { [Header("生命值")] public int CurrentHP = 0; public int MaxHP = 100; [Header("魔法值")] public int CurrentMP = 0; public int MaxMP = 0; }
在变量上使用这个属性,可以让public的变量在Inspector上隐藏,也就是无法在Editor中进行编辑。
在OnRenderImage上使用,可以让渲染顺序在非透明物体之后,透明物体之前。
例子
[ImageEffectOpaque] void OnRenderImage (RenderTexture source, RenderTexture destination){ }
渲染从从HDR变为LDR 具体使用方法不明。
在string类型上使用,可以在Editor上输入多行文字。
public class TestString : MonoBehaviour { [MultilineAttribute] public string mText; }
在变量上使用,可以指定该变量在build的时候,不要转换为目标平台的类型。
在变量上使用,在Flash平台build的时候,对该变量不进行类型检查。
Unity5.0中已经移除了这个属性。
禁止对变量和方法进行重命名。
Unity5.0中已经移除了这个属性。
在int或者float类型上使用,限制输入值的范围
public class TestRange : MonoBehaviour { [Range(0, 100)] public int HP; }
在Class上使用,添加对另一个Component的依赖。
当该Class被添加到一个GameObject上的时候,如果这个GameObject不含有依赖的Component,会自动添加该Component。
且该Componet不可被移除。
例子
[RequireComponent(typeof(Rigidbody))] public class TestRequireComponet : MonoBehaviour { }
如果尝试移除被依赖的Component,会有如下提示
在方法上添加该属性,可以网络通信中对该方法进行RPC调用。
[RPC] void RemoteMethod(){ }
RuntimeInitializeOnLoadMethodAttribute
此属性仅在Unity5上可用。
在游戏启动时,会自动调用添加了该属性的方法。
class MyClass { [RuntimeInitializeOnLoadMethod] static void OnRuntimeMethodLoad () { Debug.Log("Game loaded and is running"); } }
当一个GameObject含有使用了该属性的Component的时候,在SceneView中选择该GameObject,Hierarchy上面会自动选中该GameObject的Parent。
SerializeField
在变量上使用该属性,可以强制该变量进行序列化。即可以在Editor上对变量的值进行编辑,即使变量是private的也可以。
在UI开发中经常可见到对private的组件进行强制序列化的用法。
例子
public class TestSerializeField : MonoBehaviour { [SerializeField] private string name; [SerializeField] private Button _button; }
SharedBetweenAnimatorsAttribute
用于StateMachineBehaviour上,不同的Animator将共享这一个StateMachineBehaviour的实例,可以减少内存占用。
SpaceAttribute
使用该属性可以在Inspector上增加一些空位。 例子:
public class TestSpaceAttributeByLvmingbei : MonoBehaviour { public int nospace1 = 0; public int nospace2 = 0; [Space(10)] public int space = 0; public int nospace3 = 0; }
TextAreaAttribute
该属性可以把string在Inspector上的编辑区变成一个TextArea。
例子:
public class TestTextAreaAttributeByLvmingbei : MonoBehaviour { [TextArea] public string mText; }
这个属性可以为变量上生成一条tip,当鼠标指针移动到Inspector上时候显示。
public class TestTooltipAttributeByLvmingbei : MonoBehaviour { [Tooltip("This year is 2015!")] public int year = 0; }
UnityAPICompatibilityVersionAttribute
用来声明API的版本兼容性
UnityEngine.Serialization
FormerlySerializedAsAttribute
该属性可以令变量以另外的名称进行序列化,并且在变量自身修改名称的时候,不会丢失之前的序列化的值。
例子:
using UnityEngine; using UnityEngine.Serialization; public class MyClass : MonoBehaviour { [FormerlySerializedAs("myValue")] private string m_MyValue; public string myValue { get { return m_MyValue; } set { m_MyValue = value; } } }
UnityEngine.Editor
该package为Editor开发专用
CallbackOrderAttribute
定义Callback的顺序
CanEditMultipleObjects
Editor同时编辑多个Component的功能
CustomEditor
声明一个Class为自定义Editor的Class
CustomPreviewAttribute
将一个class标记为指定类型的自定义预览
Unity4.5以后提供的新功能
例子:
[CustomPreview(typeof(GameObject))] public class MyPreview : ObjectPreview { public override bool HasPreviewGUI() { return true; } public override void OnPreviewGUI(Rect r, GUIStyle background) { GUI.Label(r, target.name + " is being previewed"); } }
CustomPropertyDrawer
标记自定义PropertyDrawer时候使用。
当自己创建一个PropertyDrawer或者DecoratorDrawer的时候,使用该属性来标记。 TODO: 如何创建属于自己的Attribute
DrawGizmo
可以在Scene视图中显示自定义的Gizmo
下面的例子,是在Scene视图中,当挂有MyScript的GameObject被选中,且距离相机距离超过10的时候,便显示自定义的Gizmo。
Gizmo的图片需要放入Assets/Gizmo目录中。
例子:
using UnityEngine; using UnityEditor; public class MyScript : MonoBehaviour { } public class MyScriptGizmoDrawer { [DrawGizmo (GizmoType.Selected | GizmoType.Active)] static void DrawGizmoForMyScript (MyScript scr, GizmoType gizmoType) { Vector3 position = scr.transform.position; if(Vector3.Distance(position, Camera.current.transform.position) > 10f) Gizmos.DrawIcon (position, "300px-Gizmo.png"); } }
在Class上使用,可以在Unity启动的时候,运行Editor脚本。
需要该Class拥有静态的构造函数。
做一个创建一个空的gameobject的例子。
例子:
using UnityEditor; using UnityEngine; [InitializeOnLoad] class MyClass { static MyClass () { EditorApplication.update += Update; Debug.Log("Up and running"); } static void Update () { Debug.Log("Updating"); } }
InitializeOnLoadMethodAttribute
在Method上使用,是InitializeOnLoad的Method版本。
Method必须是static的。
MenuItem
在方法上使用,可以在Editor中创建一个菜单项,点击后执行该方法,可以利用该属性做很多扩展功能。 需要方法为static。
例子:
using UnityEngine; using UnityEditor; using System.Collections; public class TestMenuItem : MonoBehaviour { [MenuItem ("MyMenu/Create GameObject")] public static void CreateGameObject() { new GameObject("lvmingbei\'s GameObject"); } }
PreferenceItem
使用该属性可以定制Unity的Preference界面。
在这里就使用官方的例子:
using UnityEngine; using UnityEditor; using System.Collections; public class OurPreferences { // Have we loaded the prefs yet private static bool prefsLoaded = false; // The Preferences public static bool boolPreference = false; // Add preferences section named "My Preferences" to the Preferences Window [PreferenceItem ("My Preferences")] public static void PreferencesGUI () { // Load the preferences if (!prefsLoaded) { boolPreference = EditorPrefs.GetBool ("BoolPreferenceKey", false); prefsLoaded = true; } // Preferences GUI boolPreference = EditorGUILayout.Toggle ("Bool Preference", boolPreference); // Save the preferences if (GUI.changed) EditorPrefs.SetBool ("BoolPreferenceKey", boolPreference); } }
UnityEditor.Callbacks
这个package中是三个Callback的属性,都需要方法为static的。
OnOpenAssetAttribute
在打开一个Asset后被调用。
例子:
using UnityEngine; using UnityEditor; using UnityEditor.Callbacks; public class MyAssetHandler { [OnOpenAssetAttribute(1)] public static bool step1(int instanceID, int line) { string name = EditorUtility.InstanceIDToObject(instanceID).name; Debug.Log("Open Asset step: 1 ("+name+")"); return false; // we did not handle the open } // step2 has an attribute with index 2, so will be called after step1 [OnOpenAssetAttribute(2)] public static bool step2(int instanceID, int line) { Debug.Log("Open Asset step: 2 ("+instanceID+")"); return false; // we did not handle the open } }
PostProcessBuildAttribute
该属性是在build完成后,被调用的callback。
同时具有多个的时候,可以指定先后顺序。
例子:
using UnityEngine; using UnityEditor; using UnityEditor.Callbacks; public class MyBuildPostprocessor { [PostProcessBuildAttribute(1)] public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject) { Debug.Log( pathToBuiltProject ); } }
PostProcessSceneAttribute
使用该属性的函数,在scene被build之前,会被调用。
具体使用方法和PostProcessBuildAttribute类似。
4、常用的输入类KPI
1)获取鼠标输入(也适用与触摸屏)
01、当鼠标按钮被按下时返回true.
1 bool result = Input.GetMouseButton(0);
02、当按下指定鼠标按钮的第一帧返回true.
1 bool result = Input.GetMouseButtonDown(0);
03、当释放鼠标按键的第一帧返回true.
1 bool result = Input.GetMouseButtonUp(0);
2)获取键盘输入(PC)
01、当通过名称指定的按键被用户按住时返回true
1 bool result = Input.GetKey(KeyCode.XXX);
02、当通过名称指定的按键被用户按住下时返回true
1 bool result = Input.GetKeyDown(KeyCode.XXX);
03、当释放按键时返回true
1 bool result = Input.GetKeyDown(KeyCode.XXX);
3)虚拟轴
路径:Edit-Project Settings-Input
可以自定义虚拟轴
获取虚拟轴的输入可以用
1 float Axis01 = Input.GetAxis("Name"); 2 float Axis02 = Input.GetAxisRaw("Name"); 3 bool button01 = Input.GetButton("Name"); 4 bool button02 = Input.GetButtonDown("Name"); 5 bool button03 = Input.GetButtonUp("Name");
4、常用的获取对象用的API(待补充)
这里就记录一个工具类获取一个对象中的子对象的工具
1 /// <Summary> 2 ///根据名字查询自身以及自身的子孙物体的引用 3 ///</Summary> 4 public static class FindGrandSonByName 5 { 6 private static Transform FindSon02(Transform parenTF, string name) 7 { 8 Transform childTF = parenTF.Find(name); 9 if (childTF != null) return childTF; 10 11 int count = parenTF.childCount; 12 for (int i = 0; i < count; i++) 13 { 14 childTF = FindSon02(parenTF.GetChild(i), name); 15 if (childTF != null) return childTF; 16 } 17 return null; 18 } 19 20 }
5、3Dmath与Vector结构体
1 private void Demo01() 2 { 3 Vector3 A = this.transform.position; 4 Debug.DrawLine(Vector3.zero, this.transform.position,Color.red); 5 } 6 7 /// <summary> 8 /// 向量相加 9 /// </summary> 10 private void Demo02() 11 { 12 Vector3 Abc = T1.position - T2.position; 13 if (Input.GetKeyDown(KeyCode.A)) 14 //this.T3.Translate(Abc.normalized); 15 T3.transform.position = T3.transform.position + Abc.normalized; 16 Debug.DrawLine(T1.position, T2.position); 17 } 18 19 /// <summary> 20 /// 计算物体右前方30度,10m远的坐标 21 /// </summary> 22 private void Demo03() 23 { 24 // Vector3 A = new Vector3(10 * Mathf.Sin(Mathf.Deg2Rad * 30), 0, 10 * Mathf.Cos(Mathf.Deg2Rad * 30)); 25 //Vector3 B = this.transform.position + A; 26 Vector3 B = this.transform.TransformPoint(10 * Mathf.Sin(Mathf.Deg2Rad * 30), 0, 10 * Mathf.Cos(Mathf.Deg2Rad * 30)); 27 28 Debug.DrawLine(this.transform.position, B); 29 30 } 31 /// <summary> 32 /// 方案二_计算物体右前方30度,10m远的坐标 33 /// </summary> 34 private void Demo03Copy() 35 { 36 // Vector3 destination = this.transform.position + Quaternion.Euler(0, 30, 0) * this.transform.forward.normalized * 10;//transform.forward是自身坐标系,则目标点会随自身旋转而旋转 37 Vector3 destination = this.transform.position + Quaternion.Euler(0, 30, 0) * this.transform.rotation * new Vector3(0, 0, 1) * 10;//new Vector3(0,0,1)是世界坐标系。则不会旋转,如果改为自身坐标系,则会旋转 38 Debug.DrawLine(this.transform.position, destination); 39 } 40 41 /// <summary> 42 /// 向量弧长与角度的转换,点乘角度(0-180/0-PI) 43 /// </summary> 44 public float degree; 45 public float radiu; 46 private void Demo04() 47 { 48 radiu = Mathf.Acos(Vector3.Dot(T3.position.normalized,T2.position.normalized)); 49 degree = radiu * Mathf.Rad2Deg; 50 // print($"{degree}"); 51 } 52 /// <summary> 53 /// 叉乘角度(0-90/0-PI/2) 54 /// </summary> 55 private void Demo05() 56 { 57 radiu = Mathf.Asin(Vector3.Cross(T3.position.normalized, T2.position.normalized).magnitude); 58 degree = radiu * Mathf.Rad2Deg; 59 Debug.DrawLine(Vector3.zero, T2.position); 60 Debug.DrawLine(Vector3.zero, T3.position); 61 // print($"{degree}"); 62 } 63 64 65 /// <summary> 66 /// 判断炸弹的攻击范围 67 /// </summary> 68 public Transform Player; 69 public float area = 10; 70 private void Demo06() 71 { 72 //Quaternion. 73 float degree01 = Mathf.Acos(Player.GetComponent<CapsuleCollider>().radius / (Player.transform.position - this.transform.position).magnitude) * Mathf.Rad2Deg; 74 75 76 Vector3 JudgePoint01 = Player.transform.position + Quaternion.Euler(0, degree01, 0) * ((Player.transform.position - this.transform.position).normalized * Player.GetComponent<CapsuleCollider>().radius); 77 Vector3 JudgePoint02 = Player.transform.position + Quaternion.Euler(0, -degree01, 0) * ((Player.transform.position - this.transform.position).normalized * Player.GetComponent<CapsuleCollider>().radius); 78 79 Debug.DrawLine(JudgePoint01, this.transform.position); 80 Debug.DrawLine(JudgePoint02, this.transform.position); 81 82 if (Input.GetKeyDown(KeyCode.A)) 83 { 84 // this.transform.position += new Vector3(0,0,1); 85 if (Vector3.SqrMagnitude(JudgePoint01 - this.transform.position) <= Mathf.Pow(area, 2)) print("击中"); 86 else print("未击中"); 87 } 88 89 } 90 91 /// <summary> 92 /// 判断forward,up,right的自身坐标以及世界坐标的情况 93 /// </summary> 94 private void Demo07() 95 { 96 Vector3 A = new Vector3(0, 0, 5); 97 Debug.DrawLine(Vector3.zero, A); 98 99 this.transform.position += A; 100 } 101 /// <summary> 102 /// 直接移动,匀速移动,变速移动 103 /// </summary> 104 public Transform target; 105 private void Demo08() 106 { 107 108 //直接移动 109 //this.transform.position = target.position; 110 //匀速移动 111 //this.transform.position = Vector3.MoveTowards(this.transform.position, target.position, 10 * Time.deltaTime); 112 //变速移动 113 this.transform.position = Vector3.Lerp(this.transform.position, target.position, 0.01f);//使用Lerp要注意设定阈值 114 AnimationCurve curve; //这个动态数值可以用于设置移动比例 115 } 116 117 private void Demo09() 118 { 119 //直接旋转(注视旋转) 120 //this.transform.LookAt(target); 121 //this.transform.rotation = Quaternion.LookRotation(target.position - this.transform.position); 122 //匀速旋转 123 Quaternion targetQua = Quaternion.LookRotation(target.position - this.transform.position); 124 this.transform.rotation = Quaternion.RotateTowards(this.transform.rotation, targetQua, 0.1f); 125 } 126 127 /// <summary> 128 /// 围绕旋转 129 /// </summary> 130 /// 转速 131 private Vector3 airx; 132 private void Demo10() 133 { 134 this.transform.RotateAround(target.position, airx ,30 * Time.deltaTime); 135 Debug.DrawLine(target.position, target.position + new Vector3(0, 10, 0)); 136 Debug.DrawLine(this.transform.position, target.position, Color.red); 137 138 } 139 140 /// <summary> 141 /// 绕椭圆运动。 142 /// </summary> 143 private void Demo11() 144 { 145 int a = 10; 146 int b = 5; 147 float degree = 30 * Mathf.Deg2Rad * Time.time; 148 Vector3 change = new Vector3(a * Mathf.Cos(degree), 0, b * Mathf.Sin(degree)); 149 this.transform.position = target.position + change; 150 }
旋转(欧拉角与四元数)
注意在欧拉角的旋转中,Unity为了保证任意方位都只有独一无二的表示,Unity引擎限制了角度范围,即沿X轴旋转限制在-90~90之间,沿Y轴与Z轴限制在0~360之间,从而确保一个(X,Y,Z)的值代表独一无二的方位。
同时,欧拉角的旋转会出现万向节死锁的情况,即由于欧拉角旋转时Y轴上的旋转保持是世界坐标系的Y轴,而不是自身坐标系,因此,在物体沿X轴旋转到90或者-90的时候,自身的Z轴与世界的Y轴重合,这个时候,Y,Z轴向上的旋转保持一致或者相反。
而四元数的旋转就不会存在这个问题。
理解四元数:极力推荐:https://www.3dgep.com/understanding-quaternions/
中文翻译版:https://www.qiujiawei.com/understanding-quaternions/
常用的API如下:
1 private void Demo01() 2 { 3 //1.欧拉角--》四元数 4 this.transform.rotation *= Quaternion.Euler(0, 10, 0); 5 } 6 7 private void Demo02() 8 { 9 //2.四元数--》欧拉角 10 Quaternion qt = this.transform.rotation; 11 Vector3 euler = qt.eulerAngles; 12 } 13 14 private void Demo03() 15 { 16 //3.轴/角 17 this.transform.rotation *= Quaternion.AngleAxis(50, this.transform.up); 18 19 } 20 21 22 public Transform target01; 23 private void Demo04() 24 { 25 //4.Z轴注视旋转 26 this.transform.rotation = Quaternion.LookRotation(target01.position - this.transform.position); 27 this.transform.LookAt(target01); 28 //5.匀速旋转 29 //this.transform.rotation = Quaternion.RotateTowards(); 30 31 //6.变速旋转 32 //this.transform.rotation = Quaternion.Lerp(); 33 } 34 35 private void Demo05() 36 { 37 Quaternion dir = Quaternion.LookRotation(target01.position - this.transform.position); 38 this.transform.rotation = Quaternion.Lerp(this.transform.rotation, dir,0.5f * Time.deltaTime); 39 //6.由于Lerp值无法最终抵达,因此需要设定阈值 40 if (Quaternion.Angle(this.transform.rotation, dir) < 1) this.transform.rotation = dir; 41 } 42 43 private void Demo06() 44 { 45 //7.X轴注视旋转。 46 //this.transform.right = target01.position - this.transform.position; 47 Quaternion dir = Quaternion.FromToRotation(Vector3.right, target01.position - this.transform.position); 48 this.transform.rotation = dir; 49 }
计时器Demo
1 private int gameTimeSecound03; 2 private int gameTimeMinute; 3 private int gameRemainedSecound; 4 private Text textTimer; 5 6 private void Start() 7 { 8 textTimer = this.GetComponent<Text>(); 9 gameTimeSecound03 = 120; 10 //重复调用(要执行的方法名称,开始调用时间,调用间隔) 11 InvokeRepeating("TimeCalcutor03", 0, 1); 12 //延迟调用 13 //Invoke("需要调用的方法名称", 调用时间); 14 } 15 16 private void TimeCalcutor03() 17 { 18 if (gameTimeSecound03 <= 0) enabled = false; 19 if (textTimer.color != Color.red && gameTimeSecound03 <= 10) textTimer.color = Color.red; 20 gameTimeMinute = (int)(gameTimeSecound03 / 60); //取商取整 21 gameRemainedSecound = (int)(gameTimeSecound03 % 60);//取余取整 22 textTimer.text = $"{gameTimeMinute:D2}:{gameRemainedSecound:D2}"; 23 gameTimeSecound03--; 24 }
6、UI(UGUI&NGUI)
1)UGUI相机渲染模式详解以及分辨率自适应的问题
转自:https://blog.csdn.net/qq_15020543/article/details/82594332
一、Canvas简介
Canvas画布是承载所有UI元素的区域。Canvas实际上是一个游戏对象上绑定了Canvas组件。所有的UI元素都必须是Canvas的自对象。如果场景中没有画布,那么我们创建任何一个UI元素,都会自动创建画布,并且将新元素置于其下。
二、Canvas画布参数与应用
1.创建画布
当你创建任何一个UI元素的时候,都会自动创建画布。也可以主动创建一张画布:点击GameObject->UI->Canvas即可在Hierarchy面板创建一张画布。
2.画布参数
下面介绍一下Canvas画布的参数:
第一个参数RenderMode的渲染模式有三种:Screen Space-Overlay、Screen Space-Camera以及World Space。
1.Screen Space-Overlay模式
Screen Space-Overlay(屏幕控件-覆盖模式)的画布会填满整个屏幕空间,并将画布下面的所有的UI元素置于屏幕的最上层,或者说画布的画面永远“覆盖”其他普通的3D画面,如果屏幕尺寸被改变,画布将自动改变尺寸来匹配屏幕,如下图效果
(在此模式下,虽然在Canvas前放置了3D人物,但是在Game窗口中并不能观察到3D人物)
Screen Space-Overlay模式的画布有Pixel Perfect和Sort Layer两个参数:
(1)Pixel Perfect:只有RenderMode为Screen类型时才有的选项。使UI元素像素对应,效果就是边缘清晰不模糊。
(2)Sort Layer: Sort Layer是UGUI专用的设置,用来指示画布的深度。
************************************************************************************************************************************************
总结:此模式只适合单纯的UI开发,比如游戏的排行榜,游戏结束之后的界面,并且会自动适应屏幕大小,但是要注意,所谓的适应屏幕,不是说你把图片设置到摄像机所显示游戏的界面一样大,他就会自动适应。
我们在Canvas下面新建一个Image
这样才能达到自适应的目的。
*************************************************************************************************************************************************
2.Screen Space-Camera模式
Screen Space-Camera(屏幕空间-摄影机模式)和Screen Space-Overlay模式类似,画布也是填满整个屏幕空间,如果屏幕尺寸改变,画布也会自动改变尺寸来匹配屏幕。所不同的是,在该模式下,画布会被放置到摄影机前方。在这种渲染模式下,画布看起来 绘制在一个与摄影机固定距离的平面上。所有的UI元素都由该摄影机渲染,因此摄影机的设置会影响到UI画面。在此模式下,UI元素是由perspective也就是视角设定的,视角广度由Filed of View设置。
这种模式可以用来实现在UI上显示3D模型的需求,比如很多MMO游戏中的查看人物装备的界面,可能屏幕的左侧有一个运动的3D人物,左侧是一些UI元素。通过设置Screen Space-Camera模式就可以实现上述的需求,效果如下图所示:
它比Screen Space-Overlay模式的画布多了下面几个参数:
(1)Render Camera:渲染摄像机
(2)Plane Distance:画布距离摄像机的距离
(3)Sorting Layer: Sorting Layer是UGUI专用的设置,用来指示画布的深度。可以通过点击该栏的选项,在下拉菜单中点击“Add Sorting Layer”按钮进入标签和层的设置界面,或者点击导航菜单->edit->Project Settings->Tags and Layers进入该页面。
可以点击“+”添加Layer,或者点击“-”删除Layer。画布所使用的Sorting Layer越排在下面,显示的优先级也就越高。
(4)Order in Layer:在相同的Sort Layer下的画布显示先后顺序。数字越高,显示的优先级也就越高。
****************************************************************************************************************************************************
总结:对于Order in Layer,可以理解为栈,先进去的后显示,后进去的先显示。也就是说,数值越大,就越靠前,这个模式需要单独的渲染摄像机,这也是大多数游戏所使用的渲染模式,如果游戏元素不多,可以把Main Camera设置为渲染相机,否则就单独创建一个相机用于渲染。
默认状态下创建的UGUI的原点和世界坐标的原点是重合的,这样其实非常不方便。
解决方法就是创建一个Camera专门用于绘制UI。具体步骤如下:
1.新建一个Camera,参数如下
尽量拉到一个比较远的位置
Canvas需要设置一下Canvas Component
这下UI就不会和场景中的物体有重合了。
*************************************************************************************************************************************************
3.World Space
World Space即世界控件模式。在此模式下,画布被视为与场景中其他普通游戏对象性质相同的类似于一张面片(Plane)的游戏物体。画布的尺寸可以通过RectTransform设置,所有的UI元素可能位于普通3D物体的前面或者后面显示。当UI为场景的一部分时, 可以使用这个模式。
它有一个单独的参数Event Camera,用来指定接受事件的摄像机,可以通过画布上的GraphicRaycaster组件发射射线产生事件。
这种模式可以用来实现跟随人物移动的血条或者名称,如下图所示:
我们通过下面的表格可以对比一下三种渲染模式的区别:
渲染模式 | 画布对应屏幕 | 摄像机 | 像素对应 | 适合类型 |
Screen Space-Overlay | 是 | 不需要 | 可选 | 2D UI |
Screen Space-Camera | 是 | 需要 | 可选 | 2D UI |
World Space | 否 | 需要 | 不可选 | 3D UI |
在UI开发的时候要注意以下几个参数的设置:
一、Constant Pixel Size:
像素大小始终不变,即一个100*100的图片在任何的分辨率下都占用100*100的像素。简单比较好理解。Scale Factor是表示缩放倍数。比如是2时,即将上面图片整体是缩放两倍是不变形的。
二、Scale With Screen Size
这种模式应用场景多一点。首先逐个说明下:
1)Reference Resolution是开发时的分辨率。以后缩放就参考它。
2)Screen Match Mode 又包含三种模式
* Match Width Or Height。下面包含一个Macht属性,当处于最左边时,屏幕高度对于UI大小完全没有任何影 响,只有宽度会对UI大小产生影响。假设宽度为Reference Resolution宽度的x倍,则UI整体缩放为Reference Resolution设置参数的x倍。也就是说只有宽度等于Reference Resolution宽度时,才能做到pixel perfect,否则像素就会有拉伸 ,当处于最右边时,与上述情况正好相反,决定整体缩放值的是高度,而宽度则没有任何影响 ,处于中间某处时,对上述两者的影响进行权重加成 。所以一般我们都把其放在0.5的位置(0.618也很舒服哦)。
* Expand 缩放不剪切:当屏幕分辨率与设定不同时,选择变化较小的一个方向(横向还是纵向),进行缩放显示
,它会保证设计时分辨率能显示出来的缩放后依然能显示出来。
* Shrink 缩放剪切:当屏幕分辨率与设定不同时,选择变化较大的一个方向(横向还是纵向)进行缩放显示
,对于超出的部分剪切不显示。
三、Constant Physical Size
保持物理上不变的方式,这个应用场景较少,具体是这样的,比如你电脑分辨率是 1000*2000而你的手机分辨率也是1000*2000。虽然电脑屏幕比手机屏幕大的多,但是他们最后显示出来图片的物理大小是一样的。
*****************************************************************************************************************************************************
首先我们设置好Canvas参数,假设我们开发基准为480x800的分辨率。
然后在Canvas下面创建4个Button,并设置锚点分别对应四个角落,对UGUI的RectTransFrom不熟悉的同学可以去看下这位博主的文章。http://www.cocoachina.com/game/20160602/16570.html
这是480x800的显示效果
这是1920x1080的显示效果,大功告成!
2)UGUI事件的绑定
01、通过编辑器绑定方法
02、AddListener方法
通过代码为含有事件方法的组件注册事件。
03、通过接口实现,常用接口如下:
鼠标指针类
• IPointerEnterHandler • IPointerExitHandler • IPointerDownHandler • IPointerUpHandler • PointerClickHandler
拖拽类
• IBeginDragHandler • IDragHandler • IEndDragHandler • IDropHandler
点选类
• IUpdateSelectedHandler • ISelectHandler • IDeselectHandler
输入类
• IScrollHandler • IMoveHandler • ISubmitHandler • ICancelHandler
(待继续补充)
七、Unity3D中的协程
简述:协程也是一种程序组件,相比一般方法,协程更为灵活,方法只有一个入口,而协程有多个入口以及出口点,可以使用协程实现合作式多任务,开启协程的时候从方法调用StartCoroutine(开始协程)的位置进入,然后从协程绑定的方法中的Yield return处返回。注意:协程是单线程,并非多线程,即为同步执行的。
协程的使用:
协程的停止:
注意点:
01、在程序中调用StopCoroutine()方法只能终止以字符串形式启动(开始)的协程。
02、多个协程可以同时运行
03、IEnumerator类型的方法不能带ref或者out类型的参数
04、多脚本访问一个协程的时候,建议将协程定义为static静态。
DEMO01:
1 /// <Summary> 2 /// 改变颜色一段时候后变回原来的颜色 3 ///</Summary> 4 public class CoroutineDemo : MonoBehaviour 5 { 6 7 private MeshRenderer myRenderer; 8 9 private IEnumerator currentCoroutine; 10 11 Color currentColor; 12 private void Start() 13 { 14 myRenderer = this.GetComponent<MeshRenderer>(); 15 16 } 17 private void Update() 18 { 19 if(Input.GetKeyDown(KeyCode.Space)) 20 this.Attack(); 21 } 22 23 24 private void Attack() 25 { 26 //print("1"); 27 ColorControl(); 28 29 } 30 31 private void ColorControl() 32 { 33 if (currentCoroutine != null) 34 { 35 StopCoroutine(currentCoroutine); 36 } 37 38 currentCoroutine = ChangeColor(Color.red); 39 StartCoroutine(currentCoroutine); 40 } 41 42 IEnumerator ChangeColor(Color targetColor) 43 { 44 if(myRenderer.material.color != targetColor) 45 { 46 currentColor = myRenderer.material.color; 47 myRenderer.material.color = targetColor; 48 yield return new WaitForSeconds(2f); 49 myRenderer.material.color = currentColor; 50 } 51 else 52 { 53 54 yield return new WaitForSeconds(2f); 55 myRenderer.material.color = currentColor; 56 } 57 } 58 }
DEMO02:
1 /// <Summary> 2 /// 自动寻路 3 ///</Summary> 4 public class CoroutineDemo02 : MonoBehaviour 5 { 6 public Transform[] wayPoints; 7 private float speed; 8 9 private void Start() 10 { 11 speed = 2; 12 StartCoroutine(move()); 13 } 14 15 private IEnumerator move() 16 { 17 foreach (var item in wayPoints) 18 { 19 yield return StartCoroutine(PathRoutine(item)); 20 yield return new WaitForSeconds(2f); 21 } 22 } 23 24 private IEnumerator PathRoutine(Transform item) 25 { 26 while((this.transform.position - item.position).sqrMagnitude >0.09f) 27 { 28 transform.position = Vector3.MoveTowards(this.transform.position, item.position, speed * Time.deltaTime); 29 yield return null; 30 } 31 transform.position = item.position; 32 } 33 }
八、IO
1)创建文件夹以及文件,文件的移动与剪切
1 public class IoText : MonoBehaviour 2 { 3 //文件夹路径 4 private string directorypath01 = @"F:/aa"; 5 private string directoryPath = @"F:/aa/bb"; 6 //文件路径 7 private string filePath = @"F:/aa/bb/text.txt"; 8 //Move和Copy 剪切和复制 9 private string destinationPath = @"F:/cc/dd"; 10 private string destinationFilePath = @"F:/cc/dd/text.txt"; 11 private string destinationFilePath02 = @"F:/cc/dd/text01.txt"; 12 13 private void Start() 14 { 15 // MoveFile(filePath, destinationFilePath,destinationPath); 16 CopyFile(filePath, destinationFilePath02); 17 } 18 19 20 //创建文件夹 21 private void DirectoryCreat(string path) 22 { 23 //创建文件夹,注意创建文件夹时不能带上文件夹后面的文件名,否则会将文件以文件夹的方式进行创建。 24 if (!Directory.Exists(path)) 25 Directory.CreateDirectory(path); 26 //删除文件夹以及其子文件夹,true删除文件夹以及子文件夹,false在删除时会报错 27 Directory.Delete(path, true); 28 } 29 30 private void FileCreat(string filePath) 31 { 32 //创建文件夹以及文件 33 DirectoryInfo newDirectoryPath = new DirectoryInfo(directoryPath); 34 if (!newDirectoryPath.Exists) newDirectoryPath.Create(); 35 if (!File.Exists(filePath)) File.Create(filePath); 36 } 37 38 private void MoveFile(string filePath,string destinationFilePath,string destinationPath) 39 { 40 //Directory.CreateDirectory(destinationPath); 41 //创建目标文件夹路径 42 DirectoryInfo newDir = new DirectoryInfo(destinationPath); 43 if (!newDir.Exists) newDir.Create(); 44 //剪切 45 if (File.Exists(filePath)) File.Move(filePath, destinationFilePath); 46 else throw new Exception("文件夹为空!"); 47 } 48 49 private void CopyFile(string filePath,string desFilePath) 50 { 51 string[] newstringArr = desFilePath.Split(\'/\'); 52 string desDirPath =null; 53 for (int i = 0; i < newstringArr.Length-1; i++) 54 { 55 desDirPath += newstringArr[i]; 56 desDirPath += "/"; 57 58 } 59 60 desDirPath = desDirPath.Substring(0, desDirPath.Length - 1); 61 62 if (File.Exists(filePath) && Directory.Exists(desDirPath) && !File.Exists(desFilePath)) 63 File.Copy(filePath, desFilePath); 64 else throw new Exception("there is a error!"); 65 66 } 67 }
2)使用Streaming I/O读取StreamingAssets下面的文本文件,注意文件需要放置在如图所示的文件夹中
1 public class IoConfigText : MonoBehaviour 2 { 3 private Dictionary<string, Dictionary<string, string>> dic; 4 private string configFilePath; 5 6 private void Start() 7 { 8 configFilePath = Path.Combine(Application.streamingAssetsPath, "Config.txt"); 9 dic = new Dictionary<string, Dictionary<string, string>>(); 10 GetConfigLines(configFilePath, ref dic); 11 } 12 13 private void OnGUI() 14 { 15 16 if (GUILayout.Button("生成")) 17 { 18 print(dic["Money"]["Gold"]); 19 } 20 21 } 22 23 private void GetConfigLines(string Filepath,ref Dictionary<string, Dictionary<string, string>> dic) 24 { 25 string[] fileLines = null; 26 int dicIndex = 0; 27 if (File.Exists(Filepath)) 28 { 29 fileLines = File.ReadAllLines(Filepath); 30 } 31 for (int i = 0; i < fileLines.Length; i++) 32 { 33 if (fileLines[i].StartsWith("[")) 34 { 35 dicIndex = i; 36 CutLine(ref fileLines[i]); 37 Dictionary<string, string> sonDic = new Dictionary<string, string>(); 38 dic.Add(fileLines[i], sonDic); 39 } 40 else if( !fileLines[i].StartsWith("[") && !string.IsNullOrEmpty(fileLines[i])) 41 { 42 string stringLeft; 43 string stringRight; 44 CutLine(fileLines[i], out stringLeft, out stringRight); 45 dic[fileLines[dicIndex]].Add(stringLeft, stringRight); 46 } 47 } 48 } 49 50 /// <summary> 51 /// 根据字符串中的[]进行截取,只保留原字符串中[]中包括的部分。 52 /// </summary> 53 /// <param name="fileLine">需要变更的字符串</param> 54 private void CutLine(ref string fileLine) 55 { 56 //string newLine = fileLine.Substring(1, fileLine.IndexOf(\']\') - 1);可以用此句代替 57 string[] newLine = fileLine.Split(\'[\', \']\'); 58 fileLine = newLine[1]; 59 } 60 /// <summary> 61 /// 根据字符串的文本=进行截取,保留左右两边的部分。 62 /// </summary> 63 /// <param name="fileLine"></param> 64 private void CutLine(string fileLine,out string string01,out string string02) 65 { 66 //fileLine.Split(new char[] { \'=\' }, System.StringSplitOptions.RemoveEmptyEntries);可以用这一句代替Replace 67 fileLine = fileLine.Replace(" ", ""); 68 string[] newLine = fileLine.Split(\'=\'); 69 string01 = newLine[0]; 70 string02 = newLine[1]; 71 } 72 }
九、寻路系统
常用参数:
Radius 寻路的碰撞半径
Height寻路的碰撞高度
BaseOffset 寻路碰撞的位置
Speed 寻路物体的速度
Acceleration 转弯时的加速度
AngularSpeed 转弯时物体的角速度
StoppingDistance 停止的距离
AvoidancePriority 躲避系数
Nav网格寻路DEMO:实现点击地面以及小地图能够自动寻路
1 [RequireComponent(typeof(NavMeshAgent))] 2 public class NavText : MonoBehaviour 3 { 4 //private Transform target; 5 6 private NavMeshAgent myNavMesh; 7 8 private Ray mainRay; 9 private RaycastHit mainHit; 10 11 private Ray mapRay; 12 private RaycastHit mapHit; 13 private void Start() 14 { 15 myNavMesh = GetComponent<NavMeshAgent>(); 16 } 17 18 private void Update() 19 { 20 21 if(Input.GetMouseButtonDown(0)) 22 { 23 24 mainRay = Camera.main.ScreenPointToRay(Input.mousePosition); 25 Physics.Raycast(mainRay, out mainHit, 100); 26 if(mainHit.collider != null && mainHit.collider.gameObject.layer == 4) 27 { 28 print(mainHit.point); 29 CalculatePath(mainHit.point); 30 } 31 32 if(Camera.allCameras[1] != null) 33 { 34 mapRay = Camera.allCameras[1].ScreenPointToRay(Input.mousePosition); 35 Physics.Raycast(mapRay, out mapHit, 100); 36 if (mapHit.collider != null && mapHit.collider.gameObject.layer == 1) 37 { 38 print(mapHit.point); 39 CalculatePath(mapHit.point); 40 } 41 } 42 } 43 } 44 private void CalculatePath(Vector3 target) 45 { 46 if(myNavMesh != null) 47 myNavMesh.SetDestination(target); 48 49 } 50 }
十、动画系统Animator
转自:https://www.cnblogs.com/DGJS/p/11051525.html
新建Animator Controller文件
AnyState:任意状态;常用作播放死亡状态,不管当前角色在播放什么状态,都可以被杀死然后播放死亡动作;
Entry/Exit:进入状态机和退出状态机,进入状态机默认连接默认状态动画;
Idle:橙色(名字可以改),一般是默认动画,一般播放待机动画;
Run:一般状态(名字可以改)
动画状态机的层和参数:
Layer:动画状态机中还可以嵌套一个动画状态机;
Paramters:状态切换参数(int float bool trigger);
动画状态切换
Solo:动画切换优先;
Mute:动画切换禁止;
Has Exit time :勾选上会在切换动画时上一动作结束后再播放下一动画
Setting: ExitTime:动作切换上一动作退出时间;
Fixed Duration/Transtion Duration:动作切换时间长度;
Transtion offset:下一动作所在上一动作比例;(动手测试下);
Interruption sources:打断动画切换来源:
重要:conditions:这里面的切换动作参数是paramers里面定义的,条件可以使大于、等于、不等于、小于
Animator组件:
Controller:AnimatorController文件;
Avatar:放骨骼文件;
Apply Root motion:如果勾选代表使用动画中的位移;如果当前游戏物体上的代码组件中有OnAnimatorMove内置函数,位移就受代码控制;
Update Mode:正常更新、物理更新(FixUpdata更新)、不受时间缩放影响;
Culling Mode:
始终有动画;
当摄像机没有渲染该物体,就会停止部分Transform相关功能;
当摄像机没有渲染该物体,整个动画都不会被渲染;
代码操作Animator
战斗攻击流程图
十一、物理引擎(待补充)
射线的常见三个用法:
检测鼠标点击位置,线性检测,球形检测
1 public class ExplodeEffect : MonoBehaviour 2 { 3 4 private void Start() 5 { 6 7 } 8 9 private void Update() 10 { 11 if (Input.GetKeyDown(KeyCode.Space)) OverlapSphereCast(); 12 13 // Debug.DrawLine(this.transform.position, TargetTran.position, Color.red); 14 } 15 16 public Transform TargetTran; 17 private RaycastHit hit; 18 private void LineRayCast() 19 { 20 //线性检测,将检测过程途中的第一个物体进行输出 21 int layer = (1 << 9) | (1 << 8); 22 if (Physics.Linecast(this.transform.position, TargetTran.position, out hit, layer)) 23 hit.collider.GetComponent<MeshRenderer>().material.color = Color.red; 24 } 25 26 27 private void ScreenRayCast() 28 { 29 int layer = (1 << 9) | (1 << 8); 30 //相机发射射线的应用,可以用于获取鼠标的位置 31 //通过相机类中的ScreenPointToRay方法返回一个射线。 32 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 33 //发射射线 34 if (Physics.Raycast(ray, out hit, layer)) 35 hit.collider.GetComponent<MeshRenderer>().material.color = Color.red; 36 //实现效果,鼠标移动到物体,物体变为红色。 37 } 38 39 public float upwardsModifier;//决定了爆炸力给予的Y轴分量 40 public float force; 41 42 private void OverlapSphereCast() 43 { 44 int layer = (1 << 9) | (1 << 8); 45 //球型检测,范围检测,可用于制作爆炸效果 46 Collider[] exColliders = Physics.OverlapSphere(this.transform.position, 20, layer); 47 if (exColliders != null) 48 { 49 foreach (var item in exColliders) 50 { 51 item.GetComponent<MeshRenderer>().material.color = Color.red; 52 Rigidbody rig = item.GetComponent<Rigidbody>(); 53 //添加爆炸力效果 54 rig.AddExplosionForce(force, this.transform.position, 20, upwardsModifier, ForceMode.Impulse); 55 } 56 } 57 } 58 59 }
Demo01:https://pan.baidu.com/s/1BzZlvZU7RTQq_WyAhRCkTg llcl
Demo02:https://pan.baidu.com/s/1ffqdV7VnL0L90kMMBWH9NA q02o
十二、Itween
转自:https://www.cnblogs.com/martianzone/p/3397422.html
一.iTween 介绍
iTween是一个动画库,目的是最小的投入实现最大的产出.让你做开发更轻松,用它可以轻松实现各种动画,晃动,旋转,移动,褪色,上色,控制音频等等。
二.iTween 原理
iTween的核心是数值插值,简单说就是给iTween两个数值(开始值,结束值),它会自动生成一些中间值,例如:, 开始值-> 中间值 -> 中间值 …. -> 结束值。
这里的数值可以理解为: 数字,坐标点,角度,物体大小,物体颜色,音量大小等。
三.主要文件有两个iTween.cs 和 iTweenPath.unitypackage(编辑路径才需要这个包)
http://l6.yunpan.cn/lk/Qv44q7AV7emHG
四.如何将iTween 加入项目:
在项目中建立Plugins目录, 然后将下载的iTween.cs放到Plugins目录即可。
如果需要编辑路径, 使用import package->custom package菜单功能加入iTweenPath.unitypackage。
五.几种效果演示
1. 物体移动
iTween.MoveTo(gameObject,new Vector3(100,200,0),2);
其中第一个参数是要移动的物体
第二个参数是要移动到的目标点坐标
第三个参数是移动需要的时间,然后物体将在2秒之内移动到坐标点为(x=100,y=200,z=0)的位置。
如果你需要在物体移动过程中更好的控制,我们可以添加更多的参数(所有函数可用的参数列表可以参考http://itween.pixelplacement.com/documentation.php):
1
|
iTween.MoveTo(gameObject,iTween.Hash( "position" , new Vector3(100,200,0), "time" ,2)); |
这一段代码的效果与第一个例子效果是一样的,只是第二个参数是一个字典类型的数据,可以设置更多的参数。
通用的参数介绍如下:
position:坐标,包括x,y,z三个轴向
path:路径,是一个坐标数组,后面会讲到iTweenPath脚本的使用,配合着itweenPath可以让物体沿着路径点移动。
x:x轴向的位置,如果只设置了x轴,物体就只移动x轴,y,z轴不会改变
orienttopath:如果设置为true,物体移动到目标点的过程中,z轴会一直朝向下一个目标点
looktarget:物体朝向,物体在移动过程中会一直朝向我们设置的坐标点的坐标
looktime:物体看向looktarget或orienttopath设置坐标的时间
islocal:当物体的目标点是相对于父节点的坐标,需要把isLocal设置为true,否则为false
time speed:这两个参数都可以控制物体移动的快慢
delay:延迟时间,当物体开始移动之前等待时间
easetype:移动模式,我们可以设置一些加速度的效果,这个参数值是一个枚举iTween.Easetype
looptype:循环模式,一共有三种模式
iTween.LoopType.none:不循环
iTween.LoopType.loop:循环,物体移动到终点后会跳到起点重新移动
iTween.LoopType.pingPong:来回循环,物体移动到终点后会再以相同的模式和时间再移动到起点,然后再移动到终点,一直循环
onstart:物体开始移动之前的回调函数
onstarttarget:回调函数接收对象,默认开始之前会向iTween.MoveTo函数的第一个参数的物体发送回调,根据需要在这里设置合适的回调接收者
onstartparams:回调方法的参数
onupdate:物体在移动过程中的回调函数
onupdatetarget:物体在移动过程中回调函数的接收者
onupdateparams:移动过程中回调函数的参数
oncomplete:物体移动完成后的回调
oncompletetarget:物体移动完成后的回调函数的接收者
oncompleteparams:物体移动完成后的回调函数的参数
ignoretimescale:忽略时间缩放,时间缩放是Time.timeScale = 0.5f; 默认值为1,如果我们把时间缩放值设置小于1,我们游戏的整体时间都会放慢,就像播放慢镜头一样,如果设置ignoretimescale为true,无论我们时间怎么缩放,对物体的移动都没有影响。
只要我们了解了itween的其中一个使用方式,其他的都是一样的做法。
我们可以做的动画有:位移,旋转,缩放,音量渐变,摄像机淡入淡出,颜色的渐变,物体振动等很多有用的动画。
2. 数值过渡
iTween.ValueTo(gameObject, iTween.Hash( "from", y, "to", toY, "easetype", easeType, "loopType", loopType, "onupdate", "onupdate", "time", tm ));
3. 振动
iTween.ShakePosition(target, new Vector3(0, 0.1f, 0), 1);
4. 按路径移动
var path = GameObject.Find("Plane").GetComponent("iTweenPath").GetPath("myPath"); iTween.MoveTo(gameObject, iTween.Hash(//"position", Vector3(0, 0, 0), "path", path, "time", 20, "easetype", "linear"));
以下是iTween插件各方法的大致用法,每个方法都有简单传参和Hashtable定制两种用法,Hashtable可用iTween.Hash()生成,每个方法所需参数及其功能请参考iTween内部提示。
常用方法简介:
AudioFrom:pitch和volum属性提供的是初始值
audioTo: pitch和volum属性提供的是终结值
audioUpdate:pitch和volum属性提供的是终结值 此方法用于Update()方法中
stab:播放AudioClip一次,不用手动加载AudioSource组件
CameraFadeAdd:创建一个对象可以模拟摄相机的淡入淡出。
CameraFadeSwap:改变摄相机的淡入淡出背景图(对象为CameraFadeAdd返回对象)
CameraFadeFrom:立即改变摄相淡入淡出的透明度然后随时间返回.amount:当执行淡入淡出时,其透明度的变化速度。(透明度越大,淡入淡出越快,个人认为100为满,如果速度较快,时间较长,渐变效果会在时间快要结束时出现。此方法配合CameraFadeAdd使用,只有在CameraFadeAdd前提下,才可以进行淡入淡出操作。此方法为从CameraFadeAdd返回的对象出淡出到原来的界面。
CameraFadeTo:随时间改变摄相机淡入淡出透明度,此方法为从本界面淡入到CameraFadeAdd返回的对象
ColorFrom:即刻改变对象的颜色值然后随着时间改变其回原来的颜色(总的来说,就是对GUIText和GUITexture的颜色的淡入淡出效果)。Color:此属性代表对象变化初始值。与audioFrom有异曲同工之效
ColorTo:随着时间改变对象的颜色组。同上例一样。Color:此属性代表对象变化最终值,与audioTo有异曲同工之效
(注意,ColorFrom和ColorTo还有后面的ColorUpdate方法的NamedColorValue属性,有一些对象不具有NamedColorValue里的属性,运行时会有提示)
ColorUpdate:跟ColorTo类似,但花销的成本较少,此方法在Update方法中被调用
FadeFrom:即刻改变对象的的阿尔法值,然后随着时间将其阿尔法值变回原值。如果对象上有挂载
a Light, GUIText or GUITexture这些组件,这些组件将成为被执行的对象。
注:阿尔法值可以粗略理解为对象的透明度,值越小,透明度越大。这里的 alpha或者 amount 是变化初值
FadeTo:同上,alpha或amount是变化终值。
FadeUpdate :同FadeTo类似,在Update()方法中调用,提供时刻改变属性值的环境。不局限于 EaseType
LookFrom:即刻旋转物体让其看提供的Vector3或都Transfrom,然后随时间旋转回原来的角度值
注:物体的脸部一般以本地坐标(即物体坐标)的Z轴,脸部朝向方法,即Z轴指向方法。
LookTo:随时间旋转物体让其脸部朝向所提供的Vector3或Transform位置。
LookUpdate:同LookTo类似,在Update()方法中调用。
MoveAdd:随时间改变游戏对象的位置(原理还有点蒙,感觉跟MoveBy有点像)amount:是改变物体位置的一个值,不是目标位置的坐标。
MoveBy:增加提供的坐标到游戏对象的位置
MoveFrom:立即改变游戏对象的位置为提供的,然后随时间改变游戏对象位置到初始位置
属性:movetopath:Boolean值 ,是否自动将物体置于Ptah的起始点,默认值为真
Path:目标文件可用路径编缉器获得
PunchPosition:对物体的位置添加一个摇晃的力,使其摇晃最终归于原来的位置 其晃动大小和方法由提供的amount(Vector3)决定(方法由Vector3的x,y,z共同决定,晃动大小,由各个方法的值的大小决定)
PunchRotation:对物体的旋转添加一个摇晃的力,使其旋转最终归于初始值。其旋转角度大小和方向由提供的Vector3决定,建议用单轴而不是整个Vector3,例如(0,1,0)是绕Y轴旋转,角度大小由Vector3Y轴值大小决定
PunchScale:对物体的大小比例添加一个摇晃的力,使其大小比例变化最终归于初始值。其大小比例变
化方向和大小由提供的Vector3决定。例如(0,1,0)是在Y轴方向对物体大小变化(即变化物体的高)
,大小由该方向的值大小决定
PutOnPath :根据提供的百分比将游戏物体置于所提供路径上(1为百分之百)。
PointOnPath:根据提供的百分比返回一条路径上的Vector3的位置
RectUpdate:返回一个RECT在提供的两个值之间,大小变化根据提供的速度
Vector3Update:返回一个Vector3在提供的两个值之间,大小变化根据提供的速度
Vector2Update:返回一个Vector3在提供的两个值之间,大小变化根据提供的速度
FloatUpdate:返回一个float在提供的两个值之间,大小变化根据提供的速度
RotateAdd:对游戏物体的旋转角度随着时间增加所提供的欧拉角(顺时针旋转。Vector3三个值解析:
X,Y,Z各代表围绕哪个轴转动。其转动角度就是X,Y,Z、的值的大小。amount:欧拉角大小)
RotateBy:把提供的值乘以360,然后随着时间旋转游戏物体的角度按计算得的值。例 如(0,1,0)就是绕Y轴旋转360度。 顺时针旋转
RotateFrom:立即改变游戏物体角度的欧拉角,然后随着时间旋转回原来的角度,属性提供的欧拉角为变化初始值
RotateTo:旋转游戏物体角度到我们所提供的欧拉角角度。属性提供的欧拉角为变化终结值
RotateUpdate:跟RotateTo类似,该方法在Update中被调用,提供一个可改变属性值的环境。不用局限EaseType
ScaleAdd:随着时间根据提供的amount(Vector3)增加游戏物体的大小
ScaleBy:随着时间变形游戏物体,游戏物体最终变形大小由我们提供的amount(Vector3)值决定 算法:
最终变形大小=游戏物体初始的sacle * 我们提供的 amount值
ScaleFrom:立即改变游戏物体的比例大小,然后随时间返回游戏物体原本的大小。amount:为物体变形的初始大小
ScaleTo:随着时间改变物体的比例大小到我们提供的Scale大小(scale值)
ScaleUpdate:跟ScaleTo类似,此方法用于Update中,提供可改变属性值的环境 ,不用局限于EaeType
ShakePosition:根据提供的amount衰减其值随机摇动游戏物体的位置,其晃动大小和方向由提供的amount(Vector3)决定(方向根据Vector3随机,晃动大小,由各个方向的值的大小决定)
ShakeRotation:根据提供的amount衰减其值随机摆动旋转游戏物体的角度 。Vector3三个值解析:X,Y,Z各代表围绕哪个轴旋转。其转动角度就是X,Y,Z、的值的大小
ShakeScale:根据提供的amount衰减其值随机摆动改变游戏物体的大小。其大小比例变化方向和大小由提供的Vector3决定。例如(0,1,0)是在Y轴方向对物体大小变化(即变化物体的高),大小由该方向的值大小决定
Pause:停止iTween 如果一个iTween以ignoreTimeScale定制且设为True停止工作。 参考不同参数Pause的说明。
Stop:停止iTweens,和Pause一样,不同的参数有不同作用。
Stop By Name:停止指定名字的iTween
ValueTo:返回一个插值在两件值之间的回调函数的值(作用不明)
(Returns a value to a callback method interpolated between the supplied \'from\' and \'to\'
values for application as desired. Requires an \'onupdate\' callback that accepts the same
type as the supplied \'from\' and \'to\' properties.)