功能說明
今天要實作的功能就是老滾5、GTA、P社等游戲里面那個按下某個按鍵就能開啟除錯(開掛)的控制臺組件,老規矩,直接上實際效果圖:

如上圖所示,輸入輸出文本的控制臺本質上就是一個游戲中內嵌的命令列,那么,它一定會包含以下幾點功能:
- 一個指令輸入欄和一個回呼輸出框;
- 能夠通過“上”和“下”按鍵快速選取已經使用過的指令;
- 有help指令能列出指令清單;
- 有清屏指令;
- 有一個滾動條能瀏覽歷史,
除此以外,為了嵌在游戲當中,并且能與工程解耦,則也包含如下幾點功能:
- 通過快捷鍵快速彈出/關閉;
- 控制面板與指令實作類低耦合,
一、控制臺界面設計
先從界面設計來講起,重點說明Scrollbar的使用:
1. 添加Panel組件
如下圖左側層級面板中顯示的那樣,先插入一個Panel,命名為“Console Panel”,再在Console Panel中插入一個InputField和一個子Panel,并將子Panel命名為“Output Panel”;

2. 調節InputField和Output Panel的布局
如下圖所示,先將InputField的布局模式改為底端擴展,再將Output Panel的布局模式如紅框中那樣改為全域擴展,并調節Botttom避免與InputField重疊;


3. 添加滾動條
如下圖左側層級面板所示,在Ouput Panel中添加一個Scrollbar和一個Text組件,并將Text命名為“Output Text”;

4. 修改子組件屬性
對于Scrollbar而言,如下圖所示修改兩處內容,讓Scrollbar豎向沿父組件右側擴展,其中,Scrollbar中有幾個引數需要說明:
- Direction指的是當某一個物體超出面板的顯示范圍后,Scrollbar拖動的方向(上到下、下到上、左到右、右到左);
- Value指的是,滑塊處于整個Scrollbar的位置,0.5則表示滑塊居中;
- Number of Steps表示滑塊每次移動的步長;

而對于Output Text,添加一個Content Size Fitter組件,并將Vertical Fit 修改為“Preferred Size”,使文本在垂直方向的寬度自適應,如下圖所示:

5. Ouput Panel系結子組件
如下圖所示,在Output Panel中,添加Scroll Rect和Mask這兩個組件,修改Scroll Rect的Content引數和Vertical Scrollbar引數,并把上一步驟里的兩個組件系結上去,其中,Scroll Rect中有幾個常用引數需要解釋一下,可以根據自己的需求修改:
- Movement Type指的是滑塊拖動到邊界時的情況(Unrestricted使得滑塊能無限拖拽縱然沒有內容可以顯示,Elastic允許超過邊界但會觸發彈回影片,Clamped不允許拖動到沒有內容的地方);
- Viewpoint 用來指定滾動視窗大小;
- Scroll Sensitivity指的是玩家使用滑鼠滾輪拖動時的靈敏度;
- Horinzontal Scrollbar和Vertical Scrollbar分別用于系結水平滾動條和垂直滾動條;
- Visbility指定是內容不足時,Scrollbar是否顯示(Permanent表示無論是否有內容都會顯示滾動條,Auto Hide表示內容不足時隱藏滾動條);

6. UI效果
至此,控制臺界面的設計已經基本完成了,之后就是根據自己的審美,調節字體大小、顏色等引數了,最終的控制臺面板應該如下圖所示:

二、控制臺功能實作
考慮到控制臺指令的功能與控制臺界面的重繪顯示是兩個相互獨立的模塊,那么我們也要相應的設計兩個類:控制UI重繪的腳本類,通過指令執行方法的功能類,這兩個類之間存在一個雙向互動,即用戶輸入的字串通過UI類傳遞給功能類,回呼資料(報錯或警告資訊)從功能類傳遞給UI類,
1. UI控制腳本
對于UI而言,我們只需要執行4種基本功能:輸出回呼資訊,清屏,按下“上”鍵獲取前向指令,按下“下”鍵獲取后向指令,這些我們都只要通過判斷用戶的按鍵事件就可以完成了,復雜的方法則交給功能實作類來完成,ConsoleController類的完整代碼如下:
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 控制臺控制器腳本類,
/// </summary>
public class ConsoleController : MonoBehaviour
{
#region 可視變數
[SerializeField] [Tooltip("控制臺輸入框物件,")] private InputField inputField = null;
[SerializeField] [Tooltip("控制臺輸出框物件,")] private Text ouputText = null;
#endregion
#region 功能方法
/// <summary>
/// 第一幀呼叫之前觸發,
/// </summary>
private void Start()
{
inputField.ActivateInputField();
}
/// <summary>
/// 在幀重繪時觸發,
/// </summary>
private void Update()
{
string input = inputField.text; // 獲取輸入文本
// 按下回車輸入指令
if (Input.GetKeyDown(KeyCode.Return))
{
if (!input.Equals(""))
{
ouputText.text += ">>" + input + "\n";
string output = Console.Input(input);
if (output != null)
{
// 回呼資訊為cls時清空控制臺面板內容
if (output.Equals("cls"))
ouputText.text = "";
else
ouputText.text += output + "\n";
}
inputField.text = "";
}
}
// 按下上跳轉到上一條指令
else if (Input.GetKeyDown(KeyCode.UpArrow))
inputField.text = Console.Last();
// 按下下跳轉到下一條指令
else if (Input.GetKeyDown(KeyCode.DownArrow))
inputField.text = Console.Next();
inputField.ActivateInputField();
}
#endregion
}
2. 功能實作類
在UI類當中,我們的部分功能直接呼叫了幾個方法來執行,這部分方法就是功能實作類里需要進行封裝的,
- 回呼資訊輸出,封裝一個Input()方法,通過switch來選擇執行的內容:
/// <summary>
/// 向控制臺輸入指令,
/// </summary>
/// <param name="input">指令字串,</param>
/// <returns>回呼資訊,</returns>
public static string Input(string input)
{
// 分割字串獲取引數串列
List<string> args = new List<string>(input.Split(' '));
consoleHistory.Add(input);
position = consoleHistory.Count;
// 控制與回呼
string output = null;
switch (args[0])
{
// 各種指令內容
default:
output = "No such command.";
break;
}
return output;
}
- 歷史指令選擇,定義一個字串串列用來記錄歷史值,定義一個int型作為字串串列的指標,用來記錄當前指向的指令內容,封裝一個Next()和Last()方法改變指標位置:
/// <summary>
/// 獲取控制臺上一條歷史記錄,
/// </summary>
/// <returns>上一條指令欄位,</returns>
public static string Last()
{
if (position == -1)
return null;
position -= 1;
if (position < 0)
position = 0;
return consoleHistory[position];
}
/// <summary>
/// 獲取控制臺下一條歷史記錄,
/// </summary>
/// <returns>下一條指令欄位,</returns>
public static string Next()
{
if (position == -1)
return null;
position += 1;
if (position >= consoleHistory.Count)
position = consoleHistory.Count - 1;
return consoleHistory[position];
}
綜上所述,Console類內完整的代碼如下:
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 控制臺靜態類,用于程式內部除錯,
/// </summary>
public static class Console
{
# region 指令串列
private static readonly string[] command =
{
"help",
"cls",
"test"
};
# endregion
# region 靜態成員變數
private static int position = -1; // 當前讀取歷史記錄的位置
private static List<string> consoleHistory = new List<string>(); // 控制臺歷史記錄
# endregion
# region 靜態公有方法
/// <summary>
/// 向控制臺輸入指令,
/// </summary>
/// <param name="input">指令字串,</param>
/// <returns>回呼資訊,</returns>
public static string Input(string input)
{
// 分割字串獲取引數串列
List<string> args = new List<string>(input.Split(' '));
consoleHistory.Add(input);
position = consoleHistory.Count;
// 控制與回呼
string output = null;
switch (args[0])
{
// 幫助
case "help":
output = Show();
break;
// 清空控制臺
case "cls":
output = Clear();
break;
// 測驗
case "test":
output = Test();
break;
// 錯誤指令
default:
output = "No such command.";
break;
}
return output;
}
/// <summary>
/// 獲取控制臺上一條歷史記錄,
/// </summary>
/// <returns>上一條指令欄位,</returns>
public static string Last()
{
if (position == -1)
return null;
position -= 1;
if (position < 0)
position = 0;
return consoleHistory[position];
}
/// <summary>
/// 獲取控制臺下一條歷史記錄,
/// </summary>
/// <returns>下一條指令欄位,</returns>
public static string Next()
{
if (position == -1)
return null;
position += 1;
if (position >= consoleHistory.Count)
position = consoleHistory.Count - 1;
return consoleHistory[position];
}
#endregion
#region 靜態私有方法
/// <summary>
/// 顯示全部控制臺命令,
/// </summary>
/// <returns>回呼資訊,</returns>
private static string Show()
{
string output = null;
for (int i = 0; i < command.Length; i++)
{
output += command[i];
if (i != command.Length - 1)
output += "\n";
}
return output;
}
/// <summary>
/// 清空控制臺記錄,
/// </summary>
/// <returns>回呼資訊,</returns>
private static string Clear()
{
position = -1;
consoleHistory.Clear();
return "cls";
}
#endregion
#region 控制臺方法
/// <summary>
/// 測驗方法,
/// </summary>
/// <returns>回呼資訊,</returns>
private static string Test()
{
GameObject gameObject = Resources.Load("Test") as GameObject;
if (gameObject)
{
GameObject.Instantiate(gameObject);
return "Object has been generated.";
}
return "There have no such object.";
}
#endregion
}
3. 系結UI控制元件
在Console Panel中添加組件Console Controller,并系結輸入輸出物件:

三、控制臺的顯示和隱藏
前面兩節講述的是控制臺內部的設計,這一節要講的是,控制控制臺的顯示與隱藏,為了使類之間低耦合,方便復用與移植,我們設計兩個小類:FloatPanel類作為組件系結在Console Panel物件上,用于控制當前物件的隱藏與顯示,GameController類作為組件系結的全域的事件物件上,用于系結用戶的鍵盤輸入事件,這兩個類的完整代碼分別如下:
using UnityEngine;
/// <summary>
/// 懸浮面板組件類,
/// </summary>
public class FloatPanel : MonoBehaviour
{
# region 功能方法
/// <summary>
/// 第一幀呼叫之前觸發,
/// </summary>
private void Start()
{
gameObject.SetActive(false); // 初始化為隱藏
}
/// <summary>
/// 顯示當前物件,
/// </summary>
public void Show()
{
gameObject.SetActive(true);
}
/// <summary>
/// 隱藏當前物件,
/// </summary>
public void Hide()
{
gameObject.SetActive(false);
}
# endregion
}
using UnityEngine;
public class GameController : MonoBehaviour
{
#region 可視變數
[SerializeField] [Tooltip("控制臺物件,")] private ConsoleController consoleController = null;
#endregion
#region 基礎私有方法
/// <summary>
/// 在幀重繪時觸發,
/// </summary>
private void Update()
{
// 按下打開控制臺
if (Input.GetKeyUp(KeyCode.Tab))
consoleController.gameObject.GetComponent<FloatPanel>().Show();
// 按下退出控制臺
else if (Input.GetKeyUp(KeyCode.Escape))
consoleController.gameObject.GetComponent<FloatPanel>().Hide();
}
#endregion
}
將FloatPanel組件添加在Console Panel上,將GameController添加在EventSystem上,并分別系結物件:

測驗
按下Table鍵,打開控制臺,輸入“test”指令后,就會輸出一段回呼資訊,并在世界坐標系中創建一個新的Prefab物件實作對游戲內容的修改,出現如下圖的效果,就表示成功了,之后只需要在Console類中添加新的對應指令就可以完成自定義的控制臺功能了,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/172478.html
標籤:其他
