从零点五开始用Unity做半个2D战棋小游戏(五)
寫在最前
這次想要一個簡單且傳統的戰棋小游戲,大概的玩法是:在2D世界里創建一張由六邊形地塊組成的戰斗地圖,敵我雙方依據體力在地圖上輪流行動并向對方發動攻擊,先消滅掉所有敵人的一方將獲得勝利。
預計將分為以下幾篇(未完成前可能會調整...):
1、創建戰場(已完成)
根據預定尺寸生成戰場地圖,并隨機一些障礙物。
2、添加地圖功能 (已完成)
實現戰場格子點擊反饋,地圖導航及范圍選定。
3、添加對戰雙方 (已完成)
向戰場中添加作戰單位,作戰單位輪流行動,可進行移動、攻擊。
4、加入玩家控制 (已完成)
玩家可控制一個戰斗單位,手動選擇移動目標及攻擊單位。
5、添加常用的界面
建立界面管理器,加入一些常用的界面。
6、添加常用的戰場顯示
為戰斗單位添加血條,加入傷害文字顯示。
7、擴展作戰單位
豐富作戰單位的類型,添加職業,并加入若干不同類型的技能。
8、擴展戰場地圖
豐富戰場地圖,加入地形及道具等元素。
9、規范戰斗配置
可以通過規范化的數據結構配置戰場、職業、技能、道具等。
本次的主題是:添加常用的界面。
| 目標
使用Unity自帶的UGUI替換之前的GUI來實現一些常用界面:
1、包含開始戰斗按鈕及戰斗結束時提示文字的主界面。
2、玩家手動操作時,輔助選擇移動、攻擊及待命的彈出面板。
3、點選地圖、戰斗單位時,彈出的詳情展示面板。
實現后的效果如下圖:
戰斗結束時顯示的友情提醒
行動選擇菜單
戰場單位信息面板
不難看出,當UGUI碰撞上專業的素材后,一個個絕美的界面瞬間躍然屏上。這也給了那些常說“程序員不懂美”的家伙們一記響亮的耳光。
| 關于如何制作界面
這些界面都是用UGUI制作的,并沒有什么難度,相信上手過UGUI或NGUI的同學,只要碰到精美的紋理貼圖,都能輕松完成。
使用的是一套高雅灰主題的專業UI紋理素材
本文不會對UGUI的使用做詳細介紹,我們將重點聊聊對界面的管理。
因為只要找到了方法,做出漂亮的界面就只是時間問題罷了。
而至于證明“程序員也能憑自己的能力作出專業界面”這件事,相信上面已經做到了。
受項目大小及時間所限,這里會使用一套輕量級的界面管理方式。而在此之前,讓我們先做一些前期準備工作。
| 分配相機
為界面的繪制單獨分配一個相機(界面相機),并調整界面相機和戰場相機上的Clear Flags、CullingMask和Depth設置。
界面相機與戰場相機的設置
整個界面系統的根畫布(ScreenCanvas),是一個類型為ScreenSpace-Camera的Canvas,它的父節點ScreenUIRoot上有專門渲染界面的界面相機。
| 設置層級
ScreenCanvas下設5個節點,分別對應5個層級:背景層、基礎層、彈出層、頂層和Debug層.
界面的層級結構
各層的功能為:
背景層:裝飾性的、非功能性的界面。
基礎層:常駐的界面(主界面、角色頭像、快捷操作欄等)。
彈出層:點擊后彈出的界面(各功能界面)。
頂層:強制顯示在最上層的界面(Tips界面或走馬燈等)。
Debug層:開發時輔助調試用。
這些層級從下到上放置,遮擋關系是上層遮擋下層。當然,其順序、層數和名稱可根據實際需求進行調整。
需要注意的是,這5個節點(Transform)并非是必須的,它們只是為了Debug時能更直觀的查看層級間的關系,真正用于用于區分層級的是Canvas上的SortingLayer和OrderInLayer屬性。
SortingLayers設置中的層級與對應的節點
不同界面上Canvas所設置的Layer和Order值
| 界面管理流程
比起代碼,我覺得還是看圖來的更直觀些。
界面管理流程
整個界面管理可以簡單拆解為四個部分:打開界面、關閉界面、層級刷新及界面刷新,下面我們依次介紹。
| 打開界面
打開界面的邏輯很簡單,但需要注意的是,為了更好的使用內存,二手手游賬號賣號界面管理器維護了兩個界面緩存區:常駐緩存和臨時緩存。打開界面時如果需要加載新的界面,先去這兩個緩存區中查看一下是否有緩存過的界面,從緩存區加載比重新讀取新的界面效率要高。
| 關閉界面
當我們要關閉界面時,界面管理器會根據它的“存儲策略”決定其被關閉后的去留,有些會被放入到常駐緩存區,有些會被放到臨時緩存區,而有些則會被直接移除。
這就好比兩性交往中女生犯錯通常當時就會被原諒;男生犯錯通常需要好好表現一段時間才有可能被原諒;而單身狗連犯錯的機會都沒有。
| 層級刷新
上面介紹了打開和關閉界面。其實它們都可以被看成是在“顯示界面”。因為關閉界面也可以理解為“被這個界面遮擋覆蓋的界面,可能需要顯示了”。
因此,當有界面被打開或關閉后,界面管理器會從上到下的讓各層刷新自己的顯示狀態及對屏幕的遮擋狀態,并將這個遮擋狀態向下傳遞,用作后面層級的顯示判斷。
| 界面刷新
我們知道界面的刷新和顯示是有代價的,因為它們會對CPU及GPU的性能造成開銷。因此我為每個界面設置了是否遮擋了屏幕、不可見時是否仍然刷新及Dirty屬性。
如果一個界面遮擋了屏幕,那么它下面的界面首先應該被“隱藏”以減小渲染的壓力;其次如果不可見的界面沒有被設置為“不可見時仍然刷新”,則當需要刷新它時(界面數據發生了變化),也只是被打上Dirty標記,并在下次需要顯示的時候再刷新。
| 界面的生命周期函數
上面流程圖中的藍色部分,是界面的生命周期函數,它們會在適當的時間被界面管理器、層級或界面自身調用。
Init:初始化,界面首次被加載后調用。
OnPush:界面被顯示前,加入層級時被調用。
OnShow:界面被顯示時調用。
UpdateView:界面需要被刷新時調用。
OnHide:界面被隱藏時調用。
OnPopup:界面被關閉,從層級中移除時調用。
OnExit:界面不需要被緩存,被Destroy前調用。
一次完整的界面打開、刷新、關閉過程
| 界面的存儲策略
界面被關閉后會根據預設的存儲策略決定去留,這里定義的存儲策略有以下三種。
自動移除:很少用到的界面,每次關閉時會被直接Destroy掉。
自動移除策略
臨時緩存區:較為常用的界面,在關閉時我們把它放入一個有深度設置的緩存區,這個緩存區當收入一個新的界面時,會判斷緩存量是否已超過預設深度;如果超過了預設深度,會將最早緩存的界面彈出并Destroy掉。
臨時緩存區策略(深度為2)
常駐緩存區:需要頻繁開關的界面,在關閉時我們會把它們放入一個沒有深度設置(緩存個數限制)的緩存區。
常駐緩存區策略(僅唯一存在界面可被設置為常駐)
| 界面配置
我使用ScriptableObject對象作為界面配置的載體。每次創建新的界面時,需要同樣創建一個配置對象,并將兩者進行關聯。界面自身及使用者可以通過讀取這個對象獲取界面的配置信息。
界面配置文件的屬性構成
當然我們可以稍微修改Editor,添加一些輔助工具幫助我們快速生成界面配置文件。
一個簡單的配置文件刷新器
| 寫在最后
至此,添加常用的界面篇就介紹到這里,詳細代碼可以移步Github。
感謝您能讀到這里。
愿不忘初心。
下回見。
總結
以上是生活随笔為你收集整理的从零点五开始用Unity做半个2D战棋小游戏(五)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从零点五开始用Unity做半个2D战棋小
- 下一篇: 从零点五开始用Unity做半个2D战棋小