??腳本概念
??開發(fā)工具
??腳本結(jié)構(gòu)介紹
??腳本的生命周期
??調(diào)試方法
??Unity腳本常用的API
??Unity實現(xiàn)功能的三部曲
??前言 如果你點進來了,那么恭喜你看到了一篇關(guān)于Unity中的腳本超級詳細的全面介紹?? 本篇博客來簡單介紹一下Unity組件中很重要的一種——腳本組件 的介紹 ?? 幾乎覆蓋了關(guān)于腳本內(nèi)容的大部分,希望感興趣的小伙伴可以認真看完哦?? 在這里插入圖片描述 ??腳本 首先讓我們回顧一下上一篇文章中對Unity中的腳本 的簡單介紹
腳本 在Unity中也是一種組件,在之前的組件介紹文章中已經(jīng)說過
腳本 必須要繼承MonoBehaviour,而且文件名要和類名一致(如果在Unity里更改了C#文件的名稱,在這個腳本里也要改類名),不然腳本掛不上游戲?qū)ο蟆?/p>
在這里插入圖片描述 ??腳本概念 腳本 是附加在游戲物體上用于定義游戲?qū)ο笮袨榈闹噶畲a腳本 腳本就是Unity中寫代碼的東西!(??小白提醒~)Unity支持三種高級編程語言:C# 、JavaScript 和 Boo Script (unity4以前支持的) ??開發(fā)工具 開發(fā)工具 指的是需要用來寫代碼,寫腳本 的東西!(??小白提醒~)
MonoDevelop
unity自帶的腳本編輯器,創(chuàng)建Mono應(yīng)用程序,適用于Linux、Mac OSX和Windows的集成開發(fā)環(huán)境,支持C#和JavaScript等 這款編輯器應(yīng)該是Unity5.0或者是之前的老版本使用的,目前是被淘汰了用不到 Visual Studio
微軟公司的開發(fā)工具包,包括了整個軟件生命周期中需要的大部分工具,如團隊開發(fā)工具,集成開發(fā)環(huán)境等等。 在Unity中通過菜單設(shè)置修改默認的腳本編輯器,Visual Studio 官網(wǎng)下載鏈接在這 在這里插入圖片描述 JetBrains Rider
JetBrains Rider 是一款基于 IntelliJ 平臺和 ReSharper 的跨平臺 .NET IDE。 Rider 支持 .NET 框架、新的跨平臺框架 .NET Core 和基于 Mono 的項目。 這使您可以開發(fā)廣泛的應(yīng)用程序,包括:.NET 桌面應(yīng)用程序、服務(wù)和庫、Unity 游戲、Xamarin 應(yīng)用、ASP.NET 和 ASP.NET Core web 應(yīng)用程序。 JetBrains Rider官網(wǎng)下載鏈接在這,感興趣的可以下載試試,跟VS開發(fā)差不多 ??腳本結(jié)構(gòu)介紹 在這里說一下Monobehaviour 的繼承關(guān)系 腳本—>Monobehaviour—>Behaviour—>Component—>Object using 命名空間;public class 類名:MonoBehaviour { void 方法名() { Debug.Log("調(diào)試顯示信息" ); print("本質(zhì)就是Debug.Log方法" ); } }
下面是新建一個腳本打開的樣子,Start() 方法和 Update() 方法是Unity中新建一個腳本時自動就有的方法 Start() 方法在程序開啟的第一幀執(zhí)行Update() 方法在程序的每一幀都會執(zhí)行創(chuàng)建腳本 有好幾種方法,這里簡單介紹兩種
1.Project面板右鍵-> Create-> C# Script
2.在游戲?qū)ο?/span>上點擊Add Component ,直接輸入想要創(chuàng)建的腳本名稱就可以了
這樣腳本會自動創(chuàng)建完成并且掛載到剛才點擊的游戲?qū)ο笊厦?/strong>
??腳本的生命周期 說道腳本 ,自然要談一談Unity中的腳本生命周期 啦。
因為我們所寫的代碼都要有一個執(zhí)行順序,才能有條不紊的執(zhí)行我們需要讓程序執(zhí)行的事情,聽起來還有些繞口~
類似Android中的Activity也有自己的生命周期
先來看一張挺長的圖,這張圖幾乎涵蓋了腳本的所有方法執(zhí)行的順序啦~
看完了圖是不是感覺有點慌,有的小伙伴就要說啦:這個圖的方法那么多,看著就頭大啦,看不明白~
那不要擔心,我們今天主要講的是Unity腳本生命周期的九大回調(diào) ,也是腳本中最常用到的方法了
想讓回調(diào)函數(shù)執(zhí)行有個前提,就是 腳本必須以組件的方式掛載到場景中某一個游戲?qū)ο笊砩?/span>
九大回調(diào) 說的就是就是下面圖中這九個方法啦FixedUpdate 一般用于表現(xiàn)物理效果的時候使用會比Update效果好LateUpdate 在相機腳本上使用較好,因為相機可以在Update執(zhí)行之后運行,不會出現(xiàn)畫面Bug的現(xiàn)象其中他們的執(zhí)行順序:Awake——>OnEnable——>Start——>FixedUpdate——>Update——>LateUpdate——>OnGUI——>OnDisable——>OnDestroy
如果有多個腳本中同時在Awake ()或Start ()中寫了方法,哪一個腳本中的先執(zhí)行呢? 這個問題需要考慮,不然的話代碼中會出現(xiàn)一個時效出錯,空引用之類的錯誤
這里告訴大家一個辦法,就是下面這套流程。可以在下圖面板上將腳本的先后順序添加上,程序就會按照我們設(shè)置的順序來執(zhí)行啦
如果沒有設(shè)置這個,那不同腳本先后執(zhí)行速度就是隨機 的了,不過Awake 和Start 也都是在第一幀執(zhí)行的
Edit->Project Settings->Script Execution Order ??調(diào)試方法 Unity編輯器調(diào)試 在腳本代碼中用打印方法 調(diào)試Debug.Log() 或者print() 進行打印log調(diào)試
使用Visual Studio調(diào)試 調(diào)試步驟:
在這里插入圖片描述 也可以在調(diào)試時右鍵–>快速監(jiān)視 ,在快速監(jiān)視面板便捷的調(diào)試和查看數(shù)據(jù)。還可以在即時窗口輸入代碼進行調(diào)試
因為Update 和其他方法不同,它是逐幀運行的,所以在調(diào)試時需要單幀調(diào)試
步驟:啟動調(diào)試->運行場景->暫停游戲->加斷點->單幀執(zhí)行->結(jié)束 在這里插入圖片描述 ??Unity腳本常用的API 閱讀編程資料時經(jīng)常會看到API 這個名詞,網(wǎng)上各種高大上的解釋估計放倒了一批初學者。
API (Application Programming Interface,應(yīng)用程序編程接口)是一些預(yù)先定義的函數(shù),目的是提供應(yīng)用程序與開發(fā)人員基于某軟件或硬件得以訪問一組例程的能力,而又無需訪問源碼,或理解內(nèi)部工作機制的細節(jié)?!俣劝倏?/p>
網(wǎng)上關(guān)于API 的介紹有一大堆,大多數(shù)還舉了例子介紹
其實把API 看成一個使用文檔 就得了~哪來這么多事
既然介紹完了腳本,那就順帶介紹幾種腳本常用的API 吧,可以先來看一下Unity的核心類圖 (圖片上網(wǎng)上我能找到的最清晰的了,雖然還是有些模糊~)
GameObject(游戲?qū)ο? GameObject 游戲?qū)ο螅?span style="font-size:18px;color:#43cd80;">GameObject提供了添加、設(shè)置游戲?qū)ο?,查找、銷毀游戲?qū)ο蟮墓δ?下圖是GameObject 的屬性參數(shù)。 下面使用代碼示例來學習一下
void Start ( ) { Debug.Log(gameObject.activeSelf);//當前游戲?qū)ο蟮募せ顮顟B(tài) gameObject.SetActive(false );//設(shè)置當前對象的激活狀態(tài) gameObject.SetActive(!gameObject.activeSelf);//設(shè)置當前游戲?qū)ο蟮募せ顮顟B(tài)為當前狀態(tài)的反向狀態(tài) Debug.Log(gameObject.name);//獲取當前對象名字 Debug.Log(gameObject.tag);//獲取當前對象標簽 Debug.Log(gameObject.layer);//獲取當前對象層 Light mylight = gameObject.GetComponent(type:"Light" ) as Light; mylight = gameObject.GetComponent(typeof (Light))as Light; mylight = gameObject.GetComponent<Light>();//獲取當前游戲?qū)ο蟮慕M件 mylight = gameObject.AddComponent<Light>();//添加一個組件到游戲?qū)ο笊砩?,并返回這個組件 --- GameObject lt = GameObject.Find("Directional Light" );//通過名字找到單個游戲?qū)ο?/span> GameObject com = GameObject.FindWithTag("Player" );//通過標簽找到單個游戲?qū)ο?/span> com = GameObect.FindGameObjectWithTag("MainCanmer" ); GameObject[] coms = GameObject.FindGameObjectWithTag(MainCanmer);//通過標簽找到多個游戲?qū)ο?/span> }
特別要注意的是不同通過Find找到處于未激活狀態(tài)的對象 Inspector 面板上這里未勾選 就是未激活的意思,相應(yīng)的在Hierarcht 面板上的游戲?qū)ο缶蜁兂?strong style="font-weight: bold;color: black;">灰色。
Component(組件) Component 就是組件,在上篇博客已經(jīng)介紹了,這里再重新介紹下腳本API的使用吧
添加、獲取和銷毀組件的方法添加組件
GameObject Cube= GameObject.CreatePrimitive(PrimitiveType.Cube);//創(chuàng)建一個方格 Cube.AddComponent<BoxCollider>();//添加盒形碰撞器組件 Cube.AddComponent<Rigidbody>();//添加剛體組件 Cube.AddComponent<Test>();//添加Test腳本
獲取組件
BoxCollider boxCollider = Cube.GetComponent<BoxCollider>();//獲取盒形碰撞器組件 Rigidbody rigidbody = Cube.GetComponent<Rigidbody>();//獲取剛體組件 Test test= Cube.GetComponent<Test>();//獲取某個Test腳本 GetComponent GetComponents GetComponentInChildren GetComponentsInChildren GetComponentInParent GetComponentsInParent
銷毀組件
//()中的參數(shù)為創(chuàng)建相應(yīng)組件時的組件名稱 Destroy(boxCollider );//銷毀盒形碰撞器組件 Destroy(rigidbody);//銷毀剛體組件
Transform Transform 組件在上篇介紹組件的博客也說過了,這里在提一下在腳本使用的API 下面來用代碼示例學一下Transform 的使用
void Start ( ) {//1.控制游戲?qū)ο蟮淖儞Q Debug.Log(transform.position);//世界坐標 Debug.Log(ransform.localPostion);//本地坐標 Debug.Log(transform.eulerAngles);//世界歐拉角 Debug.Log(transform.localEulerAngles);//本地歐拉角 Debug.Log(transform.rotation);//世界旋轉(zhuǎn)(四元數(shù)) Debug.Log(transform.localRotation);//本地旋轉(zhuǎn)(四元數(shù)) //本地縮放 Debug.Log(transform.localScale); //方向 //自身前方的方向向量 Debug.Log(transform.forward); //自身右方的方向向量 Debug.Log(transform.right);//自身上方的方向向量 Debug.Log(transform.up);//描述游戲?qū)ο蟮膶蛹夑P(guān)系 //父對象 Debug.Log(transform.parent); //根對象 Debug.Log(transform.root);//設(shè)置父物體 transform.parent = lookAtTarget;//==> transform.SetParent(lookAtTarget); //遍歷所有的子對象 foreach (Transform tra in transform) { Debug.Log(tra); }//遍歷所有的子對象 for (int i = 0 ; i < transform.childCount; i++) { Debug.Log(transform.GetChild(i)); } //找當前對象的子對象 Transform sph = transform.Find("Cylinder/Sphere" ); Debug.Log(sph); }private void Update ( ) { //角色朝自身前方移動 // transform.position += transform.forward * 0.05f; // transform.Translate(transform.forward * 0.05f); //自轉(zhuǎn) transform.Rotate(new Vector3(0 ,1 ,0 ),2 ,Space.World)// ==> transform.eulerAngles += new Vector3(0 ,1 ,0 ); transform.Rotate(new Vector3(0 ,1 ,0 ),2 ,Space.Self);//==> transform.localEulerAngles += new Vector3(0 ,1 ,0 );//繞某個點沿某個軸,旋轉(zhuǎn) transform.RotateAround(new Vector3(0 , 0 , 0 ), new Vector3(0 , 1 , 0 ), 5 ); transform.LookAt(new Vector3(0 ,0 ,0 )); transform.LookAt(lookAtTarget);//看向某個對象
Vector3(三維向量) Vector3向量 ,在三維坐標系中帶有方向和大小的數(shù)據(jù),下圖介紹了Vector3 的屬性參數(shù) 下面來用代碼示例學一下 Vector3 的使用
//創(chuàng)建一個三維向量 Vector3 dir = new Vector3(1 ,2 ,3 ); //創(chuàng)建一個二維向量 Vector2 dir2 = new Vector2(3 ,3 ); //創(chuàng)建一個四維向量 Vector4 dir4 = new Vector4(1 ,2 ,3 ,4 ); //獲取一個向量的單位向量 Vector3 normalDir = dir.normalized;//將當前向量變成單位向量 dir.Normalize(); //向量的長度【?!?/span> float mag = dir.magnitude; //模的平方【用來做向量長度的對比】 float sqrMag = dir.sqrMagnitude; // Vector3.forward // Vector3.back // Vector3.left // Vector3.right // Vector3.up // Vector3.down // Vector3.zero // Vector3.one Vector3 pointA = Vector3.forward; Vector3 pointB = Vector3.right; //求兩個坐標的距離 float dis = Vector3.Distance(pointA, pointB); Vector3 dirA = Vector3.one; Vector3 dirB = Vector3.right; //求兩個向量的夾角 float angle = Vector3.Angle(dirB, dirA); float newAngle = Vector3.Angle(new Vector3(1 , 1 , 0 ), Vector3.zero); //求兩個向量的點乘 float dot = Vector3.Dot(dirA, dirB); //求兩個向量的叉乘【求兩個向量的法向量】 Vector3 normal = Vector3.Cross(dirA, dirB); //求一個向量在某個方向上的投影向量 Vector3 pro = Vector3.Project(new Vector3(1 , 1 , 0 ), Vector3.right); //求兩個向量中間的插值坐標 Vector3.Lerp(new Vector3(1 , 0 , 0 ), new Vector3(5 , 0 , 0 ), 0.5f );//由快到慢運動過去 transform.position = Vector3.Lerp(transform.position, new Vector3(5 , 0 , 0 ), 0.03f );
Quaternion(四元數(shù)旋轉(zhuǎn)) Quaternion四元數(shù) 本質(zhì)上是一種高階復數(shù),是一個四維空間,相對于復數(shù)的二維空間。在Unity里,tranform組件有一個變量名為Rotation ,它的類型就是四元數(shù) 。用于在Unity中控制旋轉(zhuǎn)的一種方式
下面來用代碼示例學一下 Quaternion 的使用
public class ReadQuaternion : MonoBehaviour { public float turnSpeed = 3f ; public Transform target; void Start ( ) { //空旋轉(zhuǎn)【相當于歐拉角的(0,0,0)】 Debug.Log(Quaternion.identity); //將當前角色的旋轉(zhuǎn)設(shè)置空旋轉(zhuǎn) transform.rotation = Quaternion.identity; } void Update ( ) { // transform.LookAt(target); //插值旋轉(zhuǎn) // transform.rotation 起點四元數(shù) //玩家指向敵人的方向向量 Vector3 dir = target.position - transform.position; //目標四元數(shù) Quaternion targetQua = Quaternion.LookRotation(dir); // transform.position = Vector3.Lerp(transform.position,Vector3.right*5,0.02f); //插值轉(zhuǎn)身 transform.rotation = Quaternion.Lerp(transform.rotation, targetQua, 0.02f * turnSpeed); } }
Time(時間) 首先介紹兩個概念:現(xiàn)實時間 和游戲時間 大多數(shù)Time 類都是依賴于游戲時間的?,F(xiàn)實時間也就是不依賴于程序內(nèi)部,就算程序暫停也會繼續(xù)計算的真實時間,而游戲時間是基于程序內(nèi)部的,可以自行調(diào)整。我們這里用的都是游戲時間 下面來用代碼示例學一下 Time 類的使用
public class ReadTimeAndMathf : MonoBehaviour { //時間縮放 public float timeScale = 1f ; public float moveSpeed = 2 ; private void FixedUpdate ( ) { Debug.Log(Time.fixedDeltaTime); transform.position += Vector3.forward * Time.fixedDeltaTime * moveSpeed; } void Update ( ) { // Debug.Log(Time.time);//游戲過來多長時間 Debug.Log(Time.deltaTime);//每幀的時間間隔 //調(diào)整時間縮放 Time.timeScale = timeScale; // transform.position += Vector3.forward * Time.deltaTime * moveSpeed; // transform.position = Vector3.Lerp(transform.position,Vector3.forward * 5 ,Time.deltaTime * moveSpeed); } }
Input鍵盤輸入方法 在游戲中我們經(jīng)常要用到按某個鍵來執(zhí)行某件事,就比如按A鍵開炮,空格鍵跳躍等等。下面就來簡單介紹一下怎樣使用 鍵盤輸入方法
public class SimplePlayerMove : MonoBehaviour { [Header("炮彈" ) ] public GameObject bullet; public float moveSpeed = 3f ; public float turnSpeed = 3f ; private float hor, ver; void OldUpdate ( ) {bool downA = Input.GetKeyDown(KeyCode.A); if (Input.GetKeyDown(KeyCode.A)) { Debug.Log("按下了A鍵" ); } if (Input.GetKeyUp(KeyCode.A)) { Debug.Log("松開了A鍵" ); } if (Input.GetKey(KeyCode.Space)) { Debug.Log("按住了空格鍵" ); } if (Input.GetKey(KeyCode.W))//前進 { transform.position +=transform.forward * Time.deltaTime * moveSpeed; }if (Input.GetKey(KeyCode.S))//后退 { transform.position -=transform.forward * Time.deltaTime * moveSpeed; } if (Input.GetKey(KeyCode.A))//左轉(zhuǎn) { transform.eulerAngles -= Vector3.up * turnSpeed; } if (Input.GetKey(KeyCode.D))//右轉(zhuǎn) { transform.eulerAngles += Vector3.up * turnSpeed; } } ----void Update ( ) { hor = Input.GetAxis("Horizontal" ); ver = Input.GetAxis("Vertical" );//transform.position += new Vector3(hor, 0, ver) *Time.deltaTime * moveSpeed; //前后移動 transform.position += ver * transform.forward * Time.deltaTime * moveSpeed; //左右轉(zhuǎn)身 transform.eulerAngles += hor * Vector3.up * turnSpeed; } }
Input鼠標輸入方法 說完了鍵盤輸入,自然還有鼠標輸入啦,那下面就來介紹一下 鼠標輸入方法
void KeyUpdate ( ) { if (Input.GetMouseButtonDown(0 )) { Debug.Log("按下了鼠標左鍵" ); } if (Input.GetMouseButtonUp(0 )) { Debug.Log("松開了鼠標左鍵" ); } if (Input.GetMouseButton(0 )) { Debug.Log("按住了鼠標左鍵" ); } } ---void Update ( ) {float hor = Input.GetAxis("Horizontal" );//Debug。Log( if (Input.GetButtonDown("Fire")); { Debug.Log("按住了開火鍵" ); // GameObject crtPlayer = Instantiate(playerPrefab); GameObject crtPlayer = Instantiate(playerPrefab, Vector3.forward, Quaternion.identity); }) if (Input.GetButtonDown("Fire" )) { Debug.Log("按住了開火鍵" ); // GameObject crtPlayer = Instantiate(playerPrefab); GameObject crtPlayer = Instantiate(playerPrefab, Vector3.forward, Quaternion.identity); } }
??Unity實現(xiàn)功能的三部曲 設(shè)置組件的屬性,或調(diào)用組件提供的方法,從而實現(xiàn)功能 注意 當程序出現(xiàn)異常,程序會自動暫停,后邊的代碼就不會執(zhí)行了 在這里插入圖片描述 ??總結(jié) 好了,關(guān)于腳本方面呢我們已經(jīng)全部介紹完了,最后還簡單提了一下Unity實現(xiàn)功能的三部曲 大家都學會了吧,不用順著網(wǎng)線來咬我了~ 其實整個游戲程序?qū)崿F(xiàn)功能的思路就是這樣,先找到這個游戲?qū)ο?,然后利用腳本寫代碼讓他做相應(yīng)的事情。 所有的游戲?qū)ο笥袟l不紊的執(zhí)行他們該干的事情,那么一個游戲程序也就自然而然的運行起來了 如果你是從之前寫的幾篇博客完整看到這里,那么我之前寫的那篇坦克大戰(zhàn)的小游戲也就可以自己做了 Unity中從倔強青銅上榮耀王者,看完這篇是不是有手就行~
不過關(guān)于Unity中UI方面還沒有介紹,大家可以在網(wǎng)上搜一下大佬的文章看看呀,后續(xù)有時間會更UI方面的知識點的~ 心動不如行動,還不趕緊一鍵三連,然后收藏夾吃灰?
祝大家早日上榮耀王者!
下篇再見啦~さようなら