游戲開發中常用的設計模式

目錄

  • 前言
  • 一、工廠模式
  • 二、單例模式
  • 三、觀察者模式
    • 觀察者模式的優勢
  • 四、狀態模式
    • 狀態模式的優勢
  • 五、策略模式
    • 策略模式的優勢
    • 策略模式與狀態模式有什么區別呢?
  • 六、組合模式
  • 七、命令模式
  • 八、裝飾器模式

前言

本文介紹了游戲開發中常用的設計模式,如工廠模式用于創建對象,單例模式確保全局唯一,觀察者模式實現對象間事件通知,狀態模式管理對象狀態轉換,策略模式提供行為選擇,組合模式構建復雜對象結構,命令模式分離操作與執行,裝飾模式動態擴展功能。

  1. 單例模式:用于確保在游戲中只存在一個實例,例如游戲管理器(Game Manager)或資源管理器(Resource Manager)。
  2. 工廠模式:用于創建對象實例,例如創建不同類型的敵人(Enemy)或武器(Weapon)。
  3. 觀察者模式:用于實現對象間的事件通知,例如實現角色(Character)與任務(Quest)的交互。
  4. 狀態模式:用于管理游戲中對象的狀態轉換,例如角色在游戲中的狀態(生命值、能量等)。
  5. 策略模式:用于實現不同的算法和行為,例如實現不同的AI(Artificial Intelligence)策略。
  6. 組合模式:用于創建和管理游戲中的復雜對象結構,例如實現游戲中的菜單(Menu)或場景(Scene)。
  7. 命令模式:用于將操作(操作)與其執行分離,例如實現游戲中的鍵盤快捷鍵。
  8. 裝飾器模式:通過創建一個包裝對象,即裝飾器,來包裹真正的對象,并且在保持接口的前提下,為它提供額外的功能。

一、工廠模式

工廠模式是一種常用的設計模式,用于創建對象,它能夠隱藏創建對象的復雜性,并且使代碼更加靈活。在游戲開發中,工廠模式通常用于創建游戲對象、敵人、道具等。

//首先我們定義一個接口,表示我們要創建的對象:
public interface IGameObject
{void Update();
}//創建具體的游戲對象類:
public class Player : IGameObject
{public void Update(){Console.WriteLine("Player is updating.");}
}public class Enemy : IGameObject
{public void Update(){Console.WriteLine("Enemy is updating.");}
}//創建一個工廠類,用于創建游戲對象
public class GameObjectFactory
{public IGameObject CreateGameObject(string type){switch (type){case "Player":return new Player();case "Enemy":return new Enemy();default:throw new ArgumentException($"Invalid game object type: {type}");}}
}//使用工廠類來創建游戲對象:
GameObjectFactory factory = new GameObjectFactory();IGameObject player = factory.CreateGameObject("Player");
player.Update();IGameObject enemy = factory.CreateGameObject("Enemy");
enemy.Update();

二、單例模式

單例模式是一種常用的設計模式,用于確保一個類只有一個實例,并提供全局訪問點。在游戲開發中,單例模式通常用于管理全局狀態、資源池等。

public class GameManager
{private static GameManager _instance;// 私有構造函數,確保只能在類內部創建實例private GameManager(){// 初始化游戲管理器Console.WriteLine("GameManager initialized.");}// 全局訪問點public static GameManager Instance{get{if (_instance == null){_instance = new GameManager();///懶漢式}return _instance;}}// 游戲管理器的功能public void StartGame(){Console.WriteLine("Game started.");}
}
GameManager gameManager = GameManager.Instance;gameManager.StartGame(); // Output: "Game started."GameManager gameManager2 = GameManager.Instance; // 和 gameManager 引用同一個對象

三、觀察者模式

觀察者模式在游戲開發中通常用于紅點系統,實現觀察者模式時要注意具體目標對象和具體觀察者對象之間不能直接調用,否則將使兩者之間緊密耦合起來,這違反了面向對象的設計原則。
觀察者模式的主要角色如下:

  • 抽象主題(Subject)角色:也叫抽象目標類,它提供了一個用于保存觀察者對象的聚集類和增加、刪除觀察者對象的方法,以及通知所有觀察者的抽象方法。
  • 具體主題(Concrete Subject)角色:也叫具體目標類,它實現抽象目標中的通知方法,當具體主題的內部狀態發生改變時,通知所有注冊過的觀察者對象。
  • 抽象觀察者(Observer)角色:它是一個抽象類或接口,它包含了一個更新自己的抽象方法,當接到具體主題的更改通知時被調用。
  • 具體觀察者(Concrete Observer)角色:實現抽象觀察者中定義的抽象方法,以便在得到目標的更改通知時更新自身的狀態。
using System.Collections.Generic;
using UnityEngine;//抽象類 觀察者
public interface Observer
{void response(); //反應
}//被觀察者
public class ConcreteSubject
{public static ConcreteSubject _instance = null;protected List<Observer> observers = new List<Observer>();public void Init(){}public static ConcreteSubject Instance(){if (_instance == null){_instance = new ConcreteSubject();}return _instance;}//增加觀察者方法public void add(Observer observer){observers.Add(observer);}//刪除觀察者方法public void remove(Observer observer){observers.Remove(observer);}public void notifyObserver(){Debug.Log("具體目標發生改變...");foreach(Observer obs in observers){obs.response();}}
}//具體觀察者1
public class ConcreteObserver1 : MonoBehaviour , Observer
{private void Start(){ConcreteSubject.Instance().add(this);}public void response(){Debug.Log("具體觀察者1作出反應!");}private void OnDestroy(){ConcreteSubject.Instance().remove(this);}
}//具體觀察者2
public class ConcreteObserver2 : MonoBehaviour, Observer
{private void Start(){ConcreteSubject.Instance().add(this);}public void response(){Debug.Log("具體觀察者2作出反應!");}private void OnDestroy(){ConcreteSubject.Instance().remove(this);}}

觀察者模式的優勢

  1. 松散耦合:觀察者模式允許構建松散耦合的類關系,這在游戲開發中非常重要,因為它可以降低系統各部分之間的耦合度。
  2. 提高系統的靈活性和可維護性:觀察者模式不僅能夠降低系統各部分之間的耦合度,還能提高系統的靈活性和可維護性。
  3. 解耦和事件驅動:觀察者模式特別適用于需要響應UI事件或進行成就系統設計的場景,它允許完全解耦控制邏輯和UI事件處理。

四、狀態模式

狀態模式(State Pattern)是一種行為設計模式,它允許一個對象在其內部狀態改變時改變其行為。在游戲開發中,狀態模式常用于實現角色的不同行為狀態切換,例如玩家角色的行走、奔跑、跳躍、攻擊等不同狀態。

// 定義抽象狀態類
public abstract class CharacterState
{protected Character character;public void SetCharacter(Character _character){this.character = _character;}// 抽象方法,子類需要實現具體行為public abstract void Update();
}// 具體狀態類:IdleState
public class IdleState : CharacterState
{public override void Update(){Debug.Log("角色處于閑置狀態");// 檢查是否應該轉換到其他狀態,如按下移動鍵則切換至MoveStateif (Input.GetKey(KeyCode.W)){character.ChangeState(new MoveState());}}
}// 具體狀態類:MoveState
public class MoveState : CharacterState
{public override void Update(){Debug.Log("角色正在移動");// 檢查是否應返回閑置狀態或切換至其他狀態if (!Input.GetKey(KeyCode.W)){character.ChangeState(new IdleState());}}
}// 角色類持有當前狀態并處理狀態切換
public class Character : MonoBehaviour
{private CharacterState currentState;public void ChangeState(CharacterState newState){if (currentState != null){currentState.SetCharacter(null);}currentState = newState;currentState.SetCharacter(this);}void Update(){currentState.Update();}
}

狀態模式的優勢

  1. 封裝狀態轉換:狀態模式將狀態轉換的邏輯封裝到狀態類內部,使得狀態之間的切換變得明確和集中。
  2. 簡化復雜條件邏輯:通過將不同狀態的行為分割開來,狀態模式減少了對象間的相互依賴,提高了可維護性和可擴展性。
  3. 清晰的狀態管理:特別是在Unity引擎中,狀態模式幫助游戲場景的切換和管理變得更加清晰。

五、策略模式

如何在Unity中實現策略模式以優化角色行為和AI策略?

在Unity中實現策略模式以優化角色行為和AI策略,可以按照以下步驟進行:

  1. 定義策略類:首先,將不同的行為或算法封裝成獨立的類(策略)。每個策略類代表一種特定的行為或算法。例如,可以為角色攻擊、移動、防御等行為分別創建一個策略類。
  2. 使用接口或抽象類:為了使策略類之間可以互相替換,建議使用接口或抽象類來定義每種策略需要實現的方法。這樣可以確保所有策略類都遵循相同的協議。
  3. 動態選擇和切換策略:在運行時根據需要動態選擇和切換不同的策略。這可以通過檢查游戲中的某些條件或事件來實現。例如,當敵人接近玩家時,可以選擇攻擊策略;當敵人遠離玩家時,可以選擇逃跑策略。
  4. 避免條件語句過多:使用策略模式可以有效減少代碼中的條件語句,從而避免代碼變得臃腫和難以維護。通過將具體算法實現從具體的業務邏輯中分離出來,可以讓算法的變化獨立于使用算法的客戶端。
  5. 示例代碼:以下是一個簡單的示例代碼,展示了如何在Unity中實現策略模式:
// 攻擊策略類
public class AttackStrategy : IStrategy
{public void PerformAction(){Debug.Log("Attacking");}
}// 移動策略類
public class MoveStrategy : IStrategy
{public void PerformAction(){Debug.Log("Moving");}
}// 防御策略類
public class DefenseStrategy : IStrategy
{public void PerformAction(){Debug.Log("防御");}
}// 策略選擇器
public class StrategySelector
{private IStrategy _strategy;public void SetStrategy(IStrategy strategy){_strategy = strategy;}public void PerformAction(){_strategy.PerformAction();}
}// 主腳本
public class Player : MonoBehaviour
{private StrategySelector _selector;void Start(){_selector = new StrategySelector();_selector.SetStrategy(new AttackStrategy());_selector.PerformAction(); // 輸出:Attacking// 根據條件切換策略if (playerHealth < 50){_selector.SetStrategy(new DefenseStrategy());_selector.PerformAction(); // 輸出:防御}}
}

策略模式的優勢

  1. 算法獨立性:策略模式使得算法可以獨立于使用它的客戶端變化。這意味著可以根據不同的游戲狀態、角色類型或玩家選擇,動態地改變游戲的行為。
  2. 靈活性和多態性:通過將算法封裝在獨立的策略類中,策略模式提供了一種更靈活的方式來處理多態行為。這使得算法的變化不會影響到使用這些算法的客戶。
  3. 簡化復雜條件邏輯:策略模式能夠減少對象間的相互依賴,并且將與特定狀態相關的行為局部化到一個狀態中,從而滿足單一職責原則。游戲開發設計模式之策略模式

策略模式與狀態模式有什么區別呢?

現在我們知道,狀態模式和策略模式的結構是相似的,但它們的意圖不同。讓我們重溫一下它們的主要不同之處:

  1. 策略模式封裝了一組相關算法,它允許Client在運行時使用可互換的行為;狀態模式幫助一個類在不同的狀態顯示不同的行為。
  2. 狀態模式封裝了對象的狀態,而策略模式封裝算法或策略。因為狀態是跟對象密切相關的,它不能被重用;而通過從Context中分離出策略或算法,我們可以重用它們。
  3. 在狀態模式中,每個狀態通過持有Context的引用,來實現狀態轉移;但是每個策略都不持有Context的引用,它們只是被Context使用。
  4. 策略實現可以作為參數傳遞給使用它的對象,例如Collections.sort(),它的參數包含一個Comparator策略。另一方面,狀態是Context對象自己的一部分,隨著時間的推移,Context對象從一個狀態轉移到另一個狀態。
  5. 雖然它們都符合OCP原則,策略模式也符合SRP原則(單一職責原則),因為每個策略都封裝自己的算法,且不依賴其他策略。一個策略的改變,并不會導致其他策略的變化。
  6. 另一個理論上的不同:策略模式定義了對象“怎么做”的部分。例如,排序對象怎么對數據排序。狀態模式定義了對象“是什么”和“什么時候做”的部分。例如,對象處于什么狀態,什么時候處在某個特定的狀態。
  7. 狀態模式中很好的定義了狀態轉移的次序;而策略模式并無此需要:Client可以自由的選擇任何策略。
  8. 一些常見的策略模式的例子是封裝算法,例如排序算法,加密算法或者壓縮算法。如果你看到你的代碼需要使用不同類型的相關算法,那么考慮使用策略模式吧。而識別何時使用狀態模式是很簡單的:如果你需要管理狀態和狀態轉移,但不想使用大量嵌套的條件語句,那么就是它了。

最后但最重要的一個不同之處是,策略的改變由Client完成;而狀態的改變,由Context或狀態自己。
參考文檔:設計模式之:狀態模式和策略模式的區別

六、組合模式

組合模式一般適用于對象的部分-整體層次分明。比如游戲中的文件夾目錄結構的管理。

樹狀結構圖
在這里插入圖片描述

using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 組合模式
/// </summary>
public class CompositeMode : MonoBehaviour
{private void Start(){INode root = new CompositeNode("Character");INode leftHand = new LeafNode("LeftHand");INode body = new CompositeNode("Body");INode rightHand = new LeafNode("RightHand");root.AddChildNode(leftHand, body, rightHand);INode leftFoot = new LeafNode("LeftFoot");INode rightFoot = new LeafNode("RightFoot");body.AddChildNode(leftFoot, rightFoot);ShowAllNode(root);}/// <summary>/// 顯示節點和其所有子節點/// </summary>private void ShowAllNode(INode node){Debug.Log(node.Name);List<INode> childNodeList = node.ChildNodeList;if (node == null || childNodeList == null){return;}foreach (INode item in childNodeList){ShowAllNode(item);}}
}/// <summary>
/// 節點抽象類
/// </summary>
public abstract class INode
{protected string mName;public string Name { get { return mName; } }protected List<INode> mChildNodeList;public List<INode> ChildNodeList { get { return mChildNodeList; } }public INode(string name){mChildNodeList = new List<INode>();mName = name;}//添加、移除、獲取子節點public abstract void AddChildNode(INode node);//如果我們想可以一次添加多個子節點,就可以這樣寫public abstract void AddChildNode(params INode[] nodes);public abstract void RemoveChildNode(INode node);public abstract INode GetChildNode(int index);}
/// <summary>
/// 葉子節點
/// </summary>
public class LeafNode : INode
{public LeafNode(string name):base(name){}//葉子節點無子節點public override void AddChildNode(INode node){throw new System.NotImplementedException();}public override void AddChildNode(params INode[] nodes){throw new System.NotImplementedException();}public override INode GetChildNode(int index){throw new System.NotImplementedException();}public override void RemoveChildNode(INode node){throw new System.NotImplementedException();}
}
/// <summary>
/// 非葉子節點
/// </summary>
public class CompositeNode : INode
{public CompositeNode(string name): base(name){}public override void AddChildNode(INode node){mChildNodeList.Add(node);}public override void AddChildNode(params INode[] nodes){foreach (INode node in nodes){mChildNodeList.Add(node);}}public override void RemoveChildNode(INode node){if (mChildNodeList.Contains(node) == false){Debug.LogError(node + "在子節點中不存在");return;}mChildNodeList.Remove(node);}public override INode GetChildNode(int index){if ((index>=0 && index<mChildNodeList.Count)==false){Debug.LogError(index + "下標不存在");return null;}return mChildNodeList[index];}
}

七、命令模式

八、裝飾器模式

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/66483.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/66483.shtml
英文地址,請注明出處:http://en.pswp.cn/web/66483.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++并發編程之異常安全性增強

在并發編程中&#xff0c;異常安全是一個非常重要的方面&#xff0c;因為并發環境下的錯誤處理比單線程環境更加復雜。當多個線程同時執行時&#xff0c;異常不僅可能影響當前線程&#xff0c;還可能影響其他線程和整個程序的穩定性。以下是一些增強并發程序異常安全性的方法&a…

各語言鏡像配置匯總

鏡像配置匯總 Nodejs [ npm ]Python [ pip ] Nodejs [ npm ] // # 記錄日期&#xff1a;2025-01-20// 查詢當前使用的鏡像 npm get registry// 設置淘寶鏡像 npm config set registry https://registry.npmmirror.com/// 恢復為官方鏡像 npm config set registry https://regi…

Navicat Premium 數據可視化

工作區&#xff0c;數據源以及圖表 數據可視化是使用可視化組件&#xff08;例如圖表&#xff0c;圖形和地圖&#xff09;的信息和數據的圖形表示。 數據可視化工具提供了一種可訪問的方式&#xff0c;用于查看和理解數據中的趨勢&#xff0c;異常值和其他模式。 在Navicat中&…

linux通過web向mac遠程傳輸字符串,mac收到后在終端中直接打印。

要通過Web從Linux向Mac遠程傳輸字符串&#xff0c;并在Mac的終端中直接打印&#xff0c;可以使用以下方法。這里假設Linux作為服務器&#xff0c;Mac作為客戶端。 方法 1&#xff1a;使用Python的HTTP服務器 在Linux上啟動一個簡單的HTTP服務器&#xff0c;Mac通過curl獲取字符…

【系統分享01】Python+Vue電影推薦系統

大家好&#xff0c;作為一名老程序員&#xff0c;今天我將帶你一起走進電影推薦系統的世界&#xff0c;分享如何利用 Django REST Framework 和 Vue 搭建一套完整的電影推薦系統&#xff0c;結合 協同過濾算法&#xff0c;根據用戶評分與影片喜好&#xff0c;精準推送用戶可能喜…

Spring Boot+Vue

Spring BootVue 前后端分離是一種非常流行且高效的開發模式&#xff0c;以下是關于其相關方面的詳細介紹&#xff1a; 前端&#xff08;Vue&#xff09;部分 ? 項目搭建 ? 使用 Vue CLI 創建項目&#xff0c;它提供了豐富的插件和配置選項&#xff0c;能夠快速生成項目基礎…

第十四章:計算機新技術

文章目錄&#xff1a; 一&#xff1a;云計算 二&#xff1a;大數據 三&#xff1a;物聯網 四&#xff1a;人工智能 五&#xff1a;移動網絡與應用 六&#xff1a;電子商務 七&#xff1a;虛擬實現 八&#xff1a;區塊鏈 一&#xff1a;云計算 概念云基于?絡&#xff0…

【大數據2025】MapReduce

MapReduce 基礎介紹 起源與發展&#xff1a;是 2004 年 10 月谷歌發表的 MAPREDUCE 論文的開源實現&#xff0c;最初用于大規模網頁數據并行處理&#xff0c;現成為 Hadoop 核心子項目之一&#xff0c;是面向批處理的分布式計算框架。基本原理&#xff1a;分為 map 和 reduce …

主從復制

簡述mysql 主從復制原理及其工作過程&#xff0c;配置一主兩從并驗證。 主從原理&#xff1a;MySQL 主從同步是一種數據庫復制技術&#xff0c;它通過將主服務器上的數據更改復制到一個或多個從服務器&#xff0c;實現數據的自動同步。 主從同步的核心原理是將主服務器上的二…

【博客之星評選】2024年度前端學習總結

故事的開端...始于2024年第一篇前端技術博客 那故事的終末...也該結束于陪伴了我一整年的前端知識了 踏入 2025 年&#xff0c;滿心激動與自豪&#xff0c;我成功闖進了《2024 年度 CSDN 博客之星總評選》的 TOP300。作為一名剛接觸技術寫作不久的萌新&#xff0c;這次能走到這…

Ubuntu 24.04 LTS 服務器折騰集

目錄 Ubuntu 更改軟件源Ubuntu 系統語言英文改中文windows 遠程鏈接 Ubuntu 圖形界面Windows 通過 openssh 連接 UbuntuUbuntu linux 文件權限Ubuntu 空閑硬盤掛載到 文件管理器的 other locationsUbuntu 開啟 SMB 服務&#xff0c;并通過 windows 訪問Ubuntu安裝Tailscale&am…

《TikTok停服:信息安全警鐘長鳴》

一、TikTok 停服事件回顧 2025 年 1 月 18 日晚&#xff0c;TikTok 通知美國用戶&#xff0c;由于美官方禁令于 19 日起生效&#xff0c;TikTok 軟件將暫時對用戶停止服務。這一消息猶如一顆重磅炸彈&#xff0c;瞬間在全球范圍內掀起軒然大波。美國用戶對此猝不及防&#xff0…

1166 Summit (25)

A summit (峰會) is a meeting of heads of state or government. Arranging the rest areas for the summit is not a simple job. The ideal arrangement of one area is to invite those heads so that everyone is a direct friend of everyone. Now given a set of tenta…

圖論DFS:黑紅樹

我的個人主頁 {\large \mathsf{{\color{Red} 我的個人主頁} } } 我的個人主頁 往 {\color{Red} {\Huge 往} } 往 期 {\color{Green} {\Huge 期} } 期 文 {\color{Blue} {\Huge 文} } 文 章 {\color{Orange} {\Huge 章}} 章 DFS 算法&#xff1a;記憶化搜索DFS 算法&#xf…

C++,設計模式,【目錄篇】

文章目錄 1. 簡介2. 設計模式的分類2.1 創建型模式&#xff08;Creational Patterns&#xff09;&#xff1a;2.2 結構型模式&#xff08;Structural Patterns&#xff09;&#xff1a;2.3 行為型模式&#xff08;Behavioral Patterns&#xff09;&#xff1a; 3. 使用設計模式…

掌握提示詞工程:大模型使用入門指南

掌握提示詞工程&#xff1a;大模型使用入門指南 近年來&#xff0c;大語言模型&#xff08;如 GPT、Claude 等&#xff09;的強大能力令人印象深刻&#xff0c;但要想充分發揮這些模型的潛力&#xff0c;僅僅依靠其預訓練能力還不夠。提示詞工程&#xff08;Prompt Engineerin…

如何使用 useMemo 和 memo 優化 React 應用性能?

使用 useMemo 和 memo 優化 React 應用性能 在構建復雜的 React 應用時&#xff0c;性能優化是確保應用流暢運行的關鍵。React 提供了多種工具來幫助開發者優化組件的渲染和計算邏輯&#xff0c;其中 useMemo 和 memo 是兩個非常有用的 Hook。本文將詳細介紹這兩個工具的使用方…

Agent Laboratory: Using LLM Agents as Research Assistants 論文簡介

加速機器學習研究的智能實驗室——Agent Laboratory 1. 引言 隨著人工智能技術的飛速發展&#xff0c;機器學習領域正以前所未有的速度推進科學發現和技術創新。然而&#xff0c;傳統的科學研究模式往往受到時間、資源和專業知識限制&#xff0c;阻礙了研究者們探索新想法的能…

【網絡協議】【http】【https】ECDHE-TLS1.2

【網絡協議】【http】【https】ECDHE-TLS1.2 ECDHE算法 1.客戶端和服務器端事先確定好使用哪種橢圓曲線&#xff0c;和曲線上的基點G&#xff0c;這兩個參數都是公開的&#xff0c; 雙方各自隨機生成一個隨機數作為私鑰d&#xff0c;并與基點 G相乘得到公鑰Q(QdG)&#xff0c…

規避路由沖突

路由沖突是指在網絡中存在兩個或多個路由器在進行路由選擇時出現矛盾&#xff0c;導致網絡數據包無法正確傳輸&#xff0c;影響網絡的正常運行。為了規避路由沖突&#xff0c;可以采取以下措施&#xff1a; 一、合理規劃IP地址 分配唯一IP&#xff1a;確保每個設備在網絡中都有…