主頁 >  其他 > 漫談Entity-Component-System

漫談Entity-Component-System

2022-10-30 07:35:36 其他

原文鏈接

簡介

對于很多人來說,ECS只是一個可以提升性能的架構,但是我覺得ECS更強大的地方在于可以降低代碼復雜度,

在游戲專案開發的程序中,一般會使用OOP的設計方式讓GameObject處理自身的業務,然后框架去管理GameObject的集合,但是使用OOP的思想進行框架設計的難點在于一開始就要構建出一個清晰類層次結構,而且在開發程序中需要改動類層次結構的可能性非常大,越到開發后期對類層次結構的改動就會越困難,

經過一段時間的開發,總會在某個時間點開始引入多重繼承,實作一個又可作業、又易理解、又易維護的多重繼承類層次結構的難度通常超過其得益,因此多數游戲作業室禁止或嚴格限制在類層次結構中使用多重繼承,若非要使用多重繼承,要求一個類只能多重繼承一些 簡單且無父類的類(min-in class),例如Shape和Animator,

也就是說在大型游戲專案中,OOP并不適用于框架設計,但是也不用完全拋棄OOP,只是在很大程度上,代碼中的類不再具體地對應現實世界中的具體物件,ECS中類的語意變得更加抽象了,

ECS有一個很重要的思想:資料都放在一邊,需要的時候就去用,不需要的時候不要動,ECS 的本質就是資料和操作分離,傳統OOP思想常常會面臨一種情況,A打了B,那么到底是A主動打了B還是B被A打了,這個函式該放在哪里,但是ECS不用糾結這個問題,資料存放到Component種,邏輯直接由System接管,借著這個思想,我們可以大幅度減少函式呼叫的層次,進而縮短資料流傳遞的深度,

基本概念

Entity由多個Component組成,Component由資料組成,System由邏輯組成,

Component(組件)

Component是資料的集合,只有變數,沒有函式,但可以有getter和setter函式,Component之間不可以直接通信,

struct Component{
	//子類將會有大量變數,以供System利用
}

Entity(物體)

Entity用來代表游戲世界中任意型別的游戲物件,宏觀上Entity是一個Component實體的集合,且擁有一個全域唯一的EntityID,用于標識Entity本身,

class Entity{
	Int32 ID;
	List<Component> components;
        //通過觀察者模式將自己注冊到System可以提升System遍歷的速度,因為只需要遍歷已經注冊的entity
}

Entity需要遵循立即創建和延遲銷毀原則,銷毀放在幀末執行,因為可能會出現這樣的情況:systemA提出要在entityA所在位置創建一個特效,然后systemB認為需要銷毀entityA,如果systemB直接銷毀了entityA,那么稍后FxSystem就會拿不到entityA的位置導致特效播放失敗(你可能會問為什么不直接把entityA的位置記錄下來,這樣就不會有問題了,這里只是簡單舉個例子,不要太深究(●'?'●)),理想的表現效果應該是,播放特效后消失,

System(系統)

System用來制定游戲的運行規則,只有函式,沒有變數,System之間的執行順序需要嚴格制定,System之間不可以直接通信,

一個 System只關心某一個固定的Component組合,這個組合集合稱為tuple,

各個System的Update順序要根據具體情況設定好,System在Update時都會遍歷所有的Entity,如果一個Entity擁有該System的tuple中指定的所有Component實體,則對該Entity進行處理,

class System{
    public abstract void Update();
}

class ASystem:System{
    Tuple tuple;

    public override void Update(){
        for(Entity entity in World.entitys){
            if(entity.components中有tuple指定的所有Component實體){
                //do something for Components
            }
        }
    }
}

一個Component會被不同System區別對待,因為每個System用到的資料可能只有其中一部分,且不一定相同,

World(世界)

World代表整個游戲世界,游戲會視情況來創建一個或兩個World,通常情況下只有一個,但是守望先鋒為了做死亡回放,有兩個World,分別是liveGame和replyGame,World下面會包含所有的System實體和Entity實體,

class World{
    List<System> systems;                   //所有System
    dictionary<Int32, Entity> entitys;      //所有Entity,Int32是Entity.ID

    //由引擎幀回圈驅動
    void Update(){
        for(System sys in systems)
            sys.Update();
    }
}

由ECS架構出來的游戲世界就像是一個資料庫表,每個Entity對應一行,每個Component對應一列,打了?代表Entity擁有Component,

Component1 Component2 ... ComponentN
EntityId1 ?
EntityId2 ? ?
...
EntityIdN ? ?

單例Component

在定義一個Component時最好先搞清楚它的資料是System資料還是Entity資料,如果是System的資料,一般設計成單例Component,例如存放玩家鍵盤輸入的 Component ,全域只需要一個,很多 System 都需要去讀這個唯一的 Component 中的資料,
單例Component顧名思義就是只有一個實體的Component,它只能用來存盤某些System狀態,單例Component在整個架構中的占比通常會很高,據說在守望先鋒中占比高達40%,其實換一個角度來看,單例Component可以看成是只有一個Component的匿名Entity單例,但可以通過GetSingletonIns介面來直接訪問,而不用通過EntityID,

例子

守望先鋒種有一個根據輸入狀態來決定是不是要把長期不產生輸入的物件踢下線的AFKSystem,該System需要物件同時具備連接Component、輸入Component等,然后AFKSystem遍歷所有符合要求的物件,根據最近輸入事件產生的時間,把長期沒有輸入事件的物件通知下線,

設計需要遵循的原則

  1. 設計并不是從Entity開始的,而是應該從System抽象出Component,最后組裝到Entity中,
  2. 設計的程序中盡量確保每個System都依賴很多Component去運行,也就是說System和Component并不是一對一的關系,而是一對多的關系,所以xxxCOM不一定有xxxSys,xxxSys不一定有xxxCOM,
    • System和Component的劃分很難在一開始就確定好,一般都是在實作的程序中看情況一步一步地去劃分System和Component,而且最侄訓分出來的System和Component一般都是比較抽象的,也就是說通常不會對應現實世界中的具體物件,可以參考下圖守望先鋒System和Component劃分的例子,
      20221029195909
  3. System盡量不改變Component的資料,
    • 可以讀資料完成的功能就不要寫資料來完成,因為寫資料會影響到使用了這些資料的模塊,如果對于其它模塊不熟悉的話,就會產生Bug,如果只是讀資料來增加功能的話,即使出Bug也只局限于新功能中,而不會影響其它模塊,這樣容易管理復雜度,而且給并行處理留下了優化空間,

使用心得

我在一個游戲demo里嘗試使用ECS去進行設計,最大的感受是所有游戲邏輯都變得那么的合理,應對改動、擴展也變得那么的輕松,加班變少了,也不再焦慮,在開始使用ECS來架構業務層之前,我對ECS還是存有一絲疑慮的,擔心會不會因為規矩太多了,導致有些功能寫不出來,中途也確實因為ECS的種種規矩,導致有些功能不好寫出來,需要用到一些奇技淫巧,劍走偏鋒,但這些技術最終造就了一個可持續維護的、解耦合的、簡潔易讀的代碼系統,據說守望團隊在將整個游戲轉成ECS之前也不確定ECS是不是真的好使,現在他們說ECS可以管理快速增長的代碼復雜性,也是事后諸葛亮,

引擎層的System比較好定義,因為引擎相關層級劃分比較明確,但是游戲業務邏輯層可能會出現各種奇奇怪怪的System,因為業務層的需求千變萬化,有時沒有辦法劃分出一個對應具體業務的System,例如我曾經在業務層定義過DamageHitSystem、PointForceSys,

推遲技術:不是非常必要馬上執行的內容可以推遲到合適的時再執行,這樣可以將副作用集中到一處,易于做優化,例如游戲可能會在某個瞬間產生大量的貼花,利用延遲技術可以將這些需要產生的貼花資料保存下來,稍后可以將部分重疊的貼花洗掉,再依據性能情況分到多個幀中去創建,可以有效平滑性能毛刺,

如果不知道該如何去劃分System,而導致System之間一定要相互通信才能完成功能,可以通過將資料放在中的一個佇列里延遲處理,比如SystemA在執行Update的時候,需要執行SystemB中的邏輯,但是這個時候還沒輪到SystemB執行Update,只能先將需要執行的內容保存到一個地方,但是System本身又沒有資料,所以SystemA只好將需要執行的內容保存到單例Component中的一個佇列里,等輪到SystemB執行Update的時候再從佇列里拿出資料來執行邏輯,

但是System之間通過單例Component有個缺點,如果向單例Component中添加太多需要延遲處理的資料,一旦出現bug就不好查了,因為這類資料是一段時間之前添加進來的,到后面才出問題的話,不好定位是何處、何時、基于什么情況添加進來的,解決方案是給每一條需要延遲處理的資料加上呼叫堆疊資訊、時間戳、一個用于描述為什么添加進來的字串,

各個System都用到的公共函式可以定義在全域,也可以作為對應System的靜態函式,這類函式叫做Utility函式,Utility函式涉及的Component最好盡可能少,不然需要作為引數傳進函式Component會很多,導致函式呼叫不太雅觀,Utility函式最好是無副作用的,即不對Component的資料做任何寫操作,只讀取資料,最后回傳計算結果,要改Component的資料的話,也要交給System來改,

函式呼叫堆疊的層次變淺了,因為邏輯被攤開到各個System,而System之間又禁止直接訪問,代碼變得扁平化,扁平化意味的函式封裝少了,所以閱讀、修改、擴展也很輕松,

如果可以把整個游戲世界都抽象成資料,存檔/讀檔功能的實作也變得容易了,存檔時只需要將所有Component資料保存下來,讀檔時只需要將所有Component資料加載進來,然后System照常運行,想想就覺得強大,這就是DOP的魅力,

優點

模式簡單

結構清晰

通過組合高度復用,用組合代替繼承,可以像拼積木一樣將任意Component組裝到任意Entity中,

擴展性強,Component和System可以隨意增刪,因為Component之間不可以直接訪問,System之間也不可以直接訪問,也就是說Component之間不存在耦合,System之間也不存在耦合,System和Component在設計原則上也不存在耦合,對于System來說,Component只是放在一邊的資料,Component提供的資料足夠就update,資料不夠就不update,所以隨時增刪任意Component和System都不會導致游戲崩潰報錯,

天然與DOP(data-oriented processing)親和,資料都被統一存放到各種各樣的Component中,System直接對這些資料進行處理,函式呼叫堆疊深度大幅度降低,流程被榷訓,

易優化性能,因為資料都被統一存放到Component中,所以如果能夠在記憶體中以合理的方式將所有Component聚合到連續的記憶體中,這樣可以大幅度提升cpu cache命中率,cpu cache命中良好的情況下,Entity的遍歷速度可以提升50倍,游戲物件越多,性能提升越明顯,ECS的這項特性給大部分人留下了深刻印象,但是大部分人也認為這就是ECS的全部,我覺得可能是被Unity的官方演示帶歪的,

易實作多執行緒,由于System之間不可以直接訪問,已經完全解耦,所以理論上可以為每個System分配一個執行緒來運行,需要注意的是,部分System的執行順序需要嚴格制定,為這部分System分配執行緒時需要注意一下執行先后順序,

缺點

在充滿限制的情況下寫代碼,有時速度會慢一些,但是習慣之后,后期開發速度會越來越快,

優化

一個entity就是一個ID,所有組成這個entity的component將會被這個ID給標記,因為不用創建entity類,可以降低記憶體的消耗,如果通過以下方式來組織架構,還可以提升cpu cache命中率,

//陣列下標代表entity的ID
ComponentA[] componentAs;
ComponentB[] componentBs;
ComponentC[] componentCs;
ComponentD[] componentDs;
...

參考資料

  • 《守望先鋒》架構設計與網路同步 -- GDC2017 精品分享實錄
  • http://gamadu.com/artemis/
  • http://gameprogrammingpatterns.com/component.html
  • http://t-machine.org/index.php/2014/03/08/data-structures-for-entity-systems-contiguous-memory/
  • http://blog.lmorchard.com/2013/11/27/entity-component-system/
  • 淺談《守望先鋒》中的 ECS 構架
    由于還要搬磚,沒有辦法一一回復私信把學習資料發給大家,我直接整理出來放在下面,覺得有幫助的話可以下載下來用于學習
    鏈接:https://pan.baidu.com/s/1C-9TE9ES9xrySqW7PfpjyQ 提取碼:cqmd

感謝各位人才的點贊收藏關注

微信搜「三年游戲人」識訓一枚有情懷的游戲人,第一時間閱讀最新內容,獲取優質作業內推

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/523122.html

標籤:其他

上一篇:云原生之旅 - 4)基礎設施即代碼 使用 Terraform 創建 Kubernetes

下一篇:Http和Https

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more