我正在创建一个 Javascript 游戏,为用户提供任务来玩。每个任务都有许多任务,当满足某些要求时这些任务就会完成。以下是我最初打算采用的设计。
如您所见,每个 Task
和 GameEvent
(简化为 Event
)都有一个类型和一些数据(封装在一个字典中) .当一个事件被发送到 QuestManager
时,它被传递给事件的 Quest
对象,这些对象又将它传递给每个假定其类型的 Task
是相同的。然后针对事件中的相关数据
测试任务中的每个要求
。我不做 1:1 检查的原因是因为一个事件可能有额外的数据。
例如 data{item: someItem, locationFound: someLocation} != requirement{item: someItem}
这在最大限度地减少需要创建的类数量方面非常有效。事件和任务是通用的,因此无需创建大量子类即可轻松创建和测试它们。
但是这种设计产生了一个问题。由于它的普遍性,如果不先找到相同类型的事件并检查发送给它的数据,就永远无法确定应该给任务什么要求。随着项目的扩展,这将成为管理的噩梦。
相反,我可以为 Event
和 Task
创建涵盖各种类型的子类,如下所示:
Event -> ItemPickupEvent, MoveEvent, etc.
Task -> ItemPickupTask, ItemPickedUpAtLocationTask, MoveTask, etc.
但它们之间的数据相似,似乎非常没有必要。
我最后的想法是创建数据对象来封装某些事件类型传递的数据,如下所示:
ItemPickupData、MoveData、TalkData 等
事件和任务可以只被赋予适当的数据对象。这为正在使用的数据提供了更好的结构。如果一条数据不应该成为需求,它可以简单地默认为空。
就可维护性和结构设计而言,这是一个合适的解决方案还是有更好的方法来解决这个问题?
谢谢。
最佳答案
Because of its generality, it is never certain what requirements should be given to a task without first finding an event of the same type and checking the data being sent to it.
据我了解,问题是在事件对象和任务对象中复制了几乎相同的数据。
例如,您有 Event { 'type': 'ItemPickup', data: { 'name': 'MagicSword', 'kind': 'weapon' } }
。
然后你想添加一个任务来拾取魔剑,你创建了 Task { 'type': 'ItemPickup', data: { 'name': 'MagicSword' } }
。
所以这就是问题所在,在这里您需要查找事件配置的准确程度,并将相同的数据放入任务对象中。
我认为将这些数据移动到单独的对象中的解决方案应该很有效。
另一件看起来不对的事情是你有 type
字段,它暗示你需要有子类。
如果您创建像 ItemPickupData
、MoveData
等数据对象,这也会得到解决。
另一种方法是让 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
基类对每个事件类都有特殊的方法,并默认拒绝它们。
然后子类可以处理特定的事件类型:
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
Requirement
类的isComplete
方法从Task
类中使用,它将类型检测重定向到事件对象。
然后事件对象将自身传递回要求的适当方法:
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)
结构可能看起来有点复杂,但根据具体情况,您可能需要 Requirement
类及其子类(例如,可以有通用的 ItemPickupRequirement
和一些特定的 PowerItemPickupRequirement
)。
关于javascript - 如何在任务系统中构建/设计事件和任务以使代码易于管理?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37948215/