Unity學習筆記--入門

2023-10-29 15:01:19

Unity引擎學習


入門:

Unity中的座標系:

左手座標系(z軸正方向向裡面)

x軸:平行螢幕向右正方向

y軸:平行螢幕向上正方向

Z軸:垂直螢幕向內為正方向

【補】openGL是右手座標系 DirectX是左手座標系

視窗:

視窗佈局Layout:

  1. Scene

    這裡我們可以看見所有的場景內容。

    視窗上的工具列:

    有關場景視窗的操作:

  2. Hierarchy(層級視窗)

    我們可以在此視窗建立或者拖入各種遊戲物件,例如模型、光源,ui等。

    在開發專案中,可以對視窗物體進行整理歸類。

    常用的快捷鍵:F2 改名字 Ctrl C V D 賦值 貼上 克隆 Delete刪除

  3. Game

    Game視窗就是我們遊戲執行時進行互動的視窗,顯示的畫面是scene場景中攝像機的物件。

  4. Project

    該視窗是存放遊戲開發資源的層級視窗。

    可以匯入的檔案型別以及格式:

    圖片:jpg,png,tga

    模型格式:fbx,max,maya

    音效:wav mp3 ogg

    文字:txt, json,bytes

    視訊:mp4

  5. Inspector

    該視窗顯示出具體的Hierarchy中具體的物體資訊。

  6. Console

    控制檯視窗,用於偵錯指令碼,列印資訊。

Inspector檢查視窗:設定遊戲具體的物件資訊

Console控制檯視窗:用於顯示偵錯資訊,報錯,警告、列印資訊等。

Scene場景資訊:顯示、操作遊戲物件的地方(場景編輯器)

Game遊戲視窗:遊戲執行時,供與玩家互動的視窗

Hierarchy層級視窗:遊戲物體的目錄

Project工程視窗:遊戲資源的資料夾

注:還有一些其它的視窗,可以在使用時候具體呼叫,Window選單選擇可以呼叫出對應視窗。

工具列和父子關係

反射機制和遊戲場景

程式在執行時候,可以檢視其它程式集或者自身的後設資料,而一個執行的程式檢視本身或者其它程式的後設資料的行為就叫反射。

例如我們在程式執行時候獲取某個程式集(資源包)中的某個指令碼(class),範例化它,呼叫其中的方法。

萬物之父:

在unity中所有的物體物件的本質是一個GameObject.

而在場景中出現的一個GameObject的物體,必定有一個transform表示物體的位置資訊,而我們是通過給GameObject中新增元件指令碼來對其進行控制。

場景的本質:

場景的本質是一個組態檔,裡面儲存對各種物件的資訊。

而場景在執行時候就是檢查物件,載入物件身上的指令碼,控制物件,這就是unity場景反射。

預設體和資源包的匯入匯出

預設體(Perfab)

預設體是一個儲存單個物件的資訊的單元。例如可以把自己設定好的(掛載著相應的元件、指令碼等資訊)一個物體拖到資料夾中自動生成該物體的預設體。預設體是一種「模板」。

預設體的修改:

  1. 在場景中顯示的預設體,直接修改,修改之後在inspector視窗中點選點選應用儲存即可應用到預設體中。

  2. 直接開啟預設體進行修改編輯。

如果指向更改物體物件,不想影響預設體,則在修改之前斷開和預製體的聯絡

資源的匯入匯出:

在project視窗中,右擊選單選擇,匯入/匯出選單

Unity指令碼

指令碼知識

1.設定指令碼IDE(Edit-->Preference開啟視窗)

2.建立指令碼

  • 類名要和指令碼名字一致(否則unity無法通過反射存取機制存取)
  • 建議不使用中文命名
  • 若想掛載到GameObjet上,類必須繼承MonoBehavior
  • 建議使用名稱空間管理

3.MonoBehavior

  • 繼承MonoBehavior之後才可以掛載物體上(unity獲取到類名通過反射找到該型別)

  • 繼承MonoBehavior之後的類 不可以new(自然不需要些建構函式)

  • 繼承MonoBehavior之後的指令碼可以在一個物體上掛載多個(如果不想讓物體繼續掛載指令碼之後,

    則在指令碼內容中新增DIsallowMultipleComponent特性)

  • 符合繼承規則,子類繼承了繼承著MonoBehavior類也可以掛載物體上

不繼承Mono

則按照普通的類來進行程式設計。不能掛載到物體上,要自己new建立,有自己的建構函式,例如儲存資訊的類。

4.指令碼的執行順序(Edit-->ProjectSetting檢視)

5.預設的指令碼內容(可以通過unity編輯器內容中更改,一般不做修改)

生命週期函數

就是一副遊戲畫面,每秒24幀以上,我們可以認為畫面流暢。unity內部已經按照一種「死迴圈」來不斷地呼叫相關地函數(生命週期函數),這些生命週期函數就是指令碼呢物件依附的GameObject物件從出生到消亡整個生命週期中會通過反射機制自動呼叫的一些特殊函數。

Awake

當物件(指令碼)被建立時候會呼叫,一個物件只會呼叫一次

OnEnable

物件每次啟用(setActive=true)時候執行

Start

只呼叫一次,在第一次幀更新之前呼叫。

區別:二者執行的時機不同,awake執行相當於建構函式,指令碼一掛載就執行

FixedUpdate

物理效果邏輯寫在此處,固定時間執行,(物理更新)

可以通過Edit-->ProjectSetting -->Time 來檢視和修改

Update

幀更新函數,每一幀執行

LateUpdate

處理攝像機相關更新

等場景渲染完畢之後,再進行攝像機跟進,避免出現「穿幫」

OnDisable

與OnEnable對應,每次失活時候執行(setActive=false)

(當然,OnDestroy呼叫之前,此函數必定呼叫,指令碼的失活~睡眠,,)

OnDestroy(指令碼的永久失活~死亡)

當依附的GameObjet物件被銷燬時候呼叫

Tips:

一個指令碼可以掛載多個物件上,掛載之後,每個物件上都有自己歸屬的一個控制指令碼。相當於理解類和物件的關係。

思考?如果一個GameObjet物件掛載著Test.cs指令碼,但是GameObjet物件初始狀態是未啟用狀態,

那麼生命週期函數執行嗎?哪個執行?

答:都不執行!

Inspector視窗顯示可編輯變數

  1. protect/private不可以顯示,public修飾變數顯示
  2. [SerializeField]序列化變數,可以使private/protect修飾變數顯示。序列化是把一個物件儲存到一一個檔案中或者是資料庫欄位中。
  3. 公共變數的預設顯示 使用[HideInInspector]修飾隱藏顯示(Inspector顯示預設的資料型別,並不是所有的都顯示,例如字典)
  4. 自定義型別資料可以顯示:新增[System.Serializable]

其它的一些特性:

public struct Enemy
{
    public int defVal;
    public void Atk(){}
}

public class Test : MonoBehaviour
{
    public Enemy enemy;
    [Header("基礎屬性")]
    public string name;
    public int age;
    [Header("戰鬥屬性")] 
    public int atk;
    public int def;
    [Tooltip("我是小提示,哈哈哈")] 
    //當滑鼠懸停時候可以顯示出提示資訊
    public string ss;
    [Space()] 
    //空格一行
    public int a2;
    [Range(0, 100)] public int value;
    [Multiline(3)] public string test;//多行顯示
    [TextArea(3, 5)] public string text2;//至少3行顯示 超過5行則顯示卷軸
    [ContextMenuItem("充值100¥","Shop")]
    //右擊顯示執行事件
    public float money = 125.35f;
    private void Shop()//無參無返回型別
    {
        money += 100f;
    }
    //指令碼選單欄中點選執行的方法
    [ContextMenu("測試函數執行")]
    private void TestFun()
    {
        print("測試函數執行了!!");
    }
}

問題:如何讓公共成員不再Inspector面板上顯示?又如何讓似有或者保護成員在一在Inspector面板上顯示呢?

答:[HindInInspector] 可以隱藏預設顯示的公共成員

[SerializeField]序列化可以使預設隱藏的變數顯示

問題:為什麼加不同的特性,在inspector視窗上會有不同的效果?

答:因為Unity中式通過反射得到類的資訊,然後在Inspector視窗中顯示欄位資訊Unity內部通過反射獲取欄位的特性,當具有某種特性時候,便會做出對應的處理。

注意掛載在GameObjet物件上的指令碼,在指令碼內部修改預設值不會影響外部的預設值(依舊是0)

外部預設值在掛載時候確定的。若要更新外部預設值為指令碼中所賦予新的數值(25),則需重新掛載指令碼。

執行中修改的數值,不會儲存!---在指令碼執行中修改數值,執行結束後數值依舊是執行前的。

MonoBehaviour

  1. 獲取掛載的GameObject ~~ this.gameobjet.name; transform.poistion;transform.localScale;
  2. 設定指令碼的啟用狀態 --this.enabled=false;

重要方法:

  1. 獲取元件 this.GetComponent("Test02")

    this.GetCompont<Test>()

  2. 獲取多個指令碼

    Test2[] array=this.GetComponents<Test2>();

  3. 找子物件掛載的指令碼(預設也會找自己身上的物件指令碼)

    this.GetComponentInChildren<Test02>();//預設自己也找

    this.GetComponentInChildren<Test02>(true);//失活的子物件也找

    Test02[] arrary02=this.GetComponentsInChildren<Test02>(true);//包含自己 所有的子物件(後輩,孫子的孫子也會)指令碼陣列

  4. 得到父物件的掛載指令碼(預設也會找自己身上的指令碼)

    this.GetComponentInParent<Test02>();

    this.GetComponentsInParent<Test02>();//獲取所有前輩的所掛載指令碼

獲取父子的多個指令碼返回值可以是List<> 也可以是陣列T[],且都是無線超級追蹤!只要是前輩就找,只要是後輩都找。

  1. 嘗試獲取的指令碼

    if(this.TryGetComponent<Test02>(out listTest)){

    //邏輯處理...

    }

Unity重要元件和API

gameObject

  1. 成員變數

    this.gameobjet.name="TonyCube";//名字

    this.gameobjet.activeSelf;//是否啟用狀態

    this.gameobjet.isStatic;//是否是是靜態

    this.gameobject.layer;//int型別的 層級編號

    this.gameobject.tag;//string 層級

    this.gameobjet.transform.poistion;//獲取transform

  2. gameobjet中的靜態方法

    obj.name = "TonyCube";
    
    //---------------查詢物件---------------------------
    //根據名字查詢 效率低 在場景中遍歷查詢 無法查詢到失活物件
    GameObject objTarget = GameObject.Find("TonyCube");
    
    //通過標籤來查詢(無法查詢到失活物件)
    GameObject objTarget1=GameObject.FindWithTag("Player");
    
    //通過標籤來查詢2 與上面方法一樣(無法查詢到失活的物件)
    GameObject objTarget2 = GameObject.FindGameObjectWithTag("Player");
    
    //查詢方法總結:
    //1. 找不到未啟用的
    //2. 如果多個相同條件的物件,隨機返回某一個 無法準確指定的找到
    
    //獲取某個物件方法目前接觸到的兩種方式
    //1. 拖拉賦值
    //2. API呼叫獲取
    
    //--------------查詢多個物件(失活找不到)------------
    
    GameObject[] objects = GameObject.FindGameObjectsWithTag("Player");
    
    //此方法是呼叫的Object中的,(unity中的Object非Unity中的Object,兩個工具包中的同命名類)
    //效率比較低
    //可以找到掛載場景物體上的Test型別指令碼
    GameObject.FindObjectOfType<Test>();
    
    //----------範例化物件方法---
    //克隆物件
     GameObject.Instantiate("PerfabDemo");
    //克隆
    Instantiate("PerfabDemo2");
    
    //----------刪除物件---------
    GameObjet.Destroy(obj);
    GameObject.Destroy(obj,5);//延時5s刪除
    //刪除此指令碼(不再掛載到物體上)
    ameObject.Destroy(this);
    //刪除物件:
    //刪除指定的遊戲物件
    //刪除指定的指令碼物件
    //注意:不會馬上移除,等下一幀進行移除。知識給物件打上要移除的標識
    //在下一幀進行時候移除,相當於非同步執行
    
    //立即移除
    GameObject.DestroyImmediate(obj);
    
    //繼承Mono的也可以直接呼叫
    Destroy(obj);
    DestroyImmediate(obj);
    
    //-----------過場景不移除-------------
    GameObject.DontDestroyOnLoad(obj);
    
    DontDestroyOnLoad(obj);//繼承的Mono的可直接呼叫
    
    1. GameObjet中的成員方法
    //--------------建立物體----------------
    GameObject obj1 = new GameObject();
    GameObject obj2 = new GameObject("TonyCube");
    //掛載Test指令碼
    GameObject obj3 = new GameObject("TonyCube",typeof(Test));
    
    //--------------給物件新增指令碼----------------
    Test test2=obj.AddComponent(typeof(Test)) as Test;
    Test test=obj1.AddComponent<Test>();
    
    //--------------獲取指令碼-------------
    Test test03=GetComponent("Test");
    
    //--------------比較標籤--------------
    if (gameObject.CompareTag("Player"))
    {
     print("物件的標籤是Player");
    }
    else
    {
     print("物件的標籤不是Player");
    }
    //-------------設定啟用失活-----------
    obj1.setActive(true);
    obj2.setActive(false);
    
    

    補:不建議使用的(效率低,耗效能)

    this.gameObject.SendMessage("ThisFun");//找到自己指令碼中的函數 執行它

    this.gameObject.BroadCastMessage("函數名");//廣播行為 讓自己以及子物件中有該函數的執行

    this.gameObject.SendMessageUpwards("函數名");//向父輩物件(包括自己)傳送訊息 並執行某函數

簡單理解:

開發遊戲專案,就是開發一些指令碼,通過指令碼控制呼叫相關的資源,產生玩家產生相關的互動,並做出對應的反應。

Time

 //時間
//時間停止
Time.timeScale = 0;
//恢復正常
Time.timeScale = 1;
//倍速
Time.timeScale = 2;
//-----最近一個幀渲染時間
print(Time.deltaTime);
//不受Scale影響的幀呼叫時間
print(Time.unscaledDeltaTime);

//遊戲開始到現在執行的時間
print(Time.time);
//不受scale影響的~
print(Time.unscaledTime);

//物理幀間隔時間
print(Time.fixedDeltaTime);
//不受scale影響的~
print(Time.fixedUnscaledDeltaTime);

//幀數
//從遊戲開始到現在已經渲染出幀的個數
print(Time.frameCount);

常用的知識:

  1. 時間縮放比例(暫停 倍速遊戲)
  2. 幀間隔時間 (計算位移相關內容)
  3. 幀數(影格同步化)

Transform

transform涉及到物體的位置、旋轉、縮放等資訊,是一個常用的座標類。

Vector3
//Vector3 
//Vector是一個白哦是三維向量的結構體
Vector3 v1=new Vector3();
v1.x=1;
v1.y=2;
v1.z=3;

Vector3 v2=new Vector3(10,10);//預設z是0
Vector3 v3=new Vector3(15,15,20);

//vector3的基本運算
Vector3 sum=v2+v3;
print(v1-v2);
print(v1*15);
print(v3/2);

//常用的
print(Vector3.zero);//(0,0,0)
print(Vector3.right);//(1,0,0)
print(Vector3.left);//(-1,0,0)
print(Vector3.forward);//(0,0,1)
print(Vector3.back);//(0,0,-1)
print(Vector3.Up);//(0,1,0)
print(Vector3.down);//(0,-1,0)

//計算兩個點之間的距離
print(Vector3.Distance(v1,v2));
poistion 位置
  1. 相對世界座標系位置資訊

    print(transform.poistion);//相對於世界座標系的

  2. 相對於父節點的座標位置資訊

print(transform.localPoistion);//相對於父節點的位置

Inspector面板上顯示的座標是相對於父物件座標。

當無父物件或父物件的座標為原點座標時,localPoistion和poistion相同。

注:位置座標不可以單獨賦值,transform.poisition.x=5;//錯誤,不被允許的,要整體把座標資訊看作一個整體。

transform.poistion=new Vector3(transform.poistion.x,15,transform.poistion.z);//只更改y數值

物件本身的朝向:(好比你自己站在原地不動,那麼你的位置座標不變,你可以轉身,扭頭,這些朝向資訊會更改)

//物件的朝向
//正前方
print(transform.foward);
//正右方
print(transform.right);
//正頂方
print(transform.up);

位移

  1. 自行實現物體的移動

transform.position += transform.forward * 1 * Time.deltaTime;

  1. API

    transform.Translate(transform.forward * 1 * Time.deltaTime,Space.World);//位移大小 相對世界座標系

    區別:世界座標系和自己座標系,自己座標系下的方向還是世界座標系的方向

    正常的移動!

    //自己座標系  自己朝向的移動  
    transform.Translate(Vector3.forward*1*Time.deltaTime,Space.Self);
    

在控制人物移動時候,注意方向和方向所處的座標系。

移動函數兩個引數的理解:

移動方向:Vector3.forward,(0,0,1) transform.forward(自己的朝向 也是在世界座標系中的朝向)

移動方向的座標系:Space.World Space.Self(預設不寫)

//搭配表示移動的方向
//1. 始終沿著世界座標系的Z正方向移動
transform.Translate(Vector3.forward*1*Time.deltaTime,Space.World);
//2.始終沿著自己的正前方(z軸方向/自己的面朝向方向)移動
transform.Translate(Vector3.forward*1*Time.deltaTime,Space.Self);
//3.移動方向不好確定 自己面朝向是在世界標系下講的,而放到自己座標系下就有些奇怪
transform.Translate(transform.forward*1*Time.deltaTime,Space.Self);
//4.自己座標系下的自己面朝向走
transform.Translate(transform.forward*1*Time.deltaTime,Space.World);
角度和旋轉:
//相對世界座標系的角度(尤拉角)
print(transform.eulerAngles);
//進行父物件角度
print(transform.localEulerAngles);
//注意:如果是控制子物體旋轉 要更改其localEulerAngles數值
//與位置一樣,只能整體賦值尤拉角,不可以單獨修改某個方向的度數
//旋轉-------------
//---自轉
//預設是自己座標系    繞y軸旋轉
transform.Rotate(new Vector3(0,10*Time.deltaTime,0));
//繞世界座標系 y軸旋轉
transform.Rotate(new Vector3(0,10*Time.deltaTime,0),Space.World);

//---預設自己座標系下旋轉
transform.Rotate(Vector3.up,10*Time.deltaTime);
transform.Rotate(Vector3.up,10*Time.deltaTime,Space.World);

//---公轉
//繞著某一點 的某個軸旋轉
//繞著 0,0,0 點的 y軸旋轉
transform.RotateAround(Vector3.zero,Vector3.up, 10*Time.deltaTime);

注意:eulerAngles範圍是0~360 無負數

縮放和看向

縮放:

//縮放 
//相對於世界座標系的大小
//只可以讀 不可以改
print(transform.lossyScale);
//相對於父物件的
print(transform.localScale);

//變大/小
transform.localScale += Vector3.one + new Vector3(1, 1, 1);
//用在update中 才會不停地面向某個物體(座標點)   
//看向 盯著 原點 
transform.LookAt(Vector3.one);
//看向一個物件
transform.LookAt(targetObj);
父子關係
 //父子關係
print(transform.parent.name); 
//設定父子關係
transform.parent = null;

//使用API來設定關係
transform.SetParent(father.transform);
//一般我們不會保留子物件相對於世界座標系的資訊
//true 則會保留在世界座標系的 位置 大小 角度資訊 在父物體座標系下 相對位置轉關係
//保持在世界座標系下相同
transform.SetParent(father.transform,false);

//父親拋棄子物件
transform.DetachChildren();
//獲取子物件
//按名字來查詢子物件
//可以找到失活的物件的
//而gameobjet中查詢的則不可以查詢未啟用的物體
print(transform.Find("兒子1"));
//不能找到孫子物體
print(transform.Find("孫子位置上的物體").name);

//遍歷兒子
//列印兒子的數目(失活的也算)
print(transform.childCount);
//通過索引號的到子物體
print(transform.GetChild(0));
//遍歷子物件
for (int i = 0; i < transform.childCount; i++)
{
    print("兒子的名子"+transform.GetChild(i).name);
}
//獲取所有的兒子
//包括父親本身 還有孫物體 (後輩)
Transform[] children = GetComponentsInChildren<Transform>();
foreach (var child in children)
{
    print(child.name);
}

//兒子的操作
//判斷某個物體是否是自己的父物件
transform.IsChildOf(father.transform);
//得到自己在兄弟姐妹中的編號
transform.GetSiblingIndex();
//將自己作為兄弟們中的老大
transform.SetAsFirstSibling();
//設定為老末
transform.SetAsLastSibling();
//設定指定的兄弟
//注意:如果超出編號範圍 不會報錯 會設定為老末
transform.SetSiblingIndex(2);

練習題:給transform寫一個拓展方法,在子物件中查某個物體,並支援在子物件的子物件中查詢,返回出Transform

public static class Tools
{
    public static Transform myFindChild(this Transform father, string name)
    {
        Transform target=null;
        //首先找第一層子物件
        target = father.Find(name);
        if (target != null)
            return target;
        //在子物件中找
        for (int i = 0; i < father.childCount; i++)
        {
            //遞迴呼叫
            target=father.GetChild(i).myFindChild(name);
            if (target != null)
                return target;
        }
        return target;
    }
}
座標轉換

座標轉換在遊戲開發中常常有所使用,這裡我們介紹點、向量在本地座標系下和世界座標系下的相互轉換的API。

我們可以將世界座標系下的某個點轉換到本地座標系下,根據位置向量的正負大致判斷該點在本地座標系下的某個方位。(敵人在主角的哪個方位)

可以使用將某個本地座標點轉換到世界座標系下,獲取它在世界座標系下的位置之後,方便產生的對應的效果。(玩家和怪物在某點激戰,產生一個武器大招的效果等等)

//--------------座標轉換-----------、
print(Vector3.forward);
//世界座標轉本地座標
//轉換點
//轉換到本地座標的數值 受到縮放的影響
print(transform.InverseTransformPoint(Vector3.forward));

//轉換方向 世界-->本地 不受縮放影響
print(transform.InverseTransformDirection(Vector3.forward));
//受到縮放的影響 
print(transform.InverseTransformVector(Vector3.forward));

//本地座標點轉換成世界座標系
//點的轉換  本地座標的點轉換到世界座標系中 受到縮放影響
print(transform.TransformPoint(Vector3.forward));

//不受縮放影響的  本地方向轉換為世界方向
print(transform.TransformDirection(Vector3.forward));
//受縮放影響的  本地方向轉換為世界方向
print(transform.TransformVector(Vector3.forward));

Inpute輸入

可以在設定中檢視輸入相關的設定(Edit-->ProjectSetting)

//螢幕座標的原點 螢幕左下角
//獲取滑鼠的位置
print(Input.mousePosition);
//檢測滑鼠按鍵 0 左鍵  1 右鍵 2中鍵
//僅僅在滑鼠按下的一瞬間 執行一次
if (Input.GetMouseButtonDown(0))
{
   print("左鍵按下");
}else if (Input.GetMouseButtonDown(1))
{
   print("右鍵按下");
}else if (Input.GetMouseButtonDown(2))
{
   print("中鍵按下");
}

//滑鼠擡起
//僅僅在擡起一瞬間執行
if (Input.GetMouseButtonUp(0))
{
   print("左鍵擡起");
}else if (Input.GetMouseButtonUp(1))
{
   print("右鍵擡起");
}else if (Input.GetMouseButtonUp(2))
{
   print("中鍵擡起");
}
//滑鼠長按按下
if (Input.GetMouseButton(0))
{
   print("滑鼠左鍵長按。。。");
}

//滑鼠中鍵捲動 y值 -1向下捲動 0未捲動  1向上捲動
print(Input.mouseScrollDelta);

//鍵盤輸入
//按下
//如果傳入字串 需要傳入小寫字元
if (Input.GetKeyDown(KeyCode.A))
{
   print("A鍵按下");
}

if (Input.GetKeyUp(KeyCode.A))
{
   print("A鍵擡起");
}

if (Input.GetKey(KeyCode.A))
{
   print("A鍵長按");
}

//------------------------------
//檢測預設軸輸入 返回float型別數值
//鍵盤AD按下時 返回-1到1之間的數值
//可以自行設定 輸入軸相關的  可以控制左右移動
print(Input.GetAxis("Horizontal"));
//鍵盤SW按下時 返回-1到1之間的數值 可以控制上下移動
print(Input.GetAxis("Vertical"));
//滑鼠移動
//左右移動 
print(Input.GetAxis("Mouse X"));
//上下移動  
print(Input.GetAxis("Mouse Y"));
//GetAxisRaw 方法不存在漸變數值 只會返回 -1 0 1僅僅是表示移動的個方向
print(Input.GetAxisRaw("Horizontal"));

其它按鍵以及陀螺儀相關

//--------------------------
//是否右任意鍵按下 或者長按
if (Input.anyKey)
{
   print("有鍵在按下ing");
}

if (Input.anyKeyDown)
{
   print("有鍵按下");
   print(Input.inputString);//按下的鍵盤名字
}

//某一個手柄的按下
if (Input.GetButtonDown("Jump"))
{

}
//某一個手柄鍵擡起
if (Input.GetButtonUp("Jump"))
{

}
//某一個手柄鍵盤長按
if (Input.GetButtonDown("Jump"))
{

}

//移動裝置觸控相關
//有觸控點
if (Input.touchCount > 0)
{
   Touch point1 = Input.touches[0];
   //位置
   print(point1.position);
   //觸控點的滑動位置
   print(point1.deltaPosition);

}
//是否啟用多點觸控
Input.multiTouchEnabled = true;

//陀螺儀(重力感應)開啟
Input.gyro.enabled = true;
//重力加速度向量
print(Input.gyro.gravity);
//旋轉速度
print(Input.gyro.rotationRate);
//陀螺儀 當前旋轉的四元數
//可以使物體根據手機旋轉角度 做出相應的旋轉
print(Input.gyro.attitude);

Screen

//常用的螢幕相關的屬性
//螢幕
//獲取當前螢幕的解析度
Resolution r = Screen.currentResolution;
Debug.Log("當前解析度寬"+r.width+",解析度的高"+r.height);
//Game視窗的寬和高 非裝置的寬和高
print(Screen.width);
print(Screen.height);

//螢幕休眠模式
//永遠不息屏
Screen.sleepTimeout = SleepTimeout.NeverSleep;

//執行時候全螢幕
Screen.fullScreen = true;
//獨佔全螢幕 
Screen.fullScreenMode = FullScreenMode.ExclusiveFullScreen;
//全螢幕視窗
Screen.fullScreenMode = FullScreenMode.FullScreenWindow;
//最大化視窗
Screen.fullScreenMode = FullScreenMode.MaximizedWindow;
//視窗模式
Screen.fullScreenMode = FullScreenMode.Windowed;
//移動裝置--------------等了解
//允許左橫向
Screen.autorotateToLandscapeLeft = true;
//允許右橫向
Screen.autorotateToLandscapeRight = true;
//允許自動轉換到縱向
Screen.autorotateToPortrait = true;
//允許自動旋轉到顛倒縱向
Screen.autorotateToPortraitUpsideDown = true;
//指定螢幕顯示方向
Screen.orientation = ScreenOrientation.Landscape;//橫屏

//靜態設定方法
//設定解析度    解析度寬  解析度高  不全螢幕
Screen.SetResolution(1920,1080,false);

Camera

面板知識

//攝像機相關
//Camera
//獲取主攝像機(注意設定攝像機tag為mainCamera)

print(Camera.main.name);
//獲取攝像機的數量
print(Camera.allCamerasCount);
//得到所有的攝像機
Camera[] allCameras = Camera.allCameras;

//攝像機相關委託
Camera.onPreCull += (c) =>
{
    //當某些被剔除時候呼叫的函數
};
Camera.onPreRender += (pre) =>
{
    //渲染前執行的函數
};
Camera.onPostRender += (post) =>
{
    //攝像機選然後處理的委託
};

//介面上的引數都可以從Camera獲取
//eg:獲取深度
print(Camera.main.depth);

//世界座標系轉螢幕座標
// x,y為螢幕座標 z表示點到攝像機的距離
Camera.main.WorldToScreenPoint(transform.position);
//螢幕座標轉世界座標
//注意對Z的處理  z到底距離攝像機右多遠近
//確保螢幕座標 投射到  距離攝像機多遠的橫切面上
Vector3 poistion = Input.mousePosition;
poistion.z = 10;
Camera.main.ScreenToWorldPoint(poistion);

核心系統

光源系統

1.光源型別

  • ​ 點光源(燈泡)
  • ​ 環境光(太陽光)
  • ​ 區域光 (烘焙時候使用,面光源)
  • ​ 聚光燈(手電筒)

2.顏色Color

3.光源模式

  • ​ 實時光源Realtime
  • ​ 烘焙光源Baked
  • ​ 混合光源Mixed
  1. 光源亮度 Intensity

  2. 陰影型別Shadow Type

    1. NoShadows 無陰影
    2. HardShadows 硬陰影
    3. SoftShadows 柔和陰影 --效果更逼真
  3. Cookie 投影遮罩

    一般使用在聚光燈下,進行陰影設定,(可以想想皮影)

  4. Draw Halo 球形光環

  5. Flare 耀斑資源(攝像機上掛載Flare指令碼才可以渲染到Game視窗中)

  6. Culling Mask (剔除遮罩層,選擇光源的影響層級)

  7. Indirect Multiplier 間接光強度 (<1 每次反彈後更暗 >1每次反彈後更亮)

  8. RealttimeShadows陰影設定

  9. Cookie Size 遮罩光大小

  10. Render Mode 渲染優先順序

物理系統

剛體

碰撞發生得必要條件:兩個物體都具有碰撞體 至少有一個物體具有剛體

  1. Mass 質量 質量越大慣性越大

  2. Drag 空氣阻力 無空氣阻力

  3. Angular Drag 根據扭矩旋轉物件時影響物件的空氣阻力大小(兩物體碰撞之後會受到一種扭矩,而扭矩阻力就是影響它旋轉的阻力

  4. Use Gravity 是否受重力影響

  5. isKinematic 開啟此項 物件不會受到物理引擎驅動 (相當於一個大山,一堵牆,物體撞擊它,物體會反彈,而山和牆壁紋絲不動)

  6. Interpolate 插值運算 讓剛體移動的更平滑(如果幀更新兼具時間過長,可以考慮使用插值運算,保證物體的運動的連貫)

    1. None 不使用插值運算
    2. Interpolate 根據前一幀的變換來平滑過度
    3. Extrapolate 插值運算 根據下一幀的預估變換來平滑插值
  7. Collision Detection 碰撞檢測演演算法

補充:子彈擊穿問題!由於子彈的速度過快,在兩幀更新的時間間隔中,子彈已經穿過物體

從而檢測不到在兩幀間隔之間產生的擊穿效果。

  1. Constraints 約束

    Freeze Poistion 凍結位置

    Freeze Rotation 凍結旋轉

碰撞器Collider

碰撞器表示物體的體積,掛有碰撞器的物體有了物理邊界,不會被其它物體穿透。

3D碰撞體的種類:盒裝 、球狀、膠囊狀、網格狀、輪胎狀、地形狀。

引數:

  1. isTragger 是否是觸發器 勾選之後無物理效果,只會用於碰撞觸發事件
  2. Material 物體材質
  3. Center 碰撞體在物件體中的中心點的位置

常用碰撞器:

  • BoxCollider size 碰撞體在x、y、z方向上大小
  • Sphere Collider Radius 碰撞體球半徑大小
  • Capsule Collider Radius(兩端半球狀半徑大小) Height(高度) Direction(軸向)

不規則形狀的物體使用碰撞體組合(主要在父物件上掛載使用碰撞體)

不常用碰撞器:

  • Mesh Collider
    • Convex 勾選之後 才可以發生碰撞 最多可以右255三角形(新增剛體後必須勾選上才可以產生碰撞效果)
    • Cooking Options
    • Mesh(網格資訊)
  • Wheel Collider(不會單獨使用,汽車加上剛體才可以給輪胎新增使用 )
  • Terrain Collider (地形系統中使用)
物理材質

根據材質來決定碰撞的具體效果。

引數:

Dynamic Friction 移動時候的摩擦力

Static Friction 靜止摩擦力

Bounciness 彈性 0不會反彈 1時候完全碰撞 無能量損失

Fiction Combine 兩個碰撞物件的摩擦力的組合方式

  • ​ Average 二者的平均值
  • ​ Minimun 二者中較小的
  • ​ Maximum 二者中較大的
  • ​ Mulitipy 二者相乘

unce Combine 兩個物體的彈性組合方式

  • ​ Average 二者的平均值
  • ​ Minimun 二者中較小的
  • ​ Maximum 二者中較大的
  • ​ Mulitipy 二者相乘
碰撞檢測函數

碰撞、觸發檢測函數也屬於生命週期函數,在FixedUpdate之後反應,呼叫之後又會執行FixedUpdate。

//碰撞觸發接觸時候執行
private void OnCollisionEnter(Collision other)
{
    print(transform.name+"和"+other.name+"碰撞接觸了");
}

//碰撞接觸完成之後執行
private void OnCollisionExit(Collision other)
{
	print(transform.name+"和"+other.name+"接觸結束了");
}

//相互接過程中呼叫
private void OnCollisionStay(Collision other)
{
	print(transform.name+"和"+other.name+"接觸中ing");
}
//注:可以從接觸到的碰撞器獲取物體的所有資訊

//觸發器函數
//注意引數是Collider
 private void OnTriggerEnter(Collider other)
 {    }
private void OnTriggerExit(Collider other)
 {    }
private void OnTriggerStay(Collider other)
 {    }
剛體新增力
//獲取剛體
rigidbody = GetComponent<Rigidbody>();
//新增力作用 (相對於世界座標系)
rigidbody.AddForce(Vector3.forward*10);
//新增力作用 相對本地座標系
rigidbody.AddRelativeForce(Vector3.forward*10);

//新增扭矩 使其旋轉(繞著y軸轉)
rigidbody.AddTorque(Vector3.up);
//相對於本地座標轉
rigidbody.AddRelativeForce(Vector3.up);

//直接改變速度 速度方向相對於世界座標系
rigidbody.velocity = Vector3.forward*5;
//模擬被爆炸衝擊力衝擊(物體受到在某位置(1,1,1)產生的爆炸力 10,爆炸半徑是10 )
rigidbody.AddExplosionForce(10,Vector3.one, 10);

//力的幾種模式
//動量定理 Ft=mv  v=Ft/m
//加速度模式 給物體新增一個持續的加速度 忽略其質量
//v = a * t
rigidbody.AddForce(Vector3.forward*10,ForceMode.Acceleration);
//力的模式  給物體新增持續一個力 考慮質量 
rigidbody.AddForce(Vector3.forward*10,ForceMode.Force);
//ForceMode.Impulse ----給物體新增一個瞬間的力 與物體的質量有關 忽略時間
//
//ForceMode.VelocityChange---給物體新增一個瞬時速度 忽略質量 

Constant Force 新增該compont之後可以持續地給該物體提供力的影響。

補:剛體的休眠,剛體會在某些狀態下進行休眠.

 //休眠喚醒
if (rigidbody.IsSleeping())
{
    rigidbody.WakeUp();
}

問:讓一個物體產生位移?

  1. Update 裡 修改transform poistion
  2. Update 裡使用Transform提供的TransLate 方法
  3. 新增剛體力 rigidBody.AddForce
  4. 剛體改變速度 rigidBody.velocity =Vector3.forward*10

音效系統

1.面板引數內容

程式碼控制音訊源:

//音訊源 
audioSource = GetComponent<AudioSource>();
//播放
audioSource.Play();
//停止
audioSource.Stop();
//暫停
audioSource.Pause();
//繼續播放
audioSource.UnPause();
//延遲5s播放
audioSource.PlayDelayed(5);

思考:如何動態的進行音效播放?

  1. 給需要播放音效的物體上掛載AudioSource指令碼控制
  2. 製作掛載AudioSource元件的預製體,需要播放音效時候範例化播放
  3. 用一個AudioSource來控制不同的音效
//3.動態載入Clip音效檔案
AudioSource audioSource=gameObject.AddComponent<AudioSource>();
audioSource.clip=clip;//掛載對應的音效片段
audioSource.Play();

麥克風輸入

語音輸入...

//麥克風
public AudioClip clip;
//裝置名字(null表示預設裝置) 超出時長之後是否覆蓋   錄製時長    取樣率
clip = Microphone.Start(null, false, 10, 44100);
Microphone.End(null);//錄製結束

對於API的學習和了解,在使用時收多檢視官方檔案,不妨是個不錯的學習方法!