Unity3D學習筆記(十一):布料和協程
延遲函數:
動態資源加載:
T:Resources.Load<T>(string path);
Assets - Resources,Resources是一個資源管理的工具類,預制體放在Resources 文件夾下
絕對路徑:從磁盤的根目錄開始
相對路徑:相對於Resources文件夾下的路徑,用斜杠
斜杠(除號):Unity,網址http
反斜杠:Windows資源管理文件夾
進程:雙擊一個.exe可執行應用程序,windows就會給我們開啟一個進程來運行程序。
線程:每一個進程中,最少有一個主線程(Main函數是主線程的入口),但是可以有多個輔助線程。
主線程卡死,程序會崩潰,輔線程卡死,程序不會崩潰,可以把死循環放在輔線程裏。
協程(協同程序):協程是Unity為我們提供的一個模擬多線程的工具,並不是真的多線程。
開啟協程:
StartCoroutine(string methodName);//由此就開啟一個協程,程序會進入到協程裏面執行
StartCoroutine(methodName());//第二種方式
yield return new WaitForSecond(float time);//等待n秒
yield return null或0;//等待一幀
yield return StartCoroutine();//等待此協程執行完畢
yield break;//直接跳出,結束當前協程
停止協程:
StopCoroutines();
//當使用方法名的字符串形式開啟的協程,可以用同樣的方式停止協程
//用方法名加括號開啟的協程,只能用StopAllCoroutines停止協程
StopAllCoroutines();
協程的返回值是一個叠代器對象,Unity引擎會判斷條件是否成立,如果成立再繼續執行
協程也有生命周期,類似update,每一幀Unity都會去判斷條件是否成立
IEnumerator是叠代器
Current指向第一個元素的指針,MoveNext判斷是否可以移到下一個元素
using System.Collections; using System.Collections.Generic; using UnityEngine; public class LightOnOff : MonoBehaviour {//public int lightOnTime; //public int lightOffTime; //private Light light; public GameObject directionalLight; public GameObject[] points; private int index = 0; // Use this for initialization void Start () { //思考題 //地面,燈-點光源(白,紅,藍,綠),依次亮滅 //light = GetComponent<Light>();//InvokeRepeating("LightOn", lightOnTime, 5); //InvokeRepeating("LightOff", lightOffTime, 5); directionalLight.SetActive(false); InvokeRepeating("ChangePointLightState", 1, 1); } // Update is called once per frame void Update () { } //void LightOn() //{ // Debug.Log("LightOn"); // light.range = 10; //} //void LightOff() //{ // Debug.Log("LightOff"); // light.range = 0; //} void ChangePointLightState() { for (int i = 0; i < points.Length; i++) { if (i == index) { points[i].SetActive(true); } else { points[i].SetActive(false); } } index++; index %= points.Length;//防止數組越界 } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class AttackMonster : MonoBehaviour { private int hp = 100; // Use this for initialization void Start () { InvokeRepeating("MonsterUnderAttack", 1, 1); } // Update is called once per frame void Update () { if (Input.GetMouseButton(0)) { CancelInvoke(); Debug.Log("你取消了進攻"); } } void MonsterUnderAttack() { hp -= 10; Debug.Log("怪獸被攻擊" + hp); if (hp <= 0) { CancelInvoke(); Debug.Log("怪獸被打死了"); } } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class TimeWait : MonoBehaviour { // Use this for initialization void Start () { StartCoroutine(DelayTime()); Debug.Log("等待兩秒"); } // Update is called once per frame void Update () { } IEnumerator DelayTime() { yield return new WaitForSeconds(2f); Debug.Log("兩秒到了"); } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ResourcesScript : MonoBehaviour { // Use this for initialization void Start () { //動態資源加載 GameObject cube = Resources.Load<GameObject>("Prefabs/Cube"); Instantiate(cube); //加載多個資源 //Resources.LoadAll<GameObject>(); //異步資源加載 //異步場景切換 } // Update is called once per frame void Update () { } }
using System.Collections; using System.Collections.Generic; using System.Threading; using UnityEngine; public class LessonTest : MonoBehaviour { // Use this for initialization void Start () { //開啟線程 Thread lxmThread = new Thread(Hello); //開啟協程,方法名的字符串形式 StartCoroutine("HelloWorld");//程序從此進入,遇到yield return關鍵詞又從此跳出 Debug.Log("this is start"); //開啟協程,方法名加括號形式 //StartCoroutine(HelloWorld()); //停止協程,方法名的字符串形式,被掛起的程序不會執行 StopCoroutine("HelloWorld"); //停止所有協程 StopAllCoroutines(); //停止協程,不能用方法名加括號形式 //用方法名加括號開啟協程,只能用StopAllCoroutines關閉 //StopCoroutine(HelloWorld()); } // Update is called once per frame void Update () { } void Hello() { } IEnumerator HelloWorld() { //StopAllCoroutines();//"this is before HelloWorld"仍會執行 Debug.Log("this is before HelloWorld");//程序執行到此包括之前,和執行一個普通的方法沒有任何區別 yield return new WaitForSeconds(2);//直到遇到yield return關鍵字,程序會跳出,從哪來跳哪去(程序被掛起2秒之後再執行) Debug.Log("this is after HelloWorld");//如果叠代器對象滿足條件了,程序就會再次從此處執行。 yield return new WaitForSeconds(2);//程序從Unity引擎進入,並跳回Unity引擎,不會跳到主線程的StartCoroutine Debug.Log("this is last HelloWorld"); //等最後一幀判斷 //yield return new WaitForEndOfFrame(); //等待一幀,可以開啟另一個update //yield return null; //while (true)//模擬update,卡死模擬線程, //{ // yield return null;//程序從此跳出,等一幀再進來 //} //協程的嵌套 yield return StartCoroutine("ThankYou"); Debug.Log("this is second last HelloWorld"); //等待物理幀更新 yield return new WaitForFixedUpdate(); //等待百度響應 yield return new WWW("www.baidu.com"); //WWW的資源熱更新 } IEnumerator ThankYou() { Debug.Log("this is before ThankYou"); yield return new WaitForSeconds(2); Debug.Log("this is after ThankYou"); } }
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; public class GUITest : MonoBehaviour { public Slider slider; public Texture image; public float height; public float width; public float addHeight; public float uiNormalFactor = 4;//血條縮放因子的系數,控制血條縮放大小 private float totelHealth; private Transform player; private Camera camera; private float uiScaleFactor = 1;//血條縮放因子 private float _progress;//真實進度 private float m_progress;//虛擬進度 // Use this for initialization void Start () { player = GameObject.Find("CJY").transform; camera = Camera.main; totelHealth = width; } // Update is called once per frame void Update () { //減血,血條左邊框位置固定的,從右邊減血條寬度 if (Input.GetKeyDown(KeyCode.Space)) { width -= totelHealth * 0.1f;//減剩余血量的百分之十 if (width <= 0) { Dead(); } } } private void Dead() { Destroy(gameObject); } void OnGUI() { //OnGUI在商業中只負責調試工具,實際開發用UGUI //Rect類似屏幕坐標系坐標,x指的是UI左上角離屏幕左邊框的距離 //繪制血條 //位置:屏幕坐標轉世界轉世界坐標 //Vector3 screenPosl = camera.W Vector3 targetPos = new Vector3(player.position.x, player.position.y + addHeight, player.position.z); Vector3 screenPos = camera.WorldToScreenPoint(targetPos); uiScaleFactor = 1 / screenPos.z * uiNormalFactor;//血條縮放因子,血條近大遠小,z分量去調節 //屏幕坐標轉世界坐標系,無論在攝像機前後,都換算出一個坐標 //用點乘,判斷攝像機前後位置,在後面不繪制 Vector3 cameraForward = camera.transform.forward; Vector3 camera2Player = transform.position - camera.transform.position; if (Vector3.Dot(cameraForward, camera2Player) > 0) { GUI.DrawTexture(new Rect(screenPos.x - width / 2 * uiScaleFactor, Screen.height - screenPos.y - height / 2 * uiScaleFactor, width * uiScaleFactor, height * uiScaleFactor), image); } //場景切換 //1、場景打包,Build Setting 加入場景 //2、引用命名空間,using UnityEngine.SceneManagement; //異步切換場景,場景大,加載慢,用SceneManager只有加載完畢,才會顯示場景 if (GUI.Button(new Rect(Screen.width / 2 - width / 2, Screen.height / 2 - height / 2, width, height), "開始遊戲")) { AsyncOperation oepration = SceneManager.LoadSceneAsync("GameScene"); StartCoroutine(LoadSceneAsync(oepration)); //在協程裏加載 } GUILayout.Label("加載進度: " + m_progress);//把加載進度顯示在屏幕右上角 slider.value = m_progress;//把加載進度賦值給滾動條 } IEnumerator LoadSceneAsync(AsyncOperation oepration) { oepration.allowSceneActivation = false;//自動加載場景關閉,改為手動切換 while (m_progress < 1.0) { if (_progress < 0.9f)//小於0.9f時,讓虛擬進度追趕真實進度,真實進度會卡在0.9f自動加載場景處 { _progress = oepration.progress; m_progress = Mathf.MoveTowards(m_progress, _progress, 0.01f);//顯示虛擬進度,,速度0.01f } else//大於等於0.9f時,讓虛擬進度超過真實進度,改為追趕1.0 { m_progress = Mathf.MoveTowards(m_progress, 1.0f, 0.01f); } yield return 0;//等待1幀 } yield return new WaitForSeconds(1);//等待1秒再做切換 oepration.allowSceneActivation = true; } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class FindPath : MonoBehaviour { public Transform[] paths; public Vector3 dir; public float moveSpeed = 0.1f; public float rotSpeed = 5; int index = 0; // Use this for initialization void Start () { } // Update is called once per frame void Update () { if (Vector3.Distance(paths[index].position, transform.position) <= 0.5f) { if (index == 4) { Destroy(gameObject); } index++; index %= paths.Length; } else { dir = paths[index].position - transform.position; Quaternion targetRotation = Quaternion.LookRotation(dir); transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, 1 / Quaternion.Angle(transform.rotation, targetRotation) * rotSpeed); transform.position = Vector3.Lerp(transform.position, paths[index].position, 1 / Vector3.Distance(transform.position, paths[index].position) * moveSpeed); } } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class EnemySpawn : MonoBehaviour { public float monsterRate = 1f; private int monsterNum; // Use this for initialization void Start () { } // Update is called once per frame void Update () { } IEnumerator EnemyTeamBirth() { InvokeRepeating("EnemyBirth", 1, monsterRate); yield return new WaitForSeconds(25); InvokeRepeating("EnemyBirth", 1, monsterRate); yield return new WaitForSeconds(25); InvokeRepeating("EnemyBirth", 1, monsterRate); yield return new WaitForSeconds(25); InvokeRepeating("EnemyBirth", 1, monsterRate); yield return new WaitForSeconds(25); InvokeRepeating("EnemyBirth", 1, monsterRate); } void EnemyBirth() { monsterNum++; GameObject cube = Resources.Load<GameObject>("Prefabs/Cube"); Instantiate(cube); Debug.Log(monsterNum); if (monsterNum >= 20) { CancelInvoke(); monsterNum = 0; } } void OnGUI() { if (GUI.Button(new Rect(1, 1, 100, 20), "開始遊戲")) { StartCoroutine("EnemyTeamBirth"); } } }
復習
using System.Collections; using System.Collections.Generic; using UnityEngine; public class w07d3 : MonoBehaviour { Rigidbody rig; public float force = 10; public float radius = 5; void Start() { //rig = GetComponent<Rigidbody>(); //rig.AddForce(transform.up * force, ForceMode.Impulse); /* Ft = mv Force = 0, 持續力 F = ma // // 摘要: // /// // Add an instant force impulse to the rigidbody, using its mass. // /// Impulse = 1, t = 1, F = mv // // 摘要: // /// // Add an instant velocity change to the rigidbody, ignoring its mass. // /// VelocityChange = 2, t = m = 1, F = v // // 摘要: // /// // Add a continuous acceleration to the rigidbody, ignoring its mass. // /// Acceleration = 5 m = 1 F = v/t = a */ // 射線檢測:必須要有碰撞器組件 // 相交球 //Collider[] cols = Physics.OverlapSphere(transform.position, radius, LayerMask.GetMask("Enemy", "Boss")); //foreach (var item in cols) //{ // Debug.Log(item.transform.name); //} // 開始按鈕 // 5波小怪 每波之間間隔5秒 // 每一波有20個小怪 每個小怪 間隔1秒 } private void OnDrawGizmos() { Gizmos.color = new Color(0, 1, 0, 0.3f); Gizmos.DrawSphere(transform.position, radius); } void Update1() { RaycastHit hit; Ray ray = new Ray(transform.position, transform.forward); if (Physics.Raycast(ray, out hit, 100, LayerMask.GetMask("Enemy", "Boss"))) { Debug.Log("name:" + hit.transform.name); // 檢測到的遊戲物體的位置 Debug.Log("pos :" + hit.transform.position); // 射線與碰撞框相交的點的位置 Debug.Log("hit pos :" + hit.point); //Debug.Log("distance :" + hit.distance); Vector3 dir = hit.point - ray.origin; float distance = dir.magnitude; } } }用棧作對象池存儲對象 list - LinkedListNode,存儲節點
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Pool<T> where T : class // 對泛型做限制 // 限制為 引用類型 where T : class // 限制為 值類型 where T : struct // 限制T有默認無參構造函數 where T : new() // 限制T為某類型的子類 where T : UnityEngine.Object // 限制T 繼承了某接口 where T : IEnumerable // 組合使用 通過逗號分割 組合的時候 new()寫在末尾 { Stack<T> pool = new Stack<T>(); Stack<int> pool = new Stack<int>(); // 棧 先進後出 Queue<int> queue = new Queue<int>(); // 隊列 先進先出 LinkedList<int> link = new LinkedList<int>(); void Start() { #region 棧操作 pool.Push(10); pool.Push(5); // 入棧 int result = pool.Pop(); // 彈棧 //result = pool.Pop(); result = pool.Peek(); // 訪問棧頂元素 該元素沒有彈棧 Debug.Log(result); Debug.Log(pool.Count); #endregion #region 隊列操作 queue.Enqueue(10); // 入隊 queue.Enqueue(5); int result = queue.Dequeue(); // 出隊 元素會從隊列裏移除 result = queue.Peek(); // 訪問隊頭的元素 該元素沒有出隊 Debug.Log(result); Debug.Log(queue.Count); #endregion #region 鏈表操作 link.AddLast(10); link.AddLast(9); link.AddFirst(11); #endregion } public void Push(T item) { pool.Push(item); } public T Pop() { if (pool.Count == 0) { return default(T); // 使用類型的默認值 如果T是引用類型 返回null } return pool.Pop(); } }
using System.Collections; using System.Collections.Generic; using UnityEngine; // 管理所有的對象池 對於各個類型 都需要有相應的方法 public class PoolManager : MonoBehaviour { enum EPoolType { Bullet, Enemy, } Dictionary<EPoolType, Pool<GameObject>> pools = new Dictionary<EPoolType, Pool<GameObject>>(); public GameObject bulletPrefab; public Texture t; //Pool<GameObject> bulletPool = new Pool<GameObject>(); //Pool<GameObject> enemyPool = new Pool<GameObject>(); private void Awake() { bulletPrefab = Resources.Load<GameObject>("Bullets/Bullet"); //Resources.Load("Bullet") as GameObject; t = Resources.Load<Texture>("Effects/Light"); } void Start () { // 對於對象池做初始化操作 pools.Add(EPoolType.Bullet, new Pool<GameObject>()); pools.Add(EPoolType.Enemy, new Pool<GameObject>()); } void Update () { } public GameObject SpawnBullet(Vector3 pos, Quaternion q) { GameObject bullet = pools[EPoolType.Bullet].Pop(); //GameObject bullet = bulletPool.Pop(); if(bullet == null) { bullet = Instantiate(bulletPrefab, pos, q); } else { bullet.SetActive(true); } return bullet; } public void PushBullet(GameObject bullet) { bullet.SetActive(false); //bulletPool.Push(bullet); pools[EPoolType.Bullet].Push(bullet); } }
Unity3D學習筆記(十一):布料和協程