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