API: Application Programming Interface 应用程序编程接口算法
查看Unity文档和API手册:编程
help->Unity Manual/ Scripting Reference数组
每个脚本默认都是继承MonoBehaviour的
MonoBehaviour是继承Behaviour的
Behaviour是继承Component的
Component是继承Object的
所以Script脚本是一个Object浏览器
Manual->Scripting->Scripting Overview->Execution Order of Event Functionsapp
Reset() 只会在Editor模式下触发,运行时或build完之后就不会触发了
当在Editor模式下(运行的时候不触发),当script第一次attached到GameObject时/
当GameObject的Reset按钮被按下时会触发。
用于initialise the script's propertiesdom
Awake() 当场景开始运行时\ 当一个Prefab被实例化出来的时候会触发
Awake老是在任何Start()以前被触发
If a GameObject is inactive during start up, Awake is not called until it's made active异步
OnEnable() 当场景开始运行时\ 当一个Object被enabled时会触发
前提是这个Object为active时
OnDisable() 当一个Object becomes disabled\inactive时编辑器
OnLevelWasLoaded() is to inform the game that a new level has been loadedide
Start() is called before the first frame update (only if the script instance is enabled)函数
FixedUpdate(), Update(), LateUpdate()
FixedUpdate() 会先调用,以后是Update(),最后是LateUpdate()
FixedUpdate是每秒固定调用N次
Update和LateUpdate是每帧调用一次 -- 与运行环境有关
FixedUpdate() all physics calculations and updates occur immediately after FixedUpdate. When applying
movement calculations inside FixedUpdate, you don't need to multiply your values by Time.deltaTime
since FixedUpdate is called on a reliable timer, which is independent of the frame rate.
通常把与物理有关的更新写在FixedUpdate里 -- 能够保证物体的运动是平滑的,
Update() is called once per frame.
LateUpdate() is called once per frame, after Update has finished. A common use for LateUpdate would
be a following third-person camera: make the character move and turn inside Update, you can perform
all camera movement and rotation calculations in LateUpdate -- to ensure that the character has moved
completely before the camera tracks its position.
OnTrigger...()
OnCollision...()
yield WaitForFixedUpdate() 能够在FixedUpdate()中调用yield WaitForFixedUpdate()
OnMouse...() 是Input events
Scene Rendering以后详叙
OnApplicationPause() is called after the frame where the pause occurs but issues another frame before
actually pausing. One extra frame will be issued after OnApplicationPause is called to allow the game to
show graphics that indicate the paused state. 点击Unity中间暂停按钮的时候会调用。
OnApplicationQuit() 退出游戏的时候触发。
It is called before the application is quit; In the editor, it is called when the user stops playmode.
OnDisable() is called when the behaviour becomes disabled or inactive.
OnDestroy() is called after all frame updates for the last frame of the object's existence
Update和LateUpdate的数量是同样的,而与FixedUpdate的数量不一样
Start在第一个xxxUpdate以前
OnApplicationPause在这里出现了,点击Unity中的暂停键也没反应,不知为什么
uncheck gameoject的勾选框,执行了OnDisable(),从新勾选,执行了OnEnable()
禁用的时候全部的xxxUpdate也不会进行更新了
运行结束后:OnApplicationQuit-->OnDisable->OnDestroy
Time类
Time.captureFramerate -- 设置帧的速率,进而进行屏幕截图
Time.deltaTime -- 当前帧所用时间(单位秒)
常常在Update中使用:* Time.deltaTime 表示一秒
Time.fixedDeltaTime -- 和FixedUpdate()与Update()的区别类似
Time.smoothDeltaTime -- 平滑,不会变化过大的deltaTime
Time.time -- 从游戏开始(start of the game)到当前帧开始(the time at the beginning of this frame)所用的时间
Time.fixedTime -- 从游戏开始到最新一个FixedUpdate()开始(the latest FixedUpdate has started)所用的时间
Time.unscaledTime
Time.realtimeSinceStartup -- the real time in seconds since the game started, not affected by Time.timeScale.
It keeps increasing while the player is paused. Using realtimeSinceStartup is useful when you wanna pause the
game by setting Time.timeScale=0, but still want to be able to measure time somehow.
Time.timeSinceLevelLoad -- 从the last level has been loaded到this frame has started所用的时间
Time.frameCount -- the total number of frames that have passed
Time.timeScale -- the scale at which the time is passing, 能够用于调整游戏的播放速率,默认为1
注:
Time.time≈Time.fixedTime 游戏运行时间
Time.unscaledTime, Time.realtimeSinceStartup 不受timeScale影响 (不受游戏暂停影响), 可是time会被影响
Time.time, Time.unscaledTime 在FixedUpdate()中调用时,会返回对应的fixed的值fixedTime\fixedUnscaledTime
time = timeSinceLevelLoad : start~~the beginning of this frame
fixedTime略大于time : FixedUpdate在Update以后
fixedDeltaTime = 0.02 -- 每秒50帧,Edit->Project Setting->Time->Time Manager中能够设置
因为fixedDeltaTime设置为0.02,则deltaTime会在0.02上下波动
smoothDeltaTime的波动比deltaTime的平滑不少
timeScale = 1(默认值)
Time.deltaTime的使用 -- 经常使用于控制物体的移动
控制一个物体的移动
public Transform cube;
cube.Translate(Vector3.forward);
-- cube物体会移动很快:调用一次移动一米,每秒调用不少次
-> cube.Translate(Vector3.forward * 3 * Time.deltaTime); // 速度*当前帧所用时间=当前帧运行距离:3m/s
Time.timeScale = 1f; // 设置timeScale
--> Time.deltaTime会乘以timeScale
timeScale=0.5f; 进行慢动做回放/ timeScale=2f; 两倍播放速度等等
若timeScale=0; cube就不会移动了,经常使用timeScale=0来暂停游戏,前提是全部物体的控制都用了deltaTime
Time.realtimeSinceStartup的使用 -- 经常使用于性能测试
好比要测试某个方法、某种处理方式是否耗费性能
例:测试Method1()和Method2()哪个更耗费性能
Method1() { int i = 1 + 2; } Methods2() { int i = 1 * 2; } float time1 = Time.realtimeSinceStartup; int runCount = 1000000; for(int i=0; i<runCount; i++) { Method1(); } float times2 = Time.realtimeSinceStartup; Debug.Log(time2 - time1); // 把Method2换成Method1再执行一次,比较两次的输出便可
通常还有一个对照组:Method0() {} 方法体为空
GameObject类
与实例化有关的方法:
Instantiate()
CreatePrimitive()
1. 第一种建立物体的方法 new GameObject();
// 建立游戏物体 // 建立一个名为New Game Object的Empty Object, with Component Transform new GameObject(); // 名为Cube GameObject cube = new GameObject("Cube"); cube.AddComponent<Rigidbody>();
这个方法通常用来建立空物体,用于放置其余物体
2. 第二种建立物体的方法 GameObject.Instantiate();
一般为初始化一个Prefab或复制另一个游戏物体
使用Instantiate()建立的物体的名字默认跟上(Clone)
// 建立游戏物体 GameObject.Instantiate() // 根据Prefab // 根据另一个游戏物体 // prefab在Unity中能够被prefab或gameObject赋值 GameObject.Instantiate(prefab);
3. 第三种建立物体的方法 GameObject.CreatePrimitive();
一般用于建立一些原始的形状,如Create->3D Object->Cube/Sphere/Capsule/Cylinder等等
GameObject.CreatePrimitive(PrimitiveType.Plane);
GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
tag -- 属性,多用于判断游戏物体的分类
transform -- 全部GameObject都有一个transform组件,且不能被删除
activeSelf -- 是否locally处于激活状态
activeInHierarchy -- Is the GameObejct active in the scene? -- 多用于判断该物体是否激活
两者区别:B是A的子物体,禁用A,则A.activeInHierarchy=false; B.activeInHierarchy=false
可是A.activeSelf=false; B.activeSelf=true; (在Inspector中B是checked状态)
gameObject.SetActive(true/false); -- 设置gameObject的activeInHierarchy状态
禁用了游戏物体后,它仍是在内存中存储的,所以能够获取对应的属性
只不过物体不会在Scene中显示,Update()也再也不执行,不须要渲染,不耗费性能了
一个游戏(Game)由多个场景(Scene)组成
一个场景(Scene)由多个游戏物体(GameObject)组成
一个游戏物体(GameObject)由多个组件(Component)组成
C#的共同基类:System.Object
Unity中类的共同基类:UnityEngine.Object
Component和GameObject都是继承自UnityEngine.Object的,他们有许多相同的属性
Object中的属性:
name -- 经过GameObject或经过它的Component获取到的name都是GameObject的name
Object中的方法:
Destroy() -- Removes a gameobject/ component/ asset.
调用后,游戏物体在场景中会马上被删除,可是还没被回收;在Unity中会把将要销毁的游戏物体放入垃圾池统一管理
Destroy(gameObject);
Destroy(this); // script
Destroy(rigidbody); // remove component
Destroy(gameObject, 5); // removes gameObject in 5 seconds
DestroyImmediate() -- Destroy the object immediately, strongly recommended to use Destroy instead.
该方法会马上删除游戏物体,有可能致使空指针
DontDestroyOnLoad(gameObject) -- A场景跳转到B场景时,能够调用DontDestroyOnLoad()
通常而言跳转后,A场景中的全部游戏物体都会被销毁,调用后gameObject不会被销毁
通常用于设置一个两个场景共享的游戏物体
FindObject(s)OfType -- 根据组件类型进行全局查找,找到符合type类型的组件(返回找到的第一个)
注意,只会查找激活了的游戏物体
Light light = FindObjectOfType<Light>();
light.enabled = false;
-- 对于一个组件,使用.enabled来控制激活,对于一个对象,使用SetActive()来控制。
Transform[] transforms = FindObjectsOfType<Transform>();
foreach(Transform fransform in transforms) { ... }
Instantiate()
Instantiate(Object original);
Instantiate(Object original, Transform parent); // 设置为parent的子对象
Instantiate(Object original, Transform parent, bool instantiateInWorldSpace); // 是否用worldSpace -- 位置为局部或是世界
Instantiate(Object original, Vector3 position, Quaternion rotation);
Instantiate(Object original, Vector3 position, Quaternion rotation, Transform parent);
GameObject的变量:
activeInHierarchy; activeSelf; isStatic; layer; scene; tag; transform
GameObject的静态方法:
CreatePrimitive
Find -- 对全局进行遍历查找;耗费性能,尽可能不要在 Update中调用,少在Start中调用
GameObject gameObject = GameObject.Find("Main Camera");
gameObject.SetActive(false);
FindGameObjectWithTag -- 返回游戏物体的数组;在标签范围内进行查找 -- 较快
GameObject[] gameObjects = GameObject.FindGameObjectsWithTag("Main Camera");
FindWithTag -- 返回查找到的第一个符合条件的游戏物体
注意:通常而言,须要进行null的判断,由于有可能出现tag错误或无激活物体等状况
GameObject的方法:经过对象进行调用
AddComponent
GetComponent -- 若是游戏物体上有多个相关组件,则会返回获得的第一个
GetComponents
GetComponentInChildren -- 该游戏物体以及全部子物体
GetComponentsInChildren
GetComponentInParent -- 该游戏物体以及全部直系祖先
GetComponentsInParent
BroadcastMessage(string methodName, object paramter=null, SendMessageOptions opts) -- 广播消息
calls the method named methodName on every script in this game object or any of its children
搜索自身和子物体,如有methodName()则调用
优势:减小游戏物体之间的耦合性,由于如果使用SendMsg则须要先获得游戏物体的指向
SendMessageOptions.DontRequireReceiver:即便没有Receiver也不会报错
SendMessageOptions.RequireReceiver: 如果没有Receiver,会报错
target.BroadcastMessage("Attack", null, SendMessageOptions.DontRequireReceiver); target.BroadcastMessage("Attack", null, SendMessageOptions.RequireReceiver);
SendMessage(string methodName, object value=null, SendMessageOptions opts)
-- 针对某个对象发送消息,但不会对其子对象发送;对该对象中全部methodName()进行调用
public GameObject target;
target.SendMessage("Attack");
SendMessageUpwards(...) -- 针对该对象以及其全部ancestors(父亲&爷爷等),与BroadcastMessage相反
注意:父亲只会有一个,其余的是叔叔
target.SendMessageUpwards("Attack", null);
CompareTag -- 判断两个物体的标签是否相同
SetActive -- 激活/禁用游戏物体
注意:
对于BroadcastMessage, SendMessage, SendMessageUpwards, CompareTag,
GetComponent(s), GetComponent(s)InChildren, GetComponent(s)InParent,
在对组件Component进行这些操做时,就是在对组件所在物体进行相应操做
Component组件:
Transform
Rigidbody
Mesh Renderer
MeshFilter
Collider
NavemeshAgent
Animation
Animator
自定义脚本 .cs
MonoBehaviour中大多数都是Message事件,由Unity自动调用
变量:
runInEditMode:By default, script components are only executed in play mode. By setting this property, the MonoBehaviour will have its callback functions executed while the Editor is not in playmode.
控制脚本在EditMode运行
对应:[ExecuteInEditMode]
useGUILayout:略
继承的变量:
enabled:禁用/ 激活该脚本
注:禁用脚本是指禁用了脚本的Update方法,所以若是某脚本没有写Update(),则也不会出现勾选的框
isActiveAndEnabled:该脚本是否被激活(activeInHierarchy用来判断物体时候被激活)
gameObject:该脚本所在的游戏物体
tag, transform, name -- 脚本所在的游戏物体的对应属性
hideFlags:
静态方法:
print() -- 是MonoBehaviour的静态方法,所以只能在MonoBehaviour中调用
而Debug.Log()在任何位置都能调用
方法:
任务17:Invoke的使用
Invoke:
CancelInvoke() -- 取消当前MonoBehaviour中(不是当前对象中)全部的Invoke calls
Invoke("methodName", time) -- Invokes the mothod methodName in time seconds
InvokeRepeating("name", time, rate) -- invoke methodName(), then repeatedly every repeatRate seconds
多用于与CancelInvoke()配合使用
IsInvoking -- 判断是否有Invoke方法pending(正在被调用) -- Invoke后到执行前均返回true
至关于,Invoke会将某方法加入一个队列,IsInvoking会判断该方法是否在该队列中
i.e. 在2.0f秒以内再次按下Space时,不会执行Invoke("LaunchProjectile")
void Update() { if(Input.GetKeyDown(KeyCode.Space) && !IsInvoking("LaunchProjectile")) { Invoke("LaunchProjectile", 2.0f); } } void LaunchProjectile() { Rigidbody instance = Instantiate(projectile); instance.velocity = Random.insideUnitSphere * 5; }
任务18&19&20:协程的执行
协程:Coroutine
程序的正常执行顺序:顺序执行,遇到方法则进入方法后顺序执行方法
协程:若是方法是一个协程方法,则可看做是一个新的Thread,可能同步执行,可能有前后,具体看CPU如何调度
协程方法好处:不会阻塞当前方法的运行;能够进行协程方法自身的暂停
StartCoroutine(); StopAllCoroutines(); StopCoroutine()
StartCoroutine(IEnumerator routine)/ StartCoroutine(string methodName, object value=null)
-- Starts a coroutine
注:两种写法与StopCoroutine()的两种写法分别对应,不能混淆
注:经过methodName启动协程时,最多只能传递一个参数
void Start () { print("before changing"); // 开始Coroutine StartCoroutine(ChangeColor()); print("after changing"); } // Coroutine // 1. 返回值为IEnumerator // 2. 返回参数的时候使用yield return // 3. 使用StartCoroutine(method())进行调用 IEnumerator ChangeColor () { // 等待三秒 yield return new WaitForSeconds(3f); print("Color: before changing"); cube.GetComponent<MeshRenderer>().material.color = Color.blue; print("Color: after changing"); // 返回null yield return null; }
例子:经过协程实现颜色渐变
public class API8CoroutineFadeColor : MonoBehaviour { public GameObject cube; // private bool started = false; // Update is called once per frame void Update () { // if (Input.GetKeyDown(KeyCode.Space) && !started) { if(Input.GetKeyDown(KeyCode.Space)) { StartCoroutine(Fade()); } } IEnumerator Fade() { // started = true; for(float i = 1; i>=0; i -= 0.1f) { cube.GetComponent<MeshRenderer>().material.color=new Color(i,i,i); yield return new WaitForSeconds(0.1f); } yield return null; } }
方法2:利用差值 Lerp()
IEnumerator Fade() { while(true) { Color startColor = cube.GetComponent<MeshRenderer>().material.color; Color newColor = Color.Lerp(startColor, Color.red, 0.02f); cube.GetComponent<MeshRenderer>().material.color = newColor; yield return new WaitForSeconds(0.02f); Debug.Log(Mathf.Abs(newColor.g - Color.red.g)); if(Mathf.Abs(newColor.g - Color.red.g) <= 0.1f) { break; } yield return null; } }
StopAllCoroutines() -- Stops all coroutines running on this behaviour
StopCoroutine(string methodName)/ StopCoroutine(IEnumerator routine)
-- Stops the first coroutine named methodName, or the coroutine stored in routine running on this behaviour
使用方法与StartCoroutine对应
例1:经过IEnumerator实现
private IEnumerator coroutine; void Update () { // if (Input.GetKeyDown(KeyCode.Space) && !started) { if(Input.GetKeyDown(KeyCode.Space)) { coroutine = Fade(); StartCoroutine(coroutine); } if (Input.GetKeyDown(KeyCode.S)) { StopCoroutine(coroutine); } }
例2:经过直接填写方法名methodName实现 (最多只能传递一个参数)
void Update () { if(Input.GetKeyDown(KeyCode.Space)) { StartCoroutine("Fade"); } if (Input.GetKeyDown(KeyCode.S)) { StopCoroutine("Fade"); } }
任务21:鼠标相关事件函数
OnMouseXXX():对象为GUIElement or Collider
注:通常物体须要添加Collider后才有效
OnMouseDown() -- 鼠标按下的瞬间调用
OnMouseDrag() -- 鼠标按下后进行拖拽的时候
OnMouseEnter() -- 鼠标移入的时候
OnMouseExit() -- 鼠标移出的时候
OnMouseOver() -- 鼠标停留在物体上方的时候
OnMouseUp() -- 鼠标松开的时候(与OnMouseDown相对)
OnMouseUpAsButton() -- 鼠标在以前按下的物体上方松开时 (与OnMouseDown相对)
静态变量:Read-Only
Deg2Rad/ Rad2Deg -- Degree <-> Radian: Deg2Rad=2*PI/360; Rad2Deg=360/(2*PI)
Epsilon -- A tiny positive floating point value:
anyValue +/- Epsilon = anyValue;
0 +/- Epsilon = Epsilon
Infinity/ NegativeInfinity
PI
静态方法:
Abs() -- absolute value
Approximately() -- Compares two floats and returns true if they're similar (within Eposilon)
Floating point imprecision makes comparing floats using "=" inaccurate.
Cell() -- 向上取整(进一法),注:负数取大的(Mathf.Cell(-10.7f)=-10)
CeilToInt() -- 返回值为Int类型
Clamp(value, min, max) -- 夹紧
return min if value<min; return max if value>max; else return value
经常使用于TakeDamage()或GainHealth() -- 受伤或补血不用判断hp超出
hp = Mathf.Clamp(hp, 0, 100);
Clamp01(value) -- 夹紧在0,1之间,等同于Clamp(value, 0, 1);
ClosestPowerOfTwo(int value) -- returns the nearest power of two value
ClosestPowerOfTwo(30)=32 -- 2^5=32
Exp(float power) -- e to the power of "power"
Pow(float f, float p) -- f to the power of p
Sqrt(f) -- square root of f
DeltaAngle(float current, float target) -- the shortest difference btw two given angles given in degrees 最小夹角
Floor() -- 向下取整(退一法),注:负数取小的(Mathf.Cell(-10.7f)=-11)
FloorToInt()
Max(a,b,c...)/ Max(float[] values)/ Min(...)
MoveTowards() -- Moves a value current towards target
Lerp(float a, float b, float t) -- 插值运算
t为0~1的一个比例,t<=0时返回a,t>=1时返回b
Start() { cube.position = new Vector3(0, 0, 0); } Update() { float x = cube.position.x; float newX = Mathf.Lerp(x, 10, 0.1f);
// 从x~10 -- 从当前位置向目标位置移动10%
// 不是匀速运动,移动愈来愈慢,并且只会无限接近目标位置 cube.position = new Vector3(newX, 0, 0); }
通常状况会使用Mathf.Lerp(x, 10, Time.deltaTime * speed); -- 若干秒后到达目标位置
LerpAngle() -- 360度内的角度插值
MoveTowards(float current, float target, float maxDelta) -- 匀速运动
经常使用newX = MoveTowards(x, target, Time.deltaTime * speed);
// 一秒移动speed米,加上负号即为反向移动
PingPong(float t, float b) -- 往复运动
返回值基于t的值的变化而变化,在0~b之间
通常会使用PingPong(Time.time * speed, b); 匀速往返运动
// 若是想要返回a~b怎么办? a + PingPong(speed, b-a); 便可
Input -- 键盘事件/鼠标事件/触摸屏事件
静态变量:
acceleration -- 重力感应
gyro -- gyroscope 陀螺仪
ime..... -- input method 输入法相关
anyKey -- 当任意键key or mouse button按下时,即为true
anyKeyDown -- is true when the first frame the user hits any key or mouse button
mousePosition -- 鼠标在屏幕上的像素位置
左下方为(0, 0, 0),能够为负值,表示在窗口外
经常使用于Camera.main.screenPointToRay将位置转化为射线判断鼠标时候点到游戏物体
详见Camera的方法
静态方法:
键盘按键:GetKey...(KeyCode key)/ GetKey...(string keyName)
GetKey -- 键盘按键,被按下时会一直return true
GetKeyDown/ GetKeyUp -- 按下/ 抬起瞬间
void Update () { // GetKey if (Input.GetKeyDown(KeyCode.Space)) { // 只会在按下瞬间被触发 print("Get Space Down"); } if (Input.GetKeyUp(KeyCode.Space)) { // 只会在抬起瞬间被触发 print("Get Space Up"); } if (Input.GetKey(KeyCode.Space)) { // 按下时一直触发 print("Get Space"); } if (Input.GetKeyDown("left shift")) { print("shift"); }
鼠标按键 -- GetMouseButton...(int button)
int button: 0 for left button, 1 for right button, 2 for middle button
GetMouseButton()
GetMouseButtonDown()/ GetMouseButtonUp()
虚拟按键 -- GetButton...(String buttonName)
在InputManager中定义的即为虚拟按键
Input Manager设置:Edit -> Project Settings -> Input
alt button: alternative button, 备用物理按键
好处:1. 一个虚拟按键能够对应多个物理按键
2. 名字很形象
GetButton()/ GetButtonDown()/ GetButtonUp()
GetAxis(string axisName) -- 返回该轴上的对应值(-1~1)
经常使用cube.Translate(Vector3.forward * Time.deltaTime * Input.GetAxis("Horizontal"));
返回值是渐变效果的,因此是非匀速运动,更圆滑
GetAxisRaw(...) -- 返回的是-1/0/1 -- 返回值肯定,是匀速运动,更灵敏
触摸操做 -- GetTouch
多指触摸 -- 好用的插件 easytouch
Vector2:二维向量,或二维点坐标 -- 上下左右,少了z轴
Vector2和Vector3是struct,而不是继承MonoBehaviour的类
就像transform.position.x不能被直接赋值修改同样,是值类型,要总体赋值
修改的时候须要Vector3 pos = transform.position; pos.x = ...; transform.position = pos;
静态变量:
down/ left/ right/ up/ zero (0,0)/ one (1,1)
变量:
magnitude -- 向量的长度
sqrMagnitude -- 向量长度的平方,即x^2+y^2
用处:好比在比较两个向量的长度时,使用sqr会减小性能的消耗
normalized -- 该向量的单位化(长度变为1)
x/y/ this[int] -- 向量的各值 this[0] == x; this[1] == y
Constructors:
new Vector2(x,y);
方法:
Equals() -- 两个Vector2值相同的时候,返回true
也能够直接使用 == 来进行比较
Normalize() -- 单位化该向量
Set(newX, newY) -- 或者直接进行赋值也能够
静态方法:
Angle(Vector2 from, Vector2 to) -- 取得两个向量之间的夹角
ClampMagnitude(Vector2 vector, float maxLength) -- 将向量的长度限定在maxLength以内
Distance(Vector2 a, Vector2 b) -- 即(a-b).magnitude
Dot() -- dot product
Lerp(Vector2 a, Vector2 b, float t) -- 差值
LerpUnclamped(Vector2 a, Vector2 b, float t) -- 差值,差异在于t<0或t>1时不会返回a或b,而是继续缩小/ 扩大
Vector2 v1 = new Vector2(2, 2); Vector2 v2 = new Vector2(3, 4); print(Vector2.Lerp(v1, v2, 0.5f)); // ((2+3)/2, (2+4)/2) print(Vector2.LerpUnclamped(v1, v2, 0.5f)); // ((2+3)/2, (2+4)/2) print(Vector2.Lerp(v1, v2, 2f)); // v2 print(Vector2.LerpUnclamped(v1, v2, 2f)); // t大于1时便继续按比例扩大 (4,6)
Max/ Min(Vector2 a, Vector2 b)
MoveTowards(Vector2 a, Vector2 b, float maxDistanceDelta) -- 对x, y分别进行运算(匀速)
静态变量:
相比Vector2而言,Vector3多了z轴,因此多了两个方向 forward和 back
方法:
Cross(Vector3 lhs, Vector3 rhs) -- cross product 叉乘
叉乘的结果方向由左手法则肯定,大拇指为a,食指为b,中指即为结果方向
Distance()
Dot()
Lerp()
LerpUnclamped()
Max/ Min()
MoveTowards()
Normalize()
OrthoNormalize(Vector3 normal, Vector3 tangent) -- Normalize normal, Normalize tangent, makes sure tangent is orthogonal (90 degrees) to normal.
Project(Vector3 v, Vector3 onNormal) -- v对onNormal作投影后的结果向量
ProjectOnPlane(Vector3 v, Vector3 planeNormal) -- v对planeNormal所表示的平面作投影
Reflect(Vector3 inDirection, Vector3 inNormal) -- 返回入射光inDirection对于由inNormal所肯定 的镜子平面的反射光
Slerp(Vector3 a, Vector3 b, float t) -- spherically interpolates btw a and b (a and b are treated as directions rather than points); the direction of the returned vector is interpolated by the angle and its magnitude is interpolated btw the magnitudes of a and b
经常使用于角色的转向
SlerpUnclamped()
+ :(a+b)即向量相接(能够用来作方向的叠加)
- :(a-b)即b指向a的向量(能够用来指向)
好比敌人指向Player的向量,就用Player的坐标所在向量-敌人的坐标所在向量)
* :*数字 即magnitude变大,方向不变
/ :/数字 即magnitude变小,方向不变
== :三个值相对相同
暴击率或是爆率等
静态变量:
value -- 0.0~1.0之间的随机小数 == Range(0, 1f); (0.0 and 1.0 are inclusive)
state -- 当前seed的状态。能够被使用来保存当前随机数生成期的状态。好比使用seed1生成了三个随机数后,使用
Random.State oldState = Random.state; 保存当前状态,以后如果想要继续在该seed1下生成随机数,
只需使用Random.state = oldState; 便可继续生成seed1下的第四个随机数了。
rotation -- 随机获得一个朝向
insideUnitCircle -- 按照半径为1,圆心为(0,0)的一个圆,随机生成一个在圆内的二维坐标
能够赋值给Vector3的变量,只会改变x和y的值
insideUnitSphere -- 按照半径为1,圆心为(0,0)的一个球,随机生成一个在球内的三维坐标
InitState(int seed) -- 初始化状态
计算机产生的是伪随机数,是经过一系列计算生成的随机数,不是真正的随机数。seed就是生成随机数的算法须要的一个参数。
该方法目的是给生成随机数的算法(如Range)提供seed。一些状况下,若是要求生成的随机数有必定规律(好比Debug的时候),
就提供肯定的seed;若是要求生成不肯定的随机数,能够选择时间做为seed:
Random.InitState((int)System.DateTime.Now.Ticks);
注意,不设置InitState()的时候,Unity也会自动设置,保证 每次游戏运行时Range生成的随机数有规律可循
Range(int min, int max) -- 返回min~max之间的整数(不包含max)
Range(float min, float max) -- 返回min~max之间的小数(包含max)
ColorHSV(hueMin, hueMax, saturationMin, saturationMax, valueMin, valueMax, alphaMin, alphaMax) -- Generates a random color from HSV and alpha ranges
四元数:w, x, y, z 四个值组成一个四元数,表示一个物体的旋转
欧拉角 Rotation: 三维向量Vector3,经过x, y, z三个值肯定一个物体的旋转
注意,围绕y轴旋转时,是按照世界坐标中的y轴而不是局部坐标系的y轴旋转
而围绕x或z轴旋转是围绕自身的x或z轴进行旋转
优劣:四元数在进行旋转的计算时更方便,而欧拉角在进行肉眼观察时更直观
没有旋转时,默认的EulerAngles=(0, 0, 0); 默认的Rotation=(0, 0, 0, 1);
围绕x轴旋转90°时,默认的EulerAngles=(90, 0, 0); 默认的Rotation=(0.7, 0, 0, 0.7);
Quaternion中的方法:
Euler() -- 把一个EulerAngle变换成一个四元数
cube.rotation = Quaternion.Euler(new Vector3(45, 45, 45));
变量.eulerAngles -- 把一个四元数转换成一个欧拉角:
cube.rotation.eulerAngles;
LookRotation(Vector3 forward, Vector3 upwards=Vector3.up) -- 返回一个Quaternion,表示主
角的朝向;用来使主角面向敌人,使主角的z轴正方向与传递进来的forward方向保持一致
案例:
建立Plane做为Floor;
建立Capsule做为Player,Capsule的z轴方向即Player朝向
建立Cube做为Player的子物体,放在z轴正方向突出做为眼睛
建立Cylinder做为Enemy
Vector3 dir = enemy.position - player.position; // player指向enemy的向量
player.rotation = Quaternion.LookRotation(dir);// 将Vector3转换成四元数,并赋值
问题:若Enemy与Player不在同一高度上,则Player会弯腰或抬头,事实上不须要
解决:
dir.y = 0; 便可
问题:须要让Player慢慢转向而不是一会儿就转向
解决:
使用Quaternion.Slerp()
(若是是使用Lerp(),则是分别对w,x,y,z四个值分别作差值运算;
Slerp()是适合作旋转的;
可是Lerp()旋转较快,looks worse if the rotations are far apart
能够这么理解,Lerp()是按直线变化过去的,而Slerp()是按圆弧变化过去的)
Quaternion target = Quaternion.LookRotation(dir);
player.rotation = Quaternion.Slerp(player.rotation, target, Time.deltaTime*speed);
private bool isRotating = false; private Vector3 dir; private float speed = 2f; private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { isRotating = true; dir = enemy.position - player.position;// player指向enemy的向量 dir.y = 0; } if (isRotating) { player.rotation = Quaternion.Slerp(player.rotation, Quaternion.LookRotation(dir), Time.deltaTime * speed); // 将Vector3转换成一个四元数,并赋值) } }
控制位置:
position -- If you change the position of a Rigidbody using Rigidbody.position, the transform will be updated after the next physics simulation step. This is faster than updating the position using Transform.position as the latter will cause all attached Colliders to
recalculate their positions relative to the Rigidbody.
playerRgd.position = playerRgd.position + Vector3.forward * Time.deltaTime;
If you want to continuously move a rigidbody, use MovePosition() instead, which
takes interpolation into account. If you want to teleport a rigidbody from one position to another, with no intermediate positions being rendered.
MovePosition(Vector3 position) -- Moves the rigidbody to position.
playerRgd.MovePosition(playerRgd.position + Vector3.forward * Time.deltaTime);
控制旋转
rotation -- 和position类似,faster than Transform.rotation, 不然Collider范围发生了改变,须要
从新进行计算,因此Transform的改变会更耗费性能
MoveRotation() -- 持续转向操做就使用MoveRotation(),一次性转向操做就直接修改rotation
public Rigidbody playerRgd; public Transform enemy; private bool isRotating = false; private Vector3 dir; private float speed = 2f; void Update () { // playerRgd.position = playerRgd.position+Vector3.forward*Time.deltaTime; playerRgd.MovePosition(playerRgd.position+Vector3.forward*Time.deltaTime); if (Input.GetKeyDown(KeyCode.Space)) { isRotating = true; dir = enemy.position - playerRgd.position;// player指向enemy的向量 dir.y = 0; } if (isRotating) { // 将Vector3转换成一个四元数,并赋值) playerRgd.rotation = Quaternion.Slerp(playerRgd.rotation, Quaternion.LookRotation(dir), Time.deltaTime * speed); if (playerRgd.rotation.Equals(dir)) { isRotating = false; } } }
施加外力:
AddForce() -- 运动为加速/减速过程
注:力过小的时候也可能不运动
默认的Camera的tag="MainCamera"
实例:点击鼠标,用射线的方法判断是否点击了某个GameObject
1. 获得Camera组件
a. 查找游戏物体:GameObject.Find("MainCamera").GetComponent<Camera>();
b. 经过静态变量main获取到标签为"MainCamera"的游戏物体的Camera组件:
Camera.main;
2. 将鼠标点击转化为射线
void Update () { // 将屏幕上的点Input.mousePosition转换为一个射线 Ray ray = camera.ScreenPointToRay(Input.mousePosition); RaycastHit hit; // 检测是否碰撞到,将结果储存在hit中 bool isCollidered = Physics.Raycast(ray,out hit); if(isCollidered) { Debug.Log(hit.collider); Debug.DrawRay(ray.origin, ray.direction * 100, Color.red); } }
Application类:
datapath -- 数据路径:在不一样平台下返回值不一样
Unity Editor: <path to project folder>/Assets
Mac player: <path to player app bundle>/Contents
iOS player: <path to player app bundle>/<AppName.app>/Data
Win/Linux player: <path to exe_Data folder>/
...
persistentDataPath -- A directory path where data expected to be kept between runs can be
stored. When publishing on iOS and Android, persistentDataPath will point to a public
directory on the device. Files in this location won't be erased with each update of the
app. When you build the app, a GUID will be generated based on the Bundle Identifier,
and this GUID will be part of persistentDataPath. If you keep the same Bundle Identifier
in future versions then the app will continue accessing the same location on every
update.
streamingAssetsPath -- 流资源文件:在Project目录下新建一个StreamingAssets文件夹,该文
件夹在游戏安装好后是单独存在的,而其余文件夹下的资源会统一被打包成资源包(Most
assets in Unity are combined into the project when it is built. However, it is sometimes
useful to place files into the normal filesystem on the target machine to make them
accessible via a pathname. Any files placed in a folder called StreamingAssets in a Unity
project will be copied verbatim to a particular folder on the target machine. You can
retrieve the folder using the Application.streamingAssetsPath property.)
On MacOS or Windows: path = Application.dataPath + "/StreamingAssets"
On iOS: path = Application.dataPath + "/Raw"
On Android: path = "jar:file://" + Application.dataPath + "!/assets/"
temporaryCachePath -- 临时文件目录
print(Application.dataPath); // 工程路径 print(Application.streamingAssetsPath); // 流文件路径 print(Application.persistentDataPath); // 存储文件路径(持久化) print(Application.temporaryCachePath); // 临时文件路径
companyName:Build Settings->Player Settings中的Company Name
identifier:Android上的'package' 包名;Build Settings->Player Settings中的Bundle Identifier
installerName:安装包名
productName:游戏名Build Settings->Player Settings中的Product Name
isEditor:判断是不是在Unity Editor模式下运行
isFocused:判断用户当前是否将焦点处于该游戏
isMobilePlatform:判断当前是否在已知的移动设备上运行
isPlaying:判断是否in any kind of player or in Unity editor's playmode
platform: 返回一个RuntimePlatform类型的值,如RuntimePlatform.WindowsPlayer
某段代码只在某些特定平台下运行时可以使用
runInBackground:default is false;是否容许游戏在后台运行(没法在Android/ iOS上起做用)
version:Build Settings->Player Settings中的Version
OpenURL(string url) -- 在浏览器中打开url
Quit() -- 退出程序,注:在Editor模式下无效,须要设UnityEditor.EditorApplication.isPlayer = false;
if (Input.GetKeyDown(KeyCode.Escape)) { if (Application.isEditor) { UnityEditor.EditorApplication.isPlaying = false; } else { Application.Quit(); // 编辑器模式下无效 } }
CaptureScreenshot(string screenshotName) -- 游戏截图
已弃用,如今使用ScreenCapture.CaptureScreenshot(string)
以前加载场景的方法被放在Application中,如Application.LoadLevel(index) -- 否决的
新的类:UnityEngine.SceneManagerment.SceneManager
变量:
sceneCount -- 当前loaded的场景的总数
方法:
LoadScene() -- 加载场景
public static void LoadScene(int sceneBuildIndex, SceneManagement.LoadSceneMode mode);
public static void LoadScene(string sceneName, SceneManagement.LoadSceneMode mode);
LoadSceneMode默认值为LoadSceneMode.Single,表示销毁当前场景的游戏物体,载入新场景|
LoadSceneMode.Additive表示 Additive loads a Scene which appears in the Hierarchy window while another is active.
LoadSceneAsync() -- 异步加载场景
LoadScene()在加载场景时若是加载场景时间须要好久,则选择LoadSceneAsync()
LoadSceneAsync()会返回一个AsyncOperation类的值,存储progress、isDone等信息
能够在加载页面显示进度条、提示信息等等
CreateScene() -- Create an empty new Scene at runtime with the given name. The new Scene will be opened additively into the hierarchy alongside any existing Scenes that are currently open. For Edit-time creation, use EditorSceneManager.NewScene()
GetActiveScene() -- 返回当前场景的Scene对象
GetSceneAt(index) -- 返回该index(0~sceneCount已加载场景) 的Scene对象(能够获取该Scene的某些属性)
GetSceneByBuildIndex(index) -- 与GetSceneAt的区别在于这里的index是Build Setting中的index,而且该Scene是loaded的状态
GetSceneByName(string name)/ GetSceneByPath(string scenePath) -- 前提也是该Scene是loaded的状态
SetActiveScene(Scene scene) -- Set the scene to be active. Returns false if the Scene is not loaded yet.
The active Scene is the Scene which will be used as the target for new GameObjects instantiated by scripts.
Event:
activeSceneChanged -- 当activeScene改变时
sceneLoaded -- 当有新scene被载入时
sceneUnloaded -- 当有scene被卸载时
void Start () { SceneManager.activeSceneChanged += OnActiveSceneChanged; SceneManager.sceneLoaded += OnSceneLoaded; } private void OnActiveSceneChanged(Scene a, Scene b) { print("OnActiveSceneChanged:" + a.name + " to " + b.name); // 这里a scene的name没法获取到,由于此时a不是loaded状态 } private void OnSceneLoaded(Scene a, LoadSceneMode mode) { print("OnSceneLoaded:" + a.name + " on " + mode); // OnSceneLoaded的执行会在OnActiveSceneChanged以后 }
其余API的讲解会在Unity API经常使用方法和类详细讲解(下)中继续讲解。