Unity暑期萌新入門:靜態(tài)敵人篇

作者:Truly
大渣好。
經(jīng)過前幾篇的學習,我們已經(jīng)可以控制John在關(guān)卡里自由地行走,并且到達出口時能顯示通關(guān)的結(jié)束界面了。接下來我們會在關(guān)卡里添加敵人,當玩家被敵人發(fā)現(xiàn)時,就會重新開始關(guān)卡。今天,我們先添加雖然不會動,但是喜歡在角落里蹲人的石像鬼。

一、設(shè)置石像鬼(Gargoyle)的預(yù)制體(推薦參照GIF快速操作)
制作敵人的方法跟我們制作主角的方法有點相似,就當作重溫制作角色的步驟吧。我們要先把模型實例化為對象(GameObject)再進行操作。
流程:
1.創(chuàng)建石像鬼(Gargoyle)預(yù)制體
(1)在Project窗口中,打開文件夾Assets > Models > Characters,并找到Gargoyle?模型。
(2)把Gargoyle 模型拖動到Hierarchy窗口中,創(chuàng)建它的實例。
(3)把Hierarchy窗口中的Gargoyle?GameObject拖到Assets> Prefabs文件夾中,創(chuàng)建預(yù)制體。此時會彈出對話框,選擇Original Prefab。

2.編輯預(yù)制體
我們將會為Gargoyle預(yù)制體添加動畫、碰撞體(Collider)和視野檢測模擬(Trigger)。
在Hierarchy窗口中,點擊Gargoyle?GameObject右側(cè)的箭頭,打開預(yù)制體模式進行編輯。

(1)添加動畫
石像鬼只有一個站立觀看的Idle動畫,把設(shè)置好動畫的Animator Controller拖進預(yù)制體的Animator組件中的Controller屬性欄中即可。
①在Project窗口中打開Assets > Animation > Animators文件夾。創(chuàng)建Animator Controller,命名為Gargoyle。

②雙擊新建的Gargoyle?Animator Controller打開Animator視窗。
③在Project窗口中打開Assets > Animation > Animation文件夾。展開Gargoyle @ Idle,把Idle動畫拖進Animator視窗中。
④把設(shè)置好的Gargoyle(Animator Controller)拖進Gargoyle預(yù)制體的Animator組件的Controller屬性框。

(2)添加Collider(碰撞體)
①在Inspector窗口中,點擊Add Component,搜索并添加Capsule Collider。
②設(shè)置Collider參數(shù),使碰撞體更加貼合石像鬼:
Center:(0, 0.9, 0)
Radius:0.3
Height:1.8

(3)添加模擬檢測范圍的Trigger(觸發(fā)器)
在通關(guān)界面篇,我們設(shè)置Trigger來檢測玩家是否到達出口,這里我們用相同的方法給石像鬼設(shè)置Trigger模擬檢測范圍。
①在Gargoyle?GameObject下新建一個空的子對象(Create Empty),按F2重命名為PointOfView。
②設(shè)置PointOfView的Transform組件,調(diào)節(jié)模擬視線的位置和旋轉(zhuǎn):
Position(位置) :?(?0,?1.4,?0.4)
Rotation(旋轉(zhuǎn)) :?(?20,?0,?0)
設(shè)置完成后,確保旋轉(zhuǎn)切換工具是Local的情況下,可以看到PointOfViewde坐標的Z軸會從石像鬼的頭部指向斜下。

③在PointOfView的Inspector中點擊Add Component,搜索并添加Capsule Collider。
④設(shè)置Collider參數(shù):
在Capsule Collider組件里勾選Is Trigger,轉(zhuǎn)換為觸發(fā)器。
Center(中心) :(?0,?0,?0.95)
Radius(半徑) :0.7
Height(高) :2
Direction(方向):下拉菜單改為Z-Axis

(4)場景中擺放Gargoyle GameObject
①點擊Hierarchy窗口中,Gargoyle左側(cè)的?<?退出預(yù)制體模式。

②我們返回Scene場景中,設(shè)置Gargoyle GameObject的Transform組件:
Position:(-15.2?,0?,0.8?)
Rotation:(0?,135?,0?)

完成后Ctrl+S保存場景。我們?yōu)槭窆淼念A(yù)制體添加并設(shè)置了動畫、碰撞體以及模擬檢測范圍的觸發(fā)器,已經(jīng)完成了大部分的基本設(shè)置。

二、設(shè)置被抓界面
當判定玩家被抓時,會淡入一個被抓的界面,做法跟上一篇通關(guān)界面相似。我們偷懶一下,復制一份勝利界面,把通關(guān)前景圖替換為被抓的前景圖就可以了~
1.展開FaderCanvas,選中ExitImageBackground?GameObject,Ctrl + D進行復制。
2.把復制出來的ExitImageBackground(1) 重命名為CaughtImageBackground。展開子項,把ExitImage 重命名為CaughtImage。

3.在Inspector窗口中找到CaughtImage 的Image組件,點擊Source Image右側(cè)圓圈,選擇Caught。完成后Ctrl+S保存場景。

三、編寫腳本
1.思路
(1)判斷玩家是否進入檢測范圍。
(2)如果玩家在檢測范圍內(nèi),進行視線模擬,避免隔著障礙物(墻)還能看到玩家。
(3)玩家被發(fā)現(xiàn)后,淡入被抓的結(jié)束界面。
(4)結(jié)束界面淡入完成后,重新加載游戲。
2.新建腳本并添加為組件
(1)打開Asset > Scripts文件夾,新建腳本并命名為Observer。
(2)打開Gargoyle預(yù)制體模式,把Observer腳本拖到Hierarchy里的PointOfView GameObject中,使其添加為組件。
3.檢測玩家
(1)雙擊打開Observer腳本。
(2)范圍檢測
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
?
public class Observer : MonoBehaviour
{
??? public Transform player;??????????? //玩家
??? public GameEnding gameEnding;?????? //GameEnding腳本
?
??? bool m_IsPlayerInRange;???????????? //玩家是否在探測范圍
?
??? //玩家進入檢測范圍
??? private void OnTriggerEnter(Collider other)
??? {
??????? if (other.transform == player)
??????? {
??????????? m_IsPlayerInRange = true;
??????? }
??? }
?
??? //玩家離開檢測范圍
??? private void OnTriggerExit(Collider other)
??? {
??????? if (other.transform == player)
??????? {
??????????? m_IsPlayerInRange = false;
??????? }??
??? }
?
(3)視線模擬
當玩家進入檢測范圍時,為了避免在墻后也能發(fā)現(xiàn)玩家的情況,因此在Update()里我們用射線(Ray)進行視線模擬。射線從敵人的位置射向玩家,當射線直接打到玩家時,淡入結(jié)束界面。由于玩家的position在腳底,所以我們加上Vector3.up(0,1,0),讓射線能夠往玩家身上打。
//每幀執(zhí)行
??? private void Update()
??? {???????
??????? //如果玩家進入燈光范圍,用射線模擬視線檢測
??????? if (m_IsPlayerInRange)
??????? {
??????????? //射線方向為從敵人指向玩家
??????????? Vector3 direction = player.position + Vector3.up - transform.position ;
??????????? Ray ray = new Ray(transform.position, direction);?? //(射線的起點,方向)
??????????? RaycastHit raycastHit;????????????????????????????? //儲存射線的碰撞信息????????????????
?
??????????? if (Physics.Raycast(ray, out raycastHit))
????????? ??{
??????????????? if (raycastHit.collider.transform == player)??? //如果射線打中的是玩家??????
??????????????? {
??????????????????? gameEnding.CaughtPlayer();????????????????? //玩家被抓
??????????????? }
??????????? }
??????? }
??? }
}
?
gameEnding.CaughtPlayer(); 這里暫時會報紅,完成下邊的步驟后便會解決。
到這一步我們完成了視線檢測,Ctrl + S保存腳本。
4.修改GameEnding腳本(建議直接看修改后腳本)
當玩家在檢測范圍內(nèi),并且能被射線能直接打中時,淡入被抓的結(jié)束界面,這時候我們需要回到GameEnding腳本進行編輯。
(1)增加變量
public CanvasGroup CaughtImageBackgroud:被抓的界面
bool m_IsPlayerCaught :玩家是否被抓

(2)為EndLevel()添加參數(shù)
當玩家通關(guān)或者被抓時,控制對應(yīng)的界面淡入,淡入完成后判斷是否重新載入游戲,因此我們需用CanvasGroup(勝利/失敗界面)和bool(是否重載游戲)變量作為參數(shù)。
void EndLevel(CanvasGroup imageCanvasGroup,bool doRestart)
(3)重載場景
重載場景需要在腳本上方添加命名空間:using UnityEngine.SceneManagement;
場景的索引從0開始數(shù)且我們只有一個場景,因此我們重啟的場景用:SceneManager.LoadScene(0);

(4)Update()
每幀檢測時,我們需要判斷玩家是到達終點還是被抓,然后給EndLevel()輸入對應(yīng)的參數(shù)。

修改后的腳本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
?
public class GameEnding : MonoBehaviour
{
??? public float fadeDuration = 1f;???????????????????? //淡入淡出時間1S
??? public float displayImageDuration = 1f;???????????? //淡入完畢后等待時間1S
??? public GameObject player;?????????????????????????? //玩家
??? public CanvasGroup exitBackgroundImageCanvasGroup;? //用于調(diào)節(jié)通關(guān)界面alpha值的CanvasGroup???
??? public CanvasGroup caughtBackgroundImageCanvasGroup;//用于調(diào)節(jié)被抓界面alpha值的CanvasGroup
?
??? bool m_IsPlayerAtExit;????????????????????????????? //玩家是否到終點
??? bool m_IsPlayerCaught;????????????????????????????? //玩家是否被抓
??? float m_Timer;????????????????????????????????????? //計時器?
?
??? /// <summary>
??? /// 達到出口,進入觸發(fā)器時調(diào)用
??? /// </summary>
??? /// <param name="other"></param>
??? private void OnTriggerEnter(Collider other)
??? {
??????? //如果進入觸發(fā)器范圍的是玩家,改變布爾變量
??????? if (other.gameObject == player)????????????????
??????? {
??????????? m_IsPlayerAtExit = true;
??????? }
??? }
?
??? //玩家被抓時在Observer腳本里調(diào)用
??? public void CaughtPlayer()
??? {
??????? m_IsPlayerCaught = true;
??? }
?
??? /// <summary>
??? /// 每幀調(diào)用
??? /// </summary>
??? private void Update()
??? {??????
??????? if (m_IsPlayerAtExit)?????? //如果玩家到達出口,執(zhí)行EndLevel();??????? ??????????????
??????? {
??????????? EndLevel(exitBackgroundImageCanvasGroup,false);
??????? }
??????? else if (m_IsPlayerCaught)? //如果玩家被抓,執(zhí)行重新加載場景;
??????? {
??????????? EndLevel(caughtBackgroundImageCanvasGroup,true);
??????? }
??? }
?
??? /// <summary>
??? /// 關(guān)卡結(jié)束
??? /// </summary>
??? void EndLevel(CanvasGroup imageCanvasGroup,bool doRestart)
??? {
??????? m_Timer = m_Timer + Time.deltaTime; //計時器,每幀累加時間
???????
??????? //alpha值隨著百分比改變
??????? imageCanvasGroup.alpha = m_Timer / fadeDuration;
?
?????? ?//當計時器時間大于總等待時間
??????? if (m_Timer > fadeDuration + displayImageDuration)
??????? {
??????????? if (doRestart)
??????????? {
??????????????? SceneManager.LoadScene(0);//場景載入,只有一個場景,下標為0
??????????? }
??????????? else
??????????? {
??????????????? Application.Quit();//退出游戲,這個要游戲打包后才會執(zhí)行,在Unity里不會關(guān)閉游戲。
??????????? }
??????? }
??? }
}
?
修改完成后保存腳本。
四、在Unity中為腳本引用賦值
(1)選中PointOfView?GameObject,為Observer腳本的引用賦值。

(2)選中GameEnding?GameObject,為GameEnding腳本的引用賦值。

進行到這里,石像鬼設(shè)置基本完成,Ctrl + S保存場景。
結(jié)語:這一篇中我們添加并設(shè)置了石像鬼的預(yù)制體(動畫、碰撞盒和模擬檢測范圍的觸發(fā)器),在腳本中用射線模擬視線檢測。運行游戲,試試石像鬼的檢測是否奏效吧~下一篇,我們將添加動態(tài)的敵人---幽靈,結(jié)合在環(huán)境篇中設(shè)置的NavMesh,使幽靈可以在設(shè)定的路線上巡邏。
迫不及待想自行開始制作的小伙伴,可以瀏覽John Lemon's Haunted Jaunt官方教程:https://learn.unity.com/project/john-lemon-s-haunted-jaunt-3d-beginner
咱們的游戲開發(fā)交流群也歡迎強勢插入:869551769
希望參與線下游戲開發(fā)學習的,歡~~~~~~迎訪問:http://levelpp.com/