データ駆動設計
最近、この質問のようなものをコードレビューに提出しました。
いくつかの提案と改善を行った結果、辞書(またはJSON)に基づいた武器の作成に比較的柔軟性を持たせることができるシンプルなコードが作成されました。データは実行時に解釈さWeapon
れ、スクリプトインタープリター全体に依存する必要なく、クラス自体によって簡単な検証が行われます。
データ駆動設計は、Pythonがインタープリター言語であるにもかかわらず(ソースファイルとデータファイルの両方を再コンパイルすることなく編集できます)、提示したような場合に行うべき正しいことのように聞こえます。この質問では、コンセプト、その長所と短所についてさらに詳しく説明します。また、コーネル大学に関する素晴らしいプレゼンテーションもあります。
おそらくスクリプト言語(LUAなど)を使用してデータxエンジンの相互作用と一般的なスクリプトを処理するC ++などの他の言語、およびデータを格納する特定のデータ形式(XMLなど)と比較して、Pythonは実際に行うことができますそれだけですべて(標準を考慮しますdict
がweakref
、後者は特にリソースのロードとキャッシング用です)。
しかし、独立した開発者は、この記事で提案されているように、データ駆動型のアプローチを極端に取り入れることはできません。
データ駆動型の設計はどのくらいですか?ゲームエンジンにゲーム固有のコードが1行含まれているとは思わない。ない1。ハードコードされた武器の種類はありません。ハードコーディングされたHUDレイアウトはありません。ハードコーディングされたユニットAIはありません。N。郵便番号 ジルチ。
おそらく、Pythonを使用すると、生産性と拡張性の両方を目指して、オブジェクト指向アプローチとデータ駆動アプローチの両方のメリットを享受できます。
簡単なサンプル処理
コードレビューで説明した特定のケースでは、辞書に「静的属性」と解釈されるロジックの両方が格納されます-武器に条件付き動作がある場合。
以下の例では、剣はクラス 'antipaladin'のキャラクターの手の中にいくつかの能力とステータスを持ち、他のキャラクターが使用する場合はステータスが低くなります。
WEAPONS = {
"bastard's sting": {
# magic enhancement, weight, value, dmg, and other attributes would go here.
"magic": 2,
# Those lists would contain the name of effects the weapon provides by default.
# They are empty because, in this example, the effects are only available in a
# specific condition.
"on_turn_actions": [],
"on_hit_actions": [],
"on_equip": [
{
"type": "check",
"condition": {
'object': 'owner',
'attribute': 'char_class',
'value': "antipaladin"
},
True: [
{
"type": "action",
"action": "add_to",
"args": {
"category": "on_hit",
"actions": ["unholy"]
}
},
{
"type": "action",
"action": "add_to",
"args": {
"category": "on_turn",
"actions": ["unholy aurea"]
}
},
{
"type": "action",
"action": "set_attribute",
"args": {
"field": "magic",
"value": 5
}
}
],
False: [
{
"type": "action",
"action": "set_attribute",
"args": {
"field": "magic",
"value": 2
}
}
]
}
],
"on_unequip": [
{
"type": "action",
"action": "remove_from",
"args": {
"category": "on_hit",
"actions": ["unholy"]
},
},
{
"type": "action",
"action": "remove_from",
"args": {
"category": "on_turn",
"actions": ["unholy aurea"]
},
},
{
"type": "action",
"action": "set_attribute",
"args": ["magic", 2]
}
]
}
}
テストの目的で、私は単純なクラスを作成Player
しWeapon
ました:最初に武器を保持/装備する(条件付きon_equip設定を呼び出す)ものと、後者を辞書としてデータを取得する単一のクラスとして、渡されたアイテム名に基づいてWeapon
初期化中の引数。これらは適切なゲームクラスの設計を反映していませんが、データをテストするのにまだ役立ちます。
class Player:
"""Represent the player character."""
inventory = []
def __init__(self, char_class):
"""For this example, we just store the class on the instance."""
self.char_class = char_class
def pick_up(self, item):
"""Pick an object, put in inventory, set its owner."""
self.inventory.append(item)
item.owner = self
class Weapon:
"""A type of item that can be equipped/used to attack."""
equipped = False
action_lists = {
"on_hit": "on_hit_actions",
"on_turn": "on_turn_actions",
}
def __init__(self, template):
"""Set the parameters based on a template."""
self.__dict__.update(WEAPONS[template])
def toggle_equip(self):
"""Set item status and call its equip/unequip functions."""
if self.equipped:
self.equipped = False
actions = self.on_unequip
else:
self.equipped = True
actions = self.on_equip
for action in actions:
if action['type'] == "check":
self.check(action)
elif action['type'] == "action":
self.action(action)
def check(self, dic):
"""Check a condition and call an action according to it."""
obj = getattr(self, dic['condition']['object'])
compared_att = getattr(obj, dic['condition']['attribute'])
value = dic['condition']['value']
result = compared_att == value
self.action(*dic[result])
def action(self, *dicts):
"""Perform action with args, both specified on dicts."""
for dic in dicts:
act = getattr(self, dic['action'])
args = dic['args']
if isinstance(args, list):
act(*args)
elif isinstance(args, dict):
act(**args)
def set_attribute(self, field, value):
"""Set the specified field with the given value."""
setattr(self, field, value)
def add_to(self, category, actions):
"""Add one or more actions to the category's list."""
action_list = getattr(self, self.action_lists[category])
for action in actions:
if action not in action_list:
action_list.append(action)
def remove_from(self, category, actions):
"""Remove one or more actions from the category's list."""
action_list = getattr(self, self.action_lists[category])
for action in actions:
if action in action_list:
action_list.remove(action)
将来の改善により、いつかは武器全体ではなく武器コンポーネントを処理する動的なクラフティングシステムを使用できるようになることを願っています...
テスト
- キャラクターAは武器を選択し、装備(その統計を印刷)してからドロップします。
- キャラクターBは同じ武器を選び、装備します(そして、その統計を再度印刷して、それらがどのように異なるかを示します)。
このような:
def test():
"""A simple test.
Item features should be printed differently for each player.
"""
weapon = Weapon("bastard's sting")
player1 = Player("bard")
player1.pick_up(weapon)
weapon.toggle_equip()
print("Enhancement: {}, Hit effects: {}, Other effects: {}".format(
weapon.magic, weapon.on_hit_actions, weapon.on_turn_actions))
weapon.toggle_equip()
player2 = Player("antipaladin")
player2.pick_up(weapon)
weapon.toggle_equip()
print("Enhancement: {}, Hit effects: {}, Other effects: {}".format(
weapon.magic, weapon.on_hit_actions, weapon.on_turn_actions))
if __name__ == '__main__':
test()
それは印刷する必要があります:
吟遊詩人のために
拡張:2、ヒット効果:[]、その他の効果:[]
アンチパラディン用
エンハンスメント:5、ヒット効果:['unholy']、その他の効果:['unholy aurea']