陳擎文教學網:Unity互動程式模組化設計
Unity互動程式元件化設計

☎Unity學習三步驟:Unity基礎 ➜ 各種Unity互動效果之程式設計 ➜ Unity專案
☎原理:學習各種效果的程式寫法,是基本功,可以將它變成一個一個模組,每種效果一個元件,以後要用使用可以複製程式碼
☎學習技巧:元件化,模組化,單元化,網頁化,隨查即得,快速應用

資源(Resource)

chp1. Unity的簡介,安裝,編輯軟體

chp2. Unity的教學資源

chp3. 各種遊戲引擎比較

chp4. 上課實作Unity互動範例(1-30題)

chp5. 上課實作Unity互動範例(31-60題)

【chp6.程式碼建立節點物件,改變節點組件參數】
6-1:程式碼建立一個新的cube物件

6-2:程式碼修改一個cube物件,材質為紅色,透明度為90(1~255)

6-3:點按一個cube物件,播放音效

6-4:一個cube物件,再程式碼隱藏

6-5:先隱藏一個cube物件,再程式碼顯示

6-6:先勾選物件的isKinematic有動力學反應,再程式碼取消動力學反應(產生真實力學)

6-7:程式碼建立的物件節點,如何自動產生某個組件,例如navMeshAgent(RequireComponent)

【chp7.滑鼠事件,觸控事件】
7-1:觸控事件的程式碼有兩種寫法

7-2:如何做『ray射線』檢測(紅外線)

7-3:如何取得射線ray,點按觸碰點的物件的資料(名稱,位置)

7-4:如何在程式碼,顯示觸控射線點位置圓圈(以淡黃色,透明100圓圈顯示)

7-5:如何觸控射線,讓角色unityChan緩動有各種動作動畫(idle,walk,jump)

7-6:複合物件(小蜜蜂)如何觸控點按能夠有反應

【chp8.節點物件的運動方法1:移動,旋轉,縮放】
8-1:如何程式碼設定一個cube的大小

8-2:移動,有兩種位移法:(1)再移動dx,(2)移動到某坐標點

8-3:移動,有兩種速度呈現法:(1)緩動,(2)瞬間移動到某坐標點

8-4:Vector有兩種:單位向量Vector3.left,new Vedtor3(2,2,0)

8-5:unity的角度有5種,unity的旋轉方式有5種

範例8-6:用幀動畫來控制直升機移動:上下、左移右移、左轉右轉

範例8-7:AddForce(單位向量)來施力讓物體移動

範例8-8:複合物件(小蜜蜂)如何觸控點按後就能向上移動1秒

範例8-9:複合物件(小蜜蜂)如何觸控點按後就以鏡頭camera垂直方向forward遠離鏡頭1秒

8-15:計算兩個物件之間的距離

【chp9.節點物件的運動方法2:緩動移動(導航網格navMesh,Time.deltaTime)】
9-1:要讓角色緩動(在一段時間內移動到目的地),有兩種方法:導航網格navMesh,Time.deltaTime

9-2:導航網格navMesh

9-3:導航網格的特殊功能1:兩點之間跳躍(等高點的跳躍)

9-4:導航網格的特殊功能2:兩點之間跳躍(高低點的跳躍)

9-5:如何修正:導航網格轉成AR(Vuforia)的錯誤:角色會浮在空中

範例9-6:實作主角膠囊在導航網格內,自由觸控移動

範例9-7:實作主角unityChan在導航網格內,自由觸控移動+動作(idle,walk,jump)

9-8:unity的導航網格無法做到移動,如何修正

9-9:導航網格的主角,如何不動就idle,特定位置則jump,否則walk

範例9-10:實作主角unityChan的極限體能王過關遊戲


【chp10.射擊遊戲,不斷生成的prefab】
10-1.如何設定怪物不斷生成產生

10-2.兩種計時器(Invoke,InvokeRepeating)與各種計時器指令

10-3.使用生成指令Instantiate,在某物件A的位置,生成新物件B

10-4.如何讓怪物monster亂數左右移動(蛇形走位)

10-5.從某個角色物件Cube-player,如何不斷發射子彈prefab

10-6.按鈕fire才開火(會飛的子彈),按鈕取消開火

範例10-7.直升機fire攻擊不斷來襲的敵機

10-8.在dx=(-30, 30)之間產生隨機亂數,移動dx位移

10-9.如何讓產生的生成物件Instantiate,在5秒後消失

10-10.在原地不斷生成怪物


【chp11.節點物件的碰撞事件1】
11-1:碰撞事件有2種處理方式:Collision碰撞,Trigger觸發

11-2:Trigger觸發碰撞事件的設定方法(最常用的碰撞方式)

11-3:如何設定碰撞時的粒子動畫效果(例如,顯示爆炸prefab)

11-4:被關卡撞到,如何顯示煙霧動畫

11-5:為什麼設定碰撞的音效,沒有出現,如何解決(子彈打到怪物播放爆炸聲音)

11-6:開啟爆炸範圍,任何進入範圍內的怪物都會被爆炸


【chp12.節點物件的碰撞事件2】
【chp13.切換場景】
13-1:切換場景的基本指令

範例13-2:遊戲失敗產生Game Over畫面,方法1:切換場景

範例13-3:遊戲失敗產生Game Over畫面,方法2:顯示圖片(隱藏的圖片,用public讀入後顯示)

範例13-4:遊戲失敗產生Game Over畫面,方法3:顯示圖片(隱藏圖片物件,用程式碼類別物件顯示)


【chp14.分數計算,記分系統】
範例14-1:呼叫另外c#檔案的show()函數顯示分數score(方法1:public拖曵設定cs程式碼掛載的物件)

範例14-2:呼叫另外c#檔案的show()函數顯示分數score(方法2:GameObject.FindObjectOfType)

範例14-3:很多怪物攻擊主角,計算同一個score變數(方法3:GameObject.FindObjectOfType)

【chp15.RPG的狀態機,主角攻擊怪物,怪物攻擊主角】
15-1:狀態機:設定角色的5種狀態

15-2:如何判斷角色的狀態是預備態IDLE

15-3:如何判斷角色的狀態是攻擊態ATTACK

範例15-4:實作enemy的四種狀態(預備idle,走路walk,攻擊attack,死亡die)

15-5:如何怪物死亡後,不再繼續自動導航追踪主角(還要設定animator狀態,設定5秒後消失)

範例15-6:兩種怪物攻擊主角,主角按炸彈來引爆炸彈

資源
  上課程式碼即時貼網站 線上黑板 上課即時貼(guestBook)
  螢幕錄影成影片方法1:OBS OBS官網下載 OBS雲端下載2 OBS教學影片 開始錄影:F1
停止錄影:F1
檔案路徑:C:\Users\user\Videos\Captures
特色:可以錄影多個切換視窗的內容
  螢幕錄影成影片方法2:Window 10 開始錄影:Windows鍵+ G
缺點:只能夠錄影單一個視窗的內容
錄影教學影片 開始錄影:Windows鍵+ G
缺點:只能夠錄影單一個視窗的內容
  螢幕錄影成影片方法3:PowerPoint螢幕錄影功能 PowerPoint➜插入➜螢幕錄影
注意:錄製結束後,要手動存成mp4影片檔案➜空白➜滑鼠右鍵➜存成媒體檔
用PowerPoint(ppt)錄影功能及注意事項 優點:螢幕錄影,所以不只是錄製單一畫面,或單一個ppt檔案影片
注意:不建議存檔ppt檔案,因為檔案太大,只要另外儲存媒體mp4檔案即可
  網頁線上英文字典 ☎如何安裝google chrome的網頁線上英文字典工具:
->google chrome的右上角工具->更多工具->擴充功能
->左上角主選單->開啟chrome線上應用程式商店
->勾選:google製作,免費
->搜尋:google dictionary->安裝
->到chrome右上擴充功能->點按google dictionary的『詳細資料』->擴充功能選項
->my language=chinese
->打勾2個:Pop-up definitions:
(1)反白單字翻譯:Display pop-up when I double-click a word
(2)ctrl+拖曵整段翻譯: Display pop-up when I select a word or phrase

  unity package物件 man木頭人 stamp印章 3D樹木
  unity package物件 爆炸prefab動畫(WFX_Nuke) 爆炸音效(nuclear_explode_aduio) enemy角色下載(Battle Wizard Poly Art) Effect textures and prefabs(煙霧動畫prefabs物件)asset store
  unity package物件 Unity Menu物件(prefab),必須自行加入eventSystem 狐狸fox物件(prefab) 蜜蜂bee物件(prefab)
  unity package物件 直升機的asset store 敵機的asset store 各種爆炸火焰煙霧的粒子動畫asset store Effect textures and prefabs(煙霧動畫prefabs物件)asset store
  audio 揮擊木棍聲音(Whoosh) 過關成功聲音(win) 氣化聲音(hiss) 各種爆炸音效
  unity terrain貼圖 terrain貼圖
  鍵盤控制物體移動程式碼 input.getkeydown控制物體運動的4組動作txt程式碼 input manager 控制物體運動的6組動作txt程式碼 input manager 控制物體眼睛攝影機上下旋轉的1組動作txt程式碼
  鍵盤控制物件的完美移動演算法 鍵盤控制角色運動的方法1(local坐標控制:↑ ↓前進後退。 → ←控制旋轉) 鍵盤控制角色運動的方法2(global坐標控制:↑ 往北,↓往南, →往東,←往西)
3D素材資源 unity asset store https://www.unityfly.com/ 2D素材商店:craftpix 2D,3D遊戲素材商店:GDM:gamedevmarket
free3d TurboSquid unitylist Mixamo 3D角色
音效sound effect samplefocus(簡單容易使用免費) YouTube音效庫(簡單容易使用免費) findsounds(簡單容易使用免費) free sounds library
findsounds mixkit音效(1/5免費) 遊戲音效 sound jay(沒有查詢)
YouTube音效庫 小森平的免費下載音效(沒有查詢) 日文音效-Sound Effect Lab免費音效下載網站 素材吧(簡體中文)
unity c#常用方法 unity 常用方法 unity 常用方法 Unity Script 常用語法教學 unity C# 語法入門
unity 手冊 UNITY3D中文手册 UNITY參考手册 UNITY組件手册 UNITY腳本手册
unity 教學資源 unity教學 阿發教學 unity從小白到超神-youtube沒有c# 【極客學院】unity 3D教學入門
>最全unity3D教學 韓老師unity教學 最簡單Unity零基礎 unity入門,進階,高級,保姆級
>**Unity 2D平臺跳躍遊戲開發全教程(1-50) **Unity官方認證教程:3D RPG系列課程介紹 **Unity 2D 遊戲入門教程-Beginner Tutorial
>3D unity工程師的獨立遊戲開發教學 unity零基礎從小白到專業 【千鋒合集】最全Unity3D全套 整合整合Unity官方教程設計最佳學習路線
最全Unity教程
2D >unity2D入門 Unity2D官方入門教程:Ruby' Adventure unity零基礎一小時快速入門 unity零基礎開發卡牌遊戲教程
  unity unity簡介影片 snap軟體    
  學生畢業專題 110學年學生畢業專題 @幽靈獵人:第一人稱視角(unreal) @PeaceCleaner(2D unity) @彈珠台:霧界彼方(3D unity)
  @地心鐵男:under dog(2D unreal,必須有搖桿) @極菌 X Plague(2D unity,apk,安卓手機) @幻世之旅(unreal) @Aries顏色(2.5D unity)
  愛永不放棄(unreal) dark element(2D unity) 嘴砲少年家(2D unity) 尋境傳説(2D unity)
  @實驗室(3D unity)
  Goole輸入法(Input:exe) Goole 輸入法(Input:zip) 畢業門檻(Graduation threshold) 2018通識課規定
 

 
chp1.安裝unity

安裝各種版本

1.參考:unity的安裝教學影片:
安裝unity
安裝unity1
安裝unity2
安裝unity3
登入unity4
新增unity project5
unity 視窗排列6

2.注意:不要下載最新的unity,因為新版還不成熟,較不穩定。
但是:unity新版可以向下兼容,可以打開舊版的檔案,但是舊版無法打開新版檔案
所以也不要用太舊版的
(1)☎建議下載LTS版本:LTS = Long-Term Support = 長期維護版
☎版本:選擇f版本,f是穩定版,例如:2020.2.4.f1
☎建議下載Final版本
(2)專案開發版本,推薦選擇:LTS。
(3)如果需要使用新功能,可酌情考慮Final。
(4)需要修復bug,可適當升級Patch。

3.各種版本說明:
(1)Final:最終版。
當前時間的一個最終版本。
一般大的功能跨度都是在年度版本進行更新。

(2)LTS:長期穩定支持版 。
LTS版本不會有新的任何功能,API變更或改進。
適用于正在開發以及已有發佈的開發者,
每年最後一個TECH版本會成為Unity LTS穩定支持版。

(3)TECH:技術前瞻版。
Unity TECH版本適合想要使用最新版Unity的用戶。

(4)Patch:補丁版。
針對當前版本所存在的Bug進行一個修復。。

(5)Beta:外部測試版。

(6)Alpha:預覽版,內部測試版。
此版本提供最新功能的早期試用,和Beta版相比,Alpha版有較大的穩定性風險。

4.安裝方法1(自動法):下載官網的Unity Hub(64bit版軟體)
(1)安裝三步驟:
A. Download the Unity Hub
B. Choose your Unity version
C. Start your project
官網下載unity Hub
選擇:Download for Windows

選取First-time users:Start here

5.安裝方法2(手動選擇版本法):下載官網的舊版Unity Archive
官網下載舊版unity LTS
官網下載舊版unity Archive(全部版本)
注意:如果你已經安裝的版本是很久的舊版編輯器,要如何更新?
(1)☎☎要先更新unity Hub版本
(2)再下載新的LTS版本(f1)
注意:用舊版的unity hub,是無法成功下載新的unity版本的

6.執行unity必須要login帳號:
(1)可以用facebook,google帳號登入

7.Unity的License Management
(1)免費版,要選擇:personal
(2)付費版(要商業化):選擇plus,pro

8.Unity的版權授權:
unity的授權只有2天, 超過了,要再重新授權一次

9.安裝visual studio 2019
(1)可以到微軟官網下載visual studio 2019
https://visualstudio.microsoft.com/zh-hant/vs/
安裝時,在visual studio installer勾選『使用unity進行遊戲開發』
(若是已經安裝vs 2019者,則重新執行visual studio installer➜勾選『使用unity進行遊戲開發』)
https://docs.microsoft.com/zh-tw/visualstudio/cross-platform/getting-started-with-visual-studio-tools-for-unity?view=vs-2019
https://www.youtube.com/watch?v=DL96YoqGhuc

(2)在unity裡面,設定連接到visual studio 2019
Edit➜Preferences➜External Tools➜External Script Editor➜
➜C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\devenv.exe
➜選visual studio 2019

10.注意:
(1)授權:選擇:unity個人版
(2)版本:LTS: long time support長期為維護
alpha:測試版
(3)版本:選擇f版本,f是穩定版,例如:新增2020.2.4.f1
(4)unity的授權只有2天, 屆時要再重新授權一次
(5)unity的語言盡量不要選繁體中文,選english
(6)新增專案時,路徑location:不要有中文
(7)新增c# script在asset裡面建立資料夾folder = script

11.登入unity的視窗排列方式:
(1)主要有5種:2by3, 4 split,default,Tall,Wide
調整方式:Window➜Layout

 
chp2. Unity的教學資源
  unity package物件 man木頭人 stamp印章 3D樹木
  unity package物件 爆炸prefab動畫(WFX_Nuke) 爆炸音效(nuclear_explode_aduio) enemy角色下載(Battle Wizard Poly Art) Effect textures and prefabs(煙霧動畫prefabs物件)asset store
  unity package物件 直升機的asset store 敵機的asset store 各種爆炸火焰煙霧的粒子動畫asset store Effect textures and prefabs(煙霧動畫prefabs物件)asset store
  audio 揮擊木棍聲音(Whoosh) 過關成功聲音(win) 氣化聲音(hiss) 各種爆炸音效
  unity terrain貼圖 terrain貼圖
  鍵盤控制物體移動程式碼 input.getkeydown控制物體運動的4組動作txt程式碼 input manager 控制物體運動的6組動作txt程式碼 input manager 控制物體眼睛攝影機上下旋轉的1組動作txt程式碼
  鍵盤控制角色運動的方法1(local坐標控制:↑ ↓前進後退。 → ←控制旋轉) 鍵盤控制角色運動的方法2(global坐標控制:↑ 往北,↓往南, →往東,←往西)
3D素材資源 unity asset store https://www.unityfly.com/ 2D素材商店:craftpix 2D,3D遊戲素材商店:GDM:gamedevmarket
free3d TurboSquid unitylist Mixamo 3D角色
音效sound effect samplefocus(簡單容易使用免費) YouTube音效庫(簡單容易使用免費) findsounds(簡單容易使用免費) free sounds library
findsounds mixkit音效(1/5免費) 遊戲音效 sound jay(沒有查詢)
YouTube音效庫 小森平的免費下載音效(沒有查詢) 日文音效-Sound Effect Lab免費音效下載網站 素材吧(簡體中文)
unity c#常用方法 unity 常用方法 unity 常用方法 Unity Script 常用語法教學 unity C# 語法入門
unity 手冊 UNITY3D中文手册 UNITY參考手册 UNITY組件手册 UNITY腳本手册
unity 教學資源 unity教學 阿發教學 unity從小白到超神-youtube沒有c# 【極客學院】unity 3D教學入門
>最全unity3D教學 韓老師unity教學 最簡單Unity零基礎 unity入門,進階,高級,保姆級
>**Unity 2D平臺跳躍遊戲開發全教程(1-50) **Unity官方認證教程:3D RPG系列課程介紹 **Unity 2D 遊戲入門教程-Beginner Tutorial
>3D unity工程師的獨立遊戲開發教學 unity零基礎從小白到專業 【千鋒合集】最全Unity3D全套 整合整合Unity官方教程設計最佳學習路線
最全Unity教程
2D >unity2D入門 Unity2D官方入門教程:Ruby' Adventure unity零基礎一小時快速入門 unity零基礎開發卡牌遊戲教程
  unity unity簡介影片 snap軟體    
  學生畢業專題 110學年學生畢業專題 @幽靈獵人:第一人稱視角(unreal) @PeaceCleaner(2D unity) @彈珠台:霧界彼方(3D unity)
  @地心鐵男:under dog(2D unreal,必須有搖桿) @極菌 X Plague(2D unity,apk,安卓手機) @幻世之旅(unreal) @Aries顏色(2.5D unity)
  愛永不放棄(unreal) dark element(2D unity) 嘴砲少年家(2D unity) 尋境傳説(2D unity)
  @實驗室(3D unity)
 

 
chp3.各種遊戲引擎比較
目錄 1.較受歡迎的3D遊戲引擎:Unity,Unreal 2.較受歡迎的2D遊戲引擎:Godot、Defold、Unity、Unity
3.Unity 4.Godot 5.Defold 6.Unity
7.JS-based Game Engine (Pixi, Phaser) 8.國外深度比較2個較好的遊戲引擎:Godot,Defold 9.綜合評估要選用哪種遊戲引擎
1 1.3D遊戲較受歡迎的兩個引擎:Unity,Unreal
(參考:2021年是自学游戏开发最好的年代!告訴你應該选Unity还是虚幻引擎?
(參考:不写代码做那样的游戏!分享使用Unity"蓝图"工具
(參考:我学编程了!真认真学了,可还是学不会……书看不懂、怎么办?
(參考:開發遊戲的建議

(1)Unity:
☎若要寫程式碼:c#
☎若要用可視化工具(編程):bolt(2021年收購bolt,開始預設支援)
☎若要用可視化工具(狀態機):playmaker(還要外掛插件)
☎優點:輸出3D,2D(行動app)
☎優點:使用界面直觀,容易上手

(2)Unreal:
☎若要寫程式碼:c++
☎若要用可視化工具(編程):blueprint
☎優點:畫面能夠渲染地非常有質感,藝術氛圍
☎缺點:C++寫程式不容易上手
☎缺點:輸出2D(行動app)較弱


2.較受歡迎的2D遊戲引擎:Godot、Defold、Unity 和 Unity
參考文章:
(1)Finding the Perfect 2D Game Engine
(2)Godot vs Defold, Which One is Better

3.Unity不適合開發:2D休閒類遊戲
原因:
☎雖然很強大,但對於一個休閒遊戲來說,這有點矯枉過正。
☎個學習曲線較長
☎複雜,對初學者來說令人困惑。
☎還有其他引擎可以更簡單、更精簡地進行 2D。
☎帶有閃屏的 Unity 遊戲因低質量遊戲而臭名昭著。
☎C#在低端設備上的性能運行不佳(Web 構建在移動設備上)

2 2.較受歡迎的2D遊戲引擎:Godot、Defold、Unity 和 Unity
參考文章:
(1)Finding the Perfect 2D Game Engine
(2)Godot vs Defold, Which One is Better


2.2D 休閒遊戲的需求:
☎高性能引擎,構建尺寸盡可能小,
☎腳本程式語言更簡單易用。
☎html5為主要平台,android次之。
☎無需涉及編譯或本機移動工具。
☎自由開源是首選。因此,沒有前期成本並且可以靈活地捐贈/貢獻給開發。
☎支持Metal,以防需要在蘋果平台上發布。


3.2D 休閒遊戲的推薦:
經過一些初步研究,推薦是:Godot、Defold、Unity 和 Unity。

3 3.Unity
參考文章:
(1)Finding the Perfect 2D Game Engine
(2)Godot vs Defold, Which One is Better

2.Corona SDK:
(1)優點:
☎Unity 是 Corona SDK 的開源分支。
☎它是一款出色且經過實戰測試的引擎,
☎易於使用且文檔非常好。
☎Unity 使用 Lua。構建檔案大小很好(不大),對於 armv7 和 64 位,可能大約 6MB APK。我用它做了一個簡單的遊戲。即使在低端設備中,性能也沒有問題。
☎社區雖小但很好。他們有論壇和 Discord。

(2)缺點:
☎沒有可視化編輯器(必須用簡單的純代碼撰寫一切物件,與動作事件)
☎缺點,雖然支持 html5 構建,但它仍然是測試版,所以有一些問題。
☎Vulkan/Metal 版本也沒有準備好。

(3)請注意,幾個不錯的 Lua 引擎:
☎Unity
☎Gideros
☎Love2D。
它們都是開源的,可以與 Unity 相媲美。
Unity 更精緻,文檔更好。


4 1.Godot
參考文章:
(1)Finding the Perfect 2D Game Engine
(2)Godot vs Defold, Which One is Better

2.Godot:
(1)優點:
☎這個引擎最近很流行。它就像一個開源版本的 unity。
☎很多文章說,Godot比 Unity 做 2D 更好。
☎Godot 是完全開源的,擁有最寬鬆的許可。

☎主要語言是GDScript(類似python),
☎但也支持C#、C++等。不同的語言有不同的成熟度和性能。

☎功能豐富,同時支持 2D 和 3D。
☎有畫面編輯器,小巧且快速。包含大量電池。具有內置動畫/骨骼、磁貼編輯器等。
☎它也是可擴展的。

☎教學文檔很好。還有很多文章和 YouTube 教程。

☎構建檔案大小 APK 大約為 7.1-11.8MB(編譯時可以剝離模塊)。
☎對於 html5,最小的(壓縮的)大約是 2.9MB。

☎它的開發非常活躍,由於它的受歡迎程度,它的未來看起來很光明。

(2)缺點:
☎GDScript 是官方語言,它不是 Python,人們說它性能不佳。
☎C# 支持看起來不錯,但會使構建大小更大。


5 5.Defold
參考文章:
(1)Finding the Perfect 2D Game Engine
(2)Godot vs Defold, Which One is Better

2.Defold:
(1)優點:
☎Defold 是一個基於 Lua 的遊戲引擎,帶有可視化編輯器。
☎目前已經“開源”open source,這讓它更具吸引力。

☎它是 King(超級流行的 Candy Crush Saga 背後的公司)的一部分,現在在 Defold 基金會下獨立。

☎功能高度完善,並註意高性能/低引擎開銷。

☎構建規模令人印象深刻。
☎html5 構建低至 800kb 左右(gzipped)。
☎apk 大小 ~1.7mb。

☎文檔很好。它要求我們按照 defold 的方式做事(比如物件之間的傳遞參數)。

☎為了成熟,Kings 在他們自己的一些流行遊戲中使用它(不過 Candy Crush Saga 使用不同的引擎)。因此,Defold 在這種規模上進行了實戰測試。

☎關於支持,它有小而活躍且響應迅速的社區。

(2)缺點:


6 6.Unity
參考文章:
(1)Finding the Perfect 2D Game Engine
(2)Godot vs Defold, Which One is Better
(3)Unity 極速入門

2.Unity:
(1)優點:
☎使用界面非常類似Unity(兩者很像,用起來非常容易彼此熟悉上手)
☎而unity最大的問題個人覺得就是這個APK打包出來體積大的嚇人。
☎但cocos HTML 的構建檔案大小是最好的(很小)。只有 ~155kb gzipped 因為它使用原生 js。

☎unity:現在傾向用於3d遊戲和虛擬實境,這兩塊
☎而cocos:終究還是華人世界2D遊戲的首選開發引擎(尤其它支援微信的小遊戲,也支援facebook的小遊戲)

☎非常適合 Facebook 即時遊戲或聊天應用程序的嵌入式遊戲。
☎最小的 apk 大約是 6mb。如果同時包含 armv7 和 arm64bit 庫,則為 11MB。

☎請注意,它會在移動設備上生成一個真正的本機應用程序。不僅僅是像 Apache Cordova 這樣的混合應用程序。

☎它來自流行的 Cocos2d-x 引擎的製造商,
☎但使用 Visual IDE ala Unity,並使用 javascript。
☎編輯器/IDE 是基於 Electron 的,沒有官方的 Linux 支持。

☎IDE 不會嘗試做所有事情。它甚至沒有內置代碼編輯器。
☎它可以很好地與外部代碼編輯器(如 VS Code)和流行的 3rd 方 2D 工具(如 tiled 和 Dragonbones/spine)集成。

☎在中國和東南亞,它很受歡迎並被騰訊等大型出版商使用,
☎因此它看起來在這種規模上經受了考驗。

☎對於社區來說,它有一個論壇和 Discord,看起來很小,但足夠活躍。


(2)缺點:
☎很難找到關於這個引擎的英文文章/資源(對外國人不利)。
☎較少的英語教程,也許它只在華人世界流行(對外國人不利)。

☎若要構建本機版本,需要設置所有本機工具堆棧並對其進行編譯。
☎因此,這不是即時的一鍵式過程。

☎此外,它是半開源的(編輯器部分是專有的,而引擎是開源的,基於 Cocos2D-X 的 fork。但是,它不能在沒有 IDE 的情況下使用)


3.Unity特色:
☎Cocos 引擎團隊敏銳地發現了:手遊頁遊化這一趨勢,
☎並做出了一個大膽的嘗試:開發類 Unity 工作流的一體化工具 Unity,
=>Unity 的工作流借鑑了 Unity 等成熟引擎工具的設計方案,這使得用過 Unity 引擎的開發者可以迅速上手 Unity 而沒有不適感。
=>開發者可以在 Unity 和 Unity 兩者間隨性轉換,使用久了,你或許會發現,做 3D 選用 Unity,做 2D 選用 Unity,兩者猶如同一個產品

☎它以頁遊主流指令碼: JavaScript 為開發語言,
=>Unity 使用 JavaScript 作為指令碼語言,一則降低了開發難度和開發成本,同時也方便執行和除錯,輕鬆涵蓋了 H5 領域的產品開發

☎能夠同時打包:三端產品【android, ios, html】,
☎還可幫開發者迅速過渡到當前流行的:【 H5、微信小遊戲、Facebook小遊戲】。
=>Facebook小遊戲 已授權 Cocos 為旗下 H5 遊戲平臺合作伙伴

☎Unity 提供了一套以內容創作為核心的【所見即所得】的編輯工具,
☎擁有 All in One 一站式服務體系:
=>整個遊戲產品開發過程中,遊戲創作團隊完全可以身處其中,通過其所建立的工作流對遊戲的資源管理、場景設計、介面佈局、精靈建立、邏輯控制、打包執行與釋出做全方位的把控

☎元件化
=>Unity 基於元件化設計,功能模組易於擴充套件。
=>該模式使開發者不再拘束於編輯器本身這個容器,可以按需對編輯器進行外掛式擴充套件,
=>用 HTML + JavaScript 等前端通用技術輕鬆擴充套件編輯器功能

4,Unity 的技術架構如下圖所示


5,以資料驅動工作流為核心的開發理念,整個工作流程如下圖所示


6,缺點:
Unity 也有不足的地方,比如 3D 功能仍處於初級研發階段,未能形成生產力

7,國內的facebook小遊戲,只要是Unity開發的遊戲


7 7.JS-based Game Engine (Pixi, Phaser, etc)
參考文章:
(1)Finding the Perfect 2D Game Engine
(2)Godot vs Defold, Which One is Better

2.JS-based Game Engine (Pixi, Phaser, etc):
(1)優點:
☎它們最適用於 html5。


(2)缺點:
☎但對於移動版本,您必須將其包裝為像 Cordova 這樣的混合應用程序,因此運行速度慢,即時反應差

8 8.國外深度比較2個較好的遊戲引擎:Godot,Defold
參考文章:
(2)Godot vs Defold, Which One is Better

1.特色的比較:
(1)Godot:
☎Godot 有更多的特點。發展非常迅速,似乎雄心勃勃。
☎它不斷添加越來越多的功能。包括大量電池。
☎但有時也會讓人不知所措和令人困惑。
☎Godot IDE,您可以在其中管理和編輯資產、場景、代碼等
(2)Defold:
☎Defold 似乎更準系統,並專注於核心功能。例如,它沒有動畫時間線編輯器,因此您將依賴脊椎或龍骨等 3rd 方。
☎Defold 有類似 Godot 的 IDE,但更簡單


2.支持3D的比較:
(1)Godot:
☎Godot 內置了 3D。人們正在等待 Godot 4.0。
(2)Defold:
☎Defold 的 3D 支持有限。我認為開發人員仍在努力改進它。


3.背後資金的比較:
(1)Godot:
☎這對軟體壽命至關重要。
☎Godot 由社區資助。它是軟件自由保護協會的成員,也有企業贊助商。
☎據我所知,這家大型科技公司還為某些功能的開發人員提供贊助,例如:Microsoft 提供 C# 支持,Mozilla 將 Godot 編輯器引入網絡。有趣的是,Godot 還通過 Epic MegaGrant 從 Epic Games 獲得了資金。 Epic 是 Unreal Engine 的製造商,這是一種商業(非直接)競爭遊戲引擎。
(2)Defold:
☎Defold 是 King, Company 的一部分,該公司因 Candy Crush Saga 而聞名。但現在它在自己的 Defold 基金會下獨立了。但是,King 仍然支持它作為企業贊助商。所以,資金看起來有點類似於Godot。


4.編輯器平台支持的比較:
☎相同的。兩者都適用於 Windows、Linux 和 macOS


5.編輯器大小的比較:
(1)Godot:
☎Godot 有一個較小的可下載文件。但是當你想導出到平台時,你需要下載額外的包。 Defold 已經包含了這個,所以我認為大小是可比的。
☎Godot 的編輯器感覺很輕巧。
☎他們的 UI 不是原生的,因為 Godot Editor 實際上是 Godot Games,這實際上非常酷。
(2)Defold:
☎Defold 在 JVM 下運行,所以感覺有點重(慢)。


6.支持的輸出平台的比較:
(1)Godot:
☎Godot:Linux、macOS、Windows、BSD、Android、iOS、BlackBerry 10、HTML5、UWP。沒有官方控制台支持,但可以通過第 3 方(Switch、PS4、Xbox)完成
(2)Defold:
☎Defold:Android、iOS、macOS、Linux、Windows、HTML5、Nintendo Switch。


7.輸出構建大小的比較:
(1)Godot:
☎兩個引擎都允許您剝離不必要的部分以減少膨脹。
(2)Defold:
☎但這是 Defold 真正閃耀的地方,它可以產生令人印象深刻的小型構建尺寸。低至 1.6MB APK 大小和 734kb html5(gzipped)
☎Defold 小尺寸使其非常適合網頁遊戲,它更接近於像 Phaser 這樣的原生 JS 引擎。

8.程式語言的比較:
(1)Godot:
☎Godot 以 GDScript 作為主要語言。語法類似於 Python,但它不是 Python。我們也可以使用其他語言進行編碼:C#、C++、Rust、Nim、JS 等。
(2)Defold:
☎Defold 使用 Lua。


9.人氣的比較:
(1)Godot:
☎Godot 在忠實和狂熱的用戶群中更受歡迎。它具有良好的勢頭和品牌知名度。它被稱為統一的開源替代品
(2)Defold:
☎Defold 目前的用戶群較小,但他們的論壇友好且樂於助人。希望它的社區也在不斷發展。


10.結論:
☎Godot:適用於桌面遊戲或 3D 桌面遊戲。
☎Defold:適用html5 和手機遊戲,在這些地方,尺寸和性能很重要。

9 9.綜合評估要選用哪種遊戲引擎
參考文章:
(1)Finding the Perfect 2D Game Engine
(2)Godot vs Defold, Which One is Better

1.若在國外工作的角度:國外有人專門評估要選用哪種遊戲引擎:
(1)3D遊戲:Unity,Unreal
(2)2D休閒遊戲:Godot、Defold


2.若以在台灣就業的角度:來評估要選用哪個遊戲引擎:
☎方法:到104人力銀行網頁查詢
☎查詢結果(2021/11/19):
(1)Unreal:找到 20 個Unreal為主要標題的相關工作
(2)Unity:找到 88 個Unity為主要標題的相關工作
(3)Unity:找到 0 個Unity為主要標題的相關工作
(4)Corona SDK:找到 0 個Corona SDK為主要標題的相關工作
(5)Godot:找到 0 個Godot為主要標題的相關工作
(6)Defold:找到 0 個Defold為主要標題的相關工作
(7)Unity:找到 47 個Unity為主要標題的相關工作
(8)Construct:找到 0 個Construct為主要標題的相關工作
(9)Scratch:找到 4 個Scratch為主要標題的相關工作
(10)app Inventor :找到 2 個app Inventor 為主要標題的相關工作

☎結論(以台灣的就業市場來看):
(1)3D遊戲引擎:首選Unity,次選Unreal
(2)2D休閒遊戲引擎(網頁遊戲引擎):首選Unity

 

 
chp4.實作Unity互動範例(1)
目錄 範例1.AddForce(單位向量)來施力讓物體移動 範例2.position移動到某個坐標,讓物體移動 範例3.translate相對位移量,讓物體移動
範例4.按鈕顯示目前分數,按鈕計算兩個數字相加 範例5.滑鼠點按Cube改顏色,有音效(按下onMouseDown放開onMouseUp物件都會變化顏色音效) 範例6.滑鼠點按Cube改顏色,改透明度,縮小,有音效
範例7.GUI界面:InputField直接控制改變足球轉速 範例8.GUI界面:Slider直接控制改變足球轉速 範例9.彈鋼琴
範例10.用Input.GetAxis的鍵盤控制圓球移動力道(瞬間移動,上下左右wsad,跳space,e左轉r右轉,滑鼠驅動移動) 範例11.用Input.GetKeyDown的鍵盤控制圓球移動(瞬間移動,上下左右wsad,跳space,e左轉r右轉) ☎範例11-2.鍵盤控制物件的完美移動5步驟
(鍵盤放開就停止,按下可以連續移動/幀動畫,space跳有力學效果)
☎範例11-3.鍵盤控制物件進階設定:向上跳只能連續跳2次(6步驟) 範例12.(虛幻物體)第三人稱視角,WSAD控制圓球移動(不向上跳),碰到獎品(觸發)得分20,100則過關 範例12-2.(真實物體)第三人稱視角,WSAD控制圓球移動(可向上跳),碰到獎品(碰撞)得分20,100則過關
範例13.(虛幻物體)第一人稱視角,WSAD控制圓球移動,碰到獎品得分20,100則過關 範例13-2.(真實物體)第一人稱視角:完美移動6步驟
範例14.手機按鈕控制移動法:按1次移動2秒的緩動動畫,幀動畫(計時器慢慢移動,緩動動畫) 範例15.使用unity官方的UI Menu來顯示隱藏不同物件
範例16.用緩動幀動畫來控制直升機移動:上下、左移右移、左轉右轉 範例17.切換場景 範例18.影片的播放,暫停,停止
範例19.Game Over畫面方法1:切換場景法 範例20.Game Over畫面方法2:顯示圖片法(先隱藏的圖片,用public讀入後顯示) 範例21.觸控事件第1種方法:在plane上觸控,發出射線,在觸碰點顯示圓圈
範例22.觸控事件第2種方法:在update上監測,是否有射線觸控,在觸碰點顯示圓圈(最常用) 範例23.如何取得發生射線ray後,發生觸碰點的物件資料(名稱,位置) 範例24.觸控移動,觸控取得寶物,100分過關
範例25.複合物件(小蜜蜂)觸控點按後向上移動的2種方法 範例26.設定怪物不斷生成產生(兩種計時器:Invoke,InvokeRepeating) 範例27.從某個角色物件player,如何不斷發射子彈bullet,打到怪物monster則消失
範例25.直升機fire攻擊不斷來襲的敵機 範例26.製作角色跳舞 範例27.wsad控制角色上下左右移動,跳躍,蹲下
範例28.實作主角膠囊在導航網格內,自由觸控移動(塔防遊戲,top war game) 範例29.實作主角unityChan在導航網格內,自由觸控移動+動作(idle,walk,jump) 範例30.呼叫另外c#檔案的show()函數顯示分數score(1)
範例31.呼叫另外c#檔案的show()函數顯示分數score(2) 範例32.很多怪物攻擊主角,計算同一個score變數 範例33.RPG遊戲的狀態機:設定角色的5種狀態
範例34.實作enemy的四種狀態(預備idle,走路walk,攻擊attack,死亡die)
1

範例1:AddForce(單位向量)來施力讓物體移動:
成果影片
成果圖

參考網頁:Addforce,RequireComponent
參考影片:RPG Enemy Set States 設置敵人的基本屬性和狀態

☎注意:AddForce(方向向量),這個方法不實用,因為必須將物件節點加入rigidbody,造成力學效果。
☎但是:我們的遊戲程式很少會用力學模式(例如重力),而是用動力學kinematic模式(只有碰撞,沒有力學效果)。

1.本題目的:一顆足球,四個按鈕,前後左右施力移動

☎注意1:施力移動,乃是針對rigidbody來施力(myrigidbody.AddForce(Vector3.right))
☎注意2:施力的單位,乃是單位向量(前後左右:Vector3.right....)
☎注意3:可以使用RequireComponent方法,來強迫該程式碼加載的物件,自動加入組件Rigidbody
也可以自己手動加入組件(add component)。

2.程式碼:
//強迫該程式碼加載的物件,自動加入組件Rigidbody
[RequireComponent(typeof(Rigidbody))]

public class csMove : MonoBehaviour
{
Rigidbody myrb;
float forceFactor = 20;

private void Start()
{
myrb = this.gameObject.GetComponent<Rigidbody>(); }
public void moveRight()
{
myrb.AddForce(Vector3.right* forceFactor);
}
public void moveLeft()
{
myrb.AddForce(Vector3.left* forceFactor);
}
public void moveForward()
{
myrb.AddForce(Vector3.forward * forceFactor);
}
public void moveBack()
{
myrb.AddForce(Vector3.back * forceFactor);
}

成果影片
成果圖

步驟圖步驟圖步驟圖
程式碼


2

範例2.position移動到某個坐標,讓物體移動:
成果影片
成果圖

☎到google輸入關鍵字:unity basketball texture
下載籃球貼圖texture

☎注意:AddForce(方向向量),這個方法不實用,因為必須將物件節點加入rigidbody,造成力學效果。
☎但是:我們的遊戲程式很少會用力學模式(例如重力),而是用動力學kinematic模式(只有碰撞,沒有力學效果)。

移動方法(1):瞬間快速移動方法,移動到某個坐標(.position = new Vector3(..):
範例:this.gameObject.transform.position = new Vector3(5, 0, 0);

移動方法(2):瞬間快速移動方法,再累積移動1個向量距離(.position += new Vector3(..):
範例:this.gameObject.transform.position += new Vector3(2.5f, 0, 0);

(比較)**.Position,.localPosition**
transform.Position = new Vector3(0, 0, 0); //以世界為中心的座標
transform.localPosition = new Vector3(0, 0, 0); //以父物件為中心的座標
☎結論:如果初學者怕麻煩,就都用.Position即可,以世界坐標,來進行移動


成果影片
成果圖

下載籃球貼圖texture

程式碼


3

範例3.translate相對位移量,讓物體移動:
成果影片
成果圖

☎到google輸入關鍵字:unity basketball texture
下載籃球貼圖texture

1.移動,有兩種位移法:(1)再移動dx,(2)移動到某坐標點
(1).再移動dx:目前位置再新增個deltaD位移
(2).移動到某處:是直接到目標坐標

2.方法1:目前位置再新增個deltaD位移(相對位移量):translate(Vector3)
當前位置再新增個位移(translate(Vector3),表示在當前位置移動一個向量):
this.gameObject.transform.Translate(new Vector2(-5, 0) * Time.deltaTime)
this.gameObject.transform.Translate(Vector2.up * Time.deltaTime);

3.方法2:瞬間快速移動方法,移動到某個坐標(.position = new Vector3(..):
若要當前位置位移(.position += new Vector3(..)
this.gameObject.transform.position -= new Vector3(5, 0, 0);

4.沿著方向和距離移動變換translation。
(1)Translate的向量寫法有四種:
//向量坐標的四種寫法:
//(A)xyz向量
GameObject.Find("Sphere").transform.Translate(1.0f, 0, 0);
//(B)向量坐標+以該物件為中心移動self/以世界中心移動world
GameObject.Find("Sphere").transform.Translate(1.0f,0,0,Space.Self);
//(C)new Vector3向量式
GameObject.Find("Sphere").transform.Translate(new Vector3(1.0f,0,0), Space.Self);
//(D)單位向量
GameObject.Find("Sphere").transform.Translate(Vector3.right, Space.Self);

(2)以某個點為中心去移動,有三種:
注意:Space.Self只針對Translate才能用,但是不能用在position
(2-1)註明以自己為軸心去移動:transform.Translate(Vector3.up*Time.deltaTime,Space.Self)
(2-2)註明以世界坐標軸去心移動:transform.Translate(Vector3.up*Time.deltaTime,Space.World)
(2-3)註明以當前Camera為軸心去移動:transform.Translate(Vector3.up*Time.deltaTime,Camera.main.transform)

//(3)沿z軸向前移動對象1個單位/秒。
transform.Translate(0,0,1*Time.deltaTime);

//(4)在世界空間中向上移動對象1個單位/秒。
transform.Translate(0,Time.deltaTime,0,Space.World);

//(5)沿z軸向前移動對象1個單位/秒。(p.s. forward 寫作的簡寫Vector3(0, 0, 1))
transform.Translate(Vector3.forward* Time.deltaTime);

//(6)在世界空間中向上移動對象1個單位/秒。
transform.Translate(Vector3.up* Time.deltaTime,Space.World);

//(7)相對於相機向右移動對象1個單位/秒。
transform.Translate(Time.deltaTime,0,0,Camera.main.transform);


成果影片
成果圖

下載籃球貼圖texture

程式碼


4

範例4.按鈕顯示目前分數,按鈕計算兩個數字相加

成果影片
成果圖成果圖

1.Unity UI的元件有兩種類別:
(1)Text、Button、Dropdown、InputField(放在legacy遺產以內)
成果圖片

(2)Text、Button、Dropdown、InputField帶TextMeshPro字尾的元件
例如:Text-TextMeshPro
成果圖片

2.Text-TextMeshPro的用途:
TextMeshPro是用於修改自定義字型相關的元件,
如果想讓字型有更多的擴充套件(渲染,特效),可以選擇帶TextMeshPro的相關元件

3.程式碼如何能夠讀入UI/InputField值
方法:
using UnityEngine.UI;

public class cs01 : MonoBehaviour
{
//讀入傳統inputField, Text(可以由前台拖曵場景元件來設定)
public Text t1;
public InputField input1;

//設定傳統text/inputField元件的方式
public void show()
{
t1.text = "分數:99分";
input1.text = "分數:99分";
}


4.程式碼如何能夠讀入UI/InputField-TextMeshPro的值
方法:
using UnityEngine.UI;
using TMPro;

public class cs01 : MonoBehaviour
{
//(必須宣告TMP_InputField物件,但要先using TMPro;)
public TMP_InputField i1,i2,i3;
//i1的設定有兩種方法:
//(1)可以由前台拖曵場景元件inputField TextMeshPro設定)
//(2)可以用以下程式碼來設定:GameObject.Find(.....

//設定TextMeshPro text/inputField元件的方式
public void add()
{
//如果不從前台拖曵InputField TextMeshPro來設定,可以用以下程式碼來直接設定
//i1 = GameObject.Find("InputField-1").GetComponent<TMP_InputField>();
//i2 = GameObject.Find("InputField-2").GetComponent<TMP_InputField>();
//i3 = GameObject.Find("InputField-3").GetComponent<TMP_InputField>();

int n1 = int.Parse(i1.text);
int n2 = int.Parse(i2.text);
int n3 = n1 + n2;
i3.text = n3.ToString();
}

5.Unity TextMeshPr UI的text,無法顯示中文字
(1)原因:新版的文字採用TMP(TextMesh Pro)來顯示字型
☎什麼是TMP:TextMesh Pro是Unity中文字渲染的終極解決方案,原本是一個第三方外掛,後被Unity收購後併入Unity,現在可以免費使用
TextMesh Pro 是 Unity 遊戲引擎的 UI 文字,不過初次使用 TextMesh Pro 可能會遇到中文無法正常顯示而感到難用
☎TMP功能:
A)大文字不會有鋸齒狀。
B)可以處理文字渲染+文字特效
C)可以替換UI Text。比如UI Text就沒辦法對文字加入特效


(2).教學網頁:如何用TMP顯示中文字
教學網頁:Unity TextMesh Pro 教學
教學影片:用TextMesh Pro顯示中文

一、 問題敘述:當我們在使用 TextMeshPro的時候會發現如果我們在 TEXT INPUT BOX中打入中文字,在畫面中只會出現正方形的框框而不是我們所要的中文字。
成果圖片

二、 要解決這個問題,首先我們要先取得中文字體的 TTF 檔
1. 打開我的電腦進入到C:\WINDOWS\Fonts的檔案位置
成果圖片
2. 挑選自己要的中文字體
成果圖片
3. 將中文字體拖曳至Unity的Assets資料夾中
成果圖片

三、接下來我們要開始製造給 TextMeshPro使用的中文字體,
首先先在Unity編輯器上方點擊Window → TextMeshPro →Font Asset Creator。
成果圖片
1). 開啟創建字體面板
成果圖片
2). 更改Font Source為剛剛所拖曳進來的中文字體(我使用的是標楷體)
成果圖片
3). 更改Character Set為Custom Character
3). 更改完畢後下面會出現一個Custom Character List
成果圖片
4). Custom Character List是你的中文字庫,接下來我們新增我們所要的中文字到字庫裡面,我們在Custom Character List打上我們所要使用的中文字
成果圖片
5). 接著修改一下字體大小,將Font Size改成Custom Size
5). 修改字體大小為24
6). 在來就是生產字體囉!按下Generate Font Atlas開始生產字體,
生產完畢後可以在右邊看到我們的字體庫(請自行新增自己所需要的文字)
成果圖片
6). 確認沒有問題後,就按下最下方的Save TextMeshPro Font Asset,並且輸入新的文字檔案名稱儲存新的文字檔(方便辨識為主)
成果圖片

四、接下來我們創建一個新的 TextMeshPro 並設定它的字體為我們剛剛創建的字體。
五、在TextMeshPro 中的TEXT INPUT BOX打入中文字
(注意! 這裡只有辦法呈現剛剛在字庫中輸入的中文字,不在字庫中的仍然會是亂碼)
成果圖片
成果圖片
成果圖片


6.成果:
成果影片
成果圖成果圖

程式碼

5

範例5.滑鼠點按Cube改顏色,有音效(按下onMouseDown放開onMouseUp物件都會變化顏色音效)

成果影片
成果圖

下載音效檔案1下載音效檔案2
下載其它按鈕音效


1.比較2種點按事件:
(1)點按button事件(限制:只能設定按下的事件):按下onClick事件
(2)滑鼠點按物件事件:按下onMouseDown事件,放開onMouseUp事件
比較設定差異:
(1)點按button事件:按下onClick事件
程式碼:cs01.cs
public void showBlue()
成果圖片
前台設定: 成果圖片

(2)滑鼠點按物件事件:按下onMouseDown事件,放開onMouseUp事件
程式碼:cs02.cs
public AudioClip au1, au2;
void OnMouseDown()
void OnMouseUp()
成果圖片
前台設定:(不需要前台設定)
成果圖片


2.設定顏色簡單做法:修改一個cube物件,材質為紅色
GameObject.Find("Cube").GetComponent<Renderer>().material.color = Color.red;


3.設定單一固定音效簡單做法:
步驟:
(1)在cube加入AudioSource組件,
(2)拖曵音效檔案➜到AudioSource的AudioClip
(3)程式碼設定:
GameObject.Find("Cube").GetComponent<AudioSource>().Play();

3.設定2種指定音效進階做法:
步驟:
(1)在cube加入AudioSource組件,
(2)程式碼設定:
public AudioClip au1, au2;
(在前台拖曵,音效檔案到au1,au2)
GameObject.Find("Cube").GetComponent<AudioSource>().clip = au1;
GameObject.Find("Cube").GetComponent<AudioSource>().Play();


6.成果:
成果影片
成果圖

程式碼cs01
程式碼cs02

11

範例11.translate相對位移量,讓物體移動(計時器慢慢移動,幀動畫):
成果影片
成果圖

☎到google輸入關鍵字:unity basketball texture
下載籃球貼圖texture

1.幀動畫設定的方法:在update裡面,設定移動距離 = x速度 * 移動時間
void Update()
{
//移動幀動畫(移動距離 = x速度 * 移動時間)
dx = speedX * Time.deltaTime;
dy = speedY * Time.deltaTime;
dz = speedZ * Time.deltaTime;
GameObject.Find("Sphere").transform.Translate(dx, dy, dz, Space.Self);

//旋轉幀動畫(旋轉角度 = x角速度 * 移動時間)
}

☎計時器:Invoke,可以設定一次時間參數 = (幾秒後)
(1)Invoke("moveStop", 2) :2秒後,執行一次moveStop

Invoke計時器:2秒后執行moveStop函數。

(2).CancelInvoke("MethodName")
取消MethodName方法的調用。

(3)如何:關閉計時器
(3-1)關閉某個定時器(執行fire函數)
CancelInvoke("fire");

(3-2)關閉全部定時器
CancelInvoke();


成果影片
成果圖

下載籃球貼圖texture

程式碼

 
範例6.滑鼠點按Cube改顏色,改透明度,縮小,有音效
透明度為90(1~255)

成果影片
成果圖成果圖
程式碼


下載音效檔案1下載音效檔案2
下載其它按鈕音效


1.最簡單最推薦,設定顏色透明度四個參數(完全程式碼控制)

(1)Color32可以設定0~255範圍的四個參數(顏色,透明度)
GameObject.Find("Cube").GetComponent<Renderer>().material.color = new Color32(238, 38, 38, 100);
官網教學:Color32
官網教學:Color32

(2)Color可以設定0~1範圍的四個參數(顏色,透明度)
//GameObject.Find("Cube").GetComponent<Renderer>().material.color = new Color(0, 1, 1, 0.4f);
官網教學:Color

2.注意:若要設定透明度,一定要加入材質球materail,否則無法顯示透明度
(1)要先建立一個紅色材質material
(2)確定material的alpha = 100
(3)把material的render mode設定為:transparent渲染模式(不可以設定Opaque渲染模式),才能夠讓程式修改alpah而渲染成透明
☎若是沒有把render mode改成transparent,那麼就算改了alpha,也沒有辦法渲染成透明的
成果圖片


3.如何程式碼設定一個物件的縮放大小:
circle1.transform.localScale = new Vector3(0.8f, 0.8f, 0.8f);

4.成果:
成果影片
成果圖成果圖
程式碼


範例7.GUI界面:InputField直接控制改變足球轉速

成果影片
成果圖
程式碼


unity football texture
下載football貼圖

1.設定物件Cube旋轉的方法:
this.gameObject.transform.Rotate(0, n1, 0, Space.Self);
或是
this.gameObject.transform.Rotate(new Vector3(0, n1, 0), Space.Self);


2.幀動畫:在update裡面的運動,就是幀動畫

3.如果一開始InputField沒有輸入數字,造成float.Parse(...)發生錯誤
解決方法:要用try....catch...,若是出現錯誤,就可以將n1 = 0
try
{
n1 = float.Parse(i1.text);
}
catch(System.Exception e2)
{
Debug.Log(e2.Message);
n1 = 0;
}

4.成果:
成果影片
成果圖
程式碼


範例8.GUI界面:Slider直接控制改變足球轉速

成果影片
成果圖
程式碼

unity football texture
下載football貼圖
1.取得滑桿silder的值:
n1 = s1.value;

2.注意:要先設定slider的minValue,maxValue
成果圖


3.如何改變滑桿slider後,即時顯示滑桿值value
參考網頁:slider
參考網頁:slider
解決方法:
(1)先建立onSliderChange()函數
public void onSliderChange()
{
t1.text = n1.ToString("0.00");
//顯示數字的格式0.00
}

(2)在slider設定on Value Changed Event事件:
成果圖

☎比較:
(1)設定button的事件:on click

(2)設定slider的事件:on Value Changed

4.成果:
成果影片
成果圖
程式碼


範例9.彈鋼琴

1.成果影片
成果影片
成果圖片
程式碼

2.download 下載素材
下載鋼琴prefabs
下載音階檔案
下載小星星樂譜


1.最簡單最推薦,設定顏色透明度四個參數(完全程式碼控制)

(1)Color32可以設定0~255範圍的四個參數(顏色,透明度)
this.gameObject.GetComponent<Renderer>().material.color = new Color32(238, 38, 38, 100);
官網教學:Color32
官網教學:Color32

(2)Color可以設定0~1範圍的四個參數(顏色,透明度)
this.gameObject.GetComponent<Renderer>().material.color = new Color(0, 1, 1, 0.4f);
官網教學:Color

2.注意:若要設定透明度,一定要加入材質球materail,否則無法顯示透明度
(1)要先建立一個紅色材質material
(2)把material的render mode設定為:transparent渲染模式(不可以設定Opaque渲染模式),才能夠讓程式修改alpah而渲染成透明
☎若是沒有把render mode改成transparent,那麼就算改了alpha,也沒有辦法渲染成透明的
成果圖片

3.如何程式碼設定琴鍵物件的縮放大小:
若要造成向下壓的效果:就只要縮小Cube的高度(y軸)
(1)正常的縮放scale:this.gameObject.transform.localScale = new Vector3(0.03f, 0.06f, 0.3f);
(2)向下壓效果的縮放scale:this.gameObject.transform.localScale = new Vector3(0.03f, 0.045f, 0.3f);
☎重點:y軸縮放率變小


4.如何顯示小星星樂譜:
方法:使用UI:Raw Image
成果圖片成果圖片


5.成果:
成果影片
成果影片
成果圖片
程式碼

範例10.用Input.GetAxis的鍵盤控制圓球移動力道(瞬間移動,上下左右wsad,跳space,e左轉r右轉,滑鼠驅動移動)

1. 成果影片
影片:球會亂跳/消失
影片:球會慣性移動無法停下來,不受控
成果圖片
程式碼1(使用rigidbody+Translate移動,造成2種移動模式的衝突,球會亂跳/消失)
程式碼2(設定球移動的最高速度上限,避免越來越快)
程式碼3(使用rigidbody+addforce移動,球一直滾,不受控)

2.download 下載素材
下載籃球貼圖

1.觀念:Unity鍵盤控制有兩種寫法:
(1)Input.GetKeyDown(KeyCode.W)
(2)Input.GetAxis("Horizontal")

2.比較:
(1)Input.GetKeyDown(KeyCode.W):
☎缺點:只能針對鍵盤碼來做出控制反應
☎缺點:按一次只能做出一個反應,無法執行連續按下鍵的設定

(2)Input.GetAxis("Horizontal"):(推薦這個方法)
Input.GetAxis()屬於Input Manage管理模組
☎優點:除了可以控制鍵盤反應,還能夠控制滑鼠,手把,搖桿,G-sensor(陀螺儀)
☎優點:可以感測玩家鍵盤(搖桿)的按壓力道,而做出相對的力道反應速度
☎優點:可以連續按下鍵盤,做出連續反應
☎優點:程式碼很短,簡單

3.Input.GetKeyDown(KeyCode.W):
(1)鍵盤的控制指令
GetKeyDown():按鍵被按下
GetKey():按鍵被一直按住時
GetKeyUp:按鍵被按住放開時

(2)例如:
if(Input.GetKeyDown(KeyCode.A) print('A被按下')
if(Input.GetKey(KeyCode.A) print('A被一直按住')
if(Input.GetKeyUp(KeyCode.A) print('A被放開')

(3)Input.GetKeyDown常用鍵盤控制指令程式碼:
Input.GetKeyDown()常用鍵盤程式碼

4.Input.GetAxis("Horizontal"):
(1)控制物件移動需要設定7組基本動作控制程式:
_A.水平x移動:Horizontal
_B.前後z移動:Vertical
_C.上下y移動:Jump
_D.左右y轉動:Rotate(需要自行加入)
_E.滑鼠水平x移動:MouseX
_F.滑鼠前後z移動:MouseZ
_G.眼睛攝影機上下x轉動:eyeRotate(另外獨立c#程式)

(2)Input Manager:
界面管理:Edit→project settiing→Input
預設有18種鍵盤滑鼠控制鍵
(3)常用的設定控制鍵(水平,垂直移動):
horizontal水平軸想的控制指令
Vertical垂直軸想的控制指令
預設上下左右鍵:WS,AD
預設上下鍵:WS(↑↓)
預設左右鍵:AD(及←→)
例如:
negative button = ←
positive button = →
Alt negative button(第二備用鍵) = A
Alt positive buttonn(第二備用鍵) = D
left:代表這左箭頭,以及a鍵,
right:代表右鍵頭以及d鍵

(4)觀念:Input.GetAxis("Horizontal")會傳回一個按鍵訊號強度值
範圍:-1~1的浮點數
功用:若為負值,則可以讓它反向移動
功用:若為正值,則可以讓它正向移動
當按下positiv按鈕時,傳回的浮點數會往+1慢慢移動。
當按下negative按鈕時,傳回的浮點數會往-1慢慢移動。
Gravity:按下後回复恢復的力道=10
(5)四個常用參數 參數設定說明影片:
_(A).gravity:代表的是放開按鈕時,傳回的浮點數往0移動的速度。
重力代表向下,代表按鈕放開後,訊號值回到地板0地方速度
3~0.1:放開按鈕後,浮點數緩慢到0,越小越慢
3~100:放開按鈕後,浮點數迅速到0,越大越快
_(B).sensitivity:代表的是按下按鈕時,傳回的浮點數增加或減少的速度
敏銳度代表反應,代表按鈕按下時,往興奮度最大值的速度
3~0.1:按下按鈕後,浮點數緩慢到0,越小越慢
3~100:按下按鈕後,浮點數迅速到0,越大越快

(6)18個預設控制功能,就是18個Axis
每個控制項目,就是一個Axis(有其軸心名稱)
程式碼控制時,乃是取得軸心axix的值

(7)Input.GetAxis()常用鍵盤控制指令程式碼:
Input.GetAxis()常用鍵盤程式碼

4.Input.GetAxis()控制技巧:上下左右wsad,跳space,e左轉r右轉,滑鼠驅動移動
void Update()
{ //1.鍵盤移動: wsad+space跳,按越久力道越大(會感測你鍵盤按到力道,而做出移動距離)
dx = Input.GetAxis("Horizontal")*v1;
dz = Input.GetAxis("Vertical")*v1;
dy = Input.GetAxis("Jump") * v1;
this.gameObject.transform.Translate(dx, dy, dz);

//3.左轉e,右轉r(要在Input.Manager裡面新增第19個輸入項目:Rotate)
rx = 0;
ry = Input.GetAxis("Rotate");
rz = 0;
this.gameObject.transform.Rotate(rx, ry, rz);
}

5.如何新增一組新的Input.GetAxis()控制:例如:Rotate
(1)Input Manager:
界面管理:Edit→project settiing→Input
成果圖片
預設有18個內定axis
若要新增1個axis:輸入19
(2)修改第19個:改名:Rotate
成果圖片


6.Input.GetAxis()移動有個問題:一直按著鍵盤移動,速度會越來越快,直到失控
(1).鍵盤移動: wsad+space跳,按越久力道越大(會感測你鍵盤按到力道,而做出移動距離)
dx = Input.GetAxis("Horizontal") * v1;
dz = Input.GetAxis("Vertical") * v1;
dy = Input.GetAxis("Jump") * v1;

(2).問題:鍵盤若是一直按著,則會一直加速,直到失控
//印出dx,dy,dz1的數量(用Debug.log()或是print(),都可以在控制台console輸出資料)
//Debug.Log("dx=" + dx);
//Debug.Log("dy=" + dy);
//Debug.Log("dz=" + dz);
print("dx=" + dx);
print("dy=" + dy);
print("dz=" + dz);

(3).解決方法:加速若dx若超過maxVx,則dx = maxVx(限制速度,訂定速度上限)
float maxVx = 0.05f;
float maxyx = 0.05f;
float maxVz = 0.05f;
if (dx > maxVx) dx = maxVx;
if (dx < -maxVx) dx = -maxVx;
if (dy > maxyx) dy = maxyx;
if (dy < -maxyx) dy = -maxyx;
if (dz > maxVz) dz = maxVz;
if (dz < -maxVz) dz = -maxVz;
this.gameObject.transform.Translate(dx, dy, dz);


7.常見移動錯誤問題1:使用Rigidbody+Translate(),球會不合理移動(消失,或亂跳)
☎現象:若Sphere開啟rigidbody有重力效果了,卻用Translate()移動,會造成球移動出現錯誤(消失,亂跳)
☎原因:Sphere開啟rigidbody➜有重力模式效果➜會滾動,會垂直加速減速
☎但是:移動若用Translate()動力學移動模式➜會造成2種移動模式的矛盾(力學,動力學)➜造成衝突(消失,亂跳)
☎解決方法:使用力學模式移動➜剛體.addforce()
☎結論:只要開啟rigidbody重力模式了➜移動,就必須用力學模式移動(剛體.addforce(),或是剛體.velocity())
☎禁止:開啟rigidbody➜就不可以用Translate()動力學移動
影片:球會亂跳/消失
程式碼1(使用rigidbody+Translate移動,造成2種移動模式的衝突,球會亂跳/消失)
下載測試範例


8.常見移動錯誤問題2:使用Rigidbody+addforce(),球會慢慢滾動,無法馬上停下來,不受控
☎原因1:若用addforce()力學移動模式➜會有力學效果(摩擦力)➜造成滾動➜無法馬上停下來,不受控
☎原因2:若用addforce()力學移動模式➜會造成球移動速度越來越快➜不受控
☎解決方法:使用第2種力學移動模式➜剛體.velocity(),設定球的速度向量,而不是推力
影片:球會慣性移動無法停下來,不受控
程式碼3(使用rigidbody+addforce移動,球一直滾,不受控)


9.成果:
成果影片
影片:球會亂跳/消失
影片:球會慣性移動無法停下來,不受控
成果圖片
程式碼1(使用rigidbody+Translate移動,造成2種移動模式的衝突,球會亂跳/消失)
程式碼2(設定球移動的最高速度上限,避免越來越快)
程式碼3(使用rigidbody+addforce移動,球一直滾,不受控)

範例11.用Input.GetKeyDown的鍵盤控制圓球移動(瞬間移動,上下左右wsad,跳space,e左轉r右轉)

1.成果影片
成果圖片
程式碼

2.download 下載素材
下載籃球貼圖

1.觀念:Unity鍵盤控制有兩種寫法:
(1)Input.GetKeyDown(KeyCode.W)
(2)Input.GetAxis("Horizontal")

2.比較:
(1)Input.GetKeyDown(KeyCode.W):
☎缺點:只能針對鍵盤碼來做出控制反應
☎缺點:按一次只能做出一個反應,無法執行連續按下鍵的設定

(2)Input.GetAxis("Horizontal"):(推薦這個方法)
Input.GetAxis()屬於Input Manage管理模組
☎優點:除了可以控制鍵盤反應,還能夠控制滑鼠,手把,搖桿,G-sensor(陀螺儀)
☎優點:可以感測玩家鍵盤(搖桿)的按壓力道,而做出相對的力道反應速度
☎優點:可以連續按下鍵盤,做出連續反應
☎優點:程式碼很短,簡單

3.Input.GetKeyDown(KeyCode.W):
(1)鍵盤的控制指令
GetKeyDown():按鍵被按下
GetKey():按鍵被一直按住時
GetKeyUp:按鍵被按住放開時

(2)例如:
if(Input.GetKeyDown(KeyCode.A) print('A被按下')
if(Input.GetKey(KeyCode.A) print('A被一直按住')
if(Input.GetKeyUp(KeyCode.A) print('A被放開')

4.Input.GetKeyDown(KeyCode..)控制技巧:上下左右wsad,跳space,e左轉r右轉
(1)Input.GetKeyDown()常用鍵盤控制指令程式碼:
Input.GetKeyDown()常用鍵盤程式碼

//移動放大倍率, 旋轉倍率
float v1 = 1, v2 = 10;

void Update()
{
if(Input.GetKeyDown(KeyCode.D)) //向右
{
this.gameObject.transform.Translate(v1, 0, 0);
}
else if(Input.GetKeyDown(KeyCode.A)) //向左
{
this.gameObject.transform.Translate(-v1, 0, 0);
}
else if (Input.GetKeyDown(KeyCode.W)) //向前
{
this.gameObject.transform.Translate(0, 0, v1);
}
else if (Input.GetKeyDown(KeyCode.S)) //向後
{
this.gameObject.transform.Translate(0, 0, -v1);
}
else if (Input.GetKeyDown(KeyCode.Space)) //跳躍
{
this.gameObject.transform.Translate(0, v1, 0);
}
else if (Input.GetKeyDown(KeyCode.E)) //左轉
{
this.gameObject.transform.Rotate(0, -v2, 0);
}
else if (Input.GetKeyDown(KeyCode.R)) //右轉
{
this.gameObject.transform.Rotate(0, v2, 0);
}
}
5.成果:
成果影片
成果圖片
程式碼


範例11-2.鍵盤控制物件的完美移動5步驟
(鍵盤放開就停止,按下可以連續移動/幀動畫,space跳有力學效果)

1.成果影片
成果圖片
程式碼

2.download 下載素材
下載籃球貼圖

3.參考網頁:
Unity自主學習(三十):完美物件移動
【阿空】物理系統!?Unity快速入門!2D平台動作遊戲 #02
【阿空】碰撞事件解析!Unity快速入門!2D平台動作遊戲 #03


3.複製:完美移動演算法的5步驟:
(1)如何能夠連續移動➜在update內設定幀動畫移動公式➜剛體.velocity=vel,才能控制鍵盤放開讓速度=0)(不能使用transform.TransLate, 剛體.AddForce)
(2)判斷鍵盤ADWS則移動➜設定vel.x=....,vel.y=....(可以用2種方式:Input.GetAxis, Input.GetKeyDown)
(3)鍵盤放開則停止移動➜判別鍵盤放開Input.GetKeyUp則vel.x=0....
(4)有向上跳躍的力學效果➜按space鍵則使用addforce移動(但沒有設定vel.y)
(5)注意:要設定vel.y (= this.gameObject.GetComponent<Rigidbody>().velocity.y


4.觀念:Unity鍵盤控制有兩種寫法:
(1)Input.GetKeyDown(KeyCode.W)
(2)Input.GetAxis("Horizontal")

5.觀念:移動有三種方式:
(1)transform.Translate
特色:直接移動,沒有力學
this.gameObject.transform.Translate(v1, 0, 0);

(2)剛體.AddForce
特色:有力學效果(重力,摩擦力,滾動)
this.gameObject.GetComponent<Rigidbody>().AddForce(Vector3.up * 350);

(3)剛體.velocity
特色:可以直接設定剛體的速度為0(例如velocity.x=0),上面2個方法都無法直接設定速度為0
this.gameObject.GetComponent<Rigidbody>().velocity = new Vector3(1,0,0);
this.gameObject.GetComponent<Rigidbody>().velocity = vel;


5.重點注意:unity 的物體移動有2種:本題採用真實物體(有rigidbody)
(1)虛幻物體移動:
特色:物體沒有重量(沒有rigidbody),無法向上跳
移動指令:transoform.Translate()

(2)真實物體移動:
特色:物體有重量(有rigidbody),可以向上跳
移動指令:剛體.addforce()
移動指令:剛體.velocity()


6.觀念:範例10,範例11的移動所遇到的問題:
(1)問題1:Input.getKeyDown+Translate()的移動,移動無法連續,會有斷點
(2)問題2:剛體.Addforce可以連續移動,但是鍵盤停止時,不會停下來,還是會繼續移動
(3)問題3:向上跳躍,必須有實際的力學拋物的物理效果
(4)問題4:向上跳躍,無法設定速度變數vel.y的值

☎所以:範例10,範例11的2種移動程式寫法,不是完美物件移動程式寫法

7.如何解決範例10,範例11的移動發生的問題:
(1)問題1:Input.getKeyDown+Translate()的移動,移動無法連續,會有斷點
☎解決方法:
在update裡面設定移動,就會造成幀動畫效果,會連續移動
GetComponent<Rigidbody>().velocity = vel

(2)問題2:剛體.Addforce可以連續移動,但是鍵盤停止時,不會停下來,還是會繼續移動
☎解決方法:
加上鍵盤放開up的判別,若按鍵盤A,則讓速度.x=0
if (Input.GetKeyUp(KeyCode.A) || Input.GetKeyUp(KeyCode.D))
{
vel.x = 0;
}

(3)問題3:向上跳躍,必須有實際的力學拋物的物理效果
☎解決方法:
this.gameObject.GetComponent<Rigidbody>().AddForce(Vector3.up * 350);

(4)問題4:向上跳躍,沒有設定速度變數vel.y的值
☎原因:使用addforce沒有設定vel.y,所以vel不完整
☎解決方法:要設定vel.y = this.gameObject.GetComponent<Rigidbody>().velocity.y;


8.結論:完美移動的5步驟演算法:
(1)如何能夠連續運動:在update內設定幀動畫移動公式:
==>必須用,剛體.velocity,才能控制鍵盤放開讓速度=0
==>不能使用transform.TransLate, 剛體.AddForce

(2)判斷鍵盤則移動:
==>可以用2種方式:Input.GetAxis, Input.GetKeyDown

(3)如何鍵盤停止則移動停止:
==>判別鍵盤放開Input.GetKeyUp,則vel.x=0....

(4)如何能夠有向上跳躍的力學效果:
==>使用剛體.addforce()

(5)但沒有設定vel.y
==>補充設定vel.y = = this.gameObject.GetComponent<Rigidbody>().velocity.y

9.成果:
成果影片
成果圖片
程式碼


☎範例11-3.鍵盤控制物件進階設定:向上跳只能連續跳2次(6步驟)

1.成果影片
成果圖片
程式碼

2.參考網頁:
剛體,碰撞器,觸發器
Unity中OnTriggerEnter執行條件
unity物件偵測(碰撞Collider2D)
Unity 碰撞事件,觸發事件
Unity中碰撞與觸發的理解與效果實現

2.download 下載素材
下載籃球貼圖


3.複製:完美移動演算法+向上跳只能連續跳2次(6步驟):
(1)如何能夠連續移動➜在update內設定幀動畫移動公式➜剛體.velocity=vel,才能控制鍵盤放開讓速度=0)(不能使用transform.TransLate, 剛體.AddForce)
(2)判斷鍵盤ADWS則移動➜設定vel.x=....,vel.y=....(可以用2種方式:Input.GetAxis, Input.GetKeyDown)
(3)鍵盤放開則停止移動➜判別鍵盤放開Input.GetKeyUp則vel.x=0....
(4)有向上跳躍的力學效果➜按space鍵則使用addforce移動(但沒有設定vel.y)
(4-1)注意:除了按了space鍵,還要判斷是否跳躍變數num<2
(4-2)注意:當發生碰撞,on_collision()則設定num=0,跳躍變數歸零
(6)還要要設定vel.y (= this.gameObject.GetComponent<Rigidbody>().velocity.y


4.觀念:Unity兩個物體的接觸,有2種方法:
(1)『碰撞器』事件:void OnCollisionEnter(Collision c)
(☎有碰撞效果)
功能:2個物體會發生物理性的碰撞,不會穿越,會停住/或反彈
功能2:可以感測到『知道2個物體發生碰撞=Collision』
適用:適用於攻擊,和被攻擊檢測

(2)『觸發器』事件:void OnTriggerEnter(Collider c)
(☎沒有碰撞效果,只是接觸)
功能1:2個物體不會發生物理性的碰撞,會穿越,不會停住
功能2:可以感測到『知道2個物體發生接觸=觸發=Trigger』
適用:適合做攻擊範圍判定

5.觀念:Unity兩個物體的2種接觸,設定方法:
(1)『碰撞器』事件:void OnCollisionEnter(Collision c)
碰撞條件1:兩者都必須具有碰撞器元件collider
碰撞條件1:主動方(運動的物體)具有剛體元件rigidbody

(2)『觸發器』事件:void OnTriggerEnter(Collider c)
觸發條件1:兩者具有碰撞元件collider
觸發條件2:其中之一,帶有剛體組件rigidbody
觸發條件3:rigidbody必須勾選『is kinematic』(動力學模式,不考慮重力),讓它只會動,但沒有重力
觸發條件4:其中之一,勾選了Is Trigger(開啟collider的觸發器警報,若是接觸雙方了,就會發出警報,才會執行onTrigger)(同時,讓collider允許對方穿越,不會有物理碰撞)


6.觀念:碰撞collision事件有三種:
(1)當進入碰撞時執行
void OnCollisionEnter(Collision c)

(2)當碰撞器與剛體接觸時每幀執行
void OnCollisionStay(Collision c)

(3)當停止碰撞時執行
void OnCollisionExit(Collision c)

7.觀念:觸發trigger事件有三種:
(1)當Collider(碰撞體)進入觸發器時執行
void OnTriggerEnter(Collider c)

(2)當Collider與觸發器接觸時每幀執行
void OnTriggerStay(Collider c)

(3)當Collider停止觸發時執行
void OnTriggerExit(Collider c)

8.注意:Trigger觸發事件注意事項:
(1)產生Trigger觸發事件的兩個物體會:『相互穿越』,例如:球穿過地板plane
(2)isTrigger檢測的是可穿越碰撞,準確的說是因為開啟IsTrigger那個物體會被物理引擎所忽略掉,所以會產生兩個物體穿過的情況。
(3)如果人物有Rigidbody,而且想讓人物和地面產生碰撞事件且不穿過地面:
方法:不勾選isTrigger,而使用OnCollisionEnter(Collision collison)檢測碰撞。

9.注意:如何向上跳只能連續跳2次:
(1) void Update()
{
.......
//如何能夠有向上跳躍的力學效果➜space鍵盤則使用addforce(但沒有設定vel.y)
//按空白鍵次數num,若num=0或1,就可以向上跳,num=2,就不可以跳
if (Input.GetKeyDown(KeyCode.Space) && num < 2)
{
this.gameObject.GetComponent<Rigidbody>().AddForce(Vector3.up * 390);
num += 1;
}
.......
}

//若發生碰撞collision,則碰撞次數num歸零=0
private void OnCollisionEnter(Collision collision)
{
num = 0;
}


10.成果:
成果影片
成果圖片
程式碼


範例12.(虛幻物體)第三人稱視角,WSAD控制圓球移動(不向上跳),碰到獎品(觸發)得分20,100則過關

1. 成果影片1
成果影片2
成果圖片成果圖片
程式碼

2.download 下載素材
下載籃球貼圖
下載碰撞音效
下載win勝利音效


4.目的:
(1)虛幻物體:但要使用Rigidbody剛體(碰撞觸發用),但不可以跳躍

(2)方法1:感應到Cylinder1~Cylinder5,則+20分,使用onTriggerEnter碰撞事件
(2-1)Sphere設定:
☎Collider(碰撞器):Sphere Collider,不勾選IsTrigger
☎設定主動的basketball(Sphere)➜新增Rigidbody(剛體),不勾選IsKinematic
(2-2)Cylinder1~Cylinder5設定:
☎Collider(碰撞器):Sphere Collider,勾選IsTrigger

(3)方法2:感應到Cylinder1~Cylinder5,則+20分,使用onTriggerEnter碰撞事件
(3-1)Sphere設定:
(3-2)設定主動的(Sphere)➜新增Rigidbody(剛體)
(3-3)把Rigidbody的Is Kinematic 打勾(動力學模式,不考慮重力)
(4)Cylinder1~Cylinder5設定:
☎Collider(碰撞器):Collider,勾選IsTrigger


5.重點注意:unity 的物體移動有2種:
(1)虛幻物體移動:
特色:物體沒有重量(沒有rigidbody),無法向上跳
移動指令:transoform.Translate()

(2)真實物體移動:
特色:物體有重量(有rigidbody),可以向上跳
移動指令:剛體.addforce()
移動指令:剛體.velocity()


6.碰撞的觀念:
(1)有 2 種碰撞方式:
一、Collision碰撞,造成物理碰撞,可以在碰撞時執行OnCollision函式。
( 接觸 + 物理碰撞)
二、Trigger觸發,取消所有物理碰撞,可以在觸發時執行OnTrigger函式。
(接觸,但是不發生物理力學的碰撞)
(2)參考資料網址
所以兩個物件A跟B接觸時,不可能同時產生碰撞 + 觸發,最多產生其中一種,
但是可以辦到讓A跟B產生碰撞,A跟C產生觸發。

(3)碰撞需要 2 個條件:Collider,Rigidbody
(3-1)想要讓程式判斷兩個物件有接觸,則雙方都必需要有Collider(碰撞器),
(3-2)並且正在動的一方一定要有Rigidbody(剛體)才有效,另一方有沒有剛體無所謂。
※也就是說,如果動的一方沒有剛體,它去撞的靜止一方即使有剛體,也是當作沒撞到。

(4)產生接觸的設定方式如下:
一、Collision碰撞:雙方都有碰撞器collider,並且至少動的一方有剛體rigidbody,
就會造成碰撞,可以執行OnCollision函式。
※但若雙方都勾了Kinematic運動學,或任一方勾了Trigger觸發器,則碰撞無效。

二、Trigger觸發:雙方都有碰撞器,並且至少動的一方有剛體,
並且至少其中一方的碰撞器有勾觸發器,就會造成觸發,可以執行OnTrigger函式。

(5)接觸後的函式又細分為Enter、Stay、Exit三種
以Trigger為例,就是OnTriggerEnter、OnTriggerStay、OnTriggerExit
(Collision依此類推)。
Enter函式是當兩個物件接觸的瞬間,會執行一次這個函式;
Stay函式是當兩個物件持續接觸時,會不斷執行這個函式;
Exit函式是當兩個物件分開的瞬間,會執行一次這個函式。

(6)詳細解釋:
【Collider碰撞器】:
最重要的核心!只要有碰撞器就會對其他物件產生碰撞;
若自身要受到碰撞的話,則需要碰撞器 + 剛體,缺一不可。
若自身要受到程式Translate位移或Rotate旋轉,也至少需要碰撞器。

(7)【IsTrigger觸發器】:
勾了就不允許自身受到/造成碰撞,也不會受到重力等物理作用力影響,
而是改為Trigger觸發,雙方碰到時會直接穿越並執行Trigger函式。
但自身還是可以受到程式位移或旋轉。

(8)【Rigidbody剛體】:
允許自身受到碰撞,且動的一方要有剛體才允許產生Trigger觸發。
加了剛體的物件才會受到物理作用力,如受到重力而落下、被物理作用力推動/旋轉,
也無法主動穿越其他碰撞器,這都是受到剛體的影響。
而對方若是沒加剛體,對方就不會受到碰撞的作用力推擠,但仍然可以判斷雙方有碰撞。

(9)【IsKinematic運動學】:
不允許自身受到碰撞,但還是會對其他物件造成碰撞。
也不會受到重力等物理作用力影響。
※雙方都勾運動學的話,雙方都不會受到碰撞,會直接穿越,
 因此會被視為沒有碰撞到,無法執行OnCollision函式。
※對自身而言,剛體+運動學的效果,就等同於不加剛體,
 唯一差異是「剛體+運動學」去撞「靜止的碰撞體」時,
 仍可以執行碰撞、觸發函式;
 但雙方都不加剛體的話,則無法執行碰撞、觸發函式。

(10)【Constraints限制】:
自身受到碰撞時,勾選的位置(Position)、角度(Rotarion)不受力。
很類似把運動學拆成六個細項來設定,但唯一差別在於仍然算有碰撞,
可以照常執行OnCollision函式。

(11)【Drag空氣阻力】:提高數值可減少落下速度、被撞飛距離。

(12)【Angular Drag角阻力】:提高數值可減少旋轉速度。



7.觸發onTrigger的程式碼寫法(碰到獎品加分20,獎品消失)
private void OnTriggerEnter(Collider other)
{
if(other.name== "Cylinder1" || other.name == "Cylinder2" || other.name == "Cylinder3" || other.name == "Cylinder4" || other.name == "Cylinder5")
{
Destroy(other.gameObject);
score += 20;
}
}

8.設定接觸後觸發trigger的2種方法:(任選一種方法即可,第1種方法最簡單)
8-1.第1種方法:設定2個條件:Collider,Rigidbody
(1)Sphere設定:
☎Collider(碰撞器):Sphere Collider,不勾選IsTrigger
☎設定主動的basketball(Sphere)➜新增Rigidbody(剛體),不勾選IsKinematic
(2)Cylinder1~Cylinder5設定:
☎Collider(碰撞器):Sphere Collider,勾選IsTrigger
(3)設定AudioSource:設定AudioClip(=mouseDown)
成果圖片

8-2.第2種方法:設定4個條件:Collider,Rigidbody,Is Trigger,Is Kinematic
(1)接觸物件雙方都必需要有Collider(碰撞器),要注意collider的外框有沒有包住物件
成果圖片成果圖片
(2)設定主動的basketball(Sphere)➜新增Rigidbody(剛體)
(3)把Rigidbody的Is Kinematic 打勾(不然就要取消勾選Graivity)
成果圖片
(4)basketball(Sphere)➜Collider(碰撞器)➜打勾 Is Trigger(觸發)
成果圖片
Cylinder1➜Collider(碰撞器)➜打勾 Is Trigger(觸發)
Cylinder2➜Collider(碰撞器)➜打勾 Is Trigger(觸發)
Cylinder3➜Collider(碰撞器)➜打勾 Is Trigger(觸發)
Cylinder4➜Collider(碰撞器)➜打勾 Is Trigger(觸發)
Cylinder5➜Collider(碰撞器)➜打勾 Is Trigger(觸發)
成果圖片


9.結論:程式碼鍵盤控制WSAD移動,第三人稱視角必須以世界坐標world移動,第一人稱視角必須以物件坐標self移動
(1)第三人稱視角,以世界坐標視角移動:Space.World
this.transform.Translate(dx, dy, dz, Space.World);

(2)第一人稱視角,以物件坐標視角移動:Space.Self
this.transform.Translate(dx, dy, dz, Space.Self);


7.成果:
成果影片1
成果影片2
成果圖片成果圖片
程式碼

範例12-2.(真實物體)第三人稱視角,WSAD控制圓球移動(可向上跳),碰到獎品(碰撞)得分20,100則過關

1.成果影片
成果圖片成果圖片
程式碼

2.download 下載素材
下載籃球貼圖
下載碰撞音效
下載win勝利音效

3.參考網頁:
剛體,碰撞器,觸發器
Unity中OnTriggerEnter執行條件
unity物件偵測(碰撞Collider2D)
Unity 碰撞事件,觸發事件
Unity中碰撞與觸發的理解與效果實現


4.目的:
(1)真實物體:使用Rigidbody剛體,可以跳躍
(2)碰撞到Cylinder1,Cylinder2,Cylinder3,則+30分,使用onCollisionEnter碰撞事件
shpere:設定rigidbody (3)感應到Cylinder4,Cylinder5,則+5分,使用onTriggerEnter碰撞事件
shpere:設定rigidbody, Cylinder4,Cylinder5的collider碰撞器:勾選isTrigger


5.重點注意:unity 的物體移動有2種:
(1)虛幻物體移動:
特色:物體沒有重量(沒有rigidbody),無法向上跳
移動指令:transoform.Translate()

(2)真實物體移動:
特色:物體有重量(有rigidbody),可以向上跳
移動指令:剛體.addforce()
移動指令:剛體.velocity()


6.複製:完美移動演算法+向上跳只能連續跳2次(6步驟):
(1)如何能夠連續移動➜在update內設定幀動畫移動公式➜剛體.velocity=vel,才能控制鍵盤放開讓速度=0)(不能使用transform.TransLate, 剛體.AddForce)
(2)判斷鍵盤ADWS則移動➜設定vel.x=....,vel.y=....(可以用2種方式:Input.GetAxis, Input.GetKeyDown)
(3)鍵盤放開則停止移動➜判別鍵盤放開Input.GetKeyUp則vel.x=0....
(4)有向上跳躍的力學效果➜按space鍵則使用addforce移動(但沒有設定vel.y)
(4-1)注意:除了按了space鍵,還要判斷是否跳躍變數num<2
(4-2)注意:當發生碰撞,on_collision()則設定num=0,跳躍變數歸零
(6)還要要設定vel.y (= this.gameObject.GetComponent<Rigidbody>().velocity.y


7.觀念:Unity兩個物體的接觸,有2種方法:
(1)『碰撞器』事件:void OnCollisionEnter(Collision c)
(☎有碰撞效果)
功能:2個物體會發生物理性的碰撞,不會穿越,會停住/或反彈
功能2:可以感測到『知道2個物體發生碰撞=Collision』
適用:適用於攻擊,和被攻擊檢測

(2)『觸發器』事件:void OnTriggerEnter(Collider c)
(☎沒有碰撞效果,只是接觸)
功能1:2個物體不會發生物理性的碰撞,會穿越,不會停住
功能2:可以感測到『知道2個物體發生接觸=觸發=Trigger』
適用:適合做攻擊範圍判定

8.觀念:Unity兩個物體的2種接觸,設定方法:
(1)『碰撞器』事件:void OnCollisionEnter(Collision c)
碰撞條件1:兩者都必須具有碰撞器元件collider
碰撞條件1:主動方(運動的物體)具有剛體元件rigidbody

(2)『觸發器』事件:void OnTriggerEnter(Collider c)
觸發條件1:兩者具有碰撞元件collider
觸發條件2:其中之一,帶有剛體組件rigidbody
觸發條件3:rigidbody必須勾選『is kinematic』(動力學模式,不考慮重力),讓它只會動,但沒有重力
觸發條件4:其中之一,勾選了Is Trigger(開啟collider的觸發器警報,若是接觸雙方了,就會發出警報,才會執行onTrigger)(同時,讓collider允許對方穿越,不會有物理碰撞)


9.觀念:碰撞collision事件有三種:
(1)當進入碰撞時執行
void OnCollisionEnter(Collision c)

(2)當碰撞器與剛體接觸時每幀執行
void OnCollisionStay(Collision c)

(3)當停止碰撞時執行
void OnCollisionExit(Collision c)

10.觀念:觸發trigger事件有三種:
(1)當Collider(碰撞體)進入觸發器時執行
void OnTriggerEnter(Collider c)

(2)當Collider與觸發器接觸時每幀執行
void OnTriggerStay(Collider c)

(3)當Collider停止觸發時執行
void OnTriggerExit(Collider c)

11.注意:Trigger觸發事件注意事項:
(1)產生Trigger觸發事件的兩個物體會:『相互穿越』,例如:球穿過地板plane
(2)isTrigger檢測的是可穿越碰撞,準確的說是因為開啟IsTrigger那個物體會被物理引擎所忽略掉,所以會產生兩個物體穿過的情況。
(3)如果人物有Rigidbody,而且想讓人物和地面產生碰撞事件且不穿過地面:
方法:不勾選isTrigger,而使用OnCollisionEnter(Collision collison)檢測碰撞。


12.觸發onTrigger的程式碼寫法(碰到獎品加分20,獎品消失)
☎注意:如何取得被觸發物件名稱:other.name
private void OnTriggerEnter(Collider other)
{
if (other.name == "Cylinder1" || other.name == "Cylinder2" || other.name == "Cylinder3" || other.name == "Cylinder4" || other.name == "Cylinder5")
{
Destroy(other.gameObject);
score += 5;
GameObject.Find("Tscore").GetComponent<Text>().text = "分數:" + score.ToString();
this.gameObject.GetComponent<AudioSource>().Play();
if (score == 100)
{
GameObject.Find("Tscore").GetComponent<Text>().text = "恭喜,過關";
this.gameObject.GetComponent<AudioSource>().clip = au1;
this.gameObject.GetComponent<AudioSource>().Play();
}
}
}

13.碰撞OnCollision的程式碼寫法(碰到獎品加分20,獎品消失)
☎注意:如何取得被碰撞物件名稱:collision.gameObject.name
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.name == "Cylinder1" || collision.gameObject.name == "Cylinder2" || collision.gameObject.name == "Cylinder3" || collision.gameObject.name == "Cylinder4" || collision.gameObject.name == "Cylinder5")
{
Destroy(collision.gameObject);
score += 30;
GameObject.Find("Tscore").GetComponent<Text>().text = "分數:" + score.ToString();
this.gameObject.GetComponent<AudioSource>().Play();
if (score == 100)
{
GameObject.Find("Tscore").GetComponent<Text>().text = "恭喜,過關";
this.gameObject.GetComponent<AudioSource>().clip = au1;
this.gameObject.GetComponent<AudioSource>().Play();
}
}
}

14.設定碰撞4個條件:Collider,Rigidbody,Is Trigge,Is Kinematic
(1)碰撞物件雙方都必需要有Collider(碰撞器),要注意collider的外框有沒有包住物件
成果圖片成果圖片
(2)設定主動的basketball(Sphere)➜新增Rigidbody(剛體)
(3)把Rigidbody的Is Kinematic 打勾(不然就要取消勾選Graivity)
成果圖片
(4)basketball(Sphere)➜Collider(碰撞器)➜打勾 Is Trigger
成果圖片
Cylinder1➜Collider(碰撞器)➜打勾 Is Trigger
Cylinder2➜Collider(碰撞器)➜打勾 Is Trigger
Cylinder3➜Collider(碰撞器)➜打勾 Is Trigger
Cylinder4➜Collider(碰撞器)➜打勾 Is Trigger
Cylinder5➜Collider(碰撞器)➜打勾 Is Trigger
成果圖片

15.結論:程式碼鍵盤控制WSAD移動,第三人稱視角必須以世界坐標world移動,第一人稱視角必須以物件坐標self移動
(1)第三人稱視角,以世界坐標視角移動:Space.World
this.transform.Translate(dx, dy, dz, Space.World);

(2)第一人稱視角,以物件坐標視角移動:Space.Self
this.transform.Translate(dx, dy, dz, Space.Self);


16.成果:
成果影片
成果圖片成果圖片
程式碼

範例13.(虛幻物體)第一人稱視角,WSAD控制圓球移動,碰到獎品得分20,100則過關

1.成果影片
成果圖片成果圖片
程式碼

2.download 下載素材
下載籃球貼圖
下載碰撞音效
下載win勝利音效


3.重點注意:unity 的物體移動有2種:
(1)虛幻物體移動:
特色:物體沒有重量(沒有rigidbody),無法向上跳
移動指令:transoform.Translate()

(2)真實物體移動:
特色:物體有重量(有rigidbody),可以向上跳
移動指令:剛體.addforce()
移動指令:剛體.velocity()


4.思路:虛幻物體移動/碰撞的重點:
(1)虛幻物體➜物體沒有重量(沒有rigidbody),無法向上跳
移動指令:transoform.Translate()
(2)照理講,虛幻物體,應該沒有剛體/rigidbody了
(3)但是問題:因為要發生碰撞collision/感應trigger,
所以➜球一定要附加有剛體/rigidbody,才能產生碰撞
(4)但是問題:一但放上rigidbody了,不就是真實物體了嗎?(不是虛幻物體)
(5)解決方法:在rigidbody➜勾選Is Kinematic(設定為動力學運動,不是力學運動)
成果圖片
(6)結果:球,還是『虛幻』物體,但也可以發生碰撞/感應事件


5.思路:虛幻物體(第一人稱視角)移動的重點:
(1)虛幻物體➜物體沒有重量(沒有rigidbody),無法向上跳
移動指令:transoform.Translate()
(2)第一人稱視角移動的方法
☎第一人稱視角,以物件坐標視角移動:Space.Self
☎範例:this.transform.Translate(dx, dy, dz, Space.Self);


3.設定碰撞4個條件:Collider,Rigidbody,Is Trigge,Is Kinematic
(1)碰撞物件雙方都必需要有Collider(碰撞器),要注意collider的外框有沒有包住物件
成果圖片成果圖片
(2)設定主動的basketball(Sphere)➜新增Rigidbody(剛體)
(3)把Rigidbody的Is Kinematic 打勾(不然就要取消勾選Graivity)
成果圖片
(4)basketball(Sphere)➜Collider(碰撞器)➜打勾 Is Trigger
成果圖片
Cylinder1➜Collider(碰撞器)➜打勾 Is Trigger
Cylinder2➜Collider(碰撞器)➜打勾 Is Trigger
Cylinder3➜Collider(碰撞器)➜打勾 Is Trigger
Cylinder4➜Collider(碰撞器)➜打勾 Is Trigger
Cylinder5➜Collider(碰撞器)➜打勾 Is Trigger
成果圖片

4.如何設定第一人稱視角
方法:把Main camera設定到Sphere的子物件,既可以跟著Sphere移動了
(main Camera的視角,從後面往前看)
成果圖片

5.結論:程式碼鍵盤控制WSAD移動,第三人稱視角必須以世界坐標world移動,第一人稱視角必須以物件坐標self移動
(1)第三人稱視角,以世界坐標視角移動:Space.World
this.transform.Translate(dx, dy, dz, Space.World);

(2)第一人稱視角,以物件坐標視角移動:Space.Self
this.transform.Translate(dx, dy, dz, Space.Self);


6.成果:
成果影片
成果圖片成果圖片
程式碼

範例13-2.(真實物體)第一人稱視角:完美移動6步驟(WSAD控制圓球移動,碰到獎品得分20,100則過關)

1.成果影片
成果圖片成果圖片
程式碼1
程式碼2

2.download 下載素材
下載籃球貼圖
下載碰撞音效
下載win勝利音效


3.目的:
(1)真實物體:使用Rigidbody剛體,可以跳躍
(2)設定第一人稱視角移動(移動Main Camera)
☎需解決Main Camera跟著球滾動的問題
(3)用完美5步驟移動
☎需解決剛體.velocity,無法局部坐標轉動後,再局部坐標方向移動的問題
(4)碰撞collision到Cylinder1,Cylinder2,則+5分,使用onCollisionEnter碰撞事件
shpere:設定rigidbody
☎需解決碰撞後,球體會反彈旋轉的問題
☎解決方法:使用感應觸發trigger,就不會有碰撞後的物理旋轉效果了
(5)感應觸發trigger到Cylinder3,Cylinder4,Cylinder5,則+30分,使用onTriggerEnter碰撞事件
shpere:設定rigidbody,
Cylinder4,Cylinder5的collider碰撞器:勾選isTrigger


4.重點注意:unity 的物體移動有2種:
(1)虛幻物體移動:
特色:物體沒有重量(沒有rigidbody),無法向上跳
移動指令:transoform.Translate()

(2)真實物體移動:
特色:物體有重量(有rigidbody),可以向上跳
移動指令:剛體.addforce()
移動指令:剛體.velocity()


5.需解決Main Camera跟著球滾動的問題
☎方法:
限制球體的轉動:rigidbody➜constraints➜freeze rotation x,z
(x軸,z軸不轉動(只讓y軸可以轉動))
成果圖片


6.☎需需解決剛體.velocity,無法局部坐標轉動後,再局部坐標方向移動的問題
(1)思路:因為是第一人稱視角移動,所以vel向量,要從世界坐標,轉成局部球體的局部坐標向量:
解決方法:計算球物體sphere的局部坐標
(2)真實物體➜物體有重量(有rigidbody),可以向上跳
移動指令:剛體.addforce()
移動指令:剛體.velocity()
(3)真實物體,第一人稱視角移動的方法
☎注意:剛體.velocity()沒有第一人稱視角移動的Space.Self功能

☎解決方法:計算球物體sphere的局部坐標
(3)步驟1:計算局部坐標的合成向量
前進合成向量 movePos = 向量(x,y) = x向量 * 移動值 + y向量 * 移動值
(錯誤) = Vector3.right* vel.x + Vector3.forward * vel.z
(正確) = transform.right * vel.x + transform.forward * vel.z;
局部坐標的結果:Vector3 movePos = transform.right * vel.x + transform.forward * vel.z;

(4)步驟2:顯示局部坐標,(vel.x, vel.y, vel.z)
vel.x = movePos.x;
vel.z = movePos.z;

(5)步驟3:局部坐標設定剛體移動的velocity,(vel.x, vel.y, vel.z)
this.gameObject.GetComponent<Rigidbody>().velocity = vel;


7.觀念:兩種單位向量:局部坐標向量 / 世界坐標向量:
☎參考網頁: Unity知識總結系列(一):物體移動方法
(1)局部(自身)座標:單位向量
Transform.forward 向前
Transform.right 向右
Transform.up 向上

(2)世界座標:單位向量
Vector3.forward 向前
Vector3.back 向後
Vector3.left 向左
Vector3.right 向右


8.☎需解決碰撞後,球體會反彈旋轉的問題
(1)原因:若是設定碰撞collision就會造成物理碰撞反彈效果
(2)解決方法:使用感應觸發trigger,就不會有碰撞後的物理旋轉效果了
方法:感應觸發trigger到Cylinder4,Cylinder5,則+5分,使用onTriggerEnter碰撞事件
shpere:設定rigidbody,
Cylinder4,Cylinder5的collider碰撞器:勾選isTrigger


9.成果:
成果影片
成果圖片成果圖片
程式碼1
程式碼2

範例14.手機按鈕控制移動法:按1次移動2秒的緩動動畫,幀動畫(計時器慢慢移動,緩動動畫)

1.成果影片
成果圖片
程式碼

2.download 下載素材
下載籃球貼圖


3.重點注意:unity 的物體移動有2種:本題採用虛幻物體(沒有rigidbody)
(1)虛幻物體移動:
特色:物體沒有重量(沒有rigidbody),無法向上跳
移動指令:transoform.Translate()

(2)真實物體移動:
特色:物體有重量(有rigidbody),可以向上跳
移動指令:剛體.addforce()
移動指令:剛體.velocity()


3.手機無法鍵盤輸入,如何做出緩慢移動的效果:
方法:使用緩動幀動畫
例如:向右移動的方法:採用幀動畫(frame animation)
原理:
(1)設定udapte()幀動畫:使用SpeedY * Time.deltaTime
void Update()
{
float dx = SpeedX * Time.deltaTime;
float dy = SpeedY * Time.deltaTime;
float dz = SpeedZ * Time.deltaTime;
this.gameObject.transform.Translate(dx, dy, dz, Space.Self);

float ry = RotateY * Time.deltaTime;
this.gameObject.transform.Rotate(0, ry, 0, Space.Self);
}
(2)按了按鈕(向右):
原理:一開始向右速度=2,但1s後後就啟動一個計時器,執行stopSpeedX,讓速度=0
public void moveRight()
{
speedX = 2;
//1秒後停止X方向速度
Invoke("stopSpeedX", 1);
}
void stopSpeedX()
{
speedX = 0;
}

4.結論:程式碼鍵盤控制WSAD移動,第三人稱視角必須以世界坐標world移動,第一人稱視角必須以物件坐標self移動
(1)第三人稱視角,以世界坐標視角移動:Space.World
this.transform.Translate(dx, dy, dz, Space.World);

(2)第一人稱視角,以物件坐標視角移動:Space.Self
this.transform.Translate(dx, dy, dz, Space.Self);


5.成果:
成果影片
成果圖片
程式碼

範例15.使用unity官方的UI Menu來顯示隱藏不同物件

1.成果影片
成果圖片成果圖片成果圖片
程式碼

2.download 下載素材
下載直升機物件(prefab)
下載蜜蜂bee物件(asset store)(或下載蜜蜂bee物件(prefab))
下載狐狸fox物件(asset store)(或下載狐狸fox物件(prefab))
下載Unity UI Menu的asset store(或下載Unity Menu物件(prefab),必須自行加入eventSystem

3.建立場景,匯入fox, bee, 直升機, UI menu
下載直升機(prefab):建議
(1)import package
成果圖片成果圖片
(2)把直升機的prefabs拖曵到場景內
成果圖片

4.使用Unity官方提供的UI/Menu物件
(1)先下載
下載Unity UI Menu的asset store
(2)import
成果圖片成果圖片
(3)切換場景:先觀看unity官方提供的各種Scene:觀看menu-3D場景
成果圖片
(4)查詢menu的物件:menu在Canvas裡面
成果圖片

(5)把menu複製到我們原本的場景sampleScene
方法:把Canvas拖曵到/asset/prefabs/,建立Canvas鑄件
成果圖片
然後再切換場景回到sampleScene,
方法:拖曵asset/prefabs/裡面的Canvas鑄件
成果圖片
測試,執行,發生錯誤
成果圖片
原因:使用UI元件,必須同時放置EventSystem元件

(6)測試:觀看新增一個button,會同時建立多少元件(可以在另外一個Scene測試)
結果:產生3個物件:Canvas,Button,EventSystem
成果圖片

(7)再自行新增一個UI/EventSystem物件
成果圖片
測試,執行,成功
成果圖片

5.建立UI/Menu的button(直升機,狐狸,蜜蜂,quit)
(1)先把prefabs➜unpack,再修改
成果圖片成果圖片
(2)建立修改button:(直升機,狐狸,蜜蜂,quit)
成果圖片

6.寫程式碼(切換物件):cs01
程式碼掛載在一個EmptyObject裡面
(1)建立一個Empty Object,加入cs01
程式碼掛載在一個EmptyObject裡面
成果圖片成果圖片

(2)隱藏物件的方法有2種:
☎方法1:GameObject.Find("Helicopter").SetActive(true);
☎方法2:public GameObject plane, bee, fox;
plane.SetActive(true);
☎建議使用方法:使用public GameObject obj1方法+ obj1.SetActive(True):

(3)常見問題:若是用方法1,先隱藏cube物件,再用程式碼讓它setActive(true),則無法顯示
因為:若是先隱藏,那麼就無法找到這個物件來(GameObjectg.fidn("cube")....找不到)
☎解決方法:用方法2,不要用方法1(GameObject.find來尋找物件),而用方法2(public GameObject obj1來設定這個cube)
成果圖片程式碼
☎注意:隱藏cube物件的方法:成果圖片

(2)程式碼:
public GameObject plane, bee, fox;
public void showPlane()
{
//注意:若是先隱藏cube物件,再用程式碼讓它setActive(true),則無法顯示
//因為:若是先隱藏,那麼就無法找到這個物件來(GameObjectg.fidn("cube")....找不到)
//解決方法:不要用GameObject.find來尋找物件,而用public GameObject obj1來設定這個cube
//GameObject.Find("Helicopter").SetActive(true);
//GameObject.Find("FantasyBee").SetActive(false);
//GameObject.Find("Fox").SetActive(false);
plane.SetActive(true);
bee.SetActive(false);
fox.SetActive(false);
}

7.如何關閉遊戲程式:
(1)關閉遊戲程式
☎指令:Application.Quit();
☎注意:Application.Quit是在遊戲Build後才會有作用,編輯狀態下是無效的

(2)編輯狀態下關閉程式
☎如果想要在編輯狀態下關閉執行的話要使用到EditorApplication.isPlaying = false這個參數(先要加入using UnityEditor;)
☎指令:
using UnityEditor;
Application.Quit();
EditorApplication.isPlaying = false;

8.成果:
成果影片
成果圖片成果圖片成果圖片
程式碼

範例16.用緩動幀動畫來控制直升機移動:上下、左移右移、左轉右轉

1.成果影片
成果影片
成果圖片成果圖片
程式碼

2.download 下載素材
下載直升機(prefab):建議
或是下載直升機(asset store)
下載直升機音效
下載Unity UI Menu的asset store(或下載Unity Menu物件(prefab),必須自行加入eventSystem

3.建立場景,匯入直升機
下載直升機(prefab):建議
(1)import package
成果圖片成果圖片
(2)把直升機的prefabs拖曵到場景內
成果圖片

4.使用Unity官方提供的UI/Menu物件
(1)先下載
下載Unity UI Menu的asset store
(2)import
成果圖片成果圖片
(3)切換場景:先觀看unity官方提供的各種Scene:觀看menu-3D場景
成果圖片
(4)查詢menu的物件:menu在Canvas裡面
成果圖片

(5)把menu複製到我們原本的場景sampleScene
方法:把Canvas拖曵到/asset/prefabs/,建立Canvas鑄件
成果圖片
然後再切換場景回到sampleScene,
方法:拖曵asset/prefabs/裡面的Canvas鑄件
成果圖片
測試,執行,發生錯誤
成果圖片
原因:使用UI元件,必須同時放置EventSystem元件

(6)測試:觀看新增一個button,會同時建立多少元件(可以在另外一個Scene測試)
結果:產生3個物件:Canvas,Button,EventSystem
成果圖片

(7)再自行新增一個UI/EventSystem物件
成果圖片
測試,執行,成功
成果圖片

5.建立UI/Menu的button(向右,向左,向前,向後,向上,向下,左轉,右轉,開,關)
(1)先把prefabs➜unpack,再修改
成果圖片成果圖片
(2)建立修改button:控制台,向右,向左,向前,向後,向上,向下,左轉,右轉,開,關
成果圖片

6.寫程式碼(緩動幀動畫,移動,旋轉):cs01
(1)程式碼,掛在直升機物件上
(2)移動:若向右,則speedX = 2,慢慢移動2秒,然後停止
(3)旋轉:若右轉,則rotateY = 10,慢慢旋轉2秒,然後停止
(4)第三人稱視角,移動必須以世界坐標為基準(Space.World)
第一人稱視角,移動必須以物件本身坐標為基準(Space.Self)
程式碼

7.直升機向上移動的方法:採用幀動畫(frame animation)
原理:
(1)設定udapte()幀動畫:使用SpeedY * Time.deltaTime
void Update()
{
float dx = SpeedX * Time.deltaTime;
float dy = SpeedY * Time.deltaTime;
float dz = SpeedZ * Time.deltaTime;
this.gameObject.transform.Translate(dx, dy, dz, Space.Self);

float ry = RotateY * Time.deltaTime;
this.gameObject.transform.Rotate(0, ry, 0, Space.Self);
}
(2)按了按鈕(向上):
原理:一開始向上速度=2,但10s後後就啟動一個計時器,執行moveY,讓速度=0
public void MoveUp()
{
SpeedY = 2;
Invoke("moveY", 10);
}
void moveY()
{
SpeedY = 0;
}

7.(開,關)顯示螺旋槳動畫的2種方法:
(1)方法1:直升機啟動螺旋槳動畫方法:
原理:把節點的component<Animation>打勾:
程式碼打開動畫方法:this.gameObject.GetComponent<Animation>().enabled = true;
public void TurnOn()
{
this.gameObject.GetComponent<Animation>().enabled = true;
this.gameObject.GetComponent<AudioSource>().Play();
}

步驟圖1 (2)方法2:讓螺旋槳物件Roter不斷旋轉
2-1.先找到螺旋槳物件:Roter
成果圖片
2-2.開:讓直升機旋轉螺旋槳,播放直升機音效
成果圖片
public void turnOn()
{
//螺旋槳物件Rotor,轉速spinY(沒有設定停止計時器,就會不斷旋轉)
spinY = 800;
//播放音效
this.gameObject.GetComponent<AudioSource>().Play();
}
//update裡面,螺旋槳物件Rotor,以自己的中心,旋轉
sy = spinY * Time.deltaTime;
GameObject.Find("Rotor").transform.Rotate(0, sy, 0, Space.Self);
//設定螺旋槳一直循環播放音效Loop
成果圖片

2-3.關:停止直升機旋轉螺旋槳,關閉直升機音效


8. 參考影片:無人機飛行
參考影片:第16章調用component(無人機飛行)

9.成果:
(1)成果影片
成果影片

(2)步驟圖:
步驟圖1步驟圖2步驟圖3步驟圖4步驟圖5

(3)成果圖:
成果圖片成果圖片
程式碼:方法1
程式碼:方法2

範例17.切換場景

1.成果影片
成果圖片成果圖片成果圖片
程式碼

2.download 下載素材
下載直升機物件(prefab)
下載蜜蜂bee物件(prefab)
下載狐狸fox物件(prefab)
下載Unity Menu物件(prefab),必須自行加入eventSystem

3.建立三個場景,分別匯入fox, bee, 直升機,每個場景都有UI menu
(1)建立三個場景:s1, s2, s3
成果圖片
(2)分別匯入 bee, 直升機,fox
(3)分別匯入 UI menu
☎注意:使用UI元件,必須同時放置EventSystem元件
(4)修改menu名稱:切換場景,到第1場景,到第2場景,到第3場景,quit
成果圖片
成果圖片
成果圖片

4.寫程式碼(切換場景):cs01
程式碼掛載在一個EmptyObject裡面/或是直升機,fox,bee
(1)切換場景的指令:
using UnityEngine.SceneManagement;
public void gotoScene1()
{
SceneManager.LoadScene("s1");
}

(2)執行結果:發生錯誤

(3)☎注意:要執行前,要先把2個場景都加入Build Scene內,才能build,才能執行
方法:
A).先切換到s1
B).File/Build Settings➜Scene to Build:s1,s2,s3
C).再切換到s2....s3
成果圖片

5.如何關閉遊戲程式:
(1)關閉遊戲程式
☎指令:Application.Quit();
☎注意:Application.Quit是在遊戲Build後才會有作用,編輯狀態下是無效的

(2)編輯狀態下關閉程式
☎如果想要在編輯狀態下關閉執行的話要使用到EditorApplication.isPlaying = false這個參數(先要加入using UnityEditor;)
☎指令:
using UnityEditor;
Application.Quit();
EditorApplication.isPlaying = false;

6.成果:
成果影片
成果圖片成果圖片成果圖片
程式碼

範例18.影片的播放,暫停,停止

1. 成果影片
成果圖片成果圖片成果圖片
play程式碼pause程式碼stop程式碼

2.download 下載素材
下載素材(手機圖片,play,stop,pause圖片,影片)

3.如何在一個plane顯示一個iphone的圖片:
(1)建立一個plane,改名:plane-phone
成果圖片
(2)先建立material(mat-phone),拖曵iphone圖片
(3)把material拖曵到plane,即可
成果圖片
(4)把plane-phone旋轉180度
成果圖片

4.為什麼在plane上加入material後,物件會變得模糊
成果圖片
原因:該material/shader = standard,預設會光線渲染而模糊
成果圖片
改善方法:該material/shader = UI/default,就會清楚顯示,不會模糊
成果圖片成果圖片成果圖片

5.建立play,stop,pause物件:
(1)建立play,stop,pause物件(plane)
plane-play:(y = 0.2)(rotation y = 180)(scale:x=0.2, z=0.15)
plane-stop:(y = 0.2)(rotation y = 180)(scale:x=0.2, z=0.15)
plane-pause:(y = 0.2)(rotation y = 180)(scale:x=0.2, z=0.15)
成果圖片

(2)建立mat-play,mat-stop,mat-pause材質
加入圖片
(3)將材質加入物件(plane)
(4)把圖片從模糊變成清晰(material/shader = UI/default)
成果圖片
(5)把play物件,預設顯示
把pause物件,預設隱藏
把stop物件,預設隱藏
成果圖片

6.建立顯示影片的螢幕物件(具有透明的材質):
(1)建立screen物件(plane-screen)
plane-scree:(y = 0.1)
(scale:x=0.9, z=1.8)
成果圖片
(2)建立mat-screen材質
(3)將材質加入物件(plane-screen)
成果圖片
結果:screen變成一個白色的螢幕
成果圖片
目標:希望螢幕能夠變成透明的
(4)方法:
步驟1:設定mat-screen的alpha=0
成果圖片
步驟2:
設定1:該material/shader = UI/default,就會清楚顯示透明背景
或設定2:該material/shader = Legacys/Transparent,就會清楚顯示透明背景
成果圖片
成果圖片
(5)把plane-screen,加入組件:Video Player
(6)設定Video Clip
成果圖片

7.如何設定plane的透明圖片的效果 = 透明背景(例如:圓形按鈕)
方法1:該material/shader = UI/default,就會清楚顯示透明背景
方法2:該material/shader = Legacys/Transparent,就會清楚顯示透明背景

8.寫程式碼(play,播放影片):play.cs
成果圖片
(1)play.cs程式碼掛載在plane-play物件裡面
(2)☎觀念:物件的點按事件
plane-play,不是按鈕(onClick),而是物件Object(onMouseDown)
Object的事件方法,要使用onMouseDown()函數
(3)如何在一個plane播放影片的指令:
☎注意:若要寫程式,讀取影片播放,一定要先using UnityEngine.Video;
A).在此plane,add component/VideoPlayer
B).拖曵影片到clip
using UnityEngine.Video;

public GameObject screen1;
public GameObject play1, stop1, pause1;
private void OnMouseDown()
{
//顯示plane-screen
screen1.SetActive(true);

//設定材質material為不透明(才能顯示影片內容)
screen1.GetComponent<Renderer>().material.color = new Color32(255, 255, 255, 255);

//在plane-screen上播放影片
screen1.gameObject.GetComponent<VideoPlayer>().Play();

//隱藏play物件
play1.SetActive(false);
stop1.SetActive(true);
pause1.SetActive(true);
}

9.寫程式碼(pause,暫停影片):pause.cs
成果圖片
using UnityEngine.Video;
public GameObject screen1, play1, pause1, stop1;
private void OnMouseDown()
{
//隱藏pause 物件
pause1.SetActive(false);
//顯示play物件
play1.SetActive(true);

//暫停影片播放
screen1.GetComponent<VideoPlayer>().Pause();
}

10.寫程式碼(stop,停止影片):stop.cs
成果圖片
using UnityEngine.Video;
public GameObject screen1, play1, pause1, stop1;
private void OnMouseDown()
{
//停止影片播放
screen1.GetComponent<VideoPlayer>().Stop();

//隱藏stop物件
stop1.SetActive(false);
//隱藏pasue物件
pause1.SetActive(false);
//顯示play物件
play1.SetActive(true);

//設定screen1的透明度 = 0
screen1.GetComponent<Renderer>().material.color = new Color32(255, 255, 255, 0);
}

11.成果:
成果影片
成果圖片成果圖片成果圖片
play程式碼pause程式碼stop程式碼

範例19:遊戲失敗產生Game Over畫面,方法1:切換場景

1.成果影片
GameOver應用影片
GameOver應用影片
成果圖片成果圖片
cs01程式碼cs02程式碼

成果影片
成果圖片成果圖片
步驟圖1步驟圖2步驟圖3步驟圖4

2.參考影片:Creating a Game Over Scene in Unity

3.建立兩個場景:s1(遊戲主畫面),s2(Game Over畫面)
(1)原理:一旦遊戲失敗,就切換到場景s2
(2)在s1場景1:建立按鈕:結束(E)
A).建立Empty Object:script_Manager➜加入程式碼:cs01.cs
B).Button➜設定hightlight color
C).Button➜設定onclick➜物件=script_Manage➜function=cs01/gotoscene
成果圖片
D).要寫2個處理切換場景的函數:
//onclick切換場景
public void gotoScene()
{
SceneManager.LoadScene("s2");
}

//鍵盤按了E則切換場景
void Update()
{ if(Input.GetKeyDown(KeyCode.E))
{
SceneManager.LoadScene("s2");
}
}
4.建立s2場景(Game Over場景)
(1)在s2場景:
(2)如何讓整個畫面背景色都是土黃色:
方法:把main camera➜Clear Flags:solid Color➜設定背景色backgroun:土黃色
成果圖片
(3)建立UI/Text_Infor:Game Over
建立UI/Text_replay:再玩一次(R)
建立UI/Text_Quit:關閉程式(Q)

(4)如何讓一個Text有Button按鈕的功能:
方法:在Text再加入組件:Button
成果圖片
設定button onclick程式碼的方法:
成果圖片

(5)撰寫程式碼cs05.cs
建立Empty Object:GameObject➜加入程式碼:cs02.cs
using UnityEngine.SceneManagement;
using UnityEditor;
public void gotoscene()
{
SceneManager.LoadScene("s1");
}

public void myquit()
{
Application.Quit();
EditorApplication.isPlaying = false;
}

private void Update()
{
if(Input.GetKeyDown(KeyCode.R))
{
SceneManager.LoadScene("s1");
}
else if(Input.GetKeyDown(KeyCode.Q))
{
Application.Quit();
EditorApplication.isPlaying = false;
}
}
(6)把Text_replay➜加入UI/button組件:結束(E)
Button➜設定hightlight color
Button➜設定onclick➜物件=GameObject➜function=cs02/gotoscene1
(7)把Text_Quit➜加入UI/button組件
Button➜設定hightlight color
Button➜設定onclick➜物件=GameObject➜function=cs02/myQuit
成果圖片

5.成果:
成果影片
GameOver應用影片
GameOver應用影片
成果圖片成果圖片
cs01程式碼cs02程式碼

範例20.Game Over畫面方法2:顯示圖片法(先隱藏的圖片,用public讀入後顯示)

1. 成果影片
GameOver應用影片
GameOver應用影片
成果圖片成果圖片
cs01程式碼

2.只需要建立1個場景:s1(遊戲主畫面)
☎原理:
(1)一旦遊戲失敗,就顯示GameOver圖片
(2)若是再玩一次(R),則重新載入場景s1

3.參考影片:Create great GAME OVER screen in Unity UI

4.建立UI/Button:結束(E)
(1)建立1個場景:s1
(2)建立UI/Button:結束(E)

4.建立UI/image(目的:填滿顏色,填滿canvas)
在Image裡面建立1個Text,2個Button
(1)建立UI/image(背景圖)
填滿canvas整個區域:(按住Alt,選擇填滿整個canvas區域) 成果圖片成果圖片
填滿顏色: 成果圖片

5.建立1個Text,2個button(顯示GameOver)
(1)在Image裡面➜建立UI/Text_Infor:Game Over
(2)在Image裡面➜建立UI/Button-replay:再玩一次(R)
(3)在Image裡面➜建立UI/Button-Quit:關閉程式(Q)
成果圖片

6.寫程式碼:cs01.cs(掛載在空白GameObject物件內)
(1)UI/Button➜加入程式碼:cs01.cs
UI/Button➜onclick:物件=GameObject,function=cs01➜showGameOver()

using UnityEditor;
using UnityEngine.SceneManagement;

public GameObject img1;
public void showGameOver()
{
//1.錯誤做法:不可以直接Find物件。然後再隱藏
// GameObject.Find("Image").SetActive(true);

//2.正確做法:用public GameObject img1,讀入後再SetActive(true)
img1.SetActive(true);
}

public void myQuit()
{
Application.Quit();
EditorApplication.isPlaying = false;
}

public void myReplay()
{ SceneManager.LoadScene("SampleScene");
}
// 鍵盤控制
void Update()
{ if(Input.GetKeyDown(KeyCode.E))
{
img1.SetActive(true);
}
else if (Input.GetKeyDown(KeyCode.R))
{
SceneManager.LoadScene("SampleScene");
}
else if (Input.GetKeyDown(KeyCode.R))
{
Application.Quit();
EditorApplication.isPlaying = false;
}
}

7.☎注意:再玩一次(R)的誤區:
(1)若用Image當作GameOver畫面,要replay時,不可以把image隱藏
原因:若只是隱藏,則不會從頭開始執行,而是接著剛剛的進度執行

(2)錯誤做法:把img1隱藏(如此,不會重新開始)
img1.SetActive(false);

(3)正確做法:
SceneManager.LoadScene("SampleScene");

8.執行前,先隱藏Image:
成果圖片

9.成果:
成果影片
GameOver應用影片
GameOver應用影片
成果圖片成果圖片
cs01程式碼

成果影片
成果圖片成果圖片
步驟圖1步驟圖2步驟圖3步驟圖4步驟圖5

ButtonEnd2程式碼showGameOverScreen程式碼
下載專案檔案

範例21.觸控事件第1種方法:在plane上觸控,發出射線,在觸碰點顯示圓圈(以淡黃色,透明100圓圈顯示)

1.成果影片
成果圖片
cs01程式碼

2.觀念:手機的觸控事件寫法,一定要使用射線Ray
原理:射線從Camera發出,經過觸控點(手指點按處,或是滑鼠點按處MousePosition),最後投射到螢幕上,碰觸到物件(碰觸點hit1)

3.建立plane,cylinder
成果圖片成果圖片

3.建立一個透明圓圈(黃色,透明度100)
設定透明度的方法:
(1)在aseet➜建立material(yellow)➜設定黃色,透明度100
成果圖片成果圖片

4.寫程式碼:cs01.cs(掛載在plane上)
(1)原理:在plane上,onmouseDown按下滑鼠/觸控後,發出射線,碰觸到物件
(2)
//在plane物件上觸控
private void OnMouseDown()
{
//發出射線(滑鼠點-camera射線)
Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
//宣告碰撞變數
RaycastHit hit1;
//判別是否射線碰到物件,發生碰撞了
if(Physics.Raycast(ray1, out hit1)==true)
{
GameObject.Find("Cylinder").transform.position = hit1.point;
}
}

5.碰觸點的位置: hit1.point

6.成果
成果影片
成果圖片
cs01程式碼

範例22.觸控事件第2種方法:在update上監測,是否有射線觸控,在觸碰點顯示圓圈(以淡黃色,透明100圓圈顯示)
方法2最常用

1.成果影片
成果圖片
cs01程式碼

2.觀念:手機的觸控事件寫法,一定要使用射線Ray
原理:射線從Camera發出,經過觸控點(手指點按處,或是滑鼠點按處MousePosition),最後投射到螢幕上,碰觸到物件(碰觸點hit1)

3.比較:
☎方法1:是在某個固定物件(例如plane)發生觸碰事件onMouseDown,然後使用射線Ray,經由滑鼠點,射到螢幕上的物件,完成物件觸碰
☎方法2:是在update裡面去監測全部的物件,是否有觸碰事件發生(例如按下滑鼠左鍵),若有,則使用射線Ray,經由滑鼠點,射到螢幕上的物件,完成物件觸碰
☎結論:方法2最常用

4.建立plane,cylinder
成果圖片成果圖片

5.建立一個透明圓圈(黃色,透明度100)
設定透明度的方法:
(1)在aseet➜建立material(yellow)➜設定黃色,透明度100
成果圖片成果圖片

6.寫程式碼:cs01.cs(掛載在空白物件empty GameObject上)
(1)原理:是在update裡面去監測全部的物件,是否有觸碰事件發生(例如按下滑鼠左鍵),若有,則使用射線Ray,經由滑鼠點,射到螢幕上的物件,完成物件觸碰
(2)
//只要按下左鍵,就偵測射線觸控的點
void Update()
{
//mouseButton = 0, 代表按下左鍵
if(Input.GetMouseButtonDown(0))
{
//發出射線(滑鼠點-camera射線)
Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
//宣告碰撞變數
RaycastHit hit1;
//判別是否射線碰到物件,發生碰撞了
if(Physics.Raycast(ray1, out hit1)==true)
{
//把黃色圈圈,移動到hit1.point
GameObject.Find("Cylinder").transform.position = hit1.point;
}
}
}

7.碰觸點的位置: hit1.point

8.成果
成果影片
成果圖片
cs01程式碼

範例23.如何取得發生射線ray後,發生觸碰點的物件資料(名稱,位置)

1.成果影片
成果圖片成果圖片成果圖片成果圖片成果圖片
cs01程式碼

2.如何取得射線ray,點按觸碰點的物件的資料(名稱,位置)
(1)觸碰點的物件名稱 = hit1.collider.gameObject.name
或是觸碰點的物件名稱 = hit1.collider.name

(2)射線觸碰點的物件坐標 = hit1.point

3.目標:點按物件後,顯示該物件的名稱

4.建立plane(地板),cube(方糖),capsule(膠囊),蜜蜂
下載蜜蜂bee物件(prefab)
成果圖片

5.建立UI/Text
成果圖片

6.寫程式碼:cs01.cs(掛載在空白物件empty GameObject上)
(1)原理:是在update裡面去監測全部的物件,是否有觸碰事件發生(例如按下滑鼠左鍵),若有,則使用射線Ray,經由滑鼠點,射到螢幕上的物件,完成物件觸碰
(2)
using UnityEngine.UI;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
//發出射線(滑鼠點-camera射線)
Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
//宣告碰撞變數
RaycastHit hit1;
//判別是否射線碰到物件,發生碰撞了
if (Physics.Raycast(ray1, out hit1) == true)
{
//觸控碰撞物件名稱:hit1.collider.name;
//在Text顯示碰撞物件名稱
GameObject.Find("Text").GetComponent<Text>().text = hit1.collider.name; }
}
}

7.觸控碰撞物件名稱:hit1.collider.name

8.為什麼蜜蜂物件觸碰後,不會顯示物件名稱:
(1)原因:蜜蜂是個複合物件,預設不會有碰撞器collider
(2)原因:觸碰是射線ray與物件的碰撞,必須設定物件的碰撞器collider
(3)解決方法:在此物件要加入組件:box collider
成果圖片

8.成果
成果影片
成果圖片成果圖片成果圖片成果圖片成果圖片
cs01程式碼

範例24.觸控移動,觸控取得寶物,100分過關

1.成果影片
成果圖片成果圖片成果圖片
程式碼

2.如何取得射線ray,點按觸碰點的物件的資料(名稱,位置)
(1)觸碰點的物件名稱 = hit1.collider.gameObject.name
或是觸碰點的物件名稱 = hit1.collider.name
(2)射線觸碰點的物件坐標 = hit1.point
(3)刪除觸碰物件:Destroy(hit1.collider.gameObject);

3.目標:點按物件後,刪除該物件
點按地點,移動到觸碰點

4.建立plane(地板),cube(方糖),capsule(膠囊),cylinder(圓柱),蜜蜂,狐狸
下載籃球貼圖texture
下載蜜蜂bee物件(prefab)
下載狐狸fox物件(prefab)
成果圖片

5.建立UI/Text
成果圖片

6.寫程式碼:cs01.cs(掛載在空白物件empty GameObject上)
(1)原理:是在update裡面去監測全部的物件,是否有觸碰事件發生(例如按下滑鼠左鍵),若有,則使用射線Ray,經由滑鼠點,射到螢幕上的物件,完成物件觸碰
(2)
using UnityEngine.UI;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
//發出射線(滑鼠點-camera射線)
Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
//宣告碰撞變數
RaycastHit hit1;
//判別是否射線碰到物件,發生碰撞了
if (Physics.Raycast(ray1, out hit1) == true)
{
//1.移動籃球位置
//GameObject.Find("籃球").transform.position = hit1.point;
GameObject.Find("籃球").transform.position = new Vector3(hit1.point.x, 0.5f, hit1.point.z);

//2.觸碰到物件,則刪除物件,加分
string txtObject = hit1.collider.name;
if (txtObject == "蜜蜂" || txtObject == "狐狸" || txtObject == "方糖" || txtObject == "膠囊" || txtObject == "圓柱")
{
//刪除物件
Destroy(hit1.collider.gameObject);
//加分
score += 20;
GameObject.Find("TextScore").GetComponent<Text>().text = score.ToString();
}
}
}
}

7.觸控碰撞物件名稱:hit1.collider.name
刪除觸碰物件:Destroy(hit1.collider.gameObject);

8.為什麼蜜蜂,狐狸物件觸碰後,沒有反應:
(1)原因:蜜蜂是個複合物件,預設不會有碰撞器collider
(2)原因:觸碰是射線ray與物件的碰撞,必須設定物件的碰撞器collider
(3)解決方法:在此物件要加入組件:box collider
成果圖片

8.成果
成果影片
成果圖片成果圖片成果圖片
程式碼

範例25.複合物件(小蜜蜂)觸控點按後向上移動的2種方法

1.成果影片
成果圖片成果圖片成果圖片
cs01程式碼cs02程式碼cs03程式碼cs04程式碼cs05程式碼

2.複合物件(小蜜蜂)觸控點按後向上移動的2種方法
☎(1)方法1:onMouseDown事件
優點,程式碼cs01可以掛載在各種物件上
特別注意:要把複合物件加上box collider才能操控移動

☎(2)方法2:update...if(input.GetMouseButtonDown(0))...ray1...hit1...
缺點1:每一個cs程式碼,只能夠掛載在一個物件,無法掛載在各種物件
而且,要判別觸控物件的物件的名稱是否為特定物件,若是才能夠移動
缺點2:若沒有判別觸控物件名稱,則會把所有物件都一定同步移動
原因:只要有一個物件被觸控到,則所有物件都可以移動

☎錯誤做法:
if(Physics.Raycast(ray1,out hit1)==true)
{
this.gameObject.transform.Translate(0, 0.2f, 0);
}

☎正確做法:要判別觸控物件的物件的名稱是否為特定物件,若是才能夠移動
if (Physics.Raycast(ray1, out hit1) == true)
{
if(hit1.collider.name =="Fox")
{
this.gameObject.transform.Translate(0, 0.2f, 0);
}
}

3.目標:點按物件後,物件向上移動

4.建立plane(地板),方糖(cube),蜜蜂(FantasyBee),狐狸(fox)
下載蜜蜂bee物件(prefab)
下載狐狸fox物件(prefab)
成果圖片

6.寫程式碼,方法1:cs01.cs(掛載在:ube,FantasyBee,fox)
(1)原理:onMouseDown事件,讓物件向上移動
(2)
private void OnMouseDown()
{
this.gameObject.transform.Translate(0, 0.2f, 0);
}
(3)結果:三個物件可以單獨移動
成果圖片

7.寫程式碼,方法2:cs02.cs(掛載在:cube,FantasyBee,fox)
(1)原理:update...if(input.GetMouseButtonDown(0))...ray1...hit1...讓物件向上移動
(2)
void Update()
{
if(Input.GetMouseButtonDown(0))
{
Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit1;
if(Physics.Raycast(ray1,out hit1)==true)
{
this.gameObject.transform.Translate(0, 0.2f, 0);
}
}
}
(3)結果:按了某個物件,卻三個同時向上移動
成果圖片
原因:按了某個觸控點,發出的射線,卻會發生在全部有cs02的物件,三個都會發出射線,
三個cs02都會執行向上移動

8.寫程式碼,方法3:cs03, cs04, cs05(分別掛載在:cube,FantasyBee,fox)
(1)原理:update...if(input.GetMouseButtonDown(0))...ray1...hit1...讓物件向上移動
(2)
void Update()
{
if(Input.GetMouseButtonDown(0))
{
Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit1;
if (Physics.Raycast(ray1, out hit1) == true)
{
if(hit1.collider.name =="Fox")
{
this.gameObject.transform.Translate(0, 0.2f, 0);
}
}
}
}
(3)結果:按了某個物件,可以單獨向上移動
成果圖片

9.為什麼蜜蜂,狐狸物件觸碰後,沒有反應:
(1)原因:蜜蜂是個複合物件,預設不會有碰撞器collider
(2)原因:觸碰是射線ray與物件的碰撞,必須設定物件的碰撞器collider
(3)解決方法:在此物件要加入組件:box collider
成果圖片

10.成果
成果影片
成果圖片成果圖片成果圖片
cs01程式碼cs02程式碼cs03程式碼cs04程式碼cs05程式碼

範例24:用幀動畫來控制直升機移動:上下、左移右移、左轉右轉

1.直升機向上移動的方法:採用幀動畫(frame animation)
原理:
(1)設定udapte()幀動畫:使用SpeedY * Time.deltaTime
void Update()
{
float dx = SpeedX * Time.deltaTime;
float dy = SpeedY * Time.deltaTime;
float dz = SpeedZ * Time.deltaTime;
this.gameObject.transform.Translate(dx, dy, dz, Space.Self);

float ry = RotateY * Time.deltaTime;
this.gameObject.transform.Rotate(0, ry, 0, Space.Self);
}
(2)按了按鈕(向上):
原理:一開始向上速度=2,但10s後後就啟動一個計時器,執行moveY,讓速度=0
public void MoveUp()
{
SpeedY = 2;
Invoke("moveY", 10);
}
void moveY()
{
SpeedY = 0;
}

2.直升機啟動螺旋槳動畫,播放音效的方法:
原理:
(1)如何把節點的component<Animation>打勾:
方法:this.gameObject.GetComponent<Animation>().enabled = true;
public void TurnOn()
{
this.gameObject.GetComponent<Animation>().enabled = true;
this.gameObject.GetComponent<AudioSource>().Play();
}

3.成果:
(1)成果影片

(2)參考影片:無人機飛行
參考影片:第16章調用component(無人機飛行)

(3)下載素材資源:
直升機的asset store
Unity UI Menu的asset store

(4)步驟圖:
步驟圖1步驟圖2步驟圖3步驟圖4步驟圖5

程式碼

範例25.直升機fire攻擊不斷來襲的敵機

成果影片
1.參考教程(無人機,射擊遊戲)
(1)參考影片:射擊遊戲
第25章1-10(射擊遊戲)

(2)參考影片:無人機飛行
參考影片:第16章調用component(無人機飛行)

2.下載素材資源:
(1).直升機的asset store
(2).敵機的asset store
(3).下載asset store粒子動畫
打到敵機爆炸的粒子動畫:WFX_ExplosiveSmoke Big Alt
玩家被敵軍碰撞的爆炸(核子爆炸)動畫:War FX:WFX_Nuke
子彈飛出去的粒子動畫:flame:WFX_FlameThrower Big Alt Looped

(4).下載爆炸音效
角色被撞爆炸:mixkit-gun-explosion-with-long-echo-1700
子彈打到敵機:mixkit-fire-explosion-1343

3.程式碼:
MonsterCreator程式碼
MonsterLogic程式碼
bullet_logic程式碼
playerLogic程式碼
helicopterControl程式碼
4.步驟圖:
步驟圖1-MonsterFolder-(MonsterCreator.cs)
步驟圖2-monster-(MonsterLogic.cs)
步驟圖3-(Sphere-bullet)-(bullet_logic.cs)
步驟圖4-(Helicopter)-(playerLogic.cs)-(helicopterControl.cs)
步驟圖5-場景的物件

5.專案檔案:
下載專案檔案

範例26.設定怪物不斷生成產生(兩種計時器:Invoke,InvokeRepeating)

1.成果影片
成果圖片成果圖片成果圖片
player_move程式碼MonsterCreate程式碼MonsterMove程式碼

步驟圖1-MonsterFolder-MonsterCreator

(1)下載已經有場景物件的專案:
下載已經有樓梯地板物件的專案
或是場景地板樓梯等prefabs預鑄件: 下載地板樓梯等prefabs

(2)建立怪物:cylinder(紫色)

(3)建立主角:Capsule(白色)


2.建立不斷出現怪物的技術:2個設定,2個指令:
(1)2個設定:
☎設定monster預鑄件(prefab)
☎設定生成monster的目錄:monster_Folder

(2)2個指令:
☎計時器(每隔3秒,就會執行MonsterAdd函數):InvokeRepeating("MonsterAdd", 0, 3)
☎MonsterAdd函數裡面,生出monster的指令:Instantiate(Monster預鑄件, monster_Folder的transform);

(3)建立怪物產生的空白物件:
☎建立一個空的gameObject(命名monster_Folder
➜位置重置歸零(transform/Reset)後,才能再移動、)

☎monster_Folder➜加上程式碼(怪物生成器):add component➜MonsterCreate.cs
☎程式碼:使用計時器,不斷產生怪物:InvokeRepeating("MonsterAdd", 0, 3);


public GameObject a1;
public GameObject a2;

void Start()
{
InvokeRepeating("MonsterAdd", 0, 3);
}

void MonsterAdd()
{
//使用目前節點MonsterFolder來生成怪物節點的transform(變換組件,可設定坐標,旋轉角度,縮放scale)
//第一個參數:是prefab
//第二個參數transform,乃是設定生成物件的位置,旋轉角度,縮放比例)
//指令:Instantiate(Monster預鑄件, monster_Folder的transform);
//實際寫法:
Instantiate(a1, a2.transform);
}

☎注意:
其中的a1,a2,必須在前台設定
public GameObject a1;
public GameObject a2;
在前台設定:
a1 = monster 預鑄件
a2 = monster_Folder


(5)建立自動導航的設定
☎全部的地板樓梯物件➜navigation static➜bake
☎怪物cylinder,主角Capsule➜NavMeshAgent組件
☎怪物cylinder➜設定MonsterMove.cs
☎主角Capsule➜設定player_move.cs
☎怪物產生器monster_Foler➜設定MonsterCreate.cs
☎最後,把怪物cylinder拖曵到素材區,形成prefabs
☎最後,刪除場景區的怪物cylinder


(6)建立主角Capsule觸控移動的程式碼:
using UnityEngine.AI;
public class player_move : MonoBehaviour
{
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray r1 = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit h1;
if(Physics.Raycast(r1,out h1)==true)
{
this.gameObject.GetComponent<NavMeshAgent>().SetDestination(h1.point);
}
}
}


(7)建立怪物cylinder追逐主角Capsule移動的程式碼:MonsterMove.cs
using UnityEngine.AI;

public class MonsterMove : MonoBehaviour
{
void Update()
{
this.gameObject.GetComponent<NavMeshAgent>().SetDestination(GameObject.Find("Capsule").transform.position);
}


(8)☎補充,可以增加程式碼3:設定怪物prefab的生命週期=300秒
Invoke("MonsterEnd", 300);
void MonsterEnd()
{
Destroy(this.gameObject);
}


(8)程式碼:
成果影片
成果圖片成果圖片成果圖片
player_move程式碼MonsterCreate程式碼MonsterMove程式碼


範例27.從某個角色物件player,如何不斷發射子彈bullet,打到怪物monster則消失

1.成果影片

遊戲2C-高皓瑜-成果影片高皓瑜-的原創百分比
遊戲2C-林嘉明-成果影片
進修2A-章正邦-影片後半有彩蛋
進修2A-林佳微-成果影片
海青40-葉嘉偉-成果影片

成果圖片成果圖片成果圖片成果圖片
playermove程式碼
bulletMove程式碼
MonsterCreate程式碼
MonsterMove程式碼


2.後續進階:自行研發技術:
(1)碰撞的爆炸效果:
☎講義:11-3:如何設定碰撞時的粒子動畫效果(例如,顯示爆炸prefab)
範例影片:直升機打怪物
下載爆炸prefab動畫
下載爆炸音效

(2)怪物碰撞到我,損失血量,或被KO
範例影片:海青40-鍾煜霄

(3)子彈打到怪物得分(不同的子彈,都去執行同一個記分的score.cs程式碼)
☎講義:範例31:呼叫另外c#檔案的show()函數顯示分數score(方法2:GameObject.FindObjectOfType)
☎如何查詢到專案裡面的cs01程式碼物件?
☎方法:GameObject.FindObjectOfType<程式碼名稱>()
cs01程式碼
cs02程式碼


☎本題實作開始:
3.下載前一個範例場景物件:
下載範例4-26(自動生成怪物,導航追player)

或是下載地板樓梯場景專案檔案
或是場景地板樓梯等prefabs預鑄件: 下載地板樓梯等prefabs
3.參考教程(無人機,射擊遊戲)
(1)參考影片:射擊遊戲
第25章1-10(射擊遊戲)

(2)參考影片:無人機飛行
參考影片:第16章調用component(無人機飛行)


4.建立發射子彈的技術:3個設定,3個指令:
(1)『發射子彈』與『不斷產生怪物』技術很像:
☎不斷產生怪物:2個設定,2個指令
☎發射子彈:3個設定,3個指令

(2)3個設定:
☎設定bullet子彈預鑄件(prefab)
☎設定生成bullet子彈的目錄:bullet_Folder
☎設定子彈開火槍管的出口點:firepoint
(為什麼還要設定firepoint:因為player會隨時移動,所以出彈口也要一直改變位置,不是固定位置生出子彈,所以要追踪player的手部的出槍口)


(3)3個指令:
☎判別按了F,就開火:if(Input.GetKeyDown(KeyCode.F))
☎生出bullet子彈的指令:GameObject node = Instantiate(bullet子彈預鑄件, bullet_Folder的transform);
☎bullet子彈產生的位置在player手部的開火槍口點firepoint:node.transform.position = firepoint.position
☎子彈要設定生命週期為5秒,否則子彈越來越多,佔滿記憶體:Invoke("selfdestory", 5)
void selfdestory()
{
Destroy(this.gameObject);
}


3.重要原理:
☎本題的碰撞是兩種方法之一:只接觸沒有物理力學效果:iskenematic(運動學),istrigger(接觸,觸發器)
☎碰撞的前提:必須有box collider,必須有rigidbox,必須有接觸設定(iskenamatic,istrigger)

4.子彈物件設定步驟:
(1)在場景先建立子彈物件(Sphere-bullet)位置重置歸零(transform/Reset)後,才能再移動、
(1-1)➜要能夠碰撞,必須加上rigidbody➜打勾is kinematic(有動力學碰撞效果,但沒有重力修改)
(1-2)➜ 打勾 is kinematic(運動學,只有移動運動,但是沒有物理力學效果)

(2)子彈的碰撞器:sphere collider ➜ 打勾 is trigger(觸發器,只是判斷接觸,沒有物理力學碰撞)

(3)add component ➜ bulletMove.cs
☎程式碼1:bullet持續往前移動:
void Update()
{
float dz = 5 * Time.deltaTime;
this.gameObject.transform.Translate(0, 0, dz, Space.Self);
}

☎程式碼2:bullet的生命週期5秒:
void Start()
{ Invoke("selfdestory", 5);
}
void selfdestory()
{
Destroy(this.gameObject);
}

☎程式碼3:接觸其它物件則彼此銷毀:
//感應觸發
private void OnTriggerEnter(Collider other)
{
if(other.gameObject.name =="monster(Clone)")
{
Destroy(this.gameObject);
Destroy(other.gameObject);
}
}

(4)建立子彈的預鑄件prefabs:
➜把子彈物件(bullet)➜拖曵到asset
➜建立bullet預鑄件prefabs


(5)如何不斷呼叫產生大量的子彈:在playerMove.cs裡面設定
(5-1)在攻擊玩家player物件➜add component➜playermove.cs
☎程式碼1:鍵盤按了F,開火,產生子彈:
//判別按了F,開火
if(Input.GetKeyDown(KeyCode.F))
{
GameObject node = Instantiate(bullet, bulletFolder);
node.transform.position = firepoint.position;
}

☎注意1:產生子彈的指令:Instantiate(bullet, bulletFolder);
bullet:子彈預鑄件(bullet),
bulletFolder:產生很多子彈預鑄件Clone的目錄

☎注意2:產生子彈要設定2個物件:
(1)bulletFolder:產生很多子彈預鑄件Clone的目錄
(2)firepoint:子彈開火槍管的出口點

☎注意3:不管bulletFolder/或是firepoint,位置都要先重置歸零
➜建立一個空的節點bulletFolder(或是bulletClone,子彈Clone群目錄),要注意位置會自動亂設定,不在原點:
➜位置要先重置歸零(transform/Reset)後,才能再移動
步驟圖1-(Sphere-bullet)-(bullet_logic.cs)
步驟圖2-(Helicopter)-(playerLogic.cs)-(helicopterControl.cs)


5.如果發現沒有出現子彈的,真正原因:
原因:若有修正,要更新子彈bullet節點時➜overridder➜apply all
注意:要setactiv(打勾顯示)時,才➜overridder➜apply all
沒有出現子彈的原因:因為是在隱藏子彈時去apply all的,所以當然不會顯示子彈


6.程式碼:
成果影片
成果圖片成果圖片成果圖片成果圖片
playermove程式碼
bulletMove程式碼
MonsterCreate程式碼
MonsterMove程式碼



範例28:實作主角膠囊在導航網格內,自由觸控移動(塔防遊戲,top war game)

參考影片:导航网格的使用
參考影片:让导航动起来吧
參考影片:网格链接与动态障碍物
參考影片:導航區域,最近導航成本

成果影片-上課範例

(1)下載已經建立地板樓梯物件的專案: 下載已經建立地板樓梯物件的專案
或是場景地板樓梯等prefabs預鑄件: 下載地板樓梯等prefabs


(2)設定AI自動導航,只要2個步驟:
設定導航網格mesht
設定導航代理人agent
☎(A).物件Object(地板plane,階梯cube...)設定為:導航網格static navigation mesh,
==>在navigation視窗,勾選:static navigation
==>然後,bake烘培,產生導航網格

☎(B).player:加入組件:nav mesh Agent

成果圖片
步驟圖1步驟圖2步驟圖3步驟圖4步驟圖5步驟圖6

(3)動態障礙物obstacle基本設定
☎動態障礙物加入nav mesh obstacle組件
成果圖片

(4)網格鏈接(可以跳下,跳高height)基本設定
☎網格鏈接必須在AI導航網格Object內勾選generate off mesh links網格鏈接
成果圖片
成果圖片

(5)網格鏈接(左右跳,跳遠distance)基本設定
☎網格鏈接必須在AI導航網格Object內勾選generate off mesh links網格鏈接
成果圖片
成果圖片
成果圖片

(6)網格鏈接(兩個特定物件地點的互跳)基本設定
☎要先建立2 個特定地點物件
☎加入off mesh link組件
☎設定誰說起始點物件,誰說結束點物件
成果圖片
成果圖片
☎設定雙向來回(預設已勾選)
成果圖片

(7)導航路徑:選擇以最低路徑成本來行走
☎要先建立水的區域
☎設定水的區域areas的成本值:3
成果圖片
☎設定水物件的areas = water
成果圖片
☎重新bake烘培
成果圖片
☎結果:在水的上方會繞過水,但在水的後方則會穿越水(因為計算成本)

(8)應用:
☎可以應用到塔防遊戲(top war game)
成果圖片
成果圖片
塔防遊戲 by 54stanley
簡易版塔防遊戲
簡易版塔防遊戲
簡易式塔防遊戲第三版
史上最好玩的塔防遊戲

(9)程式碼
playerControl程式碼
doormove程式碼
下載專案檔案

範例29:實作主角unityChan在導航網格內,自由觸控移動+動作(idle,walk,jump)

成果影片
成果圖片
步驟圖1步驟圖2步驟圖3步驟圖4步驟圖5步驟圖6
步驟圖7步驟圖8步驟圖9步驟圖10
程式碼
下載專案檔案

【chp14.分數計算,記分系統】
範例30:呼叫另外c#檔案的show()函數顯示分數score(方法1:public拖曵設定cs程式碼掛載的物件)

☎ 兩個cs程式碼檔案,都可以顯示分數score
==>方法:呼叫另外c#檔案的show()函數顯示分數score
參考影片:How to Pass Variables Between Scripts in C#
參考網頁(附上程式碼):How to Pass Variables Between Scripts in C#

成果影片

1.如何兩個cs程式碼檔案,都可以顯示分數score
(1).錯誤做法:cs02程式碼,讀取cs01程式碼的public變數score
結果:只能夠讀取到score的起始值(0)
(2)正確做法:cs02程式碼,讀取cs01程式碼的public函數show1(-1)

2.如何cs02程式碼,讀取cs01程式碼的public函數show1()
設定另外一個cs01檔案物件
public cs01 readCs;
readCs.show1(-1);

3.如何設定UI/Text的值(score)
(1)注意:抓取UI物件,要先using UnityEngine.UI;
GameObject.Find("TextScore").GetComponent<Text>().text = "Score : " + score.ToString();

成果圖片1

步驟圖2步驟圖3

cs01程式碼
cs02程式碼

範例31:呼叫另外c#檔案的show()函數顯示分數score(方法2:GameObject.FindObjectOfType)

☎ 兩個cs程式碼檔案,都可以顯示分數score
==>方法:呼叫另外c#檔案的show()函數顯示分數score
參考影片:How to Pass Variables Between Scripts in C#
參考網頁(附上程式碼):How to Pass Variables Between Scripts in C#

成果影片

1.如何找到掛載某個Script腳本的GameObject?
Unity 提供了幾個方法可以找到關於是誰引用了物件(透過程式碼搜尋)
方法(1):一種是GameObject.FindGameObjectWithTag,此法可以根據GamObject所設定的Tag,直接把物件搜尋出來

方法(2):GameObject.FindObjectOfType<T>(),此法根據元件的類別直接搜尋,如果是Script腳本就直接輸入對應名稱
功能:根據型別(元件/自定義指令碼)查詢,並返回這個類。
如果沒有任何匹配該類型的物件,則返回 null。

方法(3):使用 gameObject.GetComponent,這種方法則要先理解子母物件的關係

2.如何查詢到專案裡面的cs01程式碼物件?
☎方法:GameObject.FindObjectOfType<程式碼名稱>()
☎範例:
private cs01 readCs;
readCs = GameObject.FindObjectOfType<cs01>();
.....
readCs.show1(-1);
☎優點:
若要搜尋程式碼物件(cs01),用FindObjectOfType<程式碼名稱>()是最簡單的方法

成果圖片1

步驟圖2步驟圖3

cs01程式碼
cs02程式碼

範例32:很多怪物攻擊主角,計算同一個score變數(方法3:GameObject.FindObjectOfType)

☎ 多個怪物的攻擊主角,都可以修改分數score
==>方法:呼叫另外c#檔案的show()函數顯示分數score
成果影片

1.如何找到掛載某個Script腳本的GameObject?
方法(2):GameObject.FindObjectOfType<T>(),此法根據元件的類別直接搜尋,如果是Script腳本就直接輸入對應名稱
功能:根據型別(元件/自定義指令碼)查詢,並返回這個類。
如果沒有任何匹配該類型的物件,則返回 null。

2.如何查詢到專案裡面的程式碼score.cs物件?
☎方法:GameObject.FindObjectOfType<程式碼名稱>()
☎範例:
//若是多個怪物prefabs,改成呼叫score.cs裡面的showScore()
GameObject.FindObjectOfType<score>().showScore(-3);

☎3.統一計算分數的程式碼score.cs:
using UnityEngine.UI;

public int score2 = 30;
public GameObject img_Over;
bool gameover = false;
public void showScore(int point)
{
score2 += point;
if(score2 < 0 && gameover ==false)
{
gameover = true;
//播放過關失敗音效
this.GetComponent<AudioSource>().Play();
img_Over.SetActive(true);
}
//注意:如果沒有using UnityEngine.UI;就無法抓到參數Text
GameObject.Find("Text_energy").GetComponent<Text>().text = "體力:" + score2.ToString();
}

成果影片
成果圖片1

步驟圖2步驟圖3

score.cs程式碼
enemy_greenp_manager.cs程式碼

【chp15.RPG的狀態機,主角攻擊怪物,怪物攻擊主角】
範例33:狀態機:設定角色的5種狀態

參考影片:RPG Enemy Set States 設置敵人的基本屬性和狀態

1.RPG的角色狀態設定:準備idle,走路walk,跳躍jump,攻擊attack,死亡die

(1).專業做法:使用列舉變數enum
範例:enum EnemyStatus {IDLE,WALK,JUMP,ATTACK,DIE}
應用方法:EnemyStatus.WALK
☎注意:enum的內容項目文字,前後不需要""
☎注意:設定enum,不需要=

2.☎狀態機的程式碼結構:
//設定角色的狀態列舉
public enum EnemyStatus { IDLE, WALK, JUMP,ATTACK, DIE };

public class enemy_greenp_manager : MonoBehaviour
{
//宣告一個public的狀態機變數
public EnemyStatus itsStatus;

//判被角色狀態函數
void SwitchStatus()
{
switch(itsStatus)
{
case EnemyStatus.IDLE:
break;
case EnemyStatus.WALK:
break;
case EnemyStatus.JUMP:
break;
case EnemyStatus.ATTACK:
break;
case EnemyStatus.DIE:
break;
}
}

void Update()
{
//隨時判別角色狀態
SwitchStatus();
}

步驟圖程式碼

範例34:實作enemy的四種狀態(預備idle,走路walk,攻擊attack,死亡die)

1.問題點:自動導航navMesh,navMeshAgent的角色攻擊,遇到的問題:
☎問題:enemy敵人若是由自動導航,這個agent_enemy,遇到目標物會自動避開,不會碰撞到
(1)遇到目標主角:會自動剎車,距離0,就會停止,所以不會碰撞
(2)遇到移動式的障礙物/牆壁,agent_enemy也會自動避開

2.RPG遊戲,如何在自動導航內,靠近時就攻擊➜要符合三個條件
(1)條件1:只要距離distance1 <2.5f,就攻擊
☎方法:在update內要隨時計算兩者的距離:float distance1 = Vector3.Distance(agentChan.transform.position, agentGreen.transform.position);

☎遇到新的問題:但是若是距離一旦靠近了,就會不斷攻擊(在update內,一秒內連續攻擊60次)
修改:要加上一個冷卻時間lastattackTime變數(例如2.0),每隔2秒才能再攻擊一次
☎方法:在update裡面:lastattackTime -= Time.deltaTime;

(2)條件2:只要冷卻時間已經小於0:lastattackTime < 0

(2)條件3:只要還沒有死:this.gameObject.GetComponent<Animator>().GetInteger("status") != 4) 就可以,開始攻擊
(4)程式碼架構:
if (lastattackTime < 0 && distance1 < 2.5f && this.gameObject.GetComponent<Animator>().GetInteger("status") != 4)
{
....
}

3.RPG的角色如何攻擊對方
(1)關鍵問題:在udapte裡面若是設定距離小2.5就攻擊,那麼1秒內會連續攻擊60次
改善方法:宣告一個lastattackTime變數,超過這個時間再攻擊第二次

(2)方法:
(A)宣告變數lastattackTime:攻擊冷卻時間(攻擊一次,必須再等幾秒才能再攻擊)
public float lastattackTime = 1.5f;

(B)在update裡面判別,計算enemy與主角unityChan的距離,靠近就刺

float distance1 = Vector3.Distance(agentChan.transform.position, agentGreen.transform.position);
if (lastattackTime < 0 && distance1 < 2.5f && this.gameObject.GetComponent<Animator>().GetInteger("status") != 4)
{
靠近主角,就刺 status = 14(或12)
this.gameObject.GetComponent<Animator>().SetInteger("status", 14);
//降低主角的體力
energy -= 3;
//顯示在畫面Text_energy
GameObject.Find("Text_energy").GetComponent<Text>().text = "體力:" + energy.ToString();
//播放hit/punch音效
this.GetComponent<AudioSource>().Play();
//重新設定lastattackTime = 0.5f
lastattackTime = 1.5f;
}

☎4.成果影片
成果+設定步驟影片

5.下載素材檔案:
enemy角色下載(Battle Wizard Poly Art)
手榴彈grenade物件(asset store)
Effect textures and prefabs(煙霧動畫prefabs物件)asset store

氣化聲音(hiss)
揮擊木棍聲音(Whoosh)
過關成功聲音(win)

成果圖片
步驟圖1步驟圖2步驟圖3步驟圖4步驟圖5步驟圖6步驟圖7步驟圖8步驟圖9

5.程式碼:
doormove程式碼
playerControl程式碼
enemy_greenp_manager程式碼
gameover_manager程式碼

【chp6.程式碼建立節點物件,改變節點組件參數】
範例6-1:程式碼建立一個新的cube物件

範例6-1:程式碼建立一個新的cylinder物件

1.如何在程式碼裡面產生一個cylinder物件
GameObject circle1;
circle1 = GameObject.CreatePrimitive(PrimitiveType.Cylinder);

成果圖片程式碼
官網教學:程式碼產生一個cube

範例6:程式碼修改一個cube物件,材質為紅色,透明度為90(1~255)

1.第1種做法(最好,推薦):設定顏色透明度四個參數(完全程式碼控制)
範例: //設定button_fire為淡綠藍色
GameObject.Find("button_fire").GetComponent<Renderer>().material.color = new Color32(123, 202, 194, 100);

官網教學:Color32
官網教學:Color32


2.第2種簡單做法:範例6-2a:程式碼修改一個cube物件,材質為紅色
成果圖片程式碼
10.如何程式碼設定一個cube的材質
(1)先在aseet➜建立material(yellow):mat1➜設定黃色,透明度100
(2)程式碼設定物件的材質:circle1.GetComponent<Renderer>().material = mat1;
public Material mat1;
void Start()
{
circle1 = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
circle1.GetComponent<Renderer>().material = mat1;
}


3.第3種一般做法:範例6-2a:程式碼修改一個cube物件,材質為紅色
成果圖片程式碼
官網教學:程式碼改變材質顏色


4.第4種做法:修改材質透明度做法1:範例6-2c:建立一個material,再程式碼修改一個cube物件,材質為紅色,透明度為90(1~255)
成果圖片程式碼
影片教學:程式碼改變材質透明度
☎注意:
(1)要先建立一個紅色材質material
(2)確定material的alpha = 255
(3)把material的render mode設定為:transparent渲染模式(不可以設定Opaque渲染模式),才能夠讓程式修改alpah而渲染成透明
☎若是沒有把render mode改成transparent,那麼就算改了alpha,也沒有辦法渲染成透明的
成果圖片


5.第5種做法:修改材質透明度做法2:範例6-2d:完全程式碼控制,程式碼修改一個cube物件,材質為紅色,透明度為0.05(0~1)
成果圖片程式碼
影片教學:程式碼改變材質透明度
☎注意:
(1)不需要先建立一個紅色材質material
(2)完全用程式碼控制
(3)要用程式碼設定material的render mode設定為:transparent渲染模式(不可以設定Opaque渲染模式),才能夠讓程式修改alpah而渲染成透明
(4)設定顏色與透明度:this.GetComponent<Renderer>().material.SetColor("_Color", new Color(1, 0, 0, 0.05f))


範例6-3:點按一個cube物件,播放音效

成果圖片
下載本project成果檔案
範例6-2:建立主角john及其動作動畫

範例教學:
2.The Player Character: Part 1:
https://learn.unity.com/tutorial/the-player-character-part-1?uv=2020.2&projectId=5caf65ddedbc2a08d53c7acb#5caf6520edbc2a08d53c79fa

1.建立場景
(1)下載city場景
到asset store/輸入搜尋:city
設定price=0的過濾條件
找到:CITY package
download/import

範例6-4:一個cube物件,再程式碼隱藏

11.如何程式碼設定物件隱藏:
circle1.SetActive(false);
成果圖片程式碼
官網教學:隱藏物件

範例6-5:先隱藏一個cube物件,再程式碼顯示


☎注意:若是先隱藏cube物件,再用程式碼讓它setActive(true),則無法顯示
因為:若是先隱藏,那麼就無法找到這個物件來(GameObjectg.fidn("cube")....找不到)
解決方法:不要用GameObject.find來尋找物件,而用public GameObject obj1來設定這個cube

成果圖片程式碼

☎注意:隱藏cube物件的方法:成果圖片

6-7:程式碼建立的物件節點,如何自動產生某個組件,例如navMeshAgent(RequireComponent)

參考影片:RPG Enemy Set States 設置敵人的基本屬性和狀態

1.目的:在程式碼加載的物件建,自動產生navMeshAgent組件
目的:在程式碼建立的物件建,自動產生rigidBody組件
☎方法:
//1.強迫該程式碼加載的物件,自動加入組件Rigidbody
[RequireComponent(typeof(Rigidbody))]

//2.強迫該程式碼加載的物件,自動加入組件navMeshAgent
//注意:要執行run一次,才會主動在前台面板產生navMeshAgent組件
[RequireComponent(typeof(NavMeshAgent))]

成果圖
步驟圖步驟圖步驟圖
程式碼

【chp7.滑鼠事件,觸控事件】
7-1:觸控事件的程式碼有兩種寫法

3.觸控程式碼有兩種寫法:
☎(1)方法1:一物件一個程式碼寫法:
架構:
點按物件用onMouseDown()......
Ray ray1 = Camera.mian.ScreenPointToRay(input.MousePosition)....
RayCastHit hit1....
if (Physics.Raycast(ray1, out hit1)..

☎缺點:若是地形是3個複合物件(cube1,cube2....),就會有三個程式碼,就會有三個相同變數
☎結果:造成結果產生不穩定的衝突
☎適合:只適合單一物件,點按的程式

範例程式碼


☎(2)方法2:一個程式碼監測全部文件寫法:
架構:程式碼加入任一物件,即可偵測全部的物件
架構:
update....
if(input.GetMouseButtonDown(0)......
Ray ray1 = Camera.main.ScreenPointToRay(input.MousePosition)....
RaycastHit hit1....
if (Physics.Raycast(ray. out hit1)....

☎結論1:一般的點按按鈕:用onMouseDown...即可
☎結論2:若要射線觸碰Ray:必須用方法2

➜update....
if(input.GetMouseButtonDown(0)....
Ray ray1 = Camera.mian.ScreenPointToRay(input.MousePosition)...

範例程式碼

7-2:如何做『ray射線』檢測(紅外線)

6.如何做『射線』檢測(紅外線)
程式碼

參考影片:射線檢測

☎常見應用:RPG遊戲,用滑鼠,點按某個地面點,主角就會移動到該點,
☎方法:它們都是使用『射線檢測』法,來偵測點按的坐標,然後再移動到該點
☎原理:就是從camera發出射線,打到第一個對象物件(按cube),就是觸控點的位置,可以取得該點的坐標
(1)前提:被射線發射碰到的物體,必須有碰撞器(coliider),才會發生hit,才能知道坐標
射線到plane場外,沒有碰撞器,就不會感應

(2)幾個步驟:
(2-1)如果按了滑鼠才檢測
if(Input.GetMouseButtonDown(0))
{
(2-2)按下左鍵滑鼠,則發射射線(由攝影機發出,到螢幕screen
Ray ray1 = Camera.main.ScreenPointToRay(Input.mouse.Position);
(2-3)宣告一個射線碰撞hit變數
RaycastHit hit1;
(2-4)宣告一個偵測是否碰撞變數res(射線檢測)
bool res = Physics.Raycast(ray1, out hit1);
//out hit1:若是有碰撞,會把射線碰撞參數訊息,放到hit1裡面(out = output)
(2-5)如何有射線碰撞(res = ture),hit1就會有參數,例如碰撞坐標 = hit1.point
if(res==true){
(2-6)輸出設想碰撞坐標
Debug.log(hit1.point);
this.transform.position = hit1.point;
}
}

7-3.如何取得發生射線ray後,發生觸碰點的物件資料(名稱,位置)

1.成果影片
成果圖片成果圖片成果圖片成果圖片成果圖片
cs01程式碼

2.如何取得射線ray,點按觸碰點的物件的資料(名稱,位置)
(1)觸碰點的物件名稱 = hit1.collider.gameObject.name
或是觸碰點的物件名稱 = hit1.collider.name

(2)射線觸碰點的物件坐標 = hit1.point

3.目標:點按物件後,顯示該物件的名稱

4.建立plane(地板),cube(方糖),capsule(膠囊),蜜蜂
下載蜜蜂bee物件(prefab)
成果圖片

5.建立UI/Text
成果圖片

6.寫程式碼:cs01.cs(掛載在空白物件empty GameObject上)
(1)原理:是在update裡面去監測全部的物件,是否有觸碰事件發生(例如按下滑鼠左鍵),若有,則使用射線Ray,經由滑鼠點,射到螢幕上的物件,完成物件觸碰
(2)
using UnityEngine.UI;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
//發出射線(滑鼠點-camera射線)
Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
//宣告碰撞變數
RaycastHit hit1;
//判別是否射線碰到物件,發生碰撞了
if (Physics.Raycast(ray1, out hit1) == true)
{
//觸控碰撞物件名稱:hit1.collider.name;
//在Text顯示碰撞物件名稱
GameObject.Find("Text").GetComponent<Text>().text = hit1.collider.name; }
}
}

7.觸控碰撞物件名稱:hit1.collider.name

8.為什麼蜜蜂物件觸碰後,不會顯示物件名稱:
(1)原因:蜜蜂是個複合物件,預設不會有碰撞器collider
(2)原因:觸碰是射線ray與物件的碰撞,必須設定物件的碰撞器collider
(3)解決方法:在此物件要加入組件:box collider
成果圖片

8.成果
成果影片
成果圖片成果圖片成果圖片成果圖片成果圖片
cs01程式碼

範例7-4.觸控事件第2種方法:在update上監測,是否有射線觸控,在觸碰點顯示圓圈(以淡黃色,透明100圓圈顯示)
方法2最常用

1.成果影片
成果圖片
cs01程式碼

2.觀念:手機的觸控事件寫法,一定要使用射線Ray
原理:射線從Camera發出,經過觸控點(手指點按處,或是滑鼠點按處MousePosition),最後投射到螢幕上,碰觸到物件(碰觸點hit1)

3.比較:
☎方法1:是在某個固定物件(例如plane)發生觸碰事件onMouseDown,然後使用射線Ray,經由滑鼠點,射到螢幕上的物件,完成物件觸碰
☎方法2:是在update裡面去監測全部的物件,是否有觸碰事件發生(例如按下滑鼠左鍵),若有,則使用射線Ray,經由滑鼠點,射到螢幕上的物件,完成物件觸碰
☎結論:方法2最常用

4.建立plane,cylinder
成果圖片成果圖片

5.建立一個透明圓圈(黃色,透明度100)
設定透明度的方法:
(1)在aseet➜建立material(yellow)➜設定黃色,透明度100
成果圖片成果圖片

6.寫程式碼:cs01.cs(掛載在空白物件empty GameObject上)
(1)原理:是在update裡面去監測全部的物件,是否有觸碰事件發生(例如按下滑鼠左鍵),若有,則使用射線Ray,經由滑鼠點,射到螢幕上的物件,完成物件觸碰
(2)
//只要按下左鍵,就偵測射線觸控的點
void Update()
{
//mouseButton = 0, 代表按下左鍵
if(Input.GetMouseButtonDown(0))
{
//發出射線(滑鼠點-camera射線)
Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
//宣告碰撞變數
RaycastHit hit1;
//判別是否射線碰到物件,發生碰撞了
if(Physics.Raycast(ray1, out hit1)==true)
{
//把黃色圈圈,移動到hit1.point
GameObject.Find("Cylinder").transform.position = hit1.point;
}
}
}

7.碰觸點的位置: hit1.point

8.成果
成果影片
成果圖片
cs01程式碼

7-5:如何觸控射線,讓角色unityChan緩動有各種動作動畫(idle,walk,jump)

☎關鍵指令:設定animtor的參數status=2,agent1.GetComponent<Animator>().SetInteger("status", 2);

(1)先建立地形的多物件➜導航網格(navigation static,navigation bake)
(2)再建立角色unityChan➜加入navMesh Agent
(3)asset➜建立animator (名稱:player_Animator)
(4)再加入角色unityChan➜加入animator = player_Animator
(5)設定player_Animator:
加入wait_00動畫(idle)
加入walk_F動畫(設定參數status=1,則走路,把hasExitTime取消勾選)
加入jump_04動畫(設定參數status=2,則走路,把hasExitTime取消勾選)
(6)設定角色unityChan➜程式碼:agent_move.cs
如果觸控點物件名稱=jump2/jump3,則跳躍
否則走路
if (hit1.collider.gameObject.name == "jump2")
{
agent1.GetComponent<Animator>().SetInteger("status", 2);
}
else if (hit1.collider.gameObject.name == "jump3")
{
agent1.GetComponent<Animator>().SetInteger("status", 2);
}
else
{
agent1.GetComponent<Animator>().SetInteger("status", 1);
}

7-6:複合物件(小蜜蜂)如何觸控點按能夠有反應

成果影片


☎重要觀念1:複合物件(小蜜蜂)很容易加上程式碼組件,結果沒有反應:
原因1:複合物件(小蜜蜂)必須加入box collider組件,才能觸控反應
原因2:複合物件(小蜜蜂),必須使用rays射線觸控檢測法,才能觸控到複合物件(onMouseDown無法觸控到複合物件,只能單一物件)

☎重要觀念2:觸控事件有兩種:
(1)方法1:OnMouseDown():這個方法只適合單一物件的觸控點按,複合物件無法響應。

(2)方法2:一個程式碼監測全部文件寫法:
使用射線發出後檢查碰到的物件名稱,位置
update....
if(input.GetMouseButtonDown(0)......
Ray ray1 = Camera.main.ScreenPointToRay(input.MousePosition)....
RaycastHit hit1....
if (Physics.Raycast(ray. out hit1)....

☎注意:方法2的射線觸控檢測法,能夠控制複合物件(小蜜蜂)的移動。
☎但是有個前提條件:必須要加入box collider組件,才能成功運行。

☎結論:複合物件(小蜜蜂)的觸控事件,必須使用方法2的射線觸控檢測法,但是必須要加入box collider組件,才能成功運行。


成果影片

下載小蜜蜂素材:
方法1:到asset store,輸入bee,下載小蜜蜂
方法2:下載小蜜蜂 prefabs

成果圖
步驟圖步驟圖
程式碼cs01程式碼cs02

【chp8.節點物件的運動方法1:移動,旋轉,縮放】
範例8-1:如何程式碼設定一個cube的大小


9.如何程式碼設定一個cube的大小:
circle1.transform.localScale = new Vector3(1.5f, 0.01f, 1.5f);

8-2:移動,有兩種位移法:(1)再移動dx,(2)移動到某坐標點

1.移動,有兩種位移法:(1)再移動dx,(2)移動到某坐標點
(1).再移動dx:目前位置再新增個deltaD位移
(2).移動到某處:是直接到目標坐標

2.方法1:目前位置再新增個deltaD位移(相對位移量):translate(Vector3)
當前位置再新增個位移(translate(Vector3),表示在當前位置移動一個向量):
this.gameObject.transform.Translate(new Vector2(-5, 0) * Time.deltaTime)
this.gameObject.transform.Translate(Vector2.up * Time.deltaTime);

3.方法2:瞬間快速移動方法,移動到某個坐標(.position = new Vector3(..):
若要當前位置位移(.position += new Vector3(..)
this.gameObject.transform.position -= new Vector3(5, 0, 0);

4.沿著方向和距離移動變換translation。
(1)Translate的向量寫法有三種:
(1-1)可以直接寫向量:transform.Translate(0,0,1*Time.deltaTime);
(1-2)可以寫new Vector3:transform.Translate(new Vector3(0,0,1)*Time.deltaTime);
(1-3)可以寫單位向量Vector3.up:transform.Translate(Vector3.up*Time.deltaTime);

(2)以某個點為中心去移動,有三種:
注意:Space.Self只針對Translate才能用,但是不能用在position
(2-1)註明以自己為軸心去移動:transform.Translate(Vector3.up*Time.deltaTime,Space.Self)
(2-2)註明以世界坐標軸去心移動:transform.Translate(Vector3.up*Time.deltaTime,Space.World)
(2-3)註明以當前Camera為軸心去移動:transform.Translate(Vector3.up*Time.deltaTime,Camera.main.transform)

//(3)沿z軸向前移動對象1個單位/秒。
transform.Translate(0,0,1*Time.deltaTime);

//(4)在世界空間中向上移動對象1個單位/秒。
transform.Translate(0,Time.deltaTime,0,Space.World);

//(5)沿z軸向前移動對象1個單位/秒。(p.s. forward 寫作的簡寫Vector3(0, 0, 1))
transform.Translate(Vector3.forward* Time.deltaTime);

//(6)在世界空間中向上移動對象1個單位/秒。
transform.Translate(Vector3.up* Time.deltaTime,Space.World);

//(7)相對於相機向右移動對象1個單位/秒。
transform.Translate(Time.deltaTime,0,0,Camera.main.transform);

8-3.移動,有兩種速度呈現法:(1)緩動,(2)瞬間移動到某坐標點

8-3.移動,有兩種速度呈現法:(1)緩動,(2)瞬間移動到某坐標點
(1)緩動:慢慢位移方法:
例如:緩動,每次再移動dx,
在update(){
this.gameObject.transform.Translate(new Vector2(-5, 0) * Time.deltaTime)
this.gameObject.transform.Translate(Vector2.up * Time.deltaTime);
}

(2)瞬間快速移動方法:
例如:
移動到某個坐標:this.transform.position = new Vector3(3,0,0):
若要當前位置再位移:this.transform.position += new Vector3(3,0,0)
this.gameObject.transform.position -= new Vector3(5, 0, 0);

2.Transform類別:
參考網頁:Transform類別

(1)**常用屬性**
transform.Position = new Vector3(0, 0, 0); //以世界為中心的座標
transform.localPosition = new Vector3(0, 0, 0); //以父物件為中心的座標

(2)**常用方法**
transform.Translate(Vector3.forward * Time.deltaTime);//位移方法
transform.Rotate(xAngle, yAngle, zAngle, Space.Self); //以自身為軸心旋轉
transform.Rotate(xAngle, yAngle, zAngle, Space.World);//軸心取決於世界(scene)
☎※Time.deltaTime(幀數之間的時差)


3.利用transform.Translate移動(使用位移方法)
☎translagte方法的優點:可以控制每秒慢慢移動,不會瞬間移動

private void movement()//方法
{ //採用直接改變物件座標的方式
(1)直接改變物件座標(速度很快,瞬間移動)
//一、向右走
if (Input.GetKey("d"))//輸入.來自鍵盤(“d”)
{
this.gameObject.transform.Translate(new Vector2(5, 0));
} //此類別.這個物件.座標系統.位移(delta向量)

(2)依照一、的作法會發現物件飆很快,因此要乘上Time.deltaTime來延遲。
二、向左走;
if (Input.GetKey("a"))
{
this.gameObject.transform.Translate(new Vector2(-5, 0) * Time.deltaTime);
}

(3)可以直接使用Vector的屬性Vector2.up,就不需要new一個變數
向上走
if (Input.GetKey("w"))
{
this.gameObject.transform.Translate(Vector2.up * Time.deltaTime);
}

(4)直接使用Vector的屬性Vector2.down,就不需要new一個變數
//向下走
if (Input.GetKey("s"))
{
this.gameObject.transform.Translate(Vector2.down * Time.deltaTime);
}
}

4.利用transform.position(改變物件座標)
☎position方法的缺點:無法控制每秒慢慢移動,只會瞬間移動

public float speed; //設公開的數度變數,可在unity中設值調整
private void movement()//方法
{ //採用直接改變物件座標的方式

(1)移動方法:transform.position += new Vector3(....)
//向右走
if (Input.GetKey("d"))//輸入.來自鍵盤(“d”)
{
this.gameObject.transform.position += new Vector3(speed, 0, 0);
} //此類別.這個物件.座標系統.位置(為一個向量值x,y,z)+=這個向量

//向左走
if (Input.GetKey("a"))
{
this.gameObject.transform.position -= new Vector3(speed, 0, 0);
}

//向上走
if (Input.GetKey("w"))
{
this.gameObject.transform.position += new Vector3(0, speed, 0);
}
//向下走
if (Input.GetKey("s"))
{
this.gameObject.transform.position -= new Vector3(0, speed, 0);
}
}

8-4.Vector有兩種:單位向量Vector3.left,new Vedtor3(2,2,0)

8-4.Vector有兩種:單位向量Vector3.left,new Vedtor3(2,2,0)

(1)向量的寫法有兩種,一種要new Vedtor3(),另外一種不需要new
(1-1)要new:new Vector3(1.0f, 0.0f, 0.5f)
(1-2)不需要new:Vector3.left,就是單位向量

Vector2.up ====Vector(0,1)
Vector3.left ===== Vector(-1,0,0)
Vector3.up ===== Vector(0,1,0)
Vector3.forward ===== Vector(0,0,1)
Vector3.back === Vector(0,0,-1)

(2)向量是在坐標系中有方向、大小的值。
Vector2,二維向量,在平面座標系中,與原點的差值。Vector2(x, y)。
Vector3,三維向量,在三維空間中,與原點的差值。Vector3(x, y, z)。
x,y,z是數字,可以寫成整數(int)或浮點數(float ※數字後要加 f,例如 0.0f)。

(3)向量變數基本用法
Vector2 vec2 = new Vector2(1, 0);//int x=1 , int y=0
Vector3 vec3 = new Vector3(1.0f, 0.0f, 0.5f);//float x=1.0 , float y=0.0 , float z=0.5

Debug.Log(vec2.x); //1
Debug.Log(vec3.z); //0.5
另外補充,Vector有屬性可以使用,直接呼叫就不用設值。(請參閱unity官方文件)

(4)不需要進行new Vector,可直接呼叫的屬性
Debug.Log(Vector2.up.y); // 1
Debug.Log(Vector2.up); // (0.0,1.0)
Vector2.left 就是new Vector2(-1,0)
Vector2.right 就是new Vector2(1,0)
Vector2.up 就是new Vector2(0,1)
Vector2.down 就是new Vector2(0,-1)
Vector2.zero 就是new Vector2(0,0)
Vector2.one 就是new Vector2(1,1)

Vector3.left 就是new Vector3(-1,0,0)
Vector3.right 就是new Vector3(1,0,0)
Vector3.up 就是new Vector3(0,1,0)
Vector3.down 就是new Vector3(0,-1,0)
Vector3.forward 就是new Vector3(0,0,1)
Vector3.back 就是new Vector3(0,0,-1)

8-5.unity的角度有5種,unity的旋轉方式有5種

1.unity的角度有5種,unity的旋轉方式有5種
(1)unity 五種旋轉方式:localEulerAngles、eulerAngles、rotation、localRotation、Rotate

(1-1)Transform.localEulerAngles 自身欧拉角(var localEulerAngles : Vector3
參考網頁:Transform.localEulerAngles 自身欧拉角
範例:// Print the rotation around the parent's X Axis(打印绕父级的X轴的旋转角度
print(transform.localEulerAngles.x);

(1-2)Transform.eulerAngles 欧拉角(var eulerAngles : Vector3
參考網頁:Transform.eulerAngles 欧拉角
範例:// Print the rotation around the global X Axis(绕世界x轴的旋转角度
print(transform.eulerAngles.x);

(1-3)Transform.rotation 旋转角度(var rotation : Quaternion
參考網頁:Transform.rotation 旋转角度
原理:在世界空间坐标物体变换的旋转角度作为Quaternion储存。
/原理:Untiy作为四元数内部储存旋转角度。旋转一个物体,
/方法1:使用Transform.Rotate➜用四元数内部储存旋转角度。旋转一个物体
/方法1:使用Transform.eulerAngles➜用欧拉角➜來旋转角度。
範例1://重设世界的旋转角度 :transform.rotation = Quaternion.identity;
範例2:
//平滑倾斜物体向一个target旋转
var smooth = 2.0;
var tiltAngle = 30.0;
function Update () {
var tiltAroundZ = Input.GetAxis("Horizontal") * tiltAngle;
var tiltAroundX = Input.GetAxis("Vertical") * tiltAngle;
var target = Quaternion.Euler (tiltAroundX, 0, tiltAroundZ);
// Dampen towards the target rotation
//向target旋转阻尼
transform.rotation = Quaternion.Slerp(transform.rotation, target,
Time.deltaTime * smooth);;
}

(1-4)Transform.localRotation 自身旋转角度(四元数)
參考網頁:Transform.localRotation 自身旋转角度(四元数)
var localRotation : Quaternion
物体变换的旋转角度相对于父级的物体变换的旋转角度。
範例:
设置旋转角度同父级一样。
transform.localRotation = Quaternion.identity;

(1-5)Transform.Rotate 旋转函數
參考網頁:Transform.Rotate 旋转函數
function Rotate(eulerAngles : Vector3, relativeTo: Space = Space.Self) : void
应用一个欧拉角的旋转角度,eulerAngles.z度围绕z轴,eulerAngles.x度围绕x轴,eulerAngles.y度围绕y轴(这样的顺序)
範例:沿着x轴每秒1度慢慢的旋转物体
transform.Rotate(Vector3.right * Time.deltaTime);

範例:相对于世界坐标沿着y轴每秒1度慢慢的旋转物体
transform.Rotate(Vector3.up * Time.deltaTime, Space.World);

範例://围绕x轴每秒1度,慢慢的旋转物体
transform.Rotate(Time.deltaTime, 0, 0);

範例:相对于世界坐标,围绕y轴每秒1度,慢慢的旋转物体
transform.Rotate(0, Time.deltaTime, 0, Space.World);

☎☎☎2.設定旋轉角度,最常設定的方法:transform.localEulerAngles
原因1:相對於父節點的xyz軸,旋轉角
原因2:localEulerAngles = Vector3(10, 20, 45),歐拉角是我們常用角度
範例:node.transform.localEulerAngles = new Vector3(0, 90, 0);
原因3:localRotation是個四元數Quaternion,不好用

範例8-6:用幀動畫來控制直升機移動:上下、左移右移、左轉右轉

1.直升機向上移動的方法:採用幀動畫(frame animation)
原理:
(1)設定udapte()幀動畫:使用SpeedY * Time.deltaTime
void Update()
{
float dx = SpeedX * Time.deltaTime;
float dy = SpeedY * Time.deltaTime;
float dz = SpeedZ * Time.deltaTime;
this.gameObject.transform.Translate(dx, dy, dz, Space.Self);

float ry = RotateY * Time.deltaTime;
this.gameObject.transform.Rotate(0, ry, 0, Space.Self);
}
(2)按了按鈕(向上):
原理:一開始向上速度=2,但10s後後就啟動一個計時器,執行moveY,讓速度=0
public void MoveUp()
{
SpeedY = 2;
Invoke("moveY", 10);
}
void moveY()
{
SpeedY = 0;
}

2.直升機啟動螺旋槳動畫,播放音效的方法:
原理:
(1)如何把節點的component<Animation>打勾:
方法:this.gameObject.GetComponent<Animation>().enabled = true;
public void TurnOn()
{
this.gameObject.GetComponent<Animation>().enabled = true;
this.gameObject.GetComponent<AudioSource>().Play();
}

3.成果:
(1)成果影片

(2)參考影片:無人機飛行
參考影片:第16章調用component(無人機飛行)

(3)下載素材資源:
直升機的asset store
Unity UI Menu的asset store

(4)步驟圖:
步驟圖1步驟圖2步驟圖3步驟圖4步驟圖5

程式碼

8-7:AddForce(單位向量)來施力讓物體移動

參考網頁:Addforce,RequireComponent
參考影片:RPG Enemy Set States 設置敵人的基本屬性和狀態

☎注意:AddForce(方向向量),這個方法不實用,因為必須將物件節點加入rigidbody,造成力學效果。
☎但是:我們的遊戲程式很少會用力學模式(例如重力),而是用動力學kinematic模式(只有碰撞,沒有力學效果)。

1.本題目的:一顆足球,四個按鈕,前後左右施力移動

☎注意1:施力移動,乃是針對rigidbody來施力(myrigidbody.AddForce(Vector3.right))
☎注意2:施力的單位,乃是單位向量(前後左右:Vector3.right....)
☎注意3:可以使用RequireComponent方法,來強迫該程式碼加載的物件,自動加入組件Rigidbody
也可以自己手動加入組件(add component)。

2.程式碼:
//強迫該程式碼加載的物件,自動加入組件Rigidbody
[RequireComponent(typeof(Rigidbody))]

public class csMove : MonoBehaviour
{
Rigidbody myrb;
float forceFactor = 20;

private void Start()
{
myrb = this.gameObject.GetComponent<Rigidbody>(); }
public void moveRight()
{
myrb.AddForce(Vector3.right* forceFactor);
}
public void moveLeft()
{
myrb.AddForce(Vector3.left* forceFactor);
}
public void moveForward()
{
myrb.AddForce(Vector3.forward * forceFactor);
}
public void moveBack()
{
myrb.AddForce(Vector3.back * forceFactor);
}

成果影片

成果圖
步驟圖步驟圖步驟圖
程式碼

範例8-8:複合物件(小蜜蜂)如何觸控點按後就能向上移動1秒

成果影片

☎重要觀念1:點按物件移動,不建議用AddForce(方向向量),這個方法不實用,因為必須將物件節點加入rigidbody,造成力學效果。
但是:我們的遊戲程式很少會用力學模式(例如重力),而是用動力學kinematic模式(只有碰撞,沒有力學效果)。

☎重要觀念2:觸控事件有兩種:
(1)方法1:OnMouseDown():這個方法只適合單一物件的觸控點按,複合物件無法響應。

(2)方法2:一個程式碼監測全部文件寫法:
使用射線發出後檢查碰到的物件名稱,位置
update....
if(input.GetMouseButtonDown(0)......
Ray ray1 = Camera.main.ScreenPointToRay(input.MousePosition)....
RaycastHit hit1....
if (Physics.Raycast(ray. out hit1)....

☎注意:方法2的射線觸控檢測法,能夠控制複合物件(小蜜蜂)的移動。
☎但是有個前提條件:必須要加入box collider組件,才能成功運行。

☎結論:複合物件(小蜜蜂)的觸控事件,必須使用方法2的射線觸控檢測法,但是必須要加入box collider組件,才能成功運行。

☎重要觀念3:點按物件後移動1秒的最好方法
(1)不建議用:AddForce()函數,這個屬於力學模式(rigidbody),我們很少用力學模式。

(2)控制移動1秒:必須用計時器Invoke("StopMoveY", 1);
1秒後把幀動畫的位移分量speedY = 0

(3)幀動畫的做法(在update裡面)
float dx = speedX * Time.deltaTime;
float dy = speedY * Time.deltaTime;
float dz = speedZ * Time.deltaTime;
this.gameObject.transform.Translate(dx, dy, dz, Space.Self);


成果影片

下載小蜜蜂素材:
方法1:到asset store,輸入bee,下載小蜜蜂
方法2:下載小蜜蜂 prefabs

成果圖
步驟圖步驟圖
程式碼cs01程式碼cs02

範例8-9:複合物件(小蜜蜂)如何觸控點按後就以鏡頭camera垂直方向forward遠離鏡頭1秒

成果影片

☎(1)重要觀念1:觸控物件被點擊後,以當下鏡頭的法向方向移動:
☎鏡頭的法向方向:Camera.main.transform.forward;
原理:Camera is simply a Component attached to a GameObject. It's orientated using the relative Transform.
Main Camera forward direction in world space can be accessed this way:Camera.main.transform.forward;


☎(2)重要觀念2:小蜜蜂物件如何以鏡頭camera的法向forwrad進行幀動畫:
幀動畫:
speedCameraForward = -1*Camera.main.transform.forward;
......
this.gameObject.transform.Translate(speedCameraForward*Time.deltaTime, Space.Self);
範例:
Vector3 speedCameraForward;
void Update()
{
//判別按下滑鼠左鍵
if (Input.GetMouseButtonDown(0))
{
//(1)射線取得點按位置的坐標
Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit1;
//射線檢測
if (Physics.Raycast(ray1, out hit1) == true)
{
//觸碰點的物件名稱 = hit1.collider.gameObject.name
string name1 = hit1.collider.gameObject.name;
if(name1== "FantasyBee")
{
//1秒後執行StopMoveCameraForwar()函數,讓相機法向向量速度speedCameraForward=0
speedCameraForward = -1*Camera.main.transform.forward;
Invoke("StopMoveCameraForwar", 1);
}
}
}//endi if 判別滑鼠左鍵
//(2)幀動畫
this.gameObject.transform.Translate(speedCameraForward*Time.deltaTime, Space.Self);
}
void StopMoveCameraForwar()
{
speedCameraForward = Vector3.zero;
}


成果影片

下載小蜜蜂素材:
方法1:到asset store,輸入bee,下載小蜜蜂
方法2:下載小蜜蜂 prefabs

成果圖
步驟圖步驟圖
程式碼cs01

範例8-15:計算兩個物件(agent1與圓圈)的距離

13.計算兩個物件(agent1與圓圈)的距離:
float distance1 = Vector3.Distance(agent1.transform.position, circle1.transform.position);

【chp9.節點物件的運動方法2:緩動移動(導航網格navMesh,Time.deltaTime)】
範例9-1:要讓角色緩動(在一段時間內移動到目的地),有兩種方法:導航網格navMesh,Time.deltaTime

1.要讓角色緩動(在一段時間內移動到目的地),有兩種方法:Time.deltaTime法,導航網格法

☎(1)方法1:在update內...使用Time.deltaTime來移動Translate,或旋轉Rotate
void Update()
{
float dx = SpeedX * Time.deltaTime;
float dy = SpeedY * Time.deltaTime;
float dz = SpeedZ * Time.deltaTime;
this.gameObject.transform.Translate(dx, dy, dz, Space.Self);

float ry = RotateY * Time.deltaTime;
this.gameObject.transform.Rotate(0, ry, 0, Space.Self);
}
☎(2)方法2:導航網格系統(navMesh)
(2-1)把地形物件➜設定navigation Static
(2-2)開啟Navigation 視窗(Windows➜AI➜navigation)
(2-3)建立導航網格➜navigation視窗➜Bake➜Bake,就會建立導航網格了
(2-4)新增角色物件➜加入程式碼,讓角色緩動到觸控點
代理人(角色)
using UnityEngine.AI;
NavMeshAgent agent1;
void Start()
{
agent1 = GameObject.Find("unitychan").GetComponent<NavMeshAgent>();
}
void Update()
{
//全局偵測,是否按下滑鼠左鍵
if (Input.GetMouseButtonDown(0))
{
//發出射線(滑鼠點-camera射線)
Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
//宣告碰撞變數
RaycastHit hit1;
//判別是否射線碰到物件,發生碰撞了
if (Physics.Raycast(ray1, out hit1) == true)
{
//讓agent1移動到hit1.point
agent1.SetDestination(hit1.point);
}
}
(2-5)緩動到目的地的關鍵指令:agent1.SetDestination(hit1.point);
☎錯誤寫法:agent1.transform.position = hit1.point(這個是瞬間移動,不是緩動)

9-2:導航網格navMesh

☎導航網格navMesh

1.導航網格,navMesh, navMeshAgent:
參考影片:Unity零基础系统教学(Gamer飞羽)54-57章節教程(导航网格的使用)

2.nav = 導航 = navigation
agent = 代理人 = 行走的角色
mesh = 地板上可以行走的網格

3.設定導航網格的幾點:
(1)角色。。。都不需要設定rigidbody(也不需要勾選istrigger, iskinematic)
(2)何謂自動導航:

(A)只要滑鼠在畫面上觸控一點,角色就會自動以某個速度,結果看行走網格,走到那個地方
(B)它會根據最低成本路徑,走到目的地
(C)它可以設定:高處,跳下點
(D)它可以設定:兩處,跳遠點
(E)它可以適當:兩處,連接點(飛行)
(F)它可以設定,怪物,朝向角色而來

成果圖片
步驟圖1步驟圖2步驟圖3步驟圖4步驟圖5步驟圖6
程式碼

4.設定角色在地板網格行走的步驟:
(1)先建立地形(plane,cube。。。)
(2)建立角色
(3)設定地形的導航網格
(3-1)選取全部的地形➜右邊static➜navigation static(靜態導航)
(3-2)然後到右邊的一個『navigation』視窗➜bake(就會產生導航網格了)
但是要先設定能夠通過導航行走agent的4個基本參數(半徑,身高,agent爬斜坡的斜率上限,agent跨階梯的step上限)
經驗:這些網格的➜bake➜step height要設定高點,例如0.9(發現,若是設定0.4,則待會的主角,無法爬上階梯)
(3-3)最後按下方:bake➜產生導航網格(淺藍色網格,代表成本1的網格)
(4)設定主角為agent➜add component➜navMesh agent
(5)建立主角的程式碼➜add component➜playerControl.cs
using UnityEngine.AI;
抓取代理組件
agent1 = this.GetComponent<NavMeshAgent>();

void Update()//{
//手觸控某個位置,agent1就會朝點的位置走過去
//判別按下滑鼠左鍵
if (Input.GetMouseButtonDown(0))
{
//射線取得點按位置的坐標
Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit1;
//射線檢測
if (Physics.Raycast(ray1, out hit1) == true)
{
//取得點擊位置
Vector3 point1 = hit1.point;
//設定point1為導航目標點
agent1.SetDestination(point1);
}
}
}

9-3:導航網格的特殊功能1:兩點之間跳躍(等高點的跳躍)

☎5.導航網格的特殊功能1:兩點之間跳躍(等高點的跳躍)
(1)建立兩個等高的物件,cube1,cube2
(2)???在navigation視窗➜Object標籤➜勾選 Generate offMeshlinks ???
(3)在navigation視窗➜Bake標籤➜設定 jump distance = 100
(4)在navigation視窗➜Bake標籤➜bake(結果:產生link連線)

成果影片
成果圖片
步驟圖7步驟圖8步驟圖9步驟圖10

9-4:導航網格的特殊功能2:兩點之間跳躍(高低點的跳躍)

☎6.導航網格的特殊功能2:兩點之間跳躍(高低點的跳躍)
(1)建立兩個不等高的物件,jump2,jump3
(2)在navigation視窗➜Object標籤➜勾選 Generate offMeshlinks
(3)jump3物件➜加入offMeshLinks組件
設定start = jump2
設定end = jump3
(4)在navigation視窗➜Bake標籤➜bake(結果:產生link連線)
成果影片
成果圖片

步驟圖7步驟圖8步驟圖9步驟圖10

9-5:如何修正:導航網格轉成AR(Vuforia)的錯誤:角色會浮在空中

☎如何修正:導航網格轉成AR(Vuforia)的錯誤:角色會浮在空中
4.設定可以自動觸控導航後,若是應用Vuforia,會出現,角色懸浮在空中的問題:
解決方法1:參考:修正導航網格在AR的錯誤

Click on AR Camera which is on your scene,
then there is a script attached to the AR Camera which is "Vuforia Behaviour" script.
It has "World Center Mode" option and "DEVICE" is selected as default. You need to change it from "DEVICE" to "SPECIFIC_TARGET" and as a target you should choose your "Image Target". After that, your Nav Mesh Agent should be working very well in AR as it works with normal camera.
步驟1:選擇 ARCamera➜World Center Mode:選:SPECIFIC_TARGET
步驟2:選擇 ARCamera➜World Center:target選:Image Target
測試:成功

步驟圖1

範例9-6:實作主角膠囊在導航網格內,自由觸控移動

參考影片:导航网格的使用

成果影片

成果圖片
步驟圖1步驟圖2步驟圖3步驟圖4步驟圖5步驟圖6
程式碼
下載專案檔案

範例9-7:實作主角unityChan在導航網格內,自由觸控移動+動作(idle,walk,jump)

成果影片
成果圖片
步驟圖1步驟圖2步驟圖3步驟圖4步驟圖5步驟圖6
步驟圖7步驟圖8步驟圖9步驟圖10
程式碼
下載專案檔案

9-8:unity的導航網格無法做到移動,如何修正

☎注意:unity的官網說明,導航網格navMesh,是無法支援移動中的網格。

(1)方法1:複雜法
教學影片:Yes You CAN Have Moving NavMesh Platforms
下載程式碼

(2)方法2:簡易法
(2-1)導航網格的瓶頸:若是物件移動,navMesh導航網格不會跟著移動
(2-2)修正:
☎方法:當主角agent1,觸碰移動物件cube1後,就把agent1加入cube1內(成為子節點)
☎主要程式碼:
private void OnTriggerEnter(Collider other)
{ if(other.name == "cube1")
{ this.transform.SetParent(other.transform);
}
}
程式碼

9-9:導航網格的主角,如何不動就idle,特定位置則jump,否則walk

(0)舉例:主角unitychan,觸碰link2就跳躍jump,導航就walk,到了目的地就idle
程式碼

☎步驟:
(1)設定導航網格navMesh,設定跳躍連接點offMeshLink,
(2)在主角unitychan設定程式碼playerControl.cs

☎(3)判別原地不動,顯示idle的程式思路:
(3-1)宣告prev_position變數:代表前一個幀率時,主角unitychan的位置
Vector3 prev_position = new Vector3(0,0,0);
(3-2)原理:在update裡面➜若主角最新的位置 = prev_position➜表示主角沒有移動了➜播放idle動畫(設定status=0)
if (GameObject.Find("unitychan").transform.position == prev_position)
{
GameObject.Find("unitychan").GetComponent<Animator>().SetInteger("status", 0);
}
...............
//最後一行,設定prev_position
prev_position = GameObject.Find("unitychan").transform.position;

(3-3)程式碼:
void Update()
{
bool isjump = false;
if (Input.GetMouseButtonDown(0))
{
Debug.Log(GameObject.Find("unitychan").GetComponent<Animator>().GetInteger("status"));
//射線取得點按位置的坐標
Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit1;
//射線檢測
if(Physics.Raycast(ray1,out hit1)==true)
{
//取得點擊位置
Vector3 point1 = hit1.point;
//設定point1為導航目標點
agent1.SetDestination(point1);
//如果jump to (link2)則jump
if(hit1.collider.gameObject.name=="link2" || hit1.collider.gameObject.name == "door3")
{
...............
}
}
}
//注意:要先判斷Jump的case,否則永遠不會執行jump
if (isjump == true)
{
GameObject.Find("unitychan").GetComponent<Animator>().SetInteger("status", 2);
}
//如果原地不動,則status =0
else if (GameObject.Find("unitychan").transform.position == prev_position)
{
GameObject.Find("unitychan").GetComponent<Animator>().SetInteger("status", 0);
}
else//否則walk
{
GameObject.Find("unitychan").GetComponent<Animator>().SetInteger("status", 1);
}
//設定prev_position
prev_position = GameObject.Find("unitychan").transform.position;
}

☎(4)判別jump,顯示jump動畫的程式思路:
(4-1)宣告isjump變數:代表主角unitychan與特定link2物件是否碰撞,若是則jump
bool isjump = false;
(4-2)原理:在update裡面➜射線檢測if(Physics.Raycast(ray1,out hit1)➜若碰到link2➜if(hit1.collider.gameObject.name=="link2")➜則isjump = true
(4-3)程式碼:
void Update()
{
bool isjump = false;
if (Input.GetMouseButtonDown(0))
{
Debug.Log(GameObject.Find("unitychan").GetComponent<Animator>().GetInteger("status"));
//射線取得點按位置的坐標
Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit1;
//射線檢測
if(Physics.Raycast(ray1,out hit1)==true)
{
//取得點擊位置
Vector3 point1 = hit1.point;
//設定point1為導航目標點
agent1.SetDestination(point1);
//如果jump to (link2)則jump
if(hit1.collider.gameObject.name=="link2")
{
//設定主角jump動畫(但是沒有效果),可能是update馬上被改成status=1
//GameObject.Find("unitychan").GetComponent<Animator>().SetInteger("status", 2);
//所以用一個isjump變數
isjump = true;
}
}
}
//注意:要先判斷Jump的case,否則永遠不會執行jump
if (isjump == true)
{
GameObject.Find("unitychan").GetComponent<Animator>().SetInteger("status", 2);
}

☎(5)判別碰撞物件,顯示過關win動畫的程式思路:
(5-1)宣告iscollide變數:代表主角unitychan與特定door3物件是否碰撞
bool iscollide = false;
(5-2)原理:在OnTriggerEnter裡面➜若主角最新的位置 = prev_position➜表示主角沒有移動了➜播放idle動畫(設定status=0)
(5-3)程式碼:
bool iscollide = false;
Vector3 prev_position = new Vector3(0,0,0);
private void OnTriggerEnter(Collider other)
{
if(other.name == "door3" && iscollide==false)
{
iscollide = true;
//播放過關音效
other.GetComponent<AudioSource>().Play();
//顯示過關畫面
img_Win.SetActive(true);
}
.............
}
☎(6)playerControl程式碼:
程式碼

範例9-10:實作主角unityChan的極限體能王過關遊戲

成果影片成果影片+製作步驟
AR走桌上的衛生紙

下載素材檔案
成果圖片1成果圖片2成果圖片3成果圖片4

步驟圖1步驟圖2步驟圖3步驟圖4步驟圖5步驟圖6步驟圖7步驟圖8步驟圖9步驟圖10步驟圖11步驟圖12步驟圖13

doormove程式碼
playerControl程式碼
gameover_manager程式碼

下載專案檔案

【chp10.射擊遊戲,不斷生成的prefab】
10-1.如何設定怪物不斷生成產生

10-1.如何設定怪物不斷生成產生

MonsterCreator程式碼
步驟圖1-MonsterFolder-MonsterCreator

(1)把已經建立好的怪物monster物件(可以左右隨機前進)➜拖曵成prefab
(2)建立一個空的gameObject(命名monster_Foler➜位置重置歸零(transform/Reset)後,才能再移動、)

(3)monster_Foler➜加上程式碼(怪物生成器):add component➜MonsterCreator.cs
☎程式碼1:使用計時器,不斷產生怪物:InvokeRepeating("MonsterCreat", 0.1f, 1.0f);
void MonsterCreat()
{
//方法2:使用目前this節點的transform變動組件
GameObject node = Instantiate(MonsterPrefab, this.transform);
//設定節點生成位置
node.transform.position = this.transform.position;
}

☎程式碼2:讓怪物monster prefab不要在原點建立(會被子彈打到,在原點的z左右隨機偏移第一次出現)
float dz = Random.Range(-30, 30);
node.transform.Translate(0, 0, dz, Space.Self);
}

☎程式碼3:設定怪物prefab的生命週期=300秒
Invoke("MonsterEnd", 300);
void MonsterEnd()
{
Destroy(this.gameObject);
}

monster-(MonsterCreator.cs)程式碼

10-2.兩種計時器(Invoke,InvokeRepeating)與各種計時器指令

☎計時器的各種指令用法:
1.Invoke是延時調用函數,在用Invoke函數之前需要引入命名空間using UnityEngine.Events;

發現:Invoke類似計時器setInterval("show",3) = Invoke("show", 3)
差異:InvokeRepeating更為進階,可以設定兩個時間參數 = (幾秒後,重複時間)
差異:Invoke,可以設定一次時間參數 = (幾秒後)
(1)InvokeRepeating("show", 2, 3) :2秒後,每隔三秒執行一次show
(2)Invoke("show", 2) :2秒後,執行一次show
(3)javascript:setInterval("show",3):每隔3秒執行一次show

2.各種計時器範例:
(1).Invoke("MethodName", 2)
這個比較簡單,寫在c#腳本中,意為兩秒之后調用一次,MethodName方法。

(2).InvokeRepeating("MethodName", 1, 2)
這個方法就是多次調用Invoke,即理解為一秒后,每隔兩秒調用MethodName方法。

(3).CancelInvoke("MethodName")
取消MethodName方法的調用。

(4)如何:關閉計時器
(4-1)關閉某個定時器(執行fire函數)
CancelInvoke("fire");

(4-2)關閉全部定時器
CancelInvoke();

3.MonoBehaviour裏面有兩個內置的延時方法
(1)InvokeRepeating(methodName: string, time: float, repeatRate: float): void;
methodName: 方法名
time:多少秒後執行
repeatRate:重復執行間隔

(2)Invoke(methodName: string, time: float): void;
methodName: 方法名
time:多少秒後執行

(3)還有兩個重要的方法:
IsInvoking:用來判斷某方法是否被延時,即將執行
CancelInvoke:取消該腳本上的所有延時方法

10-3.使用生成指令Instantiate(實例),在某物件A的位置,生成新物件B

☎生成指令Instantiate的用法:

(1)語法:Instantiate實例化(要生成的物件, 物件位置, 物件旋轉值);
範例:Instantiate(gameObject, transform.position, transform.rotation);

(2)自訂生成位置:
自訂Position : new Vector3(3,0,0)
自訂Rotation : new Quaternion(0,90,0,0)
範例:Instantiate (gameObject, new Vector3(3,0,0), new Quaternion(0,90,0,0));

(3)生成新物件在場景內的目前節點位置
範例:使用目前this節點的transform變動組件
GameObject node = Instantiate(MonsterPrefab, this.transform);

(4)生成在場景內的某個資料夾物件
範例:
[Tooltip("子彈目錄")]
public Transform bulletFolder;
void fire()
{
GameObject node = Instantiate(bulletPrefab, bulletFolder);
node.transform.position = bulletFirePoint.position;
}

(5)生成在場景scenes內的根目錄
範例:
//產生爆炸預鑄體prefab,放在根目錄(null)
GameObject node1 = Instantiate(bulletExplosion, null);

10-4.如何讓怪物monster亂數左右移動(蛇形走位)

☎如何讓怪物monster亂數左右移動(蛇形走位):

1.參考教程(無人機,射擊遊戲)
(1)參考影片:射擊遊戲
第25章1-10(射擊遊戲)

(2)參考影片:無人機飛行
參考影片:第16章調用component(無人機飛行)

2.設定步驟:
(1)怪物monster外層加個空的gameObject➜位置重置歸零(transform/Reset)後,才能再移動、
➜add box collider(is triggeer)
➜add rigidbody(is kenematic)

(2)怪物monster➜add monsterLogic.cs
☎程式碼1:設定怪物monste移動(前進dx,左右dz)
➜void Update()
{
this.transform.Translate(dx, 0, dz, Space.Self);
}

☎程式碼2:設定怪物monster每隔MonsterMoveInterval時間,就改變左右dz移動
➜InvokeRepeating("moveZRandom", 0.1f, MonsterMoveInterval);

void moveZRandom()
{
float[] zspeed = { -10, 5, -5, 10 };
int zindex = Random.Range(0, 4);
MonsterZSpeed = zspeed[zindex];
}

MonsterLogic程式碼
步驟圖1-monster-(MonsterLogic.cs)


12.設定怪物monster碰撞到player玩家時,的爆炸粒子,與音效,注意事項:
(1)會發現沒有爆炸效果:
原因:player玩家節點,要有兩個組件(且打勾動態運動學的碰撞):
(1-1)player玩家節點➜加入box collider(打勾is trigger)
(1-2)player玩家節點➜加入rigid body(打勾is kenematic)

10-5.從某個角色物件Cube-player,如何不斷發射子彈prefab(Sphere-bullet)


1.參考教程(無人機,射擊遊戲)
(1)參考影片:射擊遊戲
第25章1-10(射擊遊戲)

(2)參考影片:無人機飛行
參考影片:第16章調用component(無人機飛行)

2.重要原理:
☎本題的碰撞是兩種方法之一:只接觸沒有物理力學效果:iskenematic(運動學),istrigger(接觸,觸發器)
☎碰撞的前提:必須有box collider,必須有rigidbox,必須有接觸設定(iskenamatic,istrigger)

3.步驟:
(1)在場景先建立子彈物件(Sphere-bullet)位置重置歸零(transform/Reset)後,才能再移動、
(1-1)➜要能夠碰撞,必須加上rigidbody➜打勾is kinematic(有動力學碰撞效果,但沒有重力修改)
(1-2)➜ 打勾 is kinematic(運動學,只有移動運動,但是沒有物理力學效果)

(2)sphere collider ➜ 打勾 is trigger(觸發器,只是判斷接觸,沒有物理力學碰撞)

(3)add component ➜ bulletLogic.cs
☎程式碼1:bullet持續往前移動:
update
....
this.transform.Translate()

☎程式碼2:bullet的生命週期5秒:
Invoke("selfdestory",5)
....
selfdestory()
{
Destroy(this.gameObject);
}

☎程式碼3:接觸其它物件則彼此銷毀:
OnTriggerEnter()
{
Destroy(this.gameObject);
Destroy(other.gameObject);
}

(4)把子彈物件(Sphere-bullet)➜拖曵到asset
➜建立prefabg Sphere-bullet

(5)如何不斷呼叫產生大量的子彈
(5-1)在攻擊玩家player物件(Helicopter)➜add component➜playerLogic.cs
☎程式碼1:定時不斷開火,產生子彈:
InvokeRepeating("fire",0.1,0.5)

☎程式碼2:產生子彈prefabs:
fire()
{
GameObject node = Instantiate(子彈prefabs,子彈群目錄)
}

☎程式碼3:鍵盤ad,控制玩家player物件,左右移動:
update()
{
if(Input.GetKey(KeyCode.A))
......
this.transform.position += Vector3.back* playerSpeed;
}

☎.注意:
➜建立一個空的節點monster_Foler,要注意位置會自動亂設定,不在原點:
➜位置要先重置歸零(transform/Reset)後,才能再移動

bullet_logic程式碼
playerLogic程式碼

步驟圖1-(Sphere-bullet)-(bullet_logic.cs)
步驟圖2-(Helicopter)-(playerLogic.cs)-(helicopterControl.cs)


4.如果發現沒有出現子彈的,真正原因:
原因:若有修正,要更新子彈bullet節點時➜overridder➜apply all
注意:要setactiv(打勾顯示)時,才➜overridder➜apply all
沒有出現子彈的原因:因為是在隱藏子彈時去apply all的,所以當然不會顯示子彈

10-6.按鈕fire才開火(會飛的子彈),按鈕取消開火

11.如何按鈕fire,才開火(會飛的子彈)
public void FireOn()
{
InvokeRepeating("fire", 0.01f, bulletInterval);
}
void fire()
{
GameObject node = Instantiate(bulletPrefab, bulletFolder);
node.transform.position = bulletFirePoint.position;
}

12.如何按鈕取消開火
public void FireOff()
{
//關閉某個定時器(fire)
CancelInvoke("fire");
}

範例10-7.直升機fire攻擊不斷來襲的敵機

成果影片
1.參考教程(無人機,射擊遊戲)
(1)參考影片:射擊遊戲
第25章1-10(射擊遊戲)

(2)參考影片:無人機飛行
參考影片:第16章調用component(無人機飛行)

2.下載素材資源:
(1).直升機的asset store
(2).敵機的asset store
(3).下載asset store粒子動畫
打到敵機爆炸的粒子動畫:WFX_ExplosiveSmoke Big Alt
玩家被敵軍碰撞的爆炸(核子爆炸)動畫:War FX:WFX_Nuke
子彈飛出去的粒子動畫:flame:WFX_FlameThrower Big Alt Looped

(4).下載爆炸音效
角色被撞爆炸:mixkit-gun-explosion-with-long-echo-1700
子彈打到敵機:mixkit-fire-explosion-1343

3.程式碼:
MonsterCreator程式碼
MonsterLogic程式碼
bullet_logic程式碼
playerLogic程式碼
helicopterControl程式碼
4.步驟圖:
步驟圖1-MonsterFolder-(MonsterCreator.cs)
步驟圖2-monster-(MonsterLogic.cs)
步驟圖3-(Sphere-bullet)-(bullet_logic.cs)
步驟圖4-(Helicopter)-(playerLogic.cs)-(helicopterControl.cs)
步驟圖5-場景的物件

5.專案檔案:
下載專案檔案

10-8.在dx=(-30, 30)之間產生隨機亂數,移動dx位移


1.參考教程(無人機,射擊遊戲)
(1)參考影片:射擊遊戲
第25章1-10(射擊遊戲)

(2)參考影片:無人機飛行
參考影片:第16章調用component(無人機飛行)

2.隨機亂數設定方法:
(1)在dx=(-30, 30)之間產生隨機亂數,移動dx位移
//在原點的x左右隨機偏移第一次出現
float dx = Random.Range(-30, 30);
node.transform.Translate(dx, 0, 0, Space.Self);

程式碼


10-9.如何讓產生的生成物件Instantiate,在5秒後消失


1.參考教程(無人機,射擊遊戲)
(1)參考影片:射擊遊戲
第25章1-10(射擊遊戲)

2.5秒後消失物件的設定方法:Destroy(this.gameObject)
(1)5秒後消失:
Invoke("removeObject", 5.0f);
.....
private void removeObject()
{
Destroy(this.gameObject);
}

程式碼


10-10.在原地不斷生成怪物

1.從asset讀入怪物prefab
[Tooltip("enemy_green_prefab")]
public GameObject enemy_green_prefab;

2.在場景建立怪物目錄folder
[Tooltip("enemy_green_folder")]
public GameObject enemy_green_folder;

3.從0.01s開始,每隔2秒執行show函數
void Start()
{
InvokeRepeating("show", 0.01f, 3.0f);
}

4.建立怪物物件實體(產生一個怪物物件)
注意:第二個參數,是enemy_green_folder的transform組件
void show()
{
//第二個參數,是enemy_green_folder的transform組件
Instantiate(enemy_green_prefab, enemy_green_folder.transform);
}

成果影片
成果圖片1

步驟圖2
enemy_green_Creator.cs程式碼

【chp11.節點物件的碰撞事件1】
11-1:碰撞事件有2種處理方式:Collision碰撞,Trigger觸發

☎ 碰撞的觀念:
(1)參考資料網址

(2)有 2 種碰撞方式:
一、Collision碰撞,造成物理碰撞,可以在碰撞時執行OnCollision函式。
( 接觸 + 物理碰撞)
二、Trigger觸發,取消所有物理碰撞,可以在觸發時執行OnTrigger函式。
(接觸,但是不發生物理力學的碰撞)

所以兩個物件A跟B接觸時,不可能同時產生碰撞 + 觸發,最多產生其中一種,
但是可以辦到讓A跟B產生碰撞,A跟C產生觸發。

(3)碰撞需要 2 個條件:Collider,Rigidbody
(3-1)想要讓程式判斷兩個物件有接觸,則雙方都必需要有Collider(碰撞器),
(3-2)並且正在動的一方一定要有Rigidbody(剛體)才有效,另一方有沒有剛體無所謂。
※也就是說,如果動的一方沒有剛體,它去撞的靜止一方即使有剛體,也是當作沒撞到。

(4)產生接觸的設定方式如下:
一、Collision碰撞:雙方都有碰撞器collider,並且至少動的一方有剛體rigidbody,
就會造成碰撞,可以執行OnCollision函式。
※但若雙方都勾了Kinematic運動學,或任一方勾了Trigger觸發器,則碰撞無效。

二、Trigger觸發:雙方都有碰撞器,並且至少動的一方有剛體,
並且至少其中一方的碰撞器有勾觸發器,就會造成觸發,可以執行OnTrigger函式。

(5)接觸後的函式又細分為Enter、Stay、Exit三種
以Trigger為例,就是OnTriggerEnter、OnTriggerStay、OnTriggerExit
(Collision依此類推)。
Enter函式是當兩個物件接觸的瞬間,會執行一次這個函式;
Stay函式是當兩個物件持續接觸時,會不斷執行這個函式;
Exit函式是當兩個物件分開的瞬間,會執行一次這個函式。

(6)詳細解釋:
【Collider碰撞器】:
最重要的核心!只要有碰撞器就會對其他物件產生碰撞;
若自身要受到碰撞的話,則需要碰撞器 + 剛體,缺一不可。
若自身要受到程式Translate位移或Rotate旋轉,也至少需要碰撞器。

(7)【IsTrigger觸發器】:
勾了就不允許自身受到/造成碰撞,也不會受到重力等物理作用力影響,
而是改為Trigger觸發,雙方碰到時會直接穿越並執行Trigger函式。
但自身還是可以受到程式位移或旋轉。

(8)【Rigidbody剛體】:
允許自身受到碰撞,且動的一方要有剛體才允許產生Trigger觸發。
加了剛體的物件才會受到物理作用力,如受到重力而落下、被物理作用力推動/旋轉,
也無法主動穿越其他碰撞器,這都是受到剛體的影響。
而對方若是沒加剛體,對方就不會受到碰撞的作用力推擠,但仍然可以判斷雙方有碰撞。

(9)【IsKinematic運動學】:
不允許自身受到碰撞,但還是會對其他物件造成碰撞。
也不會受到重力等物理作用力影響。
※雙方都勾運動學的話,雙方都不會受到碰撞,會直接穿越,
 因此會被視為沒有碰撞到,無法執行OnCollision函式。
※對自身而言,剛體+運動學的效果,就等同於不加剛體,
 唯一差異是「剛體+運動學」去撞「靜止的碰撞體」時,
 仍可以執行碰撞、觸發函式;
 但雙方都不加剛體的話,則無法執行碰撞、觸發函式。

(10)【Constraints限制】:
自身受到碰撞時,勾選的位置(Position)、角度(Rotarion)不受力。
很類似把運動學拆成六個細項來設定,但唯一差別在於仍然算有碰撞,
可以照常執行OnCollision函式。

(11)【Drag空氣阻力】:提高數值可減少落下速度、被撞飛距離。

(12)【Angular Drag角阻力】:提高數值可減少旋轉速度。

11-2:Trigger觸發碰撞事件的設定方法(最常用的碰撞方式)

(1)Trigger觸發碰撞的特色:
☎只是兩個物件有發生『接觸』,但是不發生物理力學的碰撞(只是單純的相觸,沒有力學效果)
☎Trigger觸發,會取消所有物理碰撞,可以在觸發時執行OnTrigger函式。

(2)碰撞需要 2 個條件:Collider,Rigidbody
(2-1)想要讓程式判斷兩個物件有接觸,則雙方都必需要有Collider(碰撞器),
(2-2)並且正在動的一方一定要有Rigidbody(剛體)才有效,另一方有沒有剛體無所謂。
※也就是說,如果動的一方沒有剛體,它去撞的靜止一方即使有剛體,也是當作沒撞到。

(3)產生接觸的設定方式如下:
(3-1)Trigger觸發:雙方都有碰撞器collider(勾選IsTrigger),並且至少動的一方有剛體rigidboy(勾選IsKinematic),
並且至少其中一方的碰撞器有勾觸發器IsTrigger,就會造成觸發,可以執行OnTrigger函式。
步驟圖1
(5)接觸後的函式又細分為Enter、Stay、Exit三種
以Trigger為例,就是OnTriggerEnter、OnTriggerStay、OnTriggerExit
(Collision依此類推)。
Enter函式是當兩個物件接觸的瞬間,會執行一次這個函式;
Stay函式是當兩個物件持續接觸時,會不斷執行這個函式;
Exit函式是當兩個物件分開的瞬間,會執行一次這個函式。

OnTriggerEnter程式碼

11-3:如何設定碰撞時的粒子動畫效果(例如,顯示爆炸prefab)

9.如何設定子彈打到怪物爆炸效果,顯示爆炸prefab

(1)asset store尋找爆炸物件(War, explode, explosion)
(2)下載到asset/prefab,(例如:WFX_ExplosiveSmoke Big Alt)
(3)在子彈bulletLogic.cs➜public public GameObject bulletExplosion;
(4)打開bullet prefab物件➜拖曵(WFX_ExplosiveSmoke Big Alt)➜到bulletExplosion
(5)碰撞時顯示爆炸(注意:爆炸prefab,放在根目錄(null)
[Tooltip("子彈爆炸prefab")]
public GameObject bulletExplosion;
private void OnTriggerEnter(Collider other)
{
//產生爆炸預鑄體prefab,放在根目錄(null)
GameObject node1 = Instantiate(bulletExplosion, null);
}

程式碼

11-4:被關卡撞到,如何顯示煙霧動畫

[Tooltip("敵方被KO的煙霧動畫prefab")]
public GameObject somke_prefab;
private GameObject node_smoke;

private void OnTriggerEnter(Collider other)
{
if (other.name == "door2" || other.name == "door1")
{ //顯示煙霧動畫,放在根目錄,但是只有出現2秒(粒子系統只設定2秒),粒子會自動消失,不需要刪除
node_smoke = Instantiate(somke_prefab, null);

//設定動畫顯示的坐標
node_smoke.transform.position = this.transform.position;

//播放音效(在前台somke_prefab物件,就進入audioSource組件)
node_smoke.GetComponent<AudioSource>().Play();
}
}

程式碼

11-5:為什麼設定碰撞的音效,沒有出現,如何解決(子彈打到怪物播放爆炸聲音)

(1)錯誤做法:若在bullet_prefab裡面加上audioSource組件,拖曵到clip,在程式碼裡面讓getComponent<AudioSource>().plaey
結果:沒有聲音
原因:子彈碰撞時因為會destory,若是先播放音效,馬上就被destory,所以不會有聲音

(2)正確做法:把聲音clip拖曵到爆炸粒子動畫prefab才對
原因:在用 Instantiate(bulletExplosion, null)顯示爆炸粒子時,會完整顯示完畢,才會消失,所以就可以用這個來播放音效(不會被destory刪除)
private void OnTriggerEnter(Collider other)
{
//刪除this,other
if (other.gameObject.name != "Monster(Clone)") return;

//刪除物件
Destroy(this.gameObject);
Destroy(other.gameObject);

//產生爆炸預鑄體prefab,放在根目錄(null)
GameObject node1 = Instantiate(bulletExplosion, null);

//播放爆炸音效
//注意:爆炸音效要放在爆炸預鑄件prefab,不要放在子彈或主角(因為被destory了,就無法播放音效)
node1.GetComponent<AudioSource>().Play();
//設定爆炸的位置
node1.transform.position = this.transform.position;
}

程式碼

11-6:開啟爆炸範圍,任何進入範圍內的怪物都會被爆炸

1.顯示爆炸動畫/播放音效,打開爆炸box collider,3秒後關閉

//大爆炸動畫prefab
public GameObject WFX_Nuke;
//大爆炸音效
private GameObject wfx1;

bool isfire = false;

private void OnTriggerEnter(Collider other)
{
if (other.name == "button_fire" && isfire == false)
{
isfire = true;

//爆炸畫面(顯示在根目錄null)
wfx1 = Instantiate(WFX_Nuke, null);
//設定爆炸畫面的位置
wfx1.transform.position = GameObject.Find("Bomb").transform.position;

//把炸彈的box_collider打開,好讓炸彈碰撞怪物,但是只有打開3秒
GameObject.Find("Bomb").GetComponent<BoxCollider>().enabled = true;

//3秒後,關閉炸彈的box_collider
Invoke("TurnOffBoxCollider", 3);
}
}

2.怪物碰撞到Bomb的box collider,就設定狀態status = die
private void OnTriggerEnter(Collider other)
{
if (other.name == "Bomb" && isDie == false)
{
//顯示煙霧動畫,放在根目錄,但是只有出現2秒(粒子系統只設定2秒),粒子會自動消失,不需要刪除
node_smoke = Instantiate(somke_prefab, null);

//設定動畫顯示的坐標
node_smoke.transform.position = this.transform.position;
//播放音效
node_smoke.GetComponent<AudioSource>().Play();

//設定animator的status=4,躺下
this.gameObject.GetComponent<Animator>().SetInteger("status", 4);

//死亡
isDie = true;
}


成果影片

下載爆炸prefab動畫(WFX_Nuke)下載爆炸音效(nuclear_explode_aduio)
成果圖片1

步驟圖2步驟圖3步驟圖4步驟圖5步驟圖6步驟圖7步驟圖8步驟圖9
playerControl.cs程式碼
enemy_greenp_manager.cs程式碼


【chp13.切換場景】
13-1:切換場景的基本指令

☎ 10.如何切換場景:
成果圖片1成果圖片2

步驟圖2步驟圖3步驟圖4步驟圖5步驟圖6步驟圖7
程式碼
(1)先using UnityEngine.SceneManagement;
(2)建立gameObject01➜加上cs06.cs
(3)cs06:建立函數:gotoNextScene(s2)
public void gotoNextScene(string name1)
{
SceneManager.LoadScene(name1);
}
(4)場景的按鈕➜onclick➜設定gameObject01➜function = cs06.cs → gotoNextScene() → 設定參數 = s2(注意:不需要加上"")
(5)☎注意:要執行前,要先把2個場景都加入Build Scene內,才能build,才能執行
(File/Build Settings➜Scene to Build:s1,s2)

(6)按鈕要加上音效:https://www.soundjay.com/button-sounds-1.html

範例13-2:遊戲失敗產生Game Over畫面,方法1:切換場景

參考影片:Creating a Game Over Scene in Unity

成果影片
成果圖片成果圖片
步驟圖1步驟圖2步驟圖3步驟圖4
程式碼
1.範例13-2:遊戲失敗產生Game Over畫面,方法1:切換場景
(1)建立兩個場景:s1,s2
(2)在s2場景2:
(2-1)如何讓整個畫面背景色都是黑色:
方法:把main camera➜Clear Flags:solid Color➜設定背景色backgroun:黑色
(2-2)建立UI/Text_Infor:Game Over
(2-3)建立UI/Text_replay:再玩一次(R)
(2-4)建立UI/Text_Quit:關閉程式(Q)
(2-5)建立Empty Object:script_Manager➜加入程式碼:gameOver.cs
using UnityEngine.SceneManagement;
public void gotoscene(string s1)
{
SceneManager.LoadScene(s1);
}
public void myQuit()
{
Application.Quit();
}
void Update()
{
if (Input.GetKeyDown(KeyCode.R))
{
SceneManager.LoadScene("s1");
}
else if (Input.GetKeyDown(KeyCode.Q))
{
Application.Quit();
}
else if (Input.GetKeyDown(KeyCode.E))
{
SceneManager.LoadScene("s2");
}
}
(2-6)把Text_replay➜加入UI/button組件:結束(E)
Button➜設定hightlight color
Button➜設定onclick➜物件=script_Manage➜function=gameOver/gotoscene,s1
(2-7)把Text_Quit➜加入UI/button組件
Button➜設定hightlight color
Button➜設定onclick➜物件=script_Manage➜function=gameOver/myQuit
(3)在s1場景1:建立按鈕(結束)
(3-1)建立Empty Object:script_Manager➜加入程式碼:gameOver.cs
(3-2)Button➜設定hightlight color
(3-3)Button➜設定onclick➜物件=script_Manage➜function=gameOver/gotoscene,s2

範例13-3:遊戲失敗產生Game Over畫面,方法2:顯示圖片(隱藏的圖片,用public讀入後顯示)

☎若用Image當作GameOver畫面,要replay時,不可以把image隱藏
原因:不會從頭開始執行,而是接著剛剛的進度執行
(1)錯誤做法:把img1隱藏(如此,不會重新開始)
img1.SetActive(false);

(2)正確做法:
SceneManager.LoadScene("SampleScene");

參考影片:Create great GAME OVER screen in Unity UI

成果影片
成果圖片成果圖片
步驟圖1步驟圖2步驟圖3步驟圖4步驟圖5

ButtonEnd2程式碼showGameOverScreen程式碼
下載專案檔案
1.範例13-3:遊戲失敗產生Game Over畫面,方法2:顯示圖片(隱藏的圖片,用public讀入後顯示)
(1)建立1個場景:s1
(2)建立UI/Button:結束(E)
(3)建立UI/image(背景圖)
(3-1)建立UI/Text_Infor:Game Over
(3-2)建立UI/Button-replay:再玩一次(R)
(3-3)建立UI/Button-Quit:關閉程式(Q)
(4)UI/Button➜加入程式碼:ButtonEnd2.cs
UI/Button➜onclick:物件=button,function=ButtonEnd2➜buttonEnd()
public GameObject img1;
public void buttonEnd()
{
img1.SetActive(true);
}
void Update()
{
if(Input.GetKeyDown(KeyCode.E))
{
img1.SetActive(true);
}
}

(5)背景圖➜加入程式碼:showGameOverScreen.cs
(5-1)UI/Button-replay➜onclick:物件=背景圖,function=showGameOverScreen➜replay()
(5-2)UI/Button-Quit➜➜onclick:物件=背景圖,function=showGameOverScreen➜Quit()
public void replay()
{
//(1)錯誤做法:把this.gameObject隱藏(結果,不會重新開始)
//隱藏目前這個物件(背景圖)
//gameObject.SetActive(false);
//(2)正確做法
SceneManager.LoadScene("SampleScene");
}
public void myquit()
{
Application.Quit();
}
void Update()
{
if(Input.GetKeyDown(KeyCode.R))
{
//隱藏目前這個物件(背景圖)
gameObject.SetActive(false);
}
else if(Input.GetKeyDown(KeyCode.Q))
{
Application.Quit();
}
}

範例13-4:遊戲失敗產生Game Over畫面,方法3:顯示圖片(隱藏圖片物件,用程式碼類別物件顯示)

參考影片:Create great GAME OVER screen in Unity UI

成果影片
成果圖片成果圖片
步驟圖1步驟圖2步驟圖3步驟圖4步驟圖5

ButtonEnd2程式碼showGameOverScreen程式碼
下載專案檔案
1.範例13-4:遊戲失敗產生Game Over畫面,方法3:顯示圖片(隱藏圖片物件,用程式碼類別物件顯示)
(1)建立1個場景:s1
(2)建立UI/Button:結束(E)
(3)建立UI/image(背景圖)
(3-1)建立UI/Text_Infor:Game Over
(3-2)建立UI/Button-replay:再玩一次(R)
(3-3)建立UI/Button-Quit:關閉程式(Q)
(4)UI/Button➜加入程式碼:ButtonEnd2.cs➜showscreen1 = 背景圖
UI/Button➜onclick:物件=button,function=ButtonEnd➜buttonEnd()

public showGameOverScreen showscreen1;
public void buttonEnd()
{
//錯誤做法:無法顯示
//GameObject.Find("背景圖").SetActive(true);

//正確做法:可以顯示背景圖片
showscreen1.show();
}

(5)背景圖➜加入程式碼:showGameOverScreen.cs
(5-1)UI/Button-replay➜onclick:物件=背景圖,function=showGameOverScreen➜replay()
(5-2)UI/Button-Quit➜➜onclick:物件=背景圖,function=showGameOverScreen➜Quit()

public void show()
{
//顯示目前這個物件(背景圖)
gameObject.SetActive(true);
}
public void replay()
{
//隱藏目前這個物件(背景圖)
gameObject.SetActive(false);
}
public void myquit()
{
Application.Quit();
}
void Update()
{
if(Input.GetKeyDown(KeyCode.R))
{
//隱藏目前這個物件(背景圖)
gameObject.SetActive(false);
}
else if(Input.GetKeyDown(KeyCode.Q))
{
Application.Quit();
}
}

9-2:導航網格navMesh

☎導航網格navMesh

1.導航網格,navMesh, navMeshAgent:
參考影片:Unity零基础系统教学(Gamer飞羽)54-57章節教程(导航网格的使用)

2.nav = 導航 = navigation
agent = 代理人 = 行走的角色
mesh = 地板上可以行走的網格

3.設定導航網格的幾點:
(1)角色。。。都不需要設定rigidbody(也不需要勾選istrigger, iskinematic)
(2)何謂自動導航:

(A)只要滑鼠在畫面上觸控一點,角色就會自動以某個速度,結果看行走網格,走到那個地方
(B)它會根據最低成本路徑,走到目的地
(C)它可以設定:高處,跳下點
(D)它可以設定:兩處,跳遠點
(E)它可以適當:兩處,連接點(飛行)
(F)它可以設定,怪物,朝向角色而來

成果圖片
步驟圖1步驟圖2步驟圖3步驟圖4步驟圖5步驟圖6
程式碼

4.設定角色在地板網格行走的步驟:
(1)先建立地形(plane,cube。。。)
(2)建立角色
(3)設定地形的導航網格
(3-1)選取全部的地形➜右邊static➜navigation static(靜態導航)
(3-2)然後到右邊的一個『navigation』視窗➜bake(就會產生導航網格了)
但是要先設定能夠通過導航行走agent的4個基本參數(半徑,身高,agent爬斜坡的斜率上限,agent跨階梯的step上限)
經驗:這些網格的➜bake➜step height要設定高點,例如0.9(發現,若是設定0.4,則待會的主角,無法爬上階梯)
(3-3)最後按下方:bake➜產生導航網格(淺藍色網格,代表成本1的網格)
(4)設定主角為agent➜add component➜navMesh agent
(5)建立主角的程式碼➜add component➜playerControl.cs
using UnityEngine.AI;
抓取代理組件
agent1 = this.GetComponent<NavMeshAgent>();

void Update()//{
//手觸控某個位置,agent1就會朝點的位置走過去
//判別按下滑鼠左鍵
if (Input.GetMouseButtonDown(0))
{
//射線取得點按位置的坐標
Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit1;
//射線檢測
if (Physics.Raycast(ray1, out hit1) == true)
{
//取得點擊位置
Vector3 point1 = hit1.point;
//設定point1為導航目標點
agent1.SetDestination(point1);
}
}
}

【chp14.分數計算,記分系統】
範例14-1:呼叫另外c#檔案的show()函數顯示分數score(方法1:public拖曵設定cs程式碼掛載的物件)

☎ 兩個cs程式碼檔案,都可以顯示分數score
==>方法:呼叫另外c#檔案的show()函數顯示分數score
參考影片:How to Pass Variables Between Scripts in C#
參考網頁(附上程式碼):How to Pass Variables Between Scripts in C#

成果影片

1.如何兩個cs程式碼檔案,都可以顯示分數score
(1).錯誤做法:cs02程式碼,讀取cs01程式碼的public變數score
結果:只能夠讀取到score的起始值(0)
(2)正確做法:cs02程式碼,讀取cs01程式碼的public函數show1(-1)

2.如何cs02程式碼,讀取cs01程式碼的public函數show1()
設定另外一個cs01檔案物件
public cs01 readCs;
readCs.show1(-1);

3.如何設定UI/Text的值(score)
(1)注意:抓取UI物件,要先using UnityEngine.UI;
GameObject.Find("TextScore").GetComponent<Text>().text = "Score : " + score.ToString();

成果圖片1

步驟圖2步驟圖3

cs01程式碼
cs02程式碼

範例14-2:呼叫另外c#檔案的show()函數顯示分數score(方法2:GameObject.FindObjectOfType)

☎ 兩個cs程式碼檔案,都可以顯示分數score
==>方法:呼叫另外c#檔案的show()函數顯示分數score
參考影片:How to Pass Variables Between Scripts in C#
參考網頁(附上程式碼):How to Pass Variables Between Scripts in C#

成果影片

1.如何找到掛載某個Script腳本的GameObject?
Unity 提供了幾個方法可以找到關於是誰引用了物件(透過程式碼搜尋)
方法(1):一種是GameObject.FindGameObjectWithTag,此法可以根據GamObject所設定的Tag,直接把物件搜尋出來

方法(2):GameObject.FindObjectOfType<T>(),此法根據元件的類別直接搜尋,如果是Script腳本就直接輸入對應名稱
功能:根據型別(元件/自定義指令碼)查詢,並返回這個類。
如果沒有任何匹配該類型的物件,則返回 null。

方法(3):使用 gameObject.GetComponent,這種方法則要先理解子母物件的關係

2.如何查詢到專案裡面的cs01程式碼物件?
☎方法:GameObject.FindObjectOfType<程式碼名稱>()
☎範例:
private cs01 readCs;
readCs = GameObject.FindObjectOfType<cs01>();
.....
readCs.show1(-1);
☎優點:
若要搜尋程式碼物件(cs01),用FindObjectOfType<程式碼名稱>()是最簡單的方法

成果圖片1

步驟圖2步驟圖3

cs01程式碼
cs02程式碼

範例14-3:很多怪物攻擊主角,計算同一個score變數(方法3:GameObject.FindObjectOfType)

☎ 多個怪物的攻擊主角,都可以修改分數score
==>方法:呼叫另外c#檔案的show()函數顯示分數score
成果影片

1.如何找到掛載某個Script腳本的GameObject?
方法(2):GameObject.FindObjectOfType<T>(),此法根據元件的類別直接搜尋,如果是Script腳本就直接輸入對應名稱
功能:根據型別(元件/自定義指令碼)查詢,並返回這個類。
如果沒有任何匹配該類型的物件,則返回 null。

2.如何查詢到專案裡面的程式碼score.cs物件?
☎方法:GameObject.FindObjectOfType<程式碼名稱>()
☎範例:
//若是多個怪物prefabs,改成呼叫score.cs裡面的showScore()
GameObject.FindObjectOfType<score>().showScore(-3);

☎3.統一計算分數的程式碼score.cs:
using UnityEngine.UI;

public int score2 = 30;
public GameObject img_Over;
bool gameover = false;
public void showScore(int point)
{
score2 += point;
if(score2 < 0 && gameover ==false)
{
gameover = true;
//播放過關失敗音效
this.GetComponent<AudioSource>().Play();
img_Over.SetActive(true);
}
//注意:如果沒有using UnityEngine.UI;就無法抓到參數Text
GameObject.Find("Text_energy").GetComponent<Text>().text = "體力:" + score2.ToString();
}

成果影片
成果圖片1

步驟圖2步驟圖3

score.cs程式碼
enemy_greenp_manager.cs程式碼

【chp15.RPG的狀態機,主角攻擊怪物,怪物攻擊主角】
15-1:狀態機:設定角色的5種狀態

參考影片:RPG Enemy Set States 設置敵人的基本屬性和狀態

1.RPG的角色狀態設定:準備idle,走路walk,跳躍jump,攻擊attack,死亡die

(1).專業做法:使用列舉變數enum
範例:enum EnemyStatus {IDLE,WALK,JUMP,ATTACK,DIE}
應用方法:EnemyStatus.WALK
☎注意:enum的內容項目文字,前後不需要""
☎注意:設定enum,不需要=

2.☎狀態機的程式碼結構:
//設定角色的狀態列舉
public enum EnemyStatus { IDLE, WALK, JUMP,ATTACK, DIE };

public class enemy_greenp_manager : MonoBehaviour
{
//宣告一個public的狀態機變數
public EnemyStatus itsStatus;

//判被角色狀態函數
void SwitchStatus()
{
switch(itsStatus)
{
case EnemyStatus.IDLE:
break;
case EnemyStatus.WALK:
break;
case EnemyStatus.JUMP:
break;
case EnemyStatus.ATTACK:
break;
case EnemyStatus.DIE:
break;
}
}

void Update()
{
//隨時判別角色狀態
SwitchStatus();
}

步驟圖程式碼

15-2:如何判斷角色的狀態是預備態idle

(0)舉例:主角unitychan,觸碰link2就跳躍jump,導航就walk,到了目的地就idle
程式碼

☎步驟:
(1)設定導航網格navMesh,設定跳躍連接點offMeshLink,
(2)在主角unitychan設定程式碼playerControl.cs

☎(3)判別原地不動,顯示idle的程式思路:
(3-1)宣告prev_position變數:代表前一個幀率時,主角unitychan的位置
Vector3 prev_position = new Vector3(0,0,0);
(3-2)原理:在update裡面➜若主角最新的位置 = prev_position➜表示主角沒有移動了➜播放idle動畫(設定status=0)
if (GameObject.Find("unitychan").transform.position == prev_position)
{
GameObject.Find("unitychan").GetComponent<Animator>().SetInteger("status", 0);
}
...............
//最後一行,設定prev_position
prev_position = GameObject.Find("unitychan").transform.position;

(3-3)程式碼:
void Update()
{
bool isjump = false;
if (Input.GetMouseButtonDown(0))
{
Debug.Log(GameObject.Find("unitychan").GetComponent<Animator>().GetInteger("status"));
//射線取得點按位置的坐標
Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit1;
//射線檢測
if(Physics.Raycast(ray1,out hit1)==true)
{
//取得點擊位置
Vector3 point1 = hit1.point;
//設定point1為導航目標點
agent1.SetDestination(point1);
//如果jump to (link2)則jump
if(hit1.collider.gameObject.name=="link2" || hit1.collider.gameObject.name == "door3")
{
...............
}
}
}
//注意:要先判斷Jump的case,否則永遠不會執行jump
if (isjump == true)
{
GameObject.Find("unitychan").GetComponent<Animator>().SetInteger("status", 2);
}
//如果原地不動,則status =0
else if (GameObject.Find("unitychan").transform.position == prev_position)
{
GameObject.Find("unitychan").GetComponent<Animator>().SetInteger("status", 0);
}
else//否則walk
{
GameObject.Find("unitychan").GetComponent<Animator>().SetInteger("status", 1);
}
//設定prev_position
prev_position = GameObject.Find("unitychan").transform.position;
}

☎(4)判別jump,顯示jump動畫的程式思路:
(4-1)宣告isjump變數:代表主角unitychan與特定link2物件是否碰撞,若是則jump
bool isjump = false;
(4-2)原理:在update裡面➜射線檢測if(Physics.Raycast(ray1,out hit1)➜若碰到link2➜if(hit1.collider.gameObject.name=="link2")➜則isjump = true
(4-3)程式碼:
void Update()
{
bool isjump = false;
if (Input.GetMouseButtonDown(0))
{
Debug.Log(GameObject.Find("unitychan").GetComponent<Animator>().GetInteger("status"));
//射線取得點按位置的坐標
Ray ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit1;
//射線檢測
if(Physics.Raycast(ray1,out hit1)==true)
{
//取得點擊位置
Vector3 point1 = hit1.point;
//設定point1為導航目標點
agent1.SetDestination(point1);
//如果jump to (link2)則jump
if(hit1.collider.gameObject.name=="link2")
{
//設定主角jump動畫(但是沒有效果),可能是update馬上被改成status=1
//GameObject.Find("unitychan").GetComponent<Animator>().SetInteger("status", 2);
//所以用一個isjump變數
isjump = true;
}
}
}
//注意:要先判斷Jump的case,否則永遠不會執行jump
if (isjump == true)
{
GameObject.Find("unitychan").GetComponent<Animator>().SetInteger("status", 2);
}

☎(5)判別碰撞物件,顯示過關win動畫的程式思路:
(5-1)宣告iscollide變數:代表主角unitychan與特定door3物件是否碰撞
bool iscollide = false;
(5-2)原理:在OnTriggerEnter裡面➜若主角最新的位置 = prev_position➜表示主角沒有移動了➜播放idle動畫(設定status=0)
(5-3)程式碼:
bool iscollide = false;
Vector3 prev_position = new Vector3(0,0,0);
private void OnTriggerEnter(Collider other)
{
if(other.name == "door3" && iscollide==false)
{
iscollide = true;
//播放過關音效
other.GetComponent<AudioSource>().Play();
//顯示過關畫面
img_Win.SetActive(true);
}
.............
}
☎(6)playerControl程式碼:
程式碼

15-3:如何判斷角色的狀態是攻擊態ATTACK

1.問題點:自動導航navMesh,navMeshAgent的角色攻擊,遇到的問題:
☎問題:enemy敵人若是由自動導航,這個agent_enemy,遇到目標物會自動避開,不會碰撞到
(1)遇到目標主角:會自動剎車,距離0,就會停止,所以不會碰撞
(2)遇到移動式的障礙物/牆壁,agent_enemy也會自動避開

2.RPG遊戲,如何在自動導航內,靠近時就攻擊➜要符合三個條件
(1)條件1:只要距離distance1 <2.5f,就攻擊
☎方法:在update內要隨時計算兩者的距離:float distance1 = Vector3.Distance(agentChan.transform.position, agentGreen.transform.position);

☎遇到新的問題:但是若是距離一旦靠近了,就會不斷攻擊(在update內,一秒內連續攻擊60次)
修改:要加上一個冷卻時間lastattackTime變數(例如2.0),每隔2秒才能再攻擊一次
☎方法:在update裡面:lastattackTime -= Time.deltaTime;

(2)條件2:只要冷卻時間已經小於0:lastattackTime < 0

(2)條件3:只要還沒有死:this.gameObject.GetComponent<Animator>().GetInteger("status") != 4) 就可以,開始攻擊
(4)程式碼架構:
if (lastattackTime < 0 && distance1 < 2.5f && this.gameObject.GetComponent<Animator>().GetInteger("status") != 4)
{
....
}

3.RPG的角色如何攻擊對方
(1)關鍵問題:在udapte裡面若是設定距離小2.5就攻擊,那麼1秒內會連續攻擊60次
改善方法:宣告一個lastattackTime變數,超過這個時間再攻擊第二次

(2)方法:
(A)宣告變數lastattackTime:攻擊冷卻時間(攻擊一次,必須再等幾秒才能再攻擊)
public float lastattackTime = 1.5f;

(B)在update裡面判別,計算enemy與主角unityChan的距離,靠近就刺

float distance1 = Vector3.Distance(agentChan.transform.position, agentGreen.transform.position);
if (lastattackTime < 0 && distance1 < 2.5f && this.gameObject.GetComponent<Animator>().GetInteger("status") != 4)
{
靠近主角,就刺 status = 14(或12)
this.gameObject.GetComponent<Animator>().SetInteger("status", 14);
//降低主角的體力
energy -= 3;
//顯示在畫面Text_energy
GameObject.Find("Text_energy").GetComponent<Text>().text = "體力:" + energy.ToString();
//播放hit/punch音效
this.GetComponent<AudioSource>().Play();
//重新設定lastattackTime = 0.5f
lastattackTime = 1.5f;
}

☎4.enemy_greenp_manager程式碼:
程式碼

範例15-4:實作enemy的四種狀態(預備idle,走路walk,攻擊attack,死亡die)

1.問題點:自動導航navMesh,navMeshAgent的角色攻擊,遇到的問題:
☎問題:enemy敵人若是由自動導航,這個agent_enemy,遇到目標物會自動避開,不會碰撞到
(1)遇到目標主角:會自動剎車,距離0,就會停止,所以不會碰撞
(2)遇到移動式的障礙物/牆壁,agent_enemy也會自動避開

2.RPG遊戲,如何在自動導航內,靠近時就攻擊➜要符合三個條件
(1)條件1:只要距離distance1 <2.5f,就攻擊
☎方法:在update內要隨時計算兩者的距離:float distance1 = Vector3.Distance(agentChan.transform.position, agentGreen.transform.position);

☎遇到新的問題:但是若是距離一旦靠近了,就會不斷攻擊(在update內,一秒內連續攻擊60次)
修改:要加上一個冷卻時間lastattackTime變數(例如2.0),每隔2秒才能再攻擊一次
☎方法:在update裡面:lastattackTime -= Time.deltaTime;

(2)條件2:只要冷卻時間已經小於0:lastattackTime < 0

(2)條件3:只要還沒有死:this.gameObject.GetComponent<Animator>().GetInteger("status") != 4) 就可以,開始攻擊
(4)程式碼架構:
if (lastattackTime < 0 && distance1 < 2.5f && this.gameObject.GetComponent<Animator>().GetInteger("status") != 4)
{
....
}

3.RPG的角色如何攻擊對方
(1)關鍵問題:在udapte裡面若是設定距離小2.5就攻擊,那麼1秒內會連續攻擊60次
改善方法:宣告一個lastattackTime變數,超過這個時間再攻擊第二次

(2)方法:
(A)宣告變數lastattackTime:攻擊冷卻時間(攻擊一次,必須再等幾秒才能再攻擊)
public float lastattackTime = 1.5f;

(B)在update裡面判別,計算enemy與主角unityChan的距離,靠近就刺

float distance1 = Vector3.Distance(agentChan.transform.position, agentGreen.transform.position);
if (lastattackTime < 0 && distance1 < 2.5f && this.gameObject.GetComponent<Animator>().GetInteger("status") != 4)
{
靠近主角,就刺 status = 14(或12)
this.gameObject.GetComponent<Animator>().SetInteger("status", 14);
//降低主角的體力
energy -= 3;
//顯示在畫面Text_energy
GameObject.Find("Text_energy").GetComponent<Text>().text = "體力:" + energy.ToString();
//播放hit/punch音效
this.GetComponent<AudioSource>().Play();
//重新設定lastattackTime = 0.5f
lastattackTime = 1.5f;
}

☎4.成果影片
成果+設定步驟影片

5.下載素材檔案:
enemy角色下載(Battle Wizard Poly Art)
手榴彈grenade物件(asset store)
Effect textures and prefabs(煙霧動畫prefabs物件)asset store

氣化聲音(hiss)
揮擊木棍聲音(Whoosh)
過關成功聲音(win)

成果圖片
步驟圖1步驟圖2步驟圖3步驟圖4步驟圖5步驟圖6步驟圖7步驟圖8步驟圖9

5.程式碼:
doormove程式碼
playerControl程式碼
enemy_greenp_manager程式碼
gameover_manager程式碼

15-5:如何怪物死亡後,不再繼續自動導航追踪主角(還要設定animator狀態,設定5秒後消失)

1.當怪物死亡後,除了要設定Animator的狀態外,還要取消追踪主角,否則死亡的怪物,還好繼續移動:
☎(1)當怪物死亡後,要設定Animator的狀態:
this.gameObject.GetComponent<Animator>().SetInteger("status", 4);

☎(2)當怪物死亡後,還要取消自動導航追踪主角,否則死亡的怪物,還好繼續移動
方法1:把原本追逐主角(SetDestination),改成追踪自己的位置
agentGreen.SetDestination(agentGreen.transform.position);

方法2:把navMeshAgent.Stop()
agentGreen.Stop();

方法3:不設定,就是還是繼續追踪主角
原因:有時候很多怪物同時死亡,就會造成道路堵塞,後面的怪物就無法追踪主角


☎(3)當怪物死亡後,可以設定怪物在5秒後消失
☎5秒後消失物件的設定方法:Destroy(this.gameObject)

Invoke("removeObject", 5.0f);
.....
private void removeObject()
{
Destroy(this.gameObject);
}


2.程式碼:
enemy_greenp_manager程式碼

範例15-6:兩種怪物攻擊主角,主角按炸彈來引爆炸彈

1.顯示爆炸動畫/播放音效,打開爆炸box collider,3秒後關閉

//大爆炸動畫prefab
public GameObject WFX_Nuke;
//大爆炸音效
private GameObject wfx1;

bool isfire = false;

private void OnTriggerEnter(Collider other)
{
if (other.name == "button_fire" && isfire == false)
{
isfire = true;

//爆炸畫面(顯示在根目錄null)
wfx1 = Instantiate(WFX_Nuke, null);
//設定爆炸畫面的位置
wfx1.transform.position = GameObject.Find("Bomb").transform.position;

//把炸彈的box_collider打開,好讓炸彈碰撞怪物,但是只有打開3秒
GameObject.Find("Bomb").GetComponent<BoxCollider>().enabled = true;

//3秒後,關閉炸彈的box_collider
Invoke("TurnOffBoxCollider", 3);
}
}

2.怪物碰撞到Bomb的box collider,就設定狀態status = die
private void OnTriggerEnter(Collider other)
{
if (other.name == "Bomb" && isDie == false)
{
//顯示煙霧動畫,放在根目錄,但是只有出現2秒(粒子系統只設定2秒),粒子會自動消失,不需要刪除
node_smoke = Instantiate(somke_prefab, null);

//設定動畫顯示的坐標
node_smoke.transform.position = this.transform.position;
//播放音效
node_smoke.GetComponent<AudioSource>().Play();

//設定animator的status=4,躺下
this.gameObject.GetComponent<Animator>().SetInteger("status", 4);

//死亡
isDie = true;
}


下載爆炸prefab動畫(WFX_Nuke)
下載爆炸音效(nuclear_explode_aduio)
成果影片
成果圖片1

步驟圖2步驟圖3步驟圖4步驟圖5步驟圖6步驟圖7步驟圖8步驟圖9
playerControl.cs程式碼
enemy_greenp_manager.cs程式碼