2

私は、プレイするためのクエストを提供するJavascriptゲームを作成しています。各クエストには、特定の要件が満たされたときに完了する多数のタスクがあります。以下は、もともと一緒に行くことを意図したデザインです。クエストシステムでイベントやタスクを構造化/設計してコードを管理しやすくするには?

enter image description here

あなたが見ることができるように、各Task及びGameEventEventに簡略化)が(辞書の中にカプセル化)タイプといくつかのデータを有しています。イベントがに送信されると、それはアクティブなQuestオブジェクトに渡され、オブジェクトはタイプが同じであると仮定して、それぞれTaskに渡します。タスクの各requirementは、そのイベントの相関dataに対してテストされます。私が1:1チェックをしないのは、イベントが追加のデータを持っている可能性があるからです。

例えばdata{item: someItem, locationFound: someLocation} != requirement{item: someItem}

これは、作成する必要があるクラスの数を最小限に抑えるという点で素晴らしい作品。イベントやタスクは一般的なものなので、大量のサブクラスを作成することなく簡単に作成してテストすることができます。

しかし、この設計では問題が発生します。その一般性のために、最初に同じタイプのイベントを見つけてそれに送信されるデータをチェックすることなく、タスクにどのような要件を与えるべきかは決して決してありません。プロジェクトが拡大するにつれて、これは管理する悪夢になります。

代わりに、私はこのように、様々なタイプをカバーするEventTaskの両方のためのサブクラスを作成することができます。

Event -> ItemPickupEvent, MoveEvent, etc. 
Task -> ItemPickupTask, ItemPickedUpAtLocationTask, MoveTask, etc. 

しかし、データは、それらの間に同様であり、非常に不必要なようです。

次のように私の最後の考えは、特定のイベントタイプによって渡されるデータをカプセル化するデータオブジェクトを作成することでした。

ItemPickupData、MoveData、TalkDataなど

イベントとタスクはちょうど適切な与えることができますデータオブジェクト。これにより、使用されているデータの構造が改善されます。データの一部を要件にしてはならない場合は、デフォルトでnullにすることができます。

メンテナンス性と構造設計上、これは適切な解決策ですか、それともこの問題にアプローチする良い方法がありますか?

ありがとうございました。

答えて

2

同じタイプのイベントを見つけてそこに送信されるデータをチェックすることなく、タスクにどのような要件を与えるべきかは、一般性のため決して特定できません。

私が理解しているように、問題はほとんど同じデータがイベントオブジェクトとタスクオブジェクトに重複していることです。

たとえば、Event { 'type': 'ItemPickup', data: { 'name': 'MagicSword', 'kind': 'weapon' } }です。

そして、あなたは魔法の剣を拾うためにタスクを追加し、Task { 'type': 'ItemPickup', data: { 'name': 'MagicSword' } }を作成します。 これは問題です。ここでは、イベントをどのように設定して、同じデータをタスクオブジェクトに入れるかを調べる必要があります。

私は、このデータを別々のオブジェクトに移動するソリューションはうまくいくと思います。 もう一つ正しく見えないのは、typeフィールドがあり、サブクラスが必要であることを示唆しています。

: そして、あなたは ItemPickupDataMoveDataのようなデータ・オブジェクトを作成する場合、これはまたなど、解決されるだろう

代替はEvent自体は、このデータオブジェクトであると、このような何か(擬似コード)を持っている必要がありますすることができます

class Event // base class for events with common data and methods 

// the subclasses can be almost empty 
class ItemPickupEvent extends Event 
class MoveEvent extends Event 

class Task 
    _requirements = [ 
     new ItemPickupEvent('MagicSword'), 
     // and maybe it is even better to wrap events into "Requirment" objects, 
     // which can, for example, be marked as completed 
     new Requirement(new MoveEvent('OldForest')), 
     ... 
    ] 

そして、あなたはその後、要件をこのように管理することができます:

class Task 
    isComplete(event) 
     complete = false 
     // go over requirements to see if this task is complete 
     for requirement in this._requirments: 
      complete = complete && requirement->isComplete(event) 
     return complete 

class Requirement 
    isComplete(event) 
     # check event class and properties to understand if the requirement is satisfied 
     if class(event) == class(this._event): 
      if event.isNameMatches(this._event): 
       this->_complete = true 
     return this->_complete 

また、クラス名のチェックなしで行うことができます。 基本的に、Requirementクラスのクラスには、各イベントクラスに対して特別なメソッドがあり、デフォルトでそれらを拒否することです。 サブクラスは、特定のイベント・タイプを扱うことができる:Requirmentクラスの

class Requirement 
    isComplete(event) 
     // redirect the type detection to event object 
     return event->isComplete(this) 

    isCompleteItemPickup(event) 
     return false 

    isCompleteMove(event) 
     return false 

class ItemPickupRequirement 
    isCompleteItemPickup(event) 
     // here we know that event is ItemPickupEvent 
     if event.isNameMatches(this._event): 
       this->_complete = true 
     return this->_complete 

class MoveRequirement 
    isCompleteItemPickup(event) 
     // here we know that event is MoveEvent 
     if event.isNameMatches(this._event): 
       this->_complete = true 
     return this->_complete 

isComplete方法はTaskクラスから使用され、それはイベントオブジェクトに対して検出を入力再指示します。 イベントオブジェクトは、当時の要件の適切な方法に自身を渡します(構造が少し複雑に見えるかもしれませんが、仕様に応じて、とにかくあなたがRequirementクラスを必要とするかもしれないし、それがサブクラスです

class Event 
    isComplete(requirement) // should be implemented in subclasses 

class ItemPickupEvent 
    isComplete(requirement) 
     // here we call the specific requirement method 
     // now we know the exact event object type (it is ItemPickupEvent) 
     return requirement->isCompleteItemPickup(this) 

class MoveEvent 
    isComplete(requirement) 
     return requirement->isCompleteMove(this) 

例えば、一般的なものはItemPickupRequirementであり、具体的なものはPowerItemPickupRequirementです。

+0

あなたの答えをありがとう。最終的には、イベントやタスクのためにサブクラスに戻すことは避けられませんでした。あなたが言うように、要件がより具体的でなければならない場合があります(つまり、追加のプロパティを処理する必要があるかもしれません)。現時点では、タスクによって必要とされるすべてのデータはEventDataオブジェクトに取り込まれるため、ヌル以外のプロパティをチェックするだけで柔軟性を試すことができます。もし何らかの理由でこれが将来問題を引き起こすならば、自分の思考に沿ったアプローチをもっと見なければならないでしょう。 – sookie

+0

@sookieもうひとつメモしておきますが、たくさんのクラスを作成しないようにするのは間違いですが、複雑さやタイピングからあなたを救うように見えるかもしれませんが、実際はそうではありません。小さなデータ構造を使用する小さなサブクラスの代わりに、すべてのエンティティのコードを記述する必要がありますが、型のない型とメソッドのないデータ構造の小さなサブクラスの管理は簡単です。 –

関連する問題