การออกแบบที่ขับเคลื่อนด้วยข้อมูล
ฉันส่งบางสิ่งเช่นคำถามนี้ไปยังการตรวจสอบโค้ดเมื่อเร็ว ๆ นี้
หลังจากข้อเสนอแนะและการปรับปรุงผลที่ได้คือรหัสง่ายๆที่จะช่วยให้ความยืดหยุ่นในการสร้างอาวุธขึ้นอยู่กับพจนานุกรม (หรือ JSON) ข้อมูลถูกตีความที่รันไทม์และการตรวจสอบง่ายทำโดยWeapon
คลาสเองโดยไม่จำเป็นต้องพึ่งพาล่ามสคริปต์ทั้งหมด
Data-Driven Design แม้ว่า Python จะเป็นภาษาที่ตีความ (ทั้งซอร์สและไฟล์ข้อมูลสามารถแก้ไขได้โดยไม่จำเป็นต้องคอมไพล์ใหม่) แต่ดูเหมือนว่าสิ่งที่ถูกต้องในกรณีเช่นที่คุณนำเสนอ คำถามนี้มีรายละเอียดเพิ่มเติมเกี่ยวกับแนวคิดข้อดีข้อเสียของมัน นอกจากนี้ยังมีการนำเสนอที่ดีเกี่ยวกับ Cornell Universityเกี่ยวกับเรื่องนี้
เมื่อเทียบกับภาษาอื่นเช่น C ++ ที่อาจใช้ภาษาสคริปต์ (เช่น LUA) เพื่อจัดการการโต้ตอบระหว่าง data x engine และการเขียนสคริปต์โดยทั่วไปและรูปแบบข้อมูลบางอย่าง (เช่น XML) เพื่อเก็บข้อมูล Python สามารถทำได้จริง มันทั้งหมดด้วยตัวเอง (พิจารณามาตรฐานdict
แต่ยังweakref
หลังเฉพาะสำหรับการโหลดทรัพยากรและแคช)
อย่างไรก็ตามผู้พัฒนาอิสระอาจไม่ใช้วิธีการที่ขับเคลื่อนด้วยข้อมูลเป็นอย่างมากดังที่แนะนำไว้ในบทความนี้ :
ฉันมีการออกแบบที่ขับเคลื่อนด้วยข้อมูลมากน้อยแค่ไหน? ฉันไม่คิดว่าเอ็นจิ้นเกมควรมีรหัสเฉพาะของเกมบรรทัดเดียว ไม่ใช่หนึ่งเดียว ไม่มีอาวุธประเภท hardcoded ไม่มีโครงร่าง HUD ที่ฮาร์ดโค้ด ไม่มีหน่วย hardcoded AI Nada ซิป zilch
บางทีด้วย 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, เอฟเฟกต์ Hit: [], เอฟเฟกต์อื่น ๆ : []
สำหรับ antipaladin
การเพิ่มประสิทธิภาพ: 5, เอฟเฟกต์ Hit: ['unholy'], เอฟเฟกต์อื่น ๆ : ['unholy aurea']