文章目錄
- 一、前言
- 二、創建工程
- 1、創建工程
- 2、創建目錄
- 三、地鐵路線地圖制作
- 1、廣州地鐵圖
- 2、HexTiles:六變形3D瓦片工具的基本操作
- 2.1、HexTiles工具下載
- 2.2、創建瓦片材質球
- 2.3、創建瓦片容器
- 2.4、繪制瓦片
- 2.5、設定瓦片高度
- 2.6、擦除瓦片
- 2.7、瓦片材質繪制:材質替換
- 2.8、大面積刷瓦片
- 2.9、大面積擦除瓦片
- 3、搭建地鐵路線
- 四、地鐵站點制作
- 1、地鐵站點模型
- 2、地鐵站點材質球
- 3、地鐵站點名稱:TextMeshPro
- 3.1、匯入字體檔案
- 3.2、安裝TextMeshPro
- 3.3、制作字符集
- 3.4、制作Font Asset
- 3.5、顯示地鐵站名字
- 3.6、保存站點預設
- 4、排放地鐵站點
- 5、地平面
- 五、導航系統:NevMesh烘焙
- 1、設定烘焙物件為Static
- 2、NevMesh烘焙
- 六、搖桿制作
- 1、搖桿圖片
- 2、Canvas與UICamera
- 3、搖桿UI制作
- 3、搖桿邏輯代碼
- 4、掛搖桿邏腳本
- 5、搖桿測驗
- 七、角色、影片與控制
- 1、角色模型下載
- 2、影片控制器
- 3、主角出場
- 4、搖桿控制主角
- 八、攝像機控制:跟隨主角與右搖桿控制旋轉
- 1、跟隨主角
- 2、右搖桿控制旋轉
- 九、天空盒SkyBox與環境霧Fog
- 1、天空盒:SkyBox
- 2、環境霧:Fog
- 十、登頂廣州塔
- 1、廣州塔模型下載
- 2、廣州塔放入場景中
- 3、檢測主角到了廣州塔底部:觸發器
- 4、詢問是否要上廣州塔:UI界面
- 5、登上塔頂
- 6、塔頂調整攝像機距離
- 十一、功能補充
- 1、粒子系統:煙花效果
- 2、音樂播放:安妮的仙境
- 3、彩蛋:天空盒道具
- 十二、工程原始碼
一、前言
嗨,大家好,我是新發,
最近比較忙,好幾天沒寫文章了,這兩天廣州疫情的新聞頻頻上熱搜,身在廣州無時無刻不吊著個心,部分同事所在的區域已經封起來了,只能遠程上班,我住的地區暫時還比較安全,不過也已經做好遠程作業的準備了,
話說回來,我在廣州上學、生活、作業了十年了,對廣州有著特殊的情愫,
我這兩天做了一個Demo,我在Unity中搭建了整個廣州地鐵路線地圖,并做了第三人稱視角相機跟隨,雙搖桿控制,可以登上廣州塔鳥瞰整個廣州,以此獻給我熱愛的大廣州,效果如下:
注:今天廣州大規模核酸檢測,下午剛檢測完畢,回家寫這篇文章直到現在(
2021-6-5 23:44),廣州加油,早日戰勝疫情!




下面我將講解一下創作程序,
二、創建工程
1、創建工程
我用的Unity版本是2021.1.7f1c1 (64-bit),選擇3D模板,工程名字起為GuangzhouGogo好了,點擊創建,

創建成功,

2、創建目錄
養成好習慣,先規范目錄結構,創建一些檔案夾,目錄結構如下:

RawAssets目錄主要是存放一些【生肉資源】或被場景依賴的資源;
注:關于【生肉資源】可以參見我之前寫的這篇文章的說明:《Unity游戲開發——新發教你做游戲(三):3種資源加載方式》
Resources目錄存放一些被代碼動態加載 資源;
Scenes目錄存放場景檔案;
Scripts目錄存放C#代碼;
ThirdPart目錄存放一些第三方工具或庫;
三、地鐵路線地圖制作
1、廣州地鐵圖
我們先找一下廣州地鐵圖,我找到的最新版本是這個:

截止到2020年6月30日,廣州市已經開通的地鐵線路有14條,全市地鐵站點有213個,
先把這個圖弄到Unity中,

圖片格式設定為Sprite (2D and UI),

把地鐵圖拖到場景中,調整坐標、旋轉、縮放,如下:

效果如下:

2、HexTiles:六變形3D瓦片工具的基本操作
2.1、HexTiles工具下載
地圖我想做得比較風格化,于是我找了一個六變形3D瓦片工具:HexTiles,這個工具可以在GitHub上找到,地址:https://github.com/RoryDungan/HexTiles

注:
2D瓦片工具可以參見我之前寫的這篇文章:《[Unity 2D] 重溫紅白機經典FC游戲,順便教你快速搭建2D游戲關卡(Tilemap | 場景 | 地圖)》
下載下來后,放入工程的ThirdPart目錄中,只需保留它的Code和Plugins檔案夾即可,如下:

2.2、創建瓦片材質球
我們要使用工具來繪制3D瓦片,我們需要先為瓦片制作材質球,我們先做一個綠色的材質球,在RawAssets/Materials/tiles目錄右鍵點擊選單Create/Material,創建材質球;

材質球重命名為green,設定材質球的顏色為綠色,然后我們不想要有反光的效果,可以調整光滑度為0;

以此類推,把地鐵路線的顏色都做一個對應的材質球~

2.3、創建瓦片容器
在Hierarchy視圖空白處右鍵點擊選單Hex tile map,創建瓦片容器,

瓦片容器上會有個HexTileMap組件,可以看到對應的功能按鈕,我們后續繪制的瓦片都會在這個Hex tile map的子節點下,

2.4、繪制瓦片
確保Scene視圖的Gizmos按鈕是激活狀態的,

點擊添加瓦片按鈕,把要使用的材質球拖到Material槽中,

然后在場景中按住滑鼠拖動即可繪制瓦片了,
2.5、設定瓦片高度
我們想要在更低一層繪制瓦片,可以調整Height offset,比如我調整為-0.5,順便把材質球改成白色,

現在我們繪制瓦片,可以看到是在原來的瓦片的下層繪制了,并且邊緣銜接處會自動補上,

2.6、擦除瓦片
我們想把繪制的瓦片擦除,可以點擊擦除瓦片按鈕,

然后在場景中點擊要擦除的瓦片,

2.7、瓦片材質繪制:材質替換
點擊材質繪制按鈕,然后目標材質,

然后點擊要繪制的瓦片即可,

2.8、大面積刷瓦片
上面我們是一個瓦片一個瓦片刷的,我們可以調整刷子的尺寸(Brush size),比如我調整為3,

這樣就可以大面積刷瓦片了,

2.9、大面積擦除瓦片
同樣,我們也可以大面積擦除瓦片,


3、搭建地鐵路線
開始沿著地鐵路線鋪路,

鋪啊鋪,鋪啊鋪,

把所有地鐵路線圖都鋪好,
四、地鐵站點制作
1、地鐵站點模型
站點就用一個簡單的柱體就好了,創建一個Cube,

拉長,

由兩個柱體(底部和頂部)組成一個站點的模型,

2、地鐵站點材質球
創建兩個材質球,分別作為站點底部和頂部的材質,

將材質球賦值給上面的柱體,

3、地鐵站點名稱:TextMeshPro
地鐵站點的名字我使用了TextMeshPro來顯示,它的好處是在3D空間下近距離觀察文字也是很清晰的,
注:關于
TextMeshPro的使用教程可以參見我之前的這篇文章:《手把手教,Unity使用TextMeshPro顯示字體》
下面我講下操作步驟,
3.1、匯入字體檔案
找一個你喜歡的字體(TTF格式),比如我找的是免費的思源字體,

將其放入Unity工程中,

3.2、安裝TextMeshPro
點擊選單Window / Package,打開Package Manger視窗,

Packages選擇Unity Registry,然后搜索textmeshpro,選擇TextMeshPro,點擊Install按鈕,如果你已經安裝過,則沒有Install按鈕了,

安裝成功后,可以看到Window選單中多了一個TextMeshPro選單,

3.3、制作字符集
我們需要為TextMeshPro創建一個字符集(一個txt檔案),把我們需要用到的字放在這個字符集檔案里,如下,在TTF同級目錄中創建一個txt檔案(characters.txt),

把廣州市所有的地鐵站名字都放在這個characters.txt檔案中,

3.4、制作Font Asset
點擊選單 Window / TextMeshPro / Font Asset Creator

首次打開會彈出下面這個視窗,點擊Import TMP Essentials按鈕,

在Font Asset Creator視窗中,設定Source Font File為我們的字體TTF檔案,設定Character Set為Characters from File,設定Character File為我們的字符集檔案characters.txt,最后點擊Generate Font Atlas按鈕,

此時會生成一個紋理,我們點擊Save保存,

保存到RawAssets/Fonts目錄中,

如下(font SDF.asset)

3.5、顯示地鐵站名字
在地鐵站節點下創建一個空物體,重命名為name,

給這個name節點添加TextMeshPro - Text組件,

在Text Input中輸入地鐵站的名字,比如珠江新城,
設定Font Asset為我們上面生成的font SDF,

此時效果如下:

設定一下字號,設定一下對其方式,

設定一下坐標和顯示區域大小,

效果如下:

為了能在四個面都看得到地鐵站名字,我們再復制出另外三份,

調整下坐標和旋轉角度,效果如下:

3.6、保存站點預設
養成好習慣,需要重復使用的物體(模板)我們最好保存成預設,將其保存到RawAssets/Prefabs目錄中,如下:

4、排放地鐵站點
按照地鐵線路,依次擺放地鐵站點,

擺呀擺,

終于把地鐵站全部弄好了,

把站點按地鐵路線收納好,方便管理,

5、地平面
創建一個Plan作為地平面,這樣影子可以投射到地面上,

給地面創建一個材質球,材質球賦值給Plan,

調整材質球顏色,

五、導航系統:NevMesh烘焙
地鐵路線有了,接下來就給它烘焙NevMesh吧,以便后面支持導航功能,
1、設定烘焙物件為Static
因為NevMesh只對場景中的靜態物件進行烘焙,所以我們需要先把地鐵路線設定為Static的,
選擇HexTileMap節點,將其設定為Static,

點擊Yes, change children,即所有的子節點都設定為Static,

2、NevMesh烘焙
點擊選單Window / AI / Navigation,

在Navigation視窗中,點擊Bake標簽頁,
調節一下Agent Radius,因為我們的地鐵路面比較窄,所以這里的Agent Radius需要調小一點,
最后點擊Bake按鈕即可,

烘焙成功后,可以看到路面上出現了藍色的網格,

同時,在場景檔案目錄中,會看到生成了一個與場景同名的檔案夾,里面的NavMesh.asset保存的就是場景的導航烘焙資訊,

六、搖桿制作
地圖有了,接下來就是主角了,不過在做主角之前,我們先把搖桿做一下吧~
1、搖桿圖片
搖桿的圖片很簡單,一個圓就可以了,

2、Canvas與UICamera
創建一個Canvas,作為后面UI的父節點,


在創建一個Camera來專門渲染Canvas,

將其重命名為UICamera,

設定UICamera的Clear Flags為Depth only,并設定Culling Mask為UI,這樣它就只會渲染UI層,把Projection設定為Orthographic(正交),

接著把Canvas的Render Mode(渲染模式)改為Screen Space - Camera(即由攝像機來渲染),然后把Render Camera設定為剛剛的UICamera,
接著再設定下解析度適配,把Canvas Scale組件的UI Scale Mode設定為Scale with Screen Size,把解析度設定為1280, 720,

另外,因為UI已交給UICamera來渲染,所以Main Camera不需要再渲染UI層了,把Main Camera的Culling Mask的UI勾選去掉,

3、搖桿UI制作
在Canvas節點上右鍵點擊選單UI / Panel,創建一個Panel,

把Image組件禁用掉,因為我們不需要Panel顯示出來,

在Panel下創建一個Image,重命名為leftJointedArm,作為左搖桿的父節點,

設定它的錨點為bottom - left,即螢屏左下角,調整坐標和寬高,

像這樣子,

把它的Color的alpha調為0,因為我們只需要利用它的區域來檢測觸碰,我們不需要肉眼看見它,

接著在它的子節點下創建兩個Image,分別命名為bg和center,

它們的Source Image都設定為搖桿的圖片資源,

分別調整下bg和center的大小和顏色透明度,效果如下:

同理再做一個右搖桿,

效果如下:

3、搖桿邏輯代碼
Unity的UGUI提供了ScrollRect組件,非常適合用來制作搖桿,我們繼承ScrollRect然后實作OnDrag和OnEndDrag方法,可以很方便地獲取到搖桿的遙控資料,另外,為了檢測區域點擊,我們再實作IPointerDownHandler介面,

創建搖桿腳本JointedArm.cs,

JointedArm .cs代碼如下:
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using System;
public class JointedArm : ScrollRect, IPointerDownHandler
{
public Action<Vector2> onDragCb;
public Action onStopCb;
protected float mRadius = 0f;
private Transform trans;
private RectTransform bgTrans;
private Camera uiCam;
private Vector3 originalPos;
protected override void Awake()
{
base.Awake();
trans = transform;
bgTrans = trans.Find("bg") as RectTransform;
uiCam = GameObject.Find("UICamera").GetComponent<Camera>();
originalPos = trans.localPosition;
}
void Update()
{
if (Input.GetMouseButtonUp(0))
{
//松手時,搖桿復位
trans.localPosition = originalPos;
this.content.localPosition = Vector3.zero;
}
}
protected override void Start()
{
base.Start();
//計算搖桿塊的半徑
mRadius = bgTrans.sizeDelta.x * 0.5f;
}
public override void OnDrag(PointerEventData eventData)
{
base.OnDrag(eventData);
var contentPostion = this.content.anchoredPosition;
if (contentPostion.magnitude > mRadius)
{
contentPostion = contentPostion.normalized * mRadius;
SetContentAnchoredPosition(contentPostion);
}
Debug.Log("搖桿滑動,方向:" + contentPostion);
if(null != onDragCb)
onDragCb(contentPostion);
}
public override void OnEndDrag(PointerEventData eventData)
{
base.OnEndDrag(eventData);
Debug.Log("搖桿拖動結束");
if (null != onStopCb)
onStopCb();
}
public void OnPointerDown(PointerEventData eventData)
{
//點擊到搖桿的區域,搖桿移動到點擊的位置
trans.position = uiCam.ScreenToWorldPoint(eventData.position);
trans.localPosition = new Vector3(trans.localPosition.x, trans.localPosition.y, 0);
}
}
4、掛搖桿邏腳本
把JointedArm .cs分別掛到leftJointedArm和rightJointedArm上,賦值對應的center,

5、搖桿測驗
運行Unity,搖桿測驗效果如下:

七、角色、影片與控制
1、角色模型下載
主角我在AssetStore上找到了一個心儀的模型,推薦給大家,
AssetStore地址:https://assetstore.unity.com/packages/3d/characters/humanoids/sci-fi/stylized-astronaut-114298

注:更多模型下載可以參見我之前寫的這篇文章:
《Unity游戲開發——新發教你做游戲(二):60個Unity免費資源獲取網站》
將模型下載匯入Unity中,

2、影片控制器
注:關于
Animator組件的詳細使用可以參見我之前寫的這篇文章:《Unity影片狀態機Animator使用》
打開角色的影片控制器檔案CharacterController,

可以看到,兩個動作,一個idle(站立)一個Run(跑),

到Parameters(引數)里面有一個AnimationPar引數,這個引數就是用來控制站立與跑著兩個影片的過渡條件的,

從Run過渡到Idle的條件是AnimationPar等于1,

從Idle過渡到Run的條件是AnimationPar等于0,

這樣,我們就可以在代碼中通過這個引數來控制影片的過渡了,例:
// public Animator anim;
// 站立 -> 跑
anim.SetInteger("AnimationPar", 1);
// 跑 -> 站立
anim.SetInteger("AnimationPar", 0);
3、主角出場
在場景中創建一個空物體,重命名為Player,

把主角模型拖到Player子節點中,把主角模型也命名為Player,

這樣,場景中出現了我們的主角了,

因為主角需要在地鐵路線上跑,我們用了導航系統NevMesh,所以主角需要掛NevMeshAgent組件,

調節Radius(半徑)與Height(高度)使之與主角模型匹配,


4、搖桿控制主角
寫一個Player.cs腳本,主要邏輯如下,
// Player.cs
using UnityEngine;
public class Player : MonoBehaviour
{
public float speed = 1f;
public float turnSpeed = 20f;
public Animator anim;
public Transform rootTrans;
public Transform modelTrans;
private bool moving = false;
private Vector3 moveDirection = Vector3.zero;
// ...
void Update()
{
if (moving)
{
// 播放跑影片
anim.SetInteger("AnimationPar", 1);
// 更新主角坐標
rootTrans.position += moveDirection * speed * Time.deltaTime;
// 更新主角朝向,使用Vector3.Lerp進行插值運算,使得角度變化不那么生硬
modelTrans.forward = Vector3.Lerp(modelTrans.forward, moveDirection, turnSpeed * Time.deltaTime);
}
else
{
// 播放站立影片
anim.SetInteger("AnimationPar", 0);
}
}
// 移動
public void Move(Vector3 direction)
{
moveDirection = direction;
moving = true;
}
// 站立
public void Stand()
{
moving = false;
}
// ...
}
將腳本掛到Player父節點上,賦值對應的變數,

為了方便管理,我們再封裝一個游戲管理器GameMgr.cs,由游戲管理器來調度搖桿與主角,
// GameMgr.cs
public Player player;
// 左搖桿
public JointedArm leftJointedArm;
// 攝像機的Transform
private Transform camTrans;
// ...
leftJointedArm.onDragCb = (direction) =>
{
// 搖桿向量轉世界坐標系下的向量
var realDirect = camTrans.localToWorldMatrix * new Vector3(direction.x, 0, direction.y);
realDirect.y = 0;
// 向量歸一化
realDirect = realDirect.normalized;
// 主角根據向量移動
player.Move(realDirect);
};
leftJointedArm.onStopCb = () => { player.Stand(); };
注:從搖桿的
2D向量轉換為控制主角的3D向量,這里我用了一個矩陣變換,camTrans.localToWorldMatrix,相當于把相對于攝像機的區域坐標轉換為世界坐標,
這樣我們的搖桿就可以控制主角移動了,不過現在攝像機并不會跟著主角移動,所以下一步我們就來做攝像機跟隨吧~

八、攝像機控制:跟隨主角與右搖桿控制旋轉
1、跟隨主角
攝像機需要始終看著主角,我們可以使用Transform的LookAt方法;
攝像機要跟著主角移動,就是根據主角當前的坐標來設定攝像機的坐標,我們可以使用Transform的position屬性,
創建一個CameraControler.cs腳本,實作攝像機控制的邏輯,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 攝像機控制
public class CameraControler : MonoBehaviour
{
// 攝像機看向的物體
public Transform lookAt;
// 攝像機自身的Transform
public Transform camTransform;
// 攝像機與目標物體的距離
public float distance = 1.2f;
private float currentX = 0.0f;
private float currentY = 20.0f;
// ...
private void Start()
{
camTransform = transform;
}
private void LateUpdate()
{
Vector3 dir = new Vector3(0, 0, -distance);
Quaternion rotation = Quaternion.Euler(currentY, currentX, 0);
camTransform.position = lookAt.position + rotation * dir;
camTransform.LookAt(lookAt.position);
}
// ...
}
把CameraControler.cs掛到攝像機上,賦值對應的變數,這樣攝像機就可以跟著主角移動了,

2、右搖桿控制旋轉
CameraControler.cs腳本中加上旋轉的邏輯,
// CameraControle.cs
// 旋轉速度
public float rotateSpeed = 0.01f;
private bool rotating;
// 旋轉偏量
private Vector2 rotateDelta;
// 限制旋轉范圍
private const float Y_ANGLE_MIN = 10f;
private const float Y_ANGLE_MAX = 50.0f;
private void Update()
{
if (rotating)
{
// 限制旋轉范圍
currentX += rotateDelta.x;
currentY += rotateDelta.y;
currentY = Mathf.Clamp(currentY, Y_ANGLE_MIN, Y_ANGLE_MAX);
}
}
// 設定旋轉偏量
public void RotateCam(Vector2 delta)
{
rotateDelta = delta * rotateSpeed;
rotating = true;
}
// 停止旋轉
public void StopRotate()
{
rotating = false;
}
private void LateUpdate()
{
Vector3 dir = new Vector3(0, 0, -distance);
Quaternion rotation = Quaternion.Euler(currentY, currentX, 0);
// 設定相機坐標
camTransform.position = lookAt.position + rotation * dir;
// 設定相機角度看向目標物體
camTransform.LookAt(lookAt.position);
}
// ..
在GameMgr.cs中添加右搖桿與相機旋轉的調度,
// GameMgr.cs
public JointedArm rightJointedArm;
public CameraControler camCtrler;
// ...
rightJointedArm.onDragCb = (direction) =>
{
camCtrler.RotateCam(direction);
};
rightJointedArm.onStopCb = () => { camCtrler.StopRotate(); };
最后記得給GameMgr.cs賦值對應的變數,

右搖桿效果如下:

九、天空盒SkyBox與環境霧Fog
1、天空盒:SkyBox
現在天空比較單一,我們加上天空盒的效果,
天空盒的資源我是在AssetStore上下載的,地址:https://assetstore.unity.com/packages/2d/textures-materials/sky/farland-skies-cloudy-crown-60004

下載下來,匯入到Unity工程中,

只需要把天空盒的材質球拖到場景中即可生效,或者選單Window / Rendering / Lighting,

點擊Environment,然后設定Skybox Material為對應的天空盒材質球即可,

我喜歡Sunset(日落)的天空效果,加上之后效果如下,是不是一下子就唯美了很多:

我們也可以通過代碼設定天空盒,例:
RenderSettings.skybox = skyMat;
可以再欣賞下其他不同天空盒的效果:
清晨:

中午:

晚霞:

午夜:

2、環境霧:Fog
不同的天空盒,需要搭配不同顏色的環境霧效,
在Lighting視窗的Environment標簽頁中即可開啟環境霧,如下:
我們可以設定霧效的顏色、密度等引數,

我們可以對比下 沒霧效與 有霧效的區別:
十、登頂廣州塔
大家應該都知道廣州的地標建筑物:廣州塔(小蠻腰),必須安排上,
1、廣州塔模型下載
我找到了廣州塔的模型,模型下載地址:https://www.3dxy.com/3dmodel/148664.html
下載FBX格式的,匯入Unity工程的RawAssets/Models目錄中,


2、廣州塔放入場景中
把廣州塔模型放入場景中,調整坐標到對應的位置,調整模型縮放,效果如下:

3、檢測主角到了廣州塔底部:觸發器
我用了觸發器來檢測主角是否到了廣州塔底部,在廣州塔底部創建一個物體,并掛上BoxCollider組件,調整碰撞體大小,如下:


把碰撞體的Is Trigger勾選上,這樣它就是一個觸發器了,

想要檢測主角是否進入了觸發器中,還需要給主角也掛上碰撞體(Collider)和剛體(Rigidbody),給主角安排上,因為我們不需要模擬重力,所以Use Gravity不要勾選,


調整碰撞體大小的時候,如果看不清楚,可以把Scene視圖的Shading Mode設定為Wireframe(線框),

這樣就可以比較清楚得看到碰撞體了,

接著寫個廣州塔觸發器腳本CantonTowerTrigger.cs,
using UnityEngine;
/// <summary>
/// 廣州塔觸發器
/// </summary>
public class CantonTowerTrigger : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
// 進入了觸發器
}
private void OnTriggerExit(Collider other)
{
// 離開了觸發器
}
// ...
}
把CantonTowerTrigger.cs腳本掛到觸發器上,

畫成圖是這樣子:

4、詢問是否要上廣州塔:UI界面
主角進入廣州塔觸發器時,彈出UI界面詢問是否要上廣州塔,
我們先做個詢問的UI界面,

層級結構如下:

把界面保存為預設,放在Resources目錄中,這樣我們就可以通過Resources.Load來加載界面資源了,
封裝一個界面管理器,方便界面的顯示與關閉,封裝一個界面基類BaseUIPanel,所有的界面都繼承這個基類,畫成關系圖是這樣子,

界面管理器和界面基類代碼如下:
using System.Collections.Generic;
using UnityEngine;
// 界面管理器
public class UIPanelMgr
{
public void Init()
{
canvas = GameObject.Find("Canvas").transform;
}
public void ShowPanel(string panelName)
{
var panel = GetPanelRes(panelName);
if(null != panel)
panel.Show();
}
public void HidePanel(string panelName)
{
if (!panels.ContainsKey(panelName))
return;
panels[panelName].Hide();
}
private BaseUIPanel GetPanelRes(string panelName)
{
if (panels.ContainsKey(panelName))
return panels[panelName];
var prefab = Resources.Load<GameObject>(panelName);
var go = Object.Instantiate(prefab);
go.transform.SetParent(canvas, false);
var panel = go.GetComponent<BaseUIPanel>();
panels.Add(panelName, panel);
return panel;
}
private Dictionary<string, BaseUIPanel> panels = new Dictionary<string, BaseUIPanel>();
private Transform canvas;
private static UIPanelMgr s_instance;
public static UIPanelMgr instance
{
get
{
if (null == s_instance)
s_instance = new UIPanelMgr();
return s_instance;
}
}
}
// 界面基類
public class BaseUIPanel : MonoBehaviour
{
protected GameObject panelObj;
protected void Awake()
{
panelObj = gameObject;
}
public virtual void Show()
{
panelObj.SetActive(true);
}
public virtual void Hide()
{
panelObj.SetActive(false);
}
}
然后寫一個詢問是否上廣州塔的界面類GoCantonTowerPanel .cs,它繼承BaseUIPanel,代碼如下,
using UnityEngine.UI;
public class GoCantonTowerPanel : BaseUIPanel
{
public Button noBtn;
public Button okBtn;
private void Start()
{
noBtn.onClick.AddListener(Hide);
okBtn.onClick.AddListener(() =>
{
// TODO:前往廣州塔頂部
Hide();
});
}
}
把腳本掛到界面根節點上,并賦值按鈕物件,

回到廣州塔觸發器中,補上顯示界面的呼叫,
// CantonTowerTrigger.cs
// 廣州塔觸發器
private void OnTriggerEnter(Collider other)
{
UIPanelMgr.instance.ShowPanel("GoCantonTowerPanel");
}
private void OnTriggerExit(Collider other)
{
UIPanelMgr.instance.HidePanel("GoCantonTowerPanel");
}
這樣,我們就實作了經過廣州塔底部地時候彈出詢問框的功能了,效果如下:

5、登上塔頂
點擊前往按鈕,主角要移動到塔頂,我們可以事先在塔頂創建一個空物體,作為一個定位,主角瞬間移動到這個位置即可,

同理,我們也在塔底放一個空物體,作為從塔上下來時的定位,

界面邏輯中如果直接操作Player類不是很合適,我們封裝一個事件管理器,通過拋事件來解耦,補上前往按鈕的點擊邏輯,
// GoCantonTowerPanel.cs
okBtn.onClick.AddListener(() =>
{
// 前往廣州塔頂部
EventDispatcher.instance.DispatchEvent(EventDef.GO_TO_CANTONTOWER_TOP);
UIPanelMgr.instance.ShowPanel("OnTopCantonTowerPanel");
Hide();
});
主角類中實作去塔頂的邏輯,
// Player.cs
/// <summary>
/// 去廣州塔頂部
/// </summary>
public void GoToCantonTowerTop(Transform towerTop)
{
canMove = false;
navAgent.enabled = false;
rootTrans.position = towerTop.position;
rootTrans.forward = towerTop.forward;
}
效果如下:

6、塔頂調整攝像機距離
在塔頂鳥瞰廣州,再做一個可以拉長鏡頭的功能,
這個功能就是在攝像機控制器CameraControler.cs中修改distance的值,也是通過事件來觸發,回應函式如下:
// CameraControler.cs
private void OnEventChangeCamDistance(params object[] args)
{
var offset = (float)args[0];
distance = originalDistance + offset * 20f;
}

十一、功能補充
1、粒子系統:煙花效果
用粒子系統做個煙花效果,

用到的粒子圖片如下,比較簡單,可以自行用PhotoShop制作:

粒子引數如下:


注:關于粒子系統的教程,可以參見我之前寫的這些文章:
《學Unity的貓——第十五章:Unity粒子系統ParticleSystem,下雪啦下雪啦》
《Unity使用ShaderGraph配合粒子系統,制作子彈拖尾特效(Fate/stay night金閃閃的大招效果)》
《手把手教你使用Unity制作一個飛機噴射火焰尾氣的粒子效果》
在場景中克隆幾個煙花粒子,用于回圈復用,

寫個煙花腳本,實作隨機坐標播放粒子的功能,
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(ParticleSystem))]
public class Fireworks : MonoBehaviour
{
private ParticleSystem particle;
private Transform trans;
void Start()
{
trans = transform;
particle = GetComponent<ParticleSystem>();
StartCoroutine(RandomLoopFireworks());
}
IEnumerator RandomLoopFireworks()
{
while (true)
{
if (particle.isPlaying)
yield return null;
// 隨機坐標
trans.position = new Vector3(Random.Range(-20, 20), Random.Range(8, 15), Random.Range(-20, 20));
particle.Play();
yield return new WaitForSeconds(Random.Range(0.3f, 1.5f));
}
}
}
效果如下:

2、音樂播放:安妮的仙境
廣州地鐵站的經典背景音樂:安妮的仙境,匯入到工程中,

給GameMgr掛上音源Audio Source組件,賦值Audio Clip為安妮的仙境,勾選Play On Awake,這樣一啟動就會自動播放,勾選Loop,這樣背景音樂就可以回圈播放了,

3、彩蛋:天空盒道具
我把天空盒做成了道具,碰到道具可以動態切換天空盒,原理也是用的觸發器,

十二、工程原始碼
本工程原始碼已上傳到CodeChina,感興趣的同學可自行下載學習,
地址:https://codechina.csdn.net/linxinfa/GuangzhouGogo
注:我使用的Unity版本:Unity 2021.1.7f1c1 (64-bit),

注:本工程僅供學習使用,未經授權不得用于商業用途!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/286219.html
標籤:其他
上一篇:C++11標準下的單例設計模式
下一篇:6步教你封殺惡意登錄服務器的ip
