日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

在Unity实现游戏命令模式

發布時間:2024/8/26 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 在Unity实现游戏命令模式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文由開發者Najmm Shora介紹在Unity中通過使用命令模式實現回放功能,撤銷功能和重做功能。我們可以使用該方法來強化自己的策略類游戲。

你是否想知道《超級食肉男孩》(Super Meat Boy)等游戲是如何實現回放功能的?其中一種方法是完全按照玩家發出的命令執行輸入,這意味著輸入需要以某種方式存儲。

命令模式可用于執行此操作和其他操作。如果你希望在策略游戲里實現撤銷和重做功能,命令模式也非常實用。
?


在本教程中,我們將使用C#實現命令模式,然后使用命令模式來遍歷3D迷宮中的機器人角色。

我們會學習到以下內容:
?

  • 命令模式的基礎知識。
  • 實現命令模式的方法。
  • 對輸入命令進行排隊,并推遲執行。
  • 在執行前,撤銷和重做已發出的命令。



本教程使用Unity 2019.1和C# 7,學習本文你需要熟悉Unity的使用,并且對C#有一定的了解。

學習準備

本教程將為你提供項目文件和素材,請發送[命令模式]到微信后臺,獲取下載地址。

下載完成項目素材后,請解壓文件,并在Unity中打開Starter項目。然后打開RW/Scenes文件夾,打開主場景。

如下圖所示,場景中有一個迷宮和機器人,左側有一個顯示指令的終端UI。地面的是一個網格,當玩家在迷宮中移動機器人時,這些網格將有助于玩家進行觀察。
?


場景中最有趣的部分是Bot對象,它代表游戲中的機器人,我們在層級窗口單擊選中該對象。
?


在檢視窗口查看該對象,可以看見它帶有Bot組件,我們將在發出輸入命令時使用該組件。
?


理解Bot的邏輯

我們打開RW/Scripts文件夾,在代碼編輯器打開Bot腳本。我們不必了解Bot腳本的作用,但要了解其中的Move方法和Shoot方法的使用。

我們發現,Move方法會接收一個類型為CardinalDirection的輸入參數。CardinalDirection是一個枚舉,類型為CardinalDirection的枚舉對象可以為Up,Down,Right或Left。

根據所選的CardinalDirection不同,機器人會在網格上朝著對應方向移動一個網格。
?


Shoot方法可以讓機器人發射炮彈,摧毀黃色的墻體,但對其它墻體毫無作用。
?


現在查看ResetToLastCheckpoint方法,我們對迷宮進行觀察。在迷宮中,有一些點被稱為檢查點。為了通過迷宮,機器人應該到達綠色檢查點。
?


在機器人穿過新檢查點時,該點會成為機器人的最后檢查點。ResetToLastCheckpoint方法會重置機器人的位置到最后檢查點。
?


什么是命令設計模式

命令模式是《設計模式:可復用面向對象軟件的基礎》(Design Patterns: Elements of Reusable Object-Oriented Software)一書中介紹的23種設計模式之一。

書中寫道:命令模式把請求封裝為對象,從而允許我們使用不同的請求,隊列或日志請求,來參數化處理其它對象,并支持可撤銷的操作。

這么表達或許難以理解,下面我們詳細講解一下。

封裝:方法調用封裝為對象的過程。
?


參數化其它對象:封裝的方法可以根據輸入參數來處理多個對象。

請求的隊列:得到的“命令”可以在執行前和其它命令一起存儲。
?

命令隊列


“Undoable”(可撤銷)在此不是指無法實現的東西,而是指可以通過撤銷功能恢復的操作。那么這些內容怎么用代碼表示呢?

簡單來說,Command類會有Execute方法,該方法可以接收一個名為Receiver的對象作為輸入參數。因此,Execute方法會由Command類進行封裝。

Command類的多個實例可以作為常規對象來傳遞,這表示它們可以存儲在數據結構中,例如:隊列,棧等。

為了執行命令,Execute方法需要進行調用。觸發執行過程的類叫作Invoker。

我們的項目中已包含一個名叫BotCommand的空類。下面我們將完成要求,讓Bot對象可以使用命令模式執行動作。


移動機器人Bot對象


實現命令模式

首先,打開RW/Scripts文件夾,在編輯器打開BotCommand腳本,并加入下面的代碼。

  • //1
  • ? ? private readonly string commandName;
  • ? ? //2
  • ? ? public BotCommand(ExecuteCallback executeMethod, string name)
  • ? ? {
  • ? ?? ???Execute = executeMethod;
  • ? ?? ???commandName = name;
  • 復制代碼


    代碼解讀:
    ?

    • commandName變量用于存儲用戶可以理解的命令名稱。
    • BotCommand構造函數會接收一個函數和一個字符串,它幫助我們設置Command對象的Execute方法和名稱。
    • ExecuteCallback委托會定義封裝方法的類型。封裝方法會返回void類型,接收類型為Bot對象作為輸入參數。
    • Execute屬性會引用封裝方法,我們要使用它來調用封裝方法。
    • ToString方法會被重寫,返回commandName字符串,該方法主要在UI中使用。



    保存改動,現在我們已經實現了命令模式。

    創建命令

    我們從RW/Scripts文件夾中打開BotInputHandler腳本。

    我們將創建BotCommand的5個實例,這些實例會分別封裝方法,從而讓Bot對象向上、下、左、右移動,以及讓機器人發射炮彈。

    添加下列代碼到BotCommand類中。

  • ? ?//1
  • ? ? private static readonly BotCommand MoveUp =
  • ? ?? ???new BotCommand(delegate (Bot bot) { bot.Move(CardinalDirection.Up); }, "moveUp");
  • ? ? //2
  • ? ? private static readonly BotCommand MoveDown =
  • ? ?? ???new BotCommand(delegate (Bot bot) { bot.Move(CardinalDirection.Down); }, "moveDown");
  • ? ? //3
  • ? ? private static readonly BotCommand MoveLeft =
  • ? ?? ???new BotCommand(delegate (Bot bot) { bot.Move(CardinalDirection.Left); }, "moveLeft");
  • ? ? //4
  • ? ? private static readonly BotCommand MoveRight =
  • ? ?? ???new BotCommand(delegate (Bot bot) { bot.Move(CardinalDirection.Right); }, "moveRight");
  • ? ? //5
  • ? ? private static readonly BotCommand Shoot =
  • ? ?? ???new BotCommand(delegate (Bot bot) { bot.Shoot(); }, "shoot");
  • 復制代碼


    在每個實例中,都有一個匿名方法傳到構造函數。該匿名方法會封裝在相應命令對象之中,每個匿名方法的簽名都符合ExecuteCallback委托設置的要求。

    此外,構造函數的第二個參數是一個字符串,表示用于指代命令的名稱。該名稱會通過命令實例的ToString方法返回,它會在后面為UI使用。

    在前4個實例中,手機游戲出售平臺匿名方法會在Bot對象上調用Move方法。

    對于MoveUp、MoveDown、MoveLeft和MoveRight命令,傳入Move方法的參數分別是CardinalDirection.Up,CardinalDirection.Down,CardinalDirection.Left和CardinalDirection.Right,這些參數對應著Bot對象的不同移動方向。

    在第5個實例上,匿名方法在Bot對象調用Shoot方法。這將在執行該命令時,讓機器人發射炮彈。

    現在我們創建了命令,這些命令需要在用戶發出輸入時進行訪問。請將下面的代碼添加到BotInputHandler中。

  • public static BotCommand HandleInput()
  • ? ? {
  • ? ?? ???if (Input.GetKeyDown(KeyCode.W))
  • ? ?? ???{
  • ? ?? ?? ?? ?return MoveUp;
  • ? ?? ???}
  • ? ?? ???else if (Input.GetKeyDown(KeyCode.S))
  • ? ?? ???{
  • ? ?? ?? ?? ?return MoveDown;
  • ? ?? ???}
  • ? ?? ???else if (Input.GetKeyDown(KeyCode.D))
  • ? ?? ???{
  • ? ?? ?? ?? ?return MoveRight;
  • ? ?? ???}
  • ? ?? ???else if (Input.GetKeyDown(KeyCode.A))
  • ? ?? ???{
  • ? ?? ?? ?? ?return MoveLeft;
  • ? ?? ???}
  • ? ?? ???else if (Input.GetKeyDown(KeyCode.F))
  • ? ?? ???{
  • ? ?? ?? ?? ?return Shoot;
  • ? ?? ???}
  • ? ?? ???return null;
  • ? ? }
  • 復制代碼


    HandleInput方法會根據用戶的按鍵,返回單個命令實例。繼續下一步前,保存改動內容。

    使用命令

    現在我們要使用創建好的命令。打開RW/Scripts文件夾,在代碼編輯器打開SceneManager腳本。在該類中,我們會發現有UIManager類型的uiManager變量的引用。

    UIManager類為場景中的終端UI提供了實用的功能性方法。此外,Bot變量引用了附加到Bot對象的Bot組件。

    我們將下面的代碼添加給SceneManager類,替換代碼注釋//1的已有代碼。
    ?

  • //1
  • ? ? private List<BotCommand> botCommands = new List<BotCommand>();
  • ? ? private Coroutine executeRoutine;
  • ? ? //2
  • ? ? private void Update()
  • ? ? {
  • ? ?? ???if (Input.GetKeyDown(KeyCode.Return))
  • ? ?? ???{
  • ? ?? ?? ?? ?ExecuteCommands();
  • ? ?? ???}
  • ? ?? ???else
  • ? ?? ???{
  • ? ?? ?? ?? ?CheckForBotCommands();
  • ? ?? ???}? ?? ?? ?
  • ? ? }
  • ? ? //3
  • ? ? private void CheckForBotCommands()
  • ? ? {
  • ? ?? ???var botCommand = BotInputHandler.HandleInput();
  • ? ?? ???if (botCommand != null && executeRoutine == null)
  • ? ?? ???{
  • ? ?? ?? ?? ?AddToCommands(botCommand);
  • ? ?? ???}
  • ? ? }
  • ? ? //4
  • ? ? private void AddToCommands(BotCommand botCommand)
  • ? ? {
  • ? ?? ???botCommands.Add(botCommand);
  • ? ?? ???//5
  • ? ?? ???uiManager.InsertNewText(botCommand.ToString());
  • ? ? }
  • ? ? //6
  • ? ? private void ExecuteCommands()
  • ? ? {
  • ? ?? ???if (executeRoutine != null)
  • ? ?? ???{
  • ? ?? ?? ?? ?return;
  • ? ?? ???}
  • ? ?? ???executeRoutine = StartCoroutine(ExecuteCommandsRoutine());
  • ? ? }
  • ? ? private IEnumerator ExecuteCommandsRoutine()
  • ? ? {
  • ? ?? ???Debug.Log("Executing...");
  • ? ?? ???//7
  • ? ?? ???uiManager.ResetScrollToTop();
  • ? ?? ???//8
  • ? ?? ???for (int i = 0, count = botCommands.Count; i < count; i++)
  • ? ?? ???{
  • ? ?? ?? ?? ?var command = botCommands[i];
  • ? ?? ?? ?? ?command.Execute(bot);
  • ? ?? ?? ?? ?//9
  • ? ?? ?? ?? ?uiManager.RemoveFirstTextLine();
  • ? ?? ?? ?? ?yield return new WaitForSeconds(CommandPauseTime);
  • ? ?? ???}
  • ? ?? ???//10
  • ? ?? ???botCommands.Clear();
  • ? ?? ???bot.ResetToLastCheckpoint();
  • ? ?? ???executeRoutine = null;
  • ? ? }
  • 復制代碼


    保存代碼,通過使用這些代碼,我們可以在游戲視圖正常運行項目。

    運行游戲并測試命令模式
    現在要構建所有內容,在Unity編輯器按下Play按鈕。

    我們可以使用W,A,S,D按鍵輸入方向命令。輸入射擊模式時,使用F鍵。最后按下回車鍵執行命令。
    ?

    ?


    現在觀察代碼添加到終端UI的方式。命令會通過它們在UI中的名稱表示,該效果通過commandName變量實現。

    在執行前,UI會滾動到頂部,執行后的代碼行會被移除。

    詳解命令代碼

    現在我們詳解在使用命令部分添加的代碼。

    botCommands列表存儲了BotCommand實例的引用。考慮到內存,我們只可以創建5個命令實例,但有多個引用指向相同的命令。此外,executeCoroutine變量引用了ExecuteCommandsRoutine,后者會處理命令的執行過程。

    如果用戶按下回車鍵,更新檢查結果,此時它會調用ExecuteCommands,否則會調用CheckForBotCommands。

    CheckForBotCommands使用來自BotInputHandler的HandleInput靜態方法,檢查用戶是否發出輸入信息,此時會返回命令。返回的命令會傳遞到AddToCommands。然而,如果命令被執行的話,即如果executeRoutine不是空的話,它會直接返回,不把任何內容傳遞給AddToCommands。因此,用戶必須等待執行過程完成。

    AddToCommands給返回的命令實例添加了新引用,返回到botCommands。

    UIManager類的InsertNewText方法會給終端UI添加新一行文字。該行文字是作為輸入參數傳給方法的字符串。我們會在此給它傳入commandName。

    ExecuteCommands方法會啟動ExecuteCommandsRoutine。

    UIManager類的ResetScrollToTop會向上滾動終端UI,它會在執行過程開始前完成。

    ExecuteCommandsRoutine有一個for循環,它會迭代botCommands列表中的命令,通過把Bot對象傳給Execute屬性返回的方法,逐個執行這些命令。在每次執行后,我們會添加CommandPauseTimeseconds時長的暫停。

    UIManager類的RemoveFirstTextLine方法會移除終端UI里的第一行文字,只要那里仍有文字。因此,每個命令執行后,它的相應名稱會從終端UI移除。

    執行所有命令后,botCommands會清空,機器人會使用ResetToLastCheckpoint,重置到最后檢查點。接著,executeRoutine會設為null,用戶可以繼續發出更多輸入信息。


    實現撤銷和重做功能

    我們再運行一次場景,嘗試到達綠色檢查點。現在無法撤銷輸入的命令,這意味著如果犯了錯,我們無法后退,除非執行完所有命令。

    我們可以通過添加撤銷功能和重做功能來解決該問題。返回SceneManager.cs腳本,在botCommands的List聲明后添加以下變量聲明。
    ?

  • private Stack <BotCommand> undoStack = new Stack <BotCommand>();
  • 復制代碼


    undoStack變量屬于來自Collections命名空間的Stack類,它會存儲撤銷的命令引用。

    現在,我們要分別為撤銷和重做添加UndoCommandEntry和RedoCommandEntry兩個方法。在SceneManager類中,添加下面代碼到ExecuteCommandsRoutine后。
    ?

  • private void UndoCommandEntry()
  • ? ? {
  • ? ?? ???//1
  • ? ?? ???if (executeRoutine != null || botCommands.Count == 0)
  • ? ?? ???{
  • ? ?? ?? ?? ?return;
  • ? ?? ???}
  • ? ?? ???undoStack.Push(botCommands[botCommands.Count - 1]);
  • ? ?? ???botCommands.RemoveAt(botCommands.Count - 1);
  • ? ?? ???//2
  • ? ?? ???uiManager.RemoveLastTextLine();
  • ? ???}
  • ? ? private void RedoCommandEntry()
  • ? ? {
  • ? ?? ???//3`
  • ? ?? ???if (undoStack.Count == 0)
  • ? ?? ???{
  • ? ?? ?? ?? ?return;
  • ? ?? ???}
  • ? ?? ???var botCommand = undoStack.Pop();
  • ? ?? ???AddToCommands(botCommand);
  • ? ? }
  • 復制代碼


    解讀這部分代碼:
    ?

    • 如果命令正在執行,或botCommands列表是空的,UndoCommandEntry方法不執行任何操作。否則,它會把最后輸入的命令引用推送到undoStack上。這部分代碼也會從botCommands列表移除命令引用。
    • UIManager類的RemoveLastTextLine方法會移除終端UI的最后一行文字,這樣在發生撤銷時,終端UI內容符合botCommands的內容。
    • 如果undoStack為空,RedoCommandEntry不執行任何操作。否則,它會把最后的命令從undoStack移出,然后通過AddToCommands把命令添加到botCommands列表。



    現在我們添加鍵盤輸入來使用這些方法。在SceneManager類中,把Update方法的主體替換為下列代碼。
    ?

  • if (Input.GetKeyDown(KeyCode.Return))
  • ? ? {
  • ? ?? ???ExecuteCommands();
  • ? ? }
  • ? ? else if (Input.GetKeyDown(KeyCode.U)) //1
  • ? ? {
  • ? ?? ???UndoCommandEntry();
  • ? ? }
  • ? ? else if (Input.GetKeyDown(KeyCode.R)) //2
  • ? ? {
  • ? ?? ???RedoCommandEntry();
  • ? ? }
  • ? ? else
  • ? ? {
  • ? ?? ???CheckForBotCommands();
  • ? ? }
  • 復制代碼


    現在按下U鍵會調用UndoCommandEntry方法,按下R鍵會調用RedoCommandEntry方法。

    處理邊緣情況

    現在我們快要完成該教程了,在完成前,我們要確定二件事:
    ?

    • 輸入新命令時,undoStack應該被清空。
    • 執行命令前,undoStack應該被清空。



    首先,我們給SceneManager添加一個新方法。添加下面的方法到CheckForBotCommands之后。
    ?

  • private void AddNewCommand(BotCommand botCommand)
  • ? ? {
  • ? ?? ???undoStack.Clear();
  • ? ?? ???AddToCommands(botCommand);
  • ? ? }
  • 復制代碼


    該方法會清空undoStack,然后調用AddToCommands方法。

    現在把CheckForBotCommands內的AddToCommands調用替換為下列代碼:

  • AddNewCommand(botCommand);
  • 復制代碼


    最后,復制粘貼下列代碼到ExecuteCommands方法內的if語句中,從而在執行前清空undoStack。
    ?

  • undoStack.Clear();
  • 復制代碼


    現在項目終于完成了,我們保存并構建項目。在Unity編輯器單擊Play按鈕。輸入命令,按下U鍵撤銷命令,按下R鍵恢復被撤銷的命令。

    下圖展示了讓機器人到達綠色檢查點。
    ?

    ?

    總結

    以上是生活随笔為你收集整理的在Unity实现游戏命令模式的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。