植物 は、プレイヤーが種を植え、水やりをして、結果として植物を収穫、販売できる参照エクスペリエンスです。
プロジェクトは、Roblox のエクスペリエンス開発中に発生する可能性のあるコモンユースケースに焦点を当てます。交換、コンパライズ、およびさまざまな実装選択の理由についての注釈は、適用可能な場合があります。これにより、最適なエクスペリエンスを作成できます。
ファイルを取得する
- ナビゲート to the 植物 エクスペリエンスページ。
- クリックする ⋯ ボタンと Studio で編集 。
ケースを使用する
植物 は次の使用ケースをカバーしています:
- セッションデータとプレイヤーデータの持続性
- UI ビュー管理
- クライアント-サーバーネットワーク
- ユーザーエクスペリエンス初心者 (FTUE)
- ハードとソフトの通貨購入
さらに、このプロジェクトは、多くのエクスペリエンスに適用できるより狭いセットの問題を解決します:
- プレイヤーが関連付けられた場所のエリアのカスタマイズ
- プレイヤーキャラクターの移動速度を管理する
- キャラクターを追うオブジェクトを作成
- キャラクターが世界のどの部分にあるかを検出する
このエクスペリエンスでは、太小、あまり分野が小さい、または興味深いデザインチャレンジにソリューションを示さないいくつかの使用ケースがあります。これらはカバーされていません。
プロジェクト構造
エクスペリエンスを作成する最初の決定は、プロジェクト を構造する方法を決めることです。これには、データモデル 内の特定のインスタンスを配置する場所と、クライアントとサーバーコードの両方のエントリポイントを整理および構造する方法が含まれます。
データモデル
次の表には、データモデルインスタンス内のコンテナサービスが置かれている説明があります。
サービス | インスタンスの種類 |
---|---|
Workspace | 静的モデルは、世界のパーツを表示する 3D 世界を表示します。これらのパーツは、プレイヤーには属さない世界の一部を表示します。これらのインスタンスをランタイムでダイナミックに作成、変更、または削除する必要はありません。 空の Class.Folder |
Lighting | 大気と照明の効果。 |
ReplicatedFirst | ロード画面を表示し、ゲームを初期化するために必要な最小限のインスタンスを含みます。 ReplicatedFirst に配置されるインスタンスの数が多くなるほど、コードを ReplicatedFirst によってレプリケートするのが待ち受けるまでの時間が長くなります。
ソース フォルダ内には、読み込み画面コードと、ゲームの残りを読み込むために必要なコードがあります。 |
ReplicatedStorage | クライアントとサーバーの両方でアクセスが必要なすべてのインスタンスのストレージコンテナとして使用されます。
|
ServerScriptService | プロジェクト内のすべてのサーバー側コードの入口ポイントとして機能する Script を含みます。 |
ServerStorage | クライアントに複製されなくてもいいすべてのインスタンスのストレージコンテナとして機能します。
|
SoundService | ゲーム内のサウンドエフェクトに使用される Sound オブジェクトを含みます。SoundService の下で、これらの Sound オブジェクトは位置があり、3D 空間でシミュレートされていません。 |
エントリポイント
ほとんどのプロジェクトは、ModuleScripts を通じて、コードをインポートできる再利用可能な Class.ModuleScript|
For the Plant microgame, a different approach is implemented through a single LocalScript that is the entry point for all client code, and a single Script that is the entry point for all server code. The correct approach for your project depends on your requirements, but a single point of entry provides greater control over the order in which systems are executed in.
次のリストは、両方のアプローチのトレードオフを説明しています:
- 単一の Script と単一の LocalScript カバーサーバーとクライアントコード。
- すべてのコードが単一のスクリプトからインタライズされるため、異なるシステムの順序をよりよく制御できます。
- オブジェクトをシステム間の参照で通過できます。
高度なシステムアーキテクチャ
プロジェクト内のトップレベルシステムは、以下で説明されています。これらのシステムの多くは、他のシステムよりも複雑であり、多くの場合、機能は別のクラスの階層に抽象化されています。
これらのシステムのそれぞれは、「シングルトン」です。つまり、それは関連するクライアントまたはサーバーの start スクリプトで初期化される非インスタンス化可能なクラスです。このガイドの後半で説明する シングルトンパターン について詳しくは、ここで説明します。
サーバー
次のシステムは、サーバーと関連付けられています。
システム | 説明 |
---|---|
ネットワーク | すべての Class.RemoteEvent と Class.RemoteFunction インスタンスを作成します。 > 0> 1> メッセージを送信および受信するメソッドをサポートしています。1> >0> 3> 4> タイプ |
PlayerDataServer |
|
マーケット |
|
CollisionGroupManager |
|
FarmManagerServer |
|
PlayerObjectsContainer |
|
タグプレイヤーズ |
|
FtueManagerServer |
|
キャラクタースポーン機 |
|
クライアント
次のシステムはクライアントと関連しています。
システム | 説明 |
---|---|
ネットワーク |
|
PlayerDataClient | メモリにローカルプレイヤーのデータを保存します。 メソッドと信号をクエリーとサブスクリプトに対応して公開します。 メソッドと信号はプレイヤーデータの変更に対応します。 |
マーケットクライアント |
|
ローカルウォークジャンプマネージャー |
|
FarmManagerClient | 特定の Class.CollectionService タグがインスタンスに適用されているのを聞き、これらのタグに「コンポーネント」を追加します。「コンポーネント」は、 Class.CollectionService タグがインスタンスに追加されるときに作 |
UI設定 | すべての UI レイヤーを初期化します。 特定のレイヤーをゲームワールドの物理セクションでのみ表示するように構成します。 ハンドルアップ特別カメラエフェクトを有効にするための特別なカメラエフェクトを構成します。 カメラエフェク |
FtueManagerClient |
|
キャラクタースプリント |
|
クライアント-サーバー通信
ほとんどの Roblox エクスペリエンスは、クライアントとサーバーの間の通信のいくつかの要素を含みます。これには、クライアントがサーバーに特定のアクションを実行するようにリクエストすること、サーバーがクライアントに更新をレプリケートすることが含まれます。
このプロジェクトでは、RemoteEvent と RemoteFunction オブジェクトを使用することをできるだけ一般的に保つために、限定することで使用量を減少させ、特別なルールを追跡するために追跡するために、Class.RemoteFunction オブジェクトを使用しています。このプロジェクトは、優先順位の順序で次のメソ
- Replication via the player data system 。
- 属性 を通じてレプリケーション。
- タグを使用して レプリケーション を行う。
- メッセージ 直接 ネットワーク モジュールを通じて。
Player Data System を介してレプリケーション
プレイヤーデータシステムは、セーブセッション間で持続するプレイヤーにデータを関連付けることができます。このシステムは、クライアントからサーバーへのレプリケートを提供し、サーバーからクライアントにデータを取得し、変更を保持するための API セットを提供します。これは、サーバーからプレイヤーの状態を変更するためのリプリケ
たとえ、UpdateCoins``Class.RemoteEvent を発射して、クライアントがコインをいくつ持っているかを告知する代わりに、次のコールを実行し、PlayerDataClient.updated イベントを通じてクライアントにサブスクリプトしていただくことができます。
PlayerDataServer:setValue(player, "coins", 5)
もちろん、これはサーバー-クライアントレプリケーションとセッション間の保持値のみに有用ですが、これはプロジェクト内の驚くほど多くの場合に適用されます、例えば:
- 現在の FTUE ステージ
- プレイヤーのインベント持ち物リスト
- プレイヤーが持っているコインの量
- プレイヤーの農場の状態
アトリビューションによるレプリケーション
サーバーが、指定された Instance にクライアントにリプレートする必要がある場合は、属性 を使用して、サーバーがリプレートする必要のある状態に関連付けることができます。Roblox は、属性 を自動的にリプレートするので、オブジェクトに関連付けられたステー
これは、実行時に作成されたインスタンスにとって特に便利です。アトリビュートが新しいインスタンスに設定される前に、データモデルにアトミックにレプリケートされるとき、コードを「待つ」必要がありません。これは、RemoteEvent または StringValue を通じて追加のデータをレプリケー
クライアントまたはサーバーから、GetAttribute() メソッドを使用して、データモデルから直接属性を読み取ることもできます。このアプローチは、GetAttributeChangedSignal() メソッドを使用して、植物の現在の状態
タグを使用したレプリケーション
CollectionService は、Instance にストリングタグを適用できます。これは、インスタンスをカテゴリに分類し、クライアントにそのカテゴリをレプリケートするのに便利です。
たとえば、CanPlant タグは、サーバーに適用されて、指定されたポットが植物を受信できることをクライアントに示すためにサーバーに適用されます。
ネットワークモジュールを介してメッセージ送信
以前のオプションが適用されない状況では、 ネットワーク モジュールを介してカスタムネットワークコールを使用できます。これは、クライアント-サーバー通信を許可する唯一のオプションであり、クライアントリクエストを処理し、サーバーのレポートを受信するために最も便利です。
植物 は、さまざまなクライアントリクエストに対応するために直接ネットワークコールを使用します。
- 植物に水やりをする
- 種を植える
- アイテムの購入
このアプローチのデメリットは、個々のメッセージには、プロジェクトの複雑さを増加させる可能性のあるいくつかのベスポーク設定が必要になることです。これは、サーバー-クライアント通信を含むすべての個々のメッセージにおいて、できるだけ避けられるようになっています。
クラスとシングルトン
Plant プロジェクトのクラスは、Roblox のインスタンスのように、クラスを作成および削除できます。クラスの構文は、オブジェクト指向プログラミング の Lua のアプローチにインスパイアされており、厳重なタイプチェック サポートを有効にするために、複数の変更を含みます。
インスタンス
プロジェクトには、Instances 多くのクラスが関連しています。指定されたクラスのオブジェクトは、new() メソッドを使用して作成されます、Roblox でインスタンスが作成される方法と一貫しています。
このパターンは、BeamBetween など、クラスがデータモデルに物理的表現を持つオブジェクトで、クラスが機能を拡張する例に
対応するインスタンス
上記の通り、このプロジェクトの多くのクラスにはデータモデル表示があり、クラスと一致して操作されるインスタンスがあります。
クラスオブジェクトがインスタンスを作成するときではなく、コードは通常 Clone() を含むプリ
構成
ルアでは、メタテーブルを使用して、クラスを コンポジション で互いに拡張できますが、プロジェクトはクラスを拡張するために new() を通じて互いに拡張することを選択します。クラスを通じて構成を組み合わせると、1>子1> オブジ
これの実際の例は、CloseButton クラスが Button クラスをラップすることで操作。
クリーンアップ
Class.Instance:Destroy()|Destroy() メソッドで、Destroy() を破壊すると、destroy() をインスタンスできるクラスも破壊できます。プロジェクトクラスの破壊メソッドは、1>
destroy() メソッドの役割は、オブジェクトによって作成されたインスタンスを破壊し、接続を切断し、子オブジェクトに destroy() を呼び出すことです。これは、接続がアクティブであるオブジェクトのインスタンスをクリーンアップしない Lua ガーゴイルのコレクターでは、インスタンスに残る接続や
シングルトン
Singletons、名前からわかるように、はクラスで 1つのオブジェクトのみが存在できる。彼らはプロジェクトのRobloxの サービス
シングトンは、new() メソッドがないため、即座にクラスとして返されます。代わりに、オブジェクトとそのメソッドと状態は、ModuleScript を介して直接返されます。シングトンがインスタンスされないの
厳密なタイプ推定
Luau は、グラデーションタイピングをサポートしています。これは、コードの一部またはすべてにオプションのタイプ定義を追加することができる意味です。このプロジェクトでは、 strict タイプチェックがすべてのスクリプトに使用されます。これは Roblox のスクリプト分析Roblox(ロブロックス)ールの最小
クラスシンタックスを入力しました
Lua でクラスを作成する方法は よくドキュメントされていますが、強力な Luau タイピングには適していません。 しかし、Luau のシンプルなアプローチでクラスのタイプを取得する方法は typeof() メソッドです:
type ClassType = typeof(Class.new())
これは機能しますが、クラスが実行時にのみ存在する値でインタラクトする場合、たとえば Player オブジェクトなど、そのクラスを初期化するときには非常に便利ではありません。さらに、 idiomatic Lua クラス構文では、クラスを self で宣言すると常にそのクラスのイン
厳密なタイプ推定をサポートするために、Plant プロジェクトは、 idiomatic Lua クラスの構文と異なるソリューションを使用しています、一部のこれらは非常に非直感的に感じるかもしれません:
- 自分 の定義は、タイプ と 構造者 の両方に重複されています。これにより、維持可能性の負荷が導入されますが、2つの定義が互いにシンクロしないと、警告がマークされます。
- クラスメソッドはドットで宣言されているため、 self は明示的に ClassType のタイプになるように宣言できます。メソッドは引き続きコロンで呼び出されることが期待されます。
--!厳格
local MyClass = {}
MyClass.__index = MyClass
export type ClassType = typeof(setmetatable(
{} :: {
property: number,
},
MyClass
))
function MyClass.new(property: number): ClassType
local self = {
property = property,
}
setmetatable(self, MyClass)
return self
end
function MyClass.addOne(self: ClassType)
self.property += 1
end
return MyClass
論理ガードの後に型をキャストする
書き込み時に、ガードコンディショナルステートの後、値の種類は optionalParameter の下にありません。たとえ、以下のガードをフォローすると、 number の下にある number のタイプは 2>player2> には広がりません。
--!厳格
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
print(optionalParameter + 1)
end
これを軽減するために、これらのガードのタイプを明示的にキャストした後、新しい変数が作成されます。
--!厳格
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
local parameter = optionalParameter :: number
print(parameter + 1)
end
データモデルの階層をトラベルする
コードベースは、実行時に作成されるオブジェクトのツリのデータモデル階層をトラベルする必要があります。これは、タイプチェックに関する興味深いチャレン入力です。書き込み時に、コードベースは タイプ のデータモデル階層を定義することができません。結果、コードベースの タイプ 情
このチャレンジに対する 1つのアプローチは、any にキャストし、その後精製することです。たとえば:
local function enableVendor(vendor: Model)
local zonePart: BasePart = (vendor :: any).ZonePart
end
このアプローチの問題は、読み取り可能性に影響を与えることです。代わりに、プロジェクトは getInstance などの一般的なモジュールを使用して、内部で any をキャストするデータモデル階層をトラベルするために使用します。
local function enableVendor(vendor: Model)
local zonePart: BasePart = getInstance(vendor, "ZonePart")
end
タイプエンジンのデータモデルの理解が進むにつれて、このようなパターンは必要なくなる可能性があります。
ユーザーインターフェイス
植物 には、コインカウンターやショップなどの非インタラクティブな 2D ユーザーインターフェイス (NID) が含まれています。これには、ショップなどのコインカウンターや交互メニューなどの非インタラクティブなヘッドアップディスプレイ (HUD) アイテムが含まれます。
UIアプローチ
Roblox UI を HTML ドキュメントに比較して、 単語 で表現されるユーザーの見るべきものを記述するオブジェクトの階層によって、略単に比較できます。Roblox UI の作成と更新に関するアプローチは、 強制 および 1>宣言1> のプラクティスに分割されて
アプローチ | 利点と欠点 |
---|---|
強制 | インパラティブアプローチでは、UI は Roblox の他のインスタンス階層と同じように扱われます。UI 構造は Studio のランタイム前に作成され、データモデルに追加されます。その後、実行時にコードが特定の UI 部分を変更するために使用されます。 このアプローチにはいくつかの欠点があります。編集時にデータモデルの複数の部分が重複するため、パラ |
デクラレーション | デクララティブアプローチでは、UI インスタンスの望ましい状態は明示的に宣言され、この状態の効率的な実装はライブラリのような |
植物 は、Roblox で UI を直接表示することで、UI の作成と操作の効果をより効果的に把握するための 強制 アプローチを使用します。これは、デクララティブ アプローチではできません。一部の繰り返しの UI 構造とロジックは、0>コ
アーキテクチャーレベルの高さ
レイヤーとコンポーネント
In Plant では、すべての UI 構造は Layer または Component です。
- Layer は、プリファブリケートされた UI 構造を ReplicatedStorage にグループ化するトップレベルのグループとして定義されます。レイヤーには、コンポーネントの数または、自分のロジックを完全にカプセル化することがあります。レイヤーの例はインベントリメニューまたは頭上ディスプレイ
- Component は再利用可能な UI 要素です。新しいコンポーネントオブジェクトがインスタンスされると、ReplicatedStorage からプリファブリケートされたテンプレートをクローンします。コンポーネントは、自体に含まれる他のコンポーネントを含むことがあります。コンポーネントの例は、一般的なボタンクラ
処理を見る
一般的な UI 管理問題の 1つは、ビューハンドリングです。このプロジェクトには、ユーザーの入力を聞くメニューと HUD アイテムの範囲があり、表示または有効にするときの管理に注意が必要です。
植物 は、 UIHandler システムを通じてこの問題にアプローチしますが、ゲーム内のすべての UI レイヤーは、HUD または0> Menu0> となっています。すべての UI レイヤーは、次のルールによりビジビティを管理されます:
- Menu と HUD レイヤーの有効な状態を切り替えることができます。
- 有効にすると、HUD レイヤーは、Menu レイヤーが有効でないと表示されません。
- 有効 Menu レイヤーはスタックに保存され、1つの Menu レイヤーのみがスタックの前部に表示されます。Menu レイヤーが有効になると、スタックの前部に挿入され、表示されます。
このアプローチは直感的であり、メニューをストーリーでナビゲートできます。 1つのメニューを別のメニューから開くと、新しいメニューを閉じると古いメニューが再び表示されます。
UI レイヤーのシングルトンは、 UIHandler に登録され、可視性が変更されると、信号を提供します。
追加の読み物
この Plant プロジェクトの全体的な概要から、関連するコンセプトとトピックについて詳しく探索するために、次のガイドを探索してください。
- クライアント-サーバーモデル — クライアント-サーバーモデルの概要。
- Luau — Roblox スクリプト言語 Lua 5.1 から派生した Luau の詳細。
- リモートイベントとコールバック — すべてのクライアント-サーバー境界の通信に関するリモートネットワークイベントとコールバックの概要。
- UI — Roblox のデザインとユーザーインターフェイスオブジェクトの詳細。