1. 程式人生 > >Unity3D學習筆記(十一):布料和協程

Unity3D學習筆記(十一):布料和協程

tro 可執行 判斷 思考 update1 iat nco game 對象

延遲函數:
動態資源加載:
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判斷是否可以移到下一個元素

技術分享圖片

技術分享圖片

協程練習: 1、塔防遊戲,5波小怪,沒波小怪間隔5秒,每波有20個小怪(間隔1秒生成1個), 3、有一個按鈕(開始遊戲), 3、按照一定的路點移動,從A點移動到B點,走到B點銷毀 AsyncOperation異步加載 YieldInstruction包含所有的異步對象 1、是否加載完成之後啟動場景 2、加載完成 3、 4、加載進度 技術分享圖片

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學習筆記(十一):布料和協程