CaptainZ

CaptainZ

Prompt Engineer. Focusing on AI, ZKP and Onchain Game. 每周一篇严肃/深度长文。专注于AI,零知识证明,全链游戏,还有心理学。
twitter

MUDv2はECSアーキテクチャを廃止しましたか?

Lattice (@latticexyz) の MUD は Web3 分野で最も古く有名な全チェーンゲームエンジンであり、以前の第一世代バージョンでは、MUDv1 が ECS に基づくフレームワークであることを明確に示しました。私は関連する紹介記事《深度解析全链游戏引擎 MUD》も書きました。そして数週間前、Lattice は V2 バージョンを発表し、MUD 全体のアーキテクチャに大きな変更を加え、ECS との関連を弱め、新しいオンチェーンデータベース「Store」を導入しました。このデータベースは「table」のデータ構造に基づいていますが、「ECS」と「table」の関係をどのように理解すべきでしょうか?新しいコンポーネント「Store」はどのように機能するのでしょうか?「ECS」は全チェーンゲームの制作に適しているのでしょうか?この記事を通じて探っていきます。
 

MUDv2 概観#

quick-start-mud-cli

MUDv2 は Ethereum アプリケーションを構築するためのフレームワークです。EVM アプリケーションの構築の複雑さを圧縮するために、緊密に統合されたソフトウェアスタックを通じて実現されています。MUDv2 には、Store(オンチェーンデータベース)、World(標準化されたアクセス制御、アップグレード、モジュールを提供するエントリポイントフレームワーク)、Foundry に基づく迅速な開発ツール、オンチェーン状態を反映するクライアントデータストレージ、SQL クエリを使用してオンチェーン状態を反映する Postgres データベース MODE が含まれています。
 
MUDv2 は rollup やチェーンではなく、オンチェーンアプリケーションを構築するために一緒に機能するライブラリとツールのセットです。MUDv2 は Ethereum メインネットに限定されず、任意の EVM 互換チェーンで機能します。MUDv2 は自治世界やオンチェーンゲームにのみ適用されるものではなく、データモデルを開発者に強制することもありません。MUD を使用して、フラットな Solidity マッピングや配列でできることは何でも行うことができます。MUDv2 のデータ可用性ソリューションは、ENS や Uniswap のような Ethereum メインネットでの一般的なデプロイメントと同様です。
 
MUDv2 の主要な考え方は、すべてのオンチェーン状態が Store(MUD オンチェーンデータベース)に保存されることです。現在、私たちがスマートコントラクトの状態を処理する方法は、いくつかの主要な問題を引き起こしています。たとえば、状態とロジックの結合により、ロジックのアップグレードが非常に困難になります。MUD を使用すると、Solidity コンパイラによって駆動されるデータストレージを使用することはありません。すべての状態は Store を使用して保存および取得され、これは効率的なオンチェーンデータベースです。Store は SQLite のようなもので、Yul で手動で最適化された埋め込みデータベースです。テーブルがあり、列と行があります。
 
MUDv2 のロジックは無状態であり、カスタム権限を持ち、コントラクト間で呼び出すことができます。MUD は World の使用を推奨します。World は異なるコントラクトから Store へのアクセスを調整するエントリポイントのカーネルです。World はデプロイ時に Store を作成します。各 Store 内のテーブルは、名前空間の下で登録され、名前で表されます。これはフラットなファイルシステムパスのようなものです。
 
MUDv2 はインデクサやサブグラフを必要とせず、フロントエンドは自動的に同期します。Store(および拡張された World)を使用すると、オンチェーンデータは自己検証され、変更は標準イベントを介してブロードキャストされます。これらのイベントとパターンは MODE によって利用されます。MODE はあなたのオンチェーン状態を SQL データベースに変換し、ミリ秒単位の遅延で更新を維持します。

ECS の本質とは#

Snip20230713_8

文献を調べた結果、私個人の見解として(著者 @hicaptainz は開発者ではないため、誤りがあれば指摘してください)、ECS(エンティティ - コンポーネント - システム)パターンの本質はデータ構造のモデリング方法であり、その核心はデータをどのように保存し、整理するかにあります。
 

  1. エンティティ(Entity):ECS パターンにおいて、エンティティは抽象的な概念であり、データを直接保持するのではなく、コンポーネントを通じてデータを関連付けます。エンティティは 1 つまたは複数のコンポーネントのコンテナと見なすことができ、その主な役割はコンポーネントに一意の識別子を提供することです。

  2. コンポーネント(Component):コンポーネントはデータのキャリアです。ECS パターンでは、すべてのデータがコンポーネントにカプセル化されています。各コンポーネントは特定の属性や動作を表し、位置、速度、色などが含まれます。コンポーネントはデータのみを含み、ロジックや動作は含みません。

  3. システム(System):システムはデータを処理する場所です。システムはエンティティのコンポーネントに基づいて、これらのエンティティをどのように処理するかを決定します。各システムには 1 つ以上の特定のタスクがあり、レンダリング、物理シミュレーション、AI ロジックなどがあります。

 
この観点から見ると、ECS パターンは確かにデータ構造のモデリング方法です。データ(コンポーネント)と動作(システム)を分離することで、データの保存と処理がより柔軟で効率的になります。このパターンの利点は以下の通りです:

 

  • 可組み性:異なるコンポーネントを組み合わせることで、さまざまな属性や動作を持つエンティティを作成でき、大量のクラスや構造を作成する必要がありません。

  • データ局所性:コンポーネントはデータのみを含むため、関連するデータを密接に保存でき、キャッシュの利用率を向上させ、パフォーマンスを向上させます。

  • 再利用性:システムはデータにのみ関心を持ち、データがどのエンティティから来ているかには関心を持たないため、複数のエンティティ間で同じシステムを再利用できます。

  • 並行性:データと動作の分離により、マルチスレッド環境でのデータの並行処理が容易になります。

ECS データ構造モデリングのいくつかのタイプ#

では、ECS を実現するデータ構造の方法にはどのようなものがあるのでしょうか?一般的に、比較的人気のある 2 つは Archetype と Sparse set であり、他にはあまり一般的でない Bitset や Reactive ECS があります。それぞれに利点と適用シーンがあります。
 

  1. Archetype(テーブルベースの ECS とも呼ばれる):Archetype(原型)は、データ局所性を最適化した保存方法であり、同じコンポーネントセットを持つエンティティを 1 つのテーブルに保存します。コンポーネントは列、エンティティは行です。Unity の ECS システムはこの方法を採用しています。Archetype の利点は、同じコンポーネントセットがメモリ内で連続しているため、キャッシュ効率とデータアクセス速度を大幅に向上させることができる点です。また、各 Archetype は含まれるコンポーネントを知っているため、実行時にエンティティを動的に作成および削除でき、大量のメモリ操作を必要としません。

  2. Sparse Set(稀疏集合とも呼ばれる):Sparse Set(スパースセット)は、配列とハッシュテーブルの利点を組み合わせた効率的なデータ保存とアクセス方法です。Sparse Set に基づく ECS は、各コンポーネントをその独自の Sparse Set に保存し、その集合はエンティティ ID をキーとします。この方法の利点は、キャッシュを効率的に利用できることと、スペースを節約できることです。ただし、この方法の欠点は、実装が比較的複雑であることです。

 
Bitset や Reactive ECS についても独自の利点がありますが、すべてのアプリケーションシーンに適しているわけではありません。
 

  • Bitset:Bitset は、エンティティが特定のコンポーネントを持っているかどうかを確認するためのシンプルで効率的な方法です。ただし、Bitset 自体はコンポーネントデータを保存できないため、通常は他の保存方法と組み合わせて使用する必要があります。

  • Reactive ECS:Reactive ECS は、コンポーネントの追加、削除、変更に対してシステムが応答できるより高度なパターンです。このパターンは、コードをより明確で理解しやすくすることができますが、実装の複雑さが増す可能性があります。
     
    私の文章《Curio はどのように ECS ゲームエンジンを OPStack に内蔵したのか?》を読んだことがある読者はすぐに気づくでしょうが、Curio が採用しているのは Sparse Set(スパースセット)の方法で ECS データを保存および操作していますが、MUDv2 はどうでしょうか?明らかに「Archetype」、つまり「テーブルベースの ECS」です。

Store コンポーネントの動作原理#

MUDv2 の Store コンポーネントが提供するデータモデルは ECS パターンをよくサポートしています。Store 内では、エンティティを表すテーブルを作成でき、各エンティティには一意のキーがあります。各エンティティにさまざまなコンポーネントを追加でき、これらのコンポーネントはテーブル内のフィールドとなります。次に、これらのコンポーネントを処理するシステムを作成できます。これらのシステムはスマートコントラクト内の関数となります。
 
たとえば、「Player」というテーブルを作成し、「position」と「health」という 2 つのフィールドを持たせることができます。次に、プレイヤーの位置を変更する「move」関数や、プレイヤーのライフを減少させる「damage」関数を作成できます。したがって、MUD の Store コンポーネントは ECS パターンを直接実装しているわけではありませんが、提供されるデータモデルは ECS パターンの実装をよくサポートしています。
 
おそらく、誰かがこのテーブルベースのオンチェーンデータベースは OOP(オブジェクト指向プログラミング)パターンもサポートしているのかと疑問に思うかもしれません。
 
Store コンポーネントはテーブルベースのデータモデルを提供しており、このモデルはリレーショナルデータベースやデータ駆動設計に近く、オブジェクト指向プログラミング(OOP)のモデルではありません。オブジェクト指向プログラミングでは、データとそのデータを操作する方法がオブジェクトにカプセル化されますが、Store のモデルではデータはテーブルに保存され、データを操作するロジックはスマートコントラクト内の関数によって処理されます。
 
しかし、これはスマートコントラクト内でオブジェクト指向の設計パターンを使用できないことを意味するわけではありません。オブジェクトを表すスマートコントラクトを作成し、このスマートコントラクトにはオブジェクトの属性を表すいくつかの状態変数や、オブジェクトのメソッドを表すいくつかの関数を持たせることができます。次に、Store を使用してこれらのオブジェクトの状態を永続化できます。
 
たとえば、「Player」というスマートコントラクトを作成し、このスマートコントラクトには「position」と「health」という 2 つの状態変数、そして「move」と「damage」という 2 つの関数を持たせることができます。次に、Store を使用して各「Player」インスタンスの「position」と「health」を保存できます。したがって、Store のデータモデルはオブジェクト指向プログラミングを直接サポートしていませんが、スマートコントラクト内でオブジェクト指向の設計パターンを使用し、Store を使用してオブジェクトの状態を永続化できます。
 
しかし、私たちが常に強調しているように、全チェーンゲームにとって ECS パターンはより適していると言えます。なぜなら、ここでの大きな利点は、新しいシステム(System)を開発の任意の段階で導入でき、正しいコンポーネントを持つ既存のエンティティや新しいエンティティに自動的にマッチするからです。これにより、システムが単一責任の小さな機能ユニットとして開発され、異なるプロジェクトに簡単にデプロイできる設計が促進され、全チェーンゲームの「可組み性」が実現されます。

オンチェーンデータの保存#

Store コンポーネントがオンチェーンデータベースを構築している以上、これらのオンチェーンデータは一体どこに存在するのでしょうか?まず、EVM の保存方法を思い出してみましょう。
 
Ethereum 仮想マシン(EVM)では、オンチェーンデータは主に 2 つのデータ構造に保存されます:ストレージ(Storage)とメモリ(Memory)。

  1. ストレージ(Storage):これは各スマートコントラクトの永続的なストレージであり、そのデータはトランザクション間で永続的です。ストレージはキーと値のペアで構成され、キーと値は 256 ビットです。Solidity では、コントラクトの状態変数はここに保存されます。たとえば、コントラクト内でuint256 public counter;を定義した場合、このcounter変数はストレージに保存されます。counterを変更する関数を呼び出すたびに、ストレージ内で更新されます。

  2. メモリ(Memory):これは各関数呼び出しの一時的なストレージであり、そのデータは関数呼び出しが終了すると消去されます。メモリは線形で、バイト配列のように考えることができます。Solidity では、関数のローカル変数はここに保存されます。

さらに、スタック(Stack)というデータ構造もあり、これは関数呼び出しのパラメータや戻り値、いくつかの一時的な計算結果を保存するために使用されます。スタックのサイズは有限で、最大 1024 個の要素を含むことができます。
 
EVM では、ストレージの読み書きコストは非常に高く、ガスを消費する必要があります。そのため、スマートコントラクトの開発者は通常、ストレージ操作を減らすためにコードを最適化しようとします。それに対して、メモリの読み書きコストははるかに低いですが、メモリのデータは関数呼び出しが終了すると消去されるため、一時的なデータの保存にのみ適しています。
 
MUDv2 の Store コンポーネントは、データを EVM のストレージ(Storage)に保存します。各スマートコントラクトは EVM 内に独自のストレージスペースを持ち、このストレージスペースは永続的です。つまり、トランザクション間でもストレージ内のデータは変わりません。
 
Store コンポーネントは、開発者がスマートコントラクト内でデータを保存および読み取るのをより便利にするための高レベルの抽象化を提供します。開発者は独自のテーブルを定義でき、各テーブルには特定のスキーマがあり、テーブル内のフィールドとその型が定義されています。次に、開発者は Store が提供する API(set、get などの関数)を使用して、これらのテーブル内のデータを操作できます。
 
これらの操作は最終的に EVM ストレージの読み書き操作に変換されます。たとえば、MyTable.set 関数を呼び出してレコードを設定すると、この関数はデータをバイトにエンコードし、EVM のストレージに書き込みます。MyTable.get 関数を呼び出してレコードを取得すると、この関数は EVM のストレージからデータを読み取り、定義した型にデコードします。

無視された状態機械の問題#

上記の分析では、ECS パターンの利点しか見えていないようですが、本当に欠点はないのでしょうか?ここでは EVM 自体と状態機械について触れます。
 
状態機械、または状態オートマトンは、オブジェクトの状態変化と入力(イベントや条件など)への応答を記述するモデルです。状態機械では、システムは任意の時点で特定の状態にあり、入力の影響を受けてある状態から別の状態に遷移します。従来のゲームエンジンでは、状態機械はゲームオブジェクト(キャラクター、敵、AI など)の動作状態を管理するために使用されます。これらの状態には、歩行、走行、ジャンプ、攻撃、防御、死亡などが含まれます。状態機械は、ゲームオブジェクトが異なる状態間での遷移を明確に定義し、制御するのに役立ちます。
 
EVM(Ethereum Virtual Machine)については、これをグローバルに分散された状態機械と見なすことができます。Ethereum ネットワークでは、各ブロックには一連のトランザクションが含まれ、これらのトランザクションが入力となります。EVM はこれらのトランザクションを実行し、トランザクションの内容に基づいて Ethereum のグローバル状態を更新します。Ethereum のグローバル状態には、すべてのアカウントの残高情報、スマートコントラクトのコード、およびスマートコントラクトが保存するデータが含まれます。トランザクションが実行されると、資金が移転されたり、コントラクトが呼び出されたり、コントラクトのストレージ状態が変更されたりし、これにより Ethereum のグローバル状態が変化します。
 
したがって、全チェーンゲームにとって、ゲームエンジン内に状態機械を保存することは必須です。しかし、ECS パターンで状態機械(特に分散状態機械)を保存することには一連の問題があります。
 
ECS 内の状態機械の問題:ECS を使用してゲームを開発する際、ECS 内に状態機械を保存するかどうかの問題に直面することがあります。この問題は予想以上に複雑であり、理由は多岐にわたります。たとえば、エンティティの状態を変更すること、エンティティを複数の状態機械に参加させること、現在の状態のエンティティを取得すること、状態機械の状態リストを変更することなどの操作は、ECS 内での課題となります。
 
ラベル状態の問題:状態機械を直感的に実装する方法の 1 つは、各状態のためにラベルを作成することです。この方法は、特定の状態のすべてのエンティティをクエリする際に良好に機能しますが、ECS の実装は通常、特定のコンポーネント / ラベルを持つすべてのエンティティを見つけるのが得意です。しかし、状態を変更するなどの他の操作では、1 つのラベルを追加し、別のラベルを削除する必要があり、これが複雑さやパフォーマンスの問題を引き起こす可能性があります。
 
Archetype というデータ構造に具体的に焦点を当てて分析してみましょう。
 
Archetype の実装方法は、同じコンポーネントセットを持つすべてのエンティティを 1 つのテーブルに集めることです。具体的な詳細を避けると、この方法はコンポーネントの効率的な反復キャッシュを提供します。なぜなら、複数のコンポーネントを連続して反復処理できるからです。しかし、これにはいくつかのコストも伴います。
 
同じコンポーネントセットを持つエンティティの集約を維持するために、コンポーネントを追加または削除するたびに、エンティティを異なるテーブル間で移動する必要があります。これはラベルにも当てはまります。したがって、状態に関連するラベルを頻繁に追加または削除することは非常に高価になる可能性があります。なぜなら、状態が変化するたびに、エンティティのすべてのコンポーネントをコピーする必要があるからです(実際には、ストレージ方式の理由から、各コンポーネントは 2 回コピーされる必要があります)。
 
この問題を解決するために、Bevy ECS の作者は新しいストレージバリアントの実装方法を提案しました。それは、Archetype とテーブルを分離することです。この方法では、テーブルはエンティティの(データ)コンポーネントのみを保存し、Archetype はコンポーネント + ラベルを保存します。複数の Archetype が同じテーブルを指すことができます。これにより、アプリケーションは定数時間でラベル(および状態)を追加および削除でき、すべてのコンポーネントをコピーする必要がなくなります。
 
おそらく、MUDv3 はこの方向に改善されるでしょう。将来的にテーブルの外に新しい Archetype ストレージコンポーネントが追加されるかどうか見てみましょう。

小結#

さて、最初の質問に答えてみましょう。テーブルは「Archetype ECS」を実現するためのモデリング方法の 1 つであり、MUDv2 は ECS を放棄したわけではなく、ちょうどテーブルというコンポーネントが OOP のアーキテクチャを実現できるということです。このテーブルは Store コンポーネントを通じて生成され、EVM のストレージに保存されます。したがって、テーブルへのすべての読み書き操作はストレージへの読み書き操作に変換されます。ECS アーキテクチャは EVM 状態機械を保存するのに多くの困難があるものの、解決策は多数存在します。これらの最適化方法に興味がある方は、著者の Twitter(@hiCaptainZ)をフォローして最新情報を入手してください。著者は現在 Gametaverse(@GametaverseDAO)で研究責任者を務めており、ノースカロライナ大学で光学工学の修士号、香港中文大学で金融の修士号を取得しています。現在の研究分野は Web3 Gaming、AI、ZKP であり、今後数週間の研究課題は以下の通りです:
 

  • 従来のゲームエンジンと Web3 ゲームエンジンの比較分析
  • ゲームの可玩性をどのように判断するか
  • Web3 におけるギャンブルゲームと GambleFi の台頭
  • 全チェーンゲームの可組み性にはどのようなものがあるか
  • Web3 ゲームの経済システム設計
  • 異なるゲームジャンルが Tokenomics の設計に与える影響
  • ECS アーキテクチャは一つのパラダイムか(利点と欠点)
  • ゼロから一に ECS を設計する方法
  • AI がチェーンゲーム制作をどのように支援するか
  • ZK ゼロ知識証明の全体像
  • ZK 技術と機械学習 ZK-ML
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。