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經常使用方法和類詳細講解(下)中繼續講解。