2.3 窗口布局
-
Unity默認窗口布局
- Hierarchy 層級窗口
- Scene 場景窗口,3D視圖窗口
- Game 游戲播放窗口
- Inspector 檢查器窗口,屬性窗口
- Project 項目窗口
- Console 控制臺窗口
-
恢復默認布局 Window | Layouts | Default
-
調大頁面字體 Preference | UI Scaling
3.1 場景
新項目默認創(chuàng)建了 SampleScene 場景 {攝像機,平行光}
3.2 游戲物體
SampleScene 里 {攝像機,平行光} 就是兩個游戲物體
添加物體
- GameObject 下拉菜單
- Hierarchy 窗口 右鍵菜單
選中物體(橙色輪廓)(Inspector顯示該物體組件屬性)
- Scene 窗口選中
- Hierarchy 窗口選中 (物體重疊時)
重命名、刪除物體
- Hierarchy 窗口選中右鍵菜單 Rename | Delete
移動物體
- Move Tool
3.3 ?3D視圖
視圖內容
- Gizmo 導航器 :表示世界坐標方向
- Grid 柵格 : 表示 XZ 坐標平面(可隱藏、配置)
- 柵格1格長度代表1個單位,尺寸單位約定為1米
- Skybox 天空盒(可隱藏)
視圖操作
- 旋轉 ALT + LMB
- 縮放 鼠標滾輪、ALT + RMB(精細)
- 平移 MMB
導航器操作 Gizmo
- 恢復y軸方向:SHIFT+點擊小方塊
- 頂視圖:點擊任意軸 (小方塊右鍵菜單)
3.4 世界坐標系
左手坐標系,當x軸向右,y軸向上,z軸向里
3.5 ?視圖中心
視圖旋轉默認按視圖中心點旋轉
- 繞一個物體旋轉,需要選中物體后按 F 鍵(層級窗口雙擊物體),視圖中心設置為該物體坐標原點,然后 ALT + LMB 旋轉
- 添加一個新物體,物體位于視圖中心,而不是 {0,0,0}
3.6 透視與正交
- Perspective 透視視圖
- 物體近大遠小
- 透視畸(ji)變:圓球在角落看起來像橢圓
- 調小Field of view(廣角設定)減少畸變
- Orthographic 正交視圖(Isometric 等距視圖)
- 物體大小與距離無關
- 常用于物體的布局、對齊操作
4.2 ?物體操作
可以在 Inspector 窗口拖動 X Y Z
- Move tool 移動工具(W):沿著坐標軸、坐標平面移動
- Rotate tool 旋轉工具(E)
- 朝XYZ軸方向旋轉,逆時針為正,順時針為負。反之相反
- 按住 CTRL 旋轉,按 15 度增量旋轉(可修改)
- Scale tool 縮放工具(R):沿著軸向、整體縮放
操作模式
- Pivot 軸心 | Center 中心點
- Global 世界坐標系 | Local 局部坐標系
更多操作
- 多選(層級窗口,視圖窗口鼠標拉框)、復制(CTRL + D)
- 激活 Active :檢查器里第一個勾選項
嘗試小插件
主要涉及單c#文件插件(切換視圖快捷功能)的安裝與使用
- 拖拽進入資源窗口后自動編譯
- AF插件:G 鍵的視圖中心與F鍵不同,不會放大框顯
5.1 ?網(wǎng)格
Mesh,存儲了模型的形狀數(shù)據(jù)
- 模型形狀由若干個小面圍合而成,內部都是中空的
- Mesh 中包含了 面、頂點坐標、面的法向 等數(shù)據(jù)
Unity中觀察模型網(wǎng)格(場景窗口右側欄,2D按鈕左邊)
- shaded 著色模式,顯示表面材質
- wireframe 線框
- shaded wireframe 線框著色(兩個都顯示)
高模:面數(shù)越多,物體表面越精細,GPU負擔也就越重
mesh filter 組件定義網(wǎng)格數(shù)據(jù)
5.2 ?材質
Material 定義物體的表面細節(jié)(顏色,金屬,透明,凹陷,突起)
創(chuàng)建、使用材質
- 在資源目錄下創(chuàng)建 Material
- 修改阿貝多albedo為藍色(反射率)
- 選中物體,把材質拖到物體上
mesh renderer 組件負責渲染,使用材質相當于修改該組件的 Materials 字段,可直接拖動材質到該字段或打開材質瀏覽器。 (檢查器窗口右上角可鎖定)
5.3 ?紋理(貼圖)
用一張圖定義物體的表面顏色。模型每個面有不同顏色,與貼圖映射,在建模軟件里完成
將貼圖拖動至 albedo,可以看到疊加的效果(反射率改為白色),按 BackSpace 清掉貼圖
建模師提供的模型,本身已經(jīng)帶了網(wǎng)格、表面材質、材質貼圖
5.5 ?更多細節(jié)
- Unity 平面(plane)是沒有厚度的;正面可見,背面透明;從正方體從內部觀察,六個面都是透明的
- 添加物體默認都是有材質的 Default-Material (引擎內部自帶),呈現(xiàn)紫紅色說明沒材質
5.6 ?FBX
模型資源
FBX模型一般包含 mesh(定義形狀),material(定義光學特性),texture(定義表面像素顏色),有的模型可能定義多個材質。將FBX模型拖動至窗口中生成對象(FBX本身也是一種預制體)
貼圖文件的路徑是約定好的,與fbx相同目錄,或者同級 Textures 目錄
材質替換(重映射)
- 在檢查器窗口找到材質屬性 | Use Embeded Materials | On Demand Remap
- 使用外部材質:材質屬性 | Location | Use External Materials | 修改解壓的材質
分解重組
- FBX里的Mesh單獨拖出生成對象,然后給定材質(也可從FBX單獨拖出)
6.1 ?資源文件
復制資源 CTRL + D
- 模型文件 .fbx
- 圖片文件 .jpg、.png、.psd、.tif
- 音頻文件 .mp3、.wav、.aiff
- 腳本文件 .cs
- 材質文件 .mat
- 場景文件 .unity (記錄物體檢查器數(shù)據(jù))(1個場景等于1個關卡)
- 描述文件 .meta (每個文件都有)
除此之外,可將選擇的文件導出成資源包 .unitypackage ,導出時可把依賴文件一并導出。再通過 .unitypackage 導入 (整個Assets目錄也可以導出)
7.1 軸心、幾何中心
Pivot 物體操縱基準點,可以在任意位置,軸心點是在建模軟件中指定的,可以用空物體當父節(jié)點修改原軸心
Center 幾何中心點,一個物體繞中心點旋轉(炮塔例子)。多個物體則是物體合體之后的中心點
7.2 ?父子關系
在 Hierarchy 窗口呈現(xiàn)兩個物體之間的關系(拖物體B到物體A下)
- 子物體會隨著父物體移動旋轉(子物體相對坐標不會變化)
- 刪除父物體,子物體一并刪除
相對坐標:子物體坐標相對于父物體(子物體坐標等于相對坐標+父節(jié)點坐標)
7.3 空物體
- Create Empty
- 場景內不可見(無網(wǎng)格信息),但有transform組件
- 用于節(jié)點的組織、管理(武器站 + 炮塔)(修改軸心);標記位置
7.4 ?Global、Local
- Global,世界坐標系:上下、東西、南北
- Local,本地坐標系:上下、前后、左右 (物體自身為軸)(小車沿車頭前進)
- y 軸 up、z 軸 forward (模型正臉方向與z軸方向一般一致)、x軸 right
8.1 ?組件
物體節(jié)點可綁定多個組件(component ),一個組件代表一個功能
如 Mesh Filter 網(wǎng)格過濾器(加載Mesh);Mesh Renderer 網(wǎng)格渲染器(渲染Mesh)
Transform 所有物體都有、不能被刪除(基礎組件)
- 位置(相對位置);旋轉(歐拉角);縮放
8.5 ?攝像機
- Z軸為拍攝方向
- 擺放攝像機:選中節(jié)點 | GameObject | Align with View 對齊視角(CTRL+SHIFT+F),將攝像機視角變?yōu)楫斍皥鼍按翱谝暯?/strong>
9.1 ?腳本
腳本組件
腳本組件,游戲驅動邏輯,類名和文件名需要一致。編譯過程是自動的
只有掛載腳本才能被調用:物體節(jié)點添加組件、拖動到檢查器窗口下面
腳本類繼承自 MonoBehaviour
獲取物體
- this 當前腳本組件對象
- this.gameObject 當前物體
- this.gameObject.name 當前物體名字(利用獲取到的物體對象獲取其他屬性)
- this.gameObject.transform 獲取 transform 組件(簡化為this.transform)
GameObject obj = this.gameObject;
string objName = obj.name;
Debug.Log(objName);
Transform tr = this.transform; // this.gameObject.transform
Vector3 pos = tr.position;
Debug.Log(pos);
物體坐標
一般常使用 localPosition ,與檢查器中的值一致
- 世界坐標值 this.transform.position
- 本地坐標值 this.transform.localPosition
修改本地坐標
this.transform.localPosition = new Vector3(0f, 0f, 3.5f);
移動物體
建議先看 10.1 幀更新
不使用 Time.deltaTime 來移動物體是不勻速的(因為時間增量不同)
正確移動方法是 速度 * 時間(每秒走固定米數(shù),每幀移動距離不同)
void Update()
{
Vector3 pos = this.transform.localPosition;
pos.z += Time.deltaTime * 10f;
this.transform.localPosition = pos;
}
9.4 播放模式
- 編輯模式
- 播放(運行)模式:更改不保存,相當于實時調試,實時修改參數(shù)并生效
- 把修改好參數(shù)的組件 | Copy Component | 退出播放 | Paste Component Values
10.1?幀更新
- Frame 游戲幀
- FrameRate 幀率/刷新率
- FPS(Frames Per Second) 每秒更新多少幀
Update(幀更新):每幀調用一次
- Time.time 游戲時間(游戲啟動后開始計時)
- Time.deltaTime 距上次幀更新的時間差(時間增量)
Unity 不支持固定幀率,但可以設置一個近似幀率 Application.targetFrameRate = 60;
11.1?物體運動
物體移動
使用 transform.Translate(dx,dy,dz) 實現(xiàn)相對運動(參數(shù)是坐標增量)
可以添加第四個參數(shù)即 transform.Translate(dx,dy,dz,space)
- Space.World 世界坐標系
- Space.Self 本地坐標系(默認)(更常用)
物體方向
GameObject.Find("Sphere") 根據(jù)名字、路徑查找物體
this.transform.LookAt(flag.transform) 將物體 Z 軸轉向某一位置,然后每幀沿著 forward 方向按 2m/s 速度前進
void Start()
{
GameObject flag = GameObject.Find("Sphere");
this.transform.LookAt(flag.transform);
}
void Update()
{
float speed = 2f;
float distance = speed * Time.deltaTime;
this.transform.Translate(0,0,distance,Space.Self);
}
兩物體間距
Vector3 的 magnitude 屬性表示向量長度
Vector3 p1 = this.transform.position;
Vector3 p2 = flag.transform.position;
Vector3 p = p2 - p1;
float distance = p.magnitude;
物體移動到另一物體停止移動
private GameObject flag;
void Start()
{
flag = GameObject.Find("Sphere");
this.transform.LookAt(flag.transform);
}
void Update()
{
Vector3 p1 = this.transform.position;
Vector3 p2 = flag.transform.position;
Vector3 p = p2 - p1;
float distance = p.magnitude;
if (distance > 0.3f)
{
float speed = 2f;
float dis = speed * Time.deltaTime;
this.transform.Translate(0,0,dis,Space.Self);
}
}
攝像機跟隨物體
選擇物體,Edit | Lock View to Selected (SHIFT + F)
12.1?物體旋轉
Quaternion 四元組
transform.rotation 不便操作,不建議使用
Euler Angle 歐拉角
- transform.eulerAngles
- transform.LocalEulerAngles
this.transform.localEulerAngles = new Vector3(0, 30, 0);
Vector3 angles = this.transform.localEulerAngles;
angles.y += 30 * Time.deltaTime;
this.transform.localEulerAngles = angles;
? transform.Rotate(dx,dy,dz,space) 與 Translate 使用方式類似
? 實現(xiàn)公轉:父物體帶動子物體旋轉
13.1 腳本運行
場景加載過程(框架自動完成)
- 創(chuàng)建節(jié)點(游戲物體)
- 實例化各個節(jié)點的組件(包括腳本組件)| 等同 new 類()
- 調用各個組件事件函數(shù)
13.2 消息函數(shù)
屬于 MonoBehaviour (統(tǒng)一行為特性類)的消息函數(shù)(事件函數(shù)、回調函數(shù))
已被禁用的物體 Start / Update 不會被調用,Awake / Start 方法只會被執(zhí)行一次
- Awake 第一階段初始化(總是會被調用)
- Start 第二階段初始化(組件被禁用不調用)
- Update 幀更新
- OnEnable 當組件啟用時調用
- OnDisable 當組件禁用時調用
?? Awake 總被調用是根據(jù)當前腳本組件的啟用、禁用狀態(tài)來說的,而不是根據(jù)整個物體節(jié)點的生效、不生效狀態(tài),物體節(jié)點如果不生效 Awake 不會被調用
另外,如果腳本組件只有一個 Awake 方法,那么腳本組件的啟用、禁用也就沒有意義,Unity不為它添加勾選框
13.3 腳本執(zhí)行順序
- 第一階段初始化(所有腳本)
- 第二階段初始化(所有腳本)
- 幀更新(所有腳本)
腳本執(zhí)行順序與層級擺放順序無關系,默認所有腳本的執(zhí)行優(yōu)先級為 0
- 選中腳本,打開 Execution Order 對話框
- 添加腳本,值越小,優(yōu)先級越高
13.4 主控腳本
一個空節(jié)點掛載游戲全局設置的腳本(高優(yōu)先級)
14.1?參數(shù)與特性
公有類成員變量:讓開發(fā)者自定義參數(shù)從而控制腳本組件功能
添加特性,為參數(shù)添加編輯器提示
[Tooltip("這個是Y軸的角速度")]
14.2 初始化順序
檢查器參數(shù)、Awake、Start 都對某一參數(shù)初始化時的調用順序
- 腳本組件實例化(檢查器參數(shù))(默認值)
- Awake 修改了參數(shù)
- Start 修改了參數(shù)(最終的值)
個人認為可以在 Awake、Start 對參數(shù)進行驗證操作
14.3 值類型
基類類型、Vector3、Color 都是結構體值類型
值類型特點
- 直接賦值
- 沒值,則默認 0
- 可空值類型可為 null(個人測試,該類型不顯示在檢查器上)
14.4 引用類型
節(jié)點、組件、資源、數(shù)組類型
public GameObject flag;
15.1?輸入
鼠標輸入
在幀更新方法添加,前兩方法針對一次鼠標事件只會True一次(成對關系),第三個方法多次True
兩個鼠標事件是全局的,腳本之間互不影響
-
Input.GetMouseButtonDown(int) 按下事件
- 0 左鍵、1 右鍵、2 中鍵
- Input.GetMounseButtonUp(int) 抬起事件
- Input.GetMouseButton(int) 鼠標狀態(tài),表示當前鍵否正在被按下
屏幕坐標
獲取屏幕長寬
private void Start()
{
int width = Screen.width;
int height = Screen.height;
Debug.Log($"{width} , {height}");
}
獲取屏幕坐標
Input.mousePosition 僅X、Y有值,屏幕左下角為原點,單位為像素
if (Input.GetMouseButtonDown(0))
{
Vector3 mousePos = Input.mousePosition;
Debug.Log(mousePos);
}
物體世界坐標轉屏幕坐標
用于判斷物體是否超出屏幕范圍(出界是物體軸心點出界,是能看到物體剩余部分的)
Camera.main.WorldToScreenPoint(pos)
X,Y是物體在屏幕的哪個位置,Z是物體距離攝像機的距離
Vector3 pos = this.transform.position;
Vector3 screenPos = Camera.main.WorldToScreenPoint(pos);
if (screenPos.x < 0 || screenPos.x > Screen.width) // 左右邊
{
Debug.Log("出界了");
}
鍵盤輸入
與鼠標輸入類似
- Input.GetKeyDown(keycode) 按下事件
- Input.GetKeyUp(keycode) 抬起事件
-
Input.GetKey(keycode) 按鍵狀態(tài)
- KeyCode.A 常量看官方文檔
16.1?組件調用
代碼操控組件
將代碼組件與音樂組件放至同節(jié)點(順序無影響) ;this.GetComponent<AudioSource>() 獲取AudioSource組件
void Update()
{
if (Input.GetMouseButtonDown(0))
PlayMusic();
}
void PlayMusic()
{
AudioSource audio = this.GetComponent<AudioSource>();
if (audio.isPlaying)
audio.Stop();
else audio.Play();
}
組件引用
情景:用主控腳本控制背景音樂空節(jié)點的音樂組件的播放
不常用方法:在檢查器設置節(jié)點引用,腳本通過物體節(jié)點獲得組件
public GameObject bgmNode;
void Update()
{
if (Input.GetMouseButtonDown(0))
PlayMusic();
}
void PlayMusic()
{
AudioSource audio = bgmNode.GetComponent<AudioSource>();
if (audio.isPlaying)
audio.Stop();
else audio.Play();
}
常用方法:在檢查器里設置組件引用,腳本直接訪問該組件
public AudioSource bgmComponent;
void Update()
{
if (Input.GetMouseButtonDown(0))
PlayMusic();
}
void PlayMusic()
{
AudioSource audio = bgmComponent;
if (audio.isPlaying)
audio.Stop();
else audio.Play();
}
- **this.GetComponent<T>() **獲取當前物體下的組件
- xxx.GetComponent<T>() 獲取其他物體下的組件
個人理解每個組件類都有 GetComponent<T> 泛型實例方法,用于獲取當前綁定的節(jié)點的各個組件
代碼組件引用
情景:用一個腳本組件控制另一個腳本組件的公開字段,如修改轉速
可以是API獲取,通過物體節(jié)點再獲取腳本組件類型,也可以直接引用,下面是直接引用做法(Unity框架自動完成組件查找過程)
public class InputLogic : MonoBehaviour
{
public FanLogic fan;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
fan.rotateY = 90f;
}
}
}
public class FanLogic : MonoBehaviour
{
public float rotateY;
void Update()
{
this.transform.Rotate(0,rotateY * Time.deltaTime,0,Space.Self);
}
}
消息調用
該方法利用反射機制,效率低,不常用,用于調用其他物體中組件的方法
- 找到目標節(jié)點
- 向目標節(jié)點發(fā)送“消息"(字符串,函數(shù)名字)
執(zhí)行過程
- 找到節(jié)點綁定的所有組件
- 在所有組件下尋找方法名對應的方法,找到就執(zhí)行,找不到就報錯
public class FanLogic : MonoBehaviour
{
public float rotateY;
void Update()
{
this.transform.Rotate(0,rotateY * Time.deltaTime,0,Space.Self);
}
void DoRotate()
{
rotateY = 90f;
}
}
public class InputLogic : MonoBehaviour
{
public GameObject fanNode;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
fanNode.SendMessage("DoRotate");
}
}
}
練習、無人機
邏輯很簡單,主控節(jié)點引用兩個腳本組件,然后根據(jù)輸入修改這兩個腳本組件的狀態(tài);代碼中通過調用各個組件的公開實例方法來修改字段成員
public class RotateLogic : MonoBehaviour
{
float m_rotateSpeed;
void Update() =>
this.transform.Rotate(0, m_rotateSpeed * Time.deltaTime, 0, Space.Self);
public void DoRotate() => m_rotateSpeed = 360*3;
public void DoStop() => m_rotateSpeed = 0;
}
public class FlyLogic : MonoBehaviour
{
float m_speed = 0;
void Update()
{
float height = this.transform.position.y;
float dy = m_speed * Time.deltaTime;
if( dy > 0 && height < 4 )
this.transform.Translate(0, dy, 0, Space.Self);
else if ( dy < 0 && height > 0)
this.transform.Translate(0, dy, 0, Space.Self);
}
public void Fly ()=> m_speed = 1;
public void Land() => m_speed = -1;
}
public class MainLogic : MonoBehaviour
{
public RotateLogic rotateLogic;
public FlyLogic flyLogic;
void Start()
{
Application.targetFrameRate = 60;
rotateLogic.DoRotate();
}
void Update()
{
if(Input.GetKey(KeyCode.W))
flyLogic.Fly();
else if (Input.GetKey(KeyCode.S))
flyLogic.Land();
}
}
17.1?節(jié)點操作
名稱查找節(jié)點
效率低,不適應變化;如果有父節(jié)點最好指定一下路徑;不存在返回null;不常用,通常用公開字段引用對象的方法;查找的是生效節(jié)點,不生效節(jié)點不納入查找范圍
void Start()
{
GameObject node = GameObject.Find("無人機/旋翼");
RotateLogic rl = node.GetComponent<RotateLogic>();
rl.DoRotate();
}
?? 如果在最前面加/
表示從根目錄開始查找
查找父級
父子級關系由 Transform 維持
- 獲取父級Transform,
- 通過父級Transform獲取父級GameObject
- 打印父節(jié)點名字
void Start()
{
Transform parent = this.transform.parent;
GameObject parentNode = parent.gameObject;
Debug.Log(parentNode.name); // 等同 transform.name
}
查找子級
transform 實現(xiàn)了迭代器接口可以被 foreach 遍歷,拿到多個子節(jié)點
void Start()
{
foreach (Transform child in transform)
{
Debug.Log(child.name);
}
}
也可通過 getChild(int) 索引獲取,下標從 0 開始。下面獲取第二個子節(jié)點transform,不存在返回null
Debug.Log(transform.GetChild(2).name);
Debug.Log(transform.GetChild(2) is Transform);
名稱查找子級
用法與名稱查找節(jié)點類似;不存在返回null;與名稱查找節(jié)點不同的是子級節(jié)點不生效,transform也能被查找到
void Start()
{
Transform t = transform.Find("旋翼 (1)/旋翼 (2)");
if (t is null) Debug.Log("Nothing found");
else
Debug.Log(t.name);
}
設置父級
transform組件實例方法 SetParent(node) 設置當前transform的父級,如果傳入null則無父節(jié)點(根目錄節(jié)點)
void Start()
{
Transform node = transform.Find("/aa");
transform.SetParent(node);
}
設置生效
生效與不生效;也相當于顯示與隱藏;個人理解為(啟用當前全部組件,禁用當前全部組件)
GameObject 類型實例的實例方法 SetActive;修改自身節(jié)點是否生效
void Start()
{
var obj = transform.gameObject;
Debug.Log(obj.activeSelf);
obj.SetActive(false);
Debug.Log(obj.activeSelf);
}
當前節(jié)點修改其他節(jié)點是否生效
private GameObject obj;
private void Start() =>
obj = GameObject.Find("aa");
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Debug.Log(obj.activeSelf);
obj.SetActive(!obj.activeSelf);
Debug.Log(obj.activeSelf);
}
}
修改子節(jié)點是否生效
void Update()
{
if (Input.GetMouseButtonDown(0))
{
GameObject obj = transform.Find("dd").gameObject;
Debug.Log(obj.activeSelf);
obj.SetActive(!obj.activeSelf);
Debug.Log(obj.activeSelf);
}
}
練習、俄羅斯方塊
private int index = 0;
private void Start()
{
foreach (Transform child in transform)
child.gameObject.SetActive(false);
transform.GetChild(index).gameObject.SetActive(true);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))ChangeShape();
}
private void ChangeShape()
{
transform.GetChild(index).gameObject.SetActive(false);
index = (index + 1) % transform.childCount;
transform.GetChild(index).gameObject.SetActive(true);
}
18.1?資源使用
資源引用
情景:掛一個AudioSource(音頻播放)組件,不指定音頻AudioClip(音頻容器類);要求利用腳本指定播放的資源
注意:在使用 PlayOneShot 實例方法的情況下,音頻播放組件的 clip 字段并沒有被設定
public AudioClip bgm;
private void Start()
{
var audioSource = GetComponent<AudioSource>();
// audioSource.clip = bgm;
// audioSource.Play();
audioSource.PlayOneShot(bgm);
}
列表引用
情景:掛一個AudioSource組件,不指定音頻AudioClip;要求利用腳本隨機播放幾首歌曲里的一首;
public AudioClip[] bgms;
private void Start()
{
if (bgms.Length == 0)
Debug.Log("我歌呢");
var audioSource = GetComponent<AudioSource>();
// audioSource.PlayOneShot(bgms[Random.Range(0,bgms.Length)]);
audioSource.clip = bgms[Random.Range(0, bgms.Length)];
audioSource.Play();
Debug.Log(audioSource.clip.name);
}
練習、三色球
直接修改顏色也可以直接修改Albedo的顏色(超出入門范疇)
public Material[] ms;
private int index = 0;
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
index = (index + 1) % ms.Length;
Material m = ms[index];
MeshRenderer mr = GetComponent<MeshRenderer>();
mr.material = m;
}
}
19.1?定時調用
延遲調用
繼承了 MonoBehaviour;利用了反射;delay、interval 是秒不是毫秒
- Invoke(string func,float delay) 只調用一次
- InvokeRepeating(string func,float delay,float interval) 首次執(zhí)行后循環(huán)調用
- IsInvoking(func) 是否在調度隊列中
- CancelInvoke(func) 取消調用、從調度隊列中移除
注意:每次 InvokeRepeating,都會添加一個新的調度(加到調度隊列),然后大循環(huán)每次循環(huán)遍歷調度隊列
private void Start()
{
// Invoke("DoSomething",1);
InvokeRepeating("DoSomething",1,2);
}
void DoSomething() =>
Debug.Log("HELLO WORLD " + Time.time);
單線程引擎
Unity 引擎核心是單線程的
private void Start()
{
Debug.Log(Thread.CurrentThread.ManagedThreadId);
InvokeRepeating("DoSomething",1,2);
}
private void Update()
{
Debug.Log(Thread.CurrentThread.ManagedThreadId);
}
void DoSomething() =>
Debug.Log(Thread.CurrentThread.ManagedThreadId);
終止調度
調用一次 CancelInvoke(string func) 終止了兩個同函數(shù)的調度;全部都取消不用加參數(shù)
private static int COUNT = 1;
private void Start()
{
InvokeRepeating("DoSomething",1,2);
InvokeRepeating("DoSomething",1,2);
}
private void Update()
{
if (Time.time > 10)
if (IsInvoking("DoSomething"))
{
CancelInvoke("DoSomething");
Debug.Log("調度被終止了");
}
}
void DoSomething()
{
int i = COUNT++;
Debug.Log($"我是任務 {i}");
}
練習、紅綠燈
public Material[] colors;
private int index = 0;
private void Start()
{
Invoke("ChangeColor",0);
}
void ChangeColor()
{
GetComponent<MeshRenderer>().material = colors[index];
index = (index + 1) % colors.Length;
if (index == 1)
Invoke("ChangeColor", 3);
else if(index == 2)
Invoke("ChangeColor", 0.5f);
else
Invoke("ChangeColor", 3);
}
練習、加速減速
public float speedY = 0f;
private int control = 1;
private void Start()
{
InvokeRepeating("SpeedControl",0,0.1f);
}
void Update()
{
transform.Rotate(0,speedY * Time.deltaTime,0,Space.Self);
if (Input.GetMouseButtonDown(0))
control = -control;
}
void SpeedControl()
{
speedY = speedY + 10f * control;
speedY = speedY > 180 ? 180 : speedY;
speedY = speedY < 0 ? 0 : speedY;
}
20.1?Vector3
特性
Vector3 是結構體,里面有三個字段 x,y,z,向量是有方向的量,有長度
- v.magnitude 長度(模長)
-
v.normalized 單位向量標準化(長度為1的向量是單位向量)
- 向量的每個分量都除以向量的模長
- 常用靜態(tài)常量有很多
- Vector3.zero (0,0,0)
- Vector3.up (0,1,0)
- Vector3.right (1,0,0)
- Vector3.forward (0,0,1)
- 向量有加減運算(初中知識)
-
向量有乘法運算
- 標量乘法:放大倍數(shù)
- 點積:Vector3.Dot(a,b)
- 叉積:Vector3.Cross(a,b)
-
不可空值類型不能被設置為 null,可以留空不寫,即默認值 0,0,0
public Vector3 speed; // = null; EXCEPTION
測距
物體之間的距離,確切的說是軸心點之間的距離
- 向量相減然后求距離
- 或直接用 Vector3.Distance(Vector3 a,Vector3 b)
public GameObject a;
public GameObject b;
private void Start()
{
Vector3 apos = a.transform.position,bpos = b.transform.position;
float disc = Vector3.Distance(apos, bpos);
Debug.Log(disc);
Debug.Log((apos-bpos).magnitude);
}
物體運動方向
讓一個物體沿著某一方向運動(Translate 有多個函數(shù)重載方法)
public Vector3 speed;
private void Update()
{
transform.Translate(speed * Time.deltaTime,Space.Self);
}
21.1?預制體
創(chuàng)建
預先制作好的物體節(jié)點(模板),*.prefab
樣本節(jié)點做好后,拖到資源目錄下,會生成預制體文件。原始樣本節(jié)點可以刪除
prefab 文件只記錄了節(jié)點的信息;不包含材質、貼圖數(shù)據(jù),僅包含引用(導出時會將依賴一并導出)
實例
- 預制體生成的節(jié)點實例在層級窗口是藍色
- 右鍵菜單有預制體相選項 | 檢查器窗口上面有預制體相關選項
- 預制體生成的節(jié)點實例可以Unpack斷開與預制體的鏈接,后續(xù)預制體的修改不會影響該節(jié)點
編輯
- 單獨編輯
- 雙擊預制體 | 點擊 Scenes 或返回箭頭退出
- 原位編輯
- 選擇預制體實例節(jié)點,點擊層級管理器右側箭頭或檢查器上的Open,此時僅選中的物體被編輯,其余物體是陪襯 | 點擊返回箭頭退出
- 有 normal/gray/hidden 三種顯示模式
- 覆蓋編輯
- 修改預制體實例節(jié)點后,點擊檢查器上的 Overrides | 這個操作也可以撤銷節(jié)點修改
22.1?動態(tài)創(chuàng)建實例
UnityEngine.Object.Instantiate(Object perfab,Transform parent) 有多個重載版本
創(chuàng)建預制體實例后,應做初始化
- parent 父節(jié)點(方便管理控制)
- position 、localPosition 位置
- eulerAngles / localEulerAngles 旋轉
- Script 自帶的腳本組件
public GameObject bulletPrefeb;
public GameObject bulletFolder;
public GameObject Canno;
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
GameObject obj = Instantiate(bulletPrefeb, null);
obj.transform.SetParent(bulletFolder.transform);
obj.transform.position = transform.position;
obj.transform.eulerAngles = Canno.transform.eulerAngles;
// obj.transform.rotation = Canno.transform.rotation;
obj.GetComponent<BulletLogic>().speed = 0.5f;
}
}
22.3?銷毀實例
比如 22.1 的子彈案例
- 飛出屏幕,銷毀
- 按射程、飛行時間銷毀
- 擊中目標,銷毀
UnityEngine.Object.Destroy(GameObject obj)
- 不要寫成 Destroy(this) ,這相當于刪除當前組件
- Destroy不會立即執(zhí);即創(chuàng)建出來實例的Start方法在Update執(zhí)行完后才會執(zhí)行 ?
public class BulletLogic : MonoBehaviour
{
public float speed;
public float maxDistance;
void Start()
{
Debug.Log("Start Start");
float lifetime = 1;
if (speed > 0) lifetime = maxDistance / speed;
Destroy(gameObject,lifetime);
Debug.Log("Start Finish");
}
void Update() =>
this.transform.Translate(0, 0, speed, Space.Self);
}
public class SimpleLogic : MonoBehaviour
{
public GameObject bulletPrefeb;
public GameObject bulletFolder;
public GameObject Canno;
public float speed = 0.5f;
public float flyTime = 2f;
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
GameObject obj = Instantiate(bulletPrefeb, null);
Debug.Log("Instantiate Start");
obj.transform.SetParent(bulletFolder.transform);
obj.transform.position = transform.position;
obj.transform.eulerAngles = Canno.transform.eulerAngles;
// obj.transform.rotation = Canno.transform.rotation;
obj.GetComponent<BulletLogic>().speed = speed;
obj.GetComponent<BulletLogic>().maxDistance = speed * flyTime;
Debug.Log("Instantiate Finish");
}
}
練習?炮口旋轉
官方建議不要獲取對象歐拉角再覆蓋歐拉角,涉及轉換的一些問題
而是固定用一個Vector3當做對象的歐拉角
private Vector3 _eulerAngles;
public float rotateSpeed = 30f;
public GameObject Canno;
void Update()
{
float delta = rotateSpeed * Time.deltaTime;
if (Input.GetKey(KeyCode.W))
if (_eulerAngles.x > -60)
_eulerAngles.x -= delta;
if (Input.GetKey(KeyCode.S))
if (_eulerAngles.x < 30)
_eulerAngles.x += delta;
if (Input.GetKey(KeyCode.A))
_eulerAngles.y -= delta;
if (Input.GetKey(KeyCode.D))
_eulerAngles.y += delta;
Canno.transform.localEulerAngles = _eulerAngles;
}
23.1?簡單物理
剛體與碰撞體
剛體 RigidBody
使物體具有物理學特性。添加剛體組件后由物理引擎負責剛體的運動
碰撞體組件 Collider
設置物體的碰撞體積范圍。也由物理引擎負責
默認添加的碰撞體一般情況下會根據(jù)網(wǎng)格自動設置尺寸,可以另外編輯
反彈與摩擦
通過物理材質,設置一些參數(shù)后將該物理材質賦給碰撞體組件的物理材質引用
運動學剛體
RigidBody 組件參數(shù) Is Kinematic 打勾,此時為運動學剛體
?零質量,不會受重力影響,但可以設置速度來移動;這種運動剛體完全由腳本控制
碰撞檢測 ?
需滿足以下兩個條件
- 物體是運動剛體
- 碰撞體開啟了 Is Trigger
- 物理引擎只負責探測(Trigger),不會阻止物體或者反彈
- 物理引擎計算的是 Collider 之間的碰撞,和物體自身形狀無關
- 當檢測到碰撞時,調用當前節(jié)點多個事件消息函數(shù),如 OnTriggerEnter
public Vector3 speed;
void Update() =>
transform.Translate(speed * Time.deltaTime,Space.Self);
private void OnTriggerEnter(Collider other)
{
Debug.Log("發(fā)生碰撞");
Debug.Log(other.name);
}
練習、子彈銷毀物體
給子彈添加如下代碼
private void OnTriggerEnter(Collider other)
{
Debug.Log(other.name);
Destroy(other.gameObject);
Destroy(gameObject);
}
25.1?射擊游戲
天空盒
Window | Rendering | Lighting (CTRL + 9)文章來源:http://www.zghlxwxcb.cn/news/detail-707887.html
子彈
public class BulletLogic : MonoBehaviour
{
public float speed = 1f;
void Update()
{
transform.Translate(0,0,speed * Time.deltaTime,Space.Self);
}
private void OnTriggerEnter(Collider other)
{
if (!other.name.StartsWith("怪獸")) return;
Destroy(other.gameObject);
Destroy(gameObject);
}
}
發(fā)射與移動
public class PlayerLogic : MonoBehaviour
{
public GameObject bulletPrefeb;
public GameObject bulletFolder;
public Transform firePos;
public Transform fireEulerAngles;
public float speed = 15f;
public float lifeTime = 3f;
public float interval = 2f;
private float _interval = 2f;
public float moveSpeed = 15f;
private void Update()
{
_interval += Time.deltaTime;
if (Input.GetMouseButtonDown(0) && _interval > interval)
{
_interval = 0f;
GameObject obj = Instantiate(bulletPrefeb, null);
obj.transform.SetParent(bulletFolder.transform);
obj.transform.position = firePos.position;
obj.transform.eulerAngles = fireEulerAngles.eulerAngles;
obj.GetComponent<BulletLogic>().speed = speed;
obj.GetComponent<BulletLogic>().lifeTime = lifeTime;
}
if(Input.GetKey(KeyCode.A))
transform.Translate(-Time.deltaTime * moveSpeed,0,0,Space.Self);
if(Input.GetKey(KeyCode.D))
transform.Translate(Time.deltaTime * moveSpeed,0,0,Space.Self);
}
}
怪獸生成器
public class CreatorLogic : MonoBehaviour
{
public GameObject enemyPrefeb;
void Start()
{
InvokeRepeating("CreateEnemy",1f,1f);
}
void CreateEnemy()
{
GameObject obj = Instantiate(enemyPrefeb,transform);
var pos = transform.position;
pos.x += Random.Range(-30, 30);
obj.transform.position = pos;
obj.transform.eulerAngles = new Vector3(0, 180, 0);
}
}
添加爆炸特效
public GameObject explosionPrefeb;
...
private void OnTriggerEnter(Collider other)
{
if (!other.name.StartsWith("怪獸")) return;
Destroy(other.gameObject);
Destroy(gameObject);
GameObject obj = Instantiate(explosionPrefeb, null); // 不要掛載子彈節(jié)點下面
// 粒子特效播放完會自毀
obj.transform.position = transform.position;
}
資源參考
Unity Documentation 、B站 阿發(fā)你好 入門視頻教程文章來源地址http://www.zghlxwxcb.cn/news/detail-707887.html
到了這里,關于Unity 游戲開發(fā)、01 基礎篇 | 知識大全、簡單功能腳本實現(xiàn)的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!