Thiết kế hướng dữ liệu
Tôi đã gửi một cái gì đó như câu hỏi này để xem xét mã gần đây.
Sau một số gợi ý và cải tiến, kết quả là một mã đơn giản cho phép sự linh hoạt tương đối trong việc tạo vũ khí dựa trên từ điển (hoặc JSON). Dữ liệu được diễn giải trong thời gian chạy và việc xác minh đơn giản được thực hiện bởi Weapon
chính lớp đó, mà không cần phải dựa vào toàn bộ trình thông dịch tập lệnh.
Data-Driven Design, mặc dù Python là ngôn ngữ được dịch (cả tệp nguồn và dữ liệu đều có thể được chỉnh sửa mà không cần biên dịch lại chúng), nghe có vẻ đúng trong trường hợp như bạn đã trình bày. Câu hỏi này đi sâu vào chi tiết hơn về khái niệm, ưu và nhược điểm của nó. Có một bài thuyết trình hay trên Đại học Cornell về nó.
So với các ngôn ngữ khác, chẳng hạn như C ++, có thể sẽ sử dụng ngôn ngữ kịch bản lệnh (như LUA) để xử lý tương tác dữ liệu và động cơ dữ liệu nói chung và một định dạng dữ liệu nhất định (như XML) để lưu trữ dữ liệu, Python thực sự có thể làm được tất cả chỉ có một mình (xem xét tiêu chuẩn dict
mà còn weakref
, sau này đặc biệt cho tải tài nguyên và bộ đệm).
Tuy nhiên, một nhà phát triển độc lập có thể không đưa cách tiếp cận dựa trên dữ liệu đến mức cực đoan như đề xuất trong bài viết này :
Tôi có bao nhiêu về thiết kế dựa trên dữ liệu? Tôi không nghĩ rằng một công cụ trò chơi nên chứa một dòng mã dành riêng cho trò chơi. Không một. Không có loại vũ khí mã hóa cứng. Không có bố cục HUD mã hóa cứng. Không có AI đơn vị mã hóa cứng. Nada. Zip. Zilch.
Có lẽ, với Python, người ta có thể hưởng lợi từ cách tiếp cận hướng đối tượng và hướng dữ liệu tốt nhất, hướng đến cả năng suất và khả năng mở rộng.
Xử lý mẫu đơn giản
Trong trường hợp cụ thể được thảo luận về đánh giá mã, một từ điển sẽ lưu trữ cả "thuộc tính tĩnh" và logic cần được giải thích - nếu vũ khí có bất kỳ hành vi có điều kiện nào.
Trong ví dụ bên dưới, một thanh kiếm sẽ có một số khả năng và chỉ số trong tay các nhân vật của lớp 'antipaladin', và không có hiệu ứng, với chỉ số thấp hơn khi được sử dụng bởi các nhân vật khác):
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]
}
]
}
}
Với mục đích thử nghiệm, tôi đã tạo ra các lớp đơn giản Player
và Weapon
: các lớp đầu tiên giữ / trang bị vũ khí (do đó gọi cài đặt on_equip có điều kiện của nó) và lớp sau là một lớp duy nhất lấy dữ liệu từ từ điển, dựa trên tên vật phẩm được truyền dưới dạng đối số trong quá trình Weapon
khởi tạo. Chúng không phản ánh thiết kế các lớp trò chơi phù hợp, nhưng vẫn có thể hữu ích để kiểm tra dữ liệu:
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)
Với một số cải tiến trong tương lai, tôi hy vọng rằng điều này thậm chí sẽ cho phép tôi có một hệ thống chế tạo năng động vào một ngày nào đó, xử lý các thành phần vũ khí thay vì toàn bộ vũ khí ...
Kiểm tra
- Nhân vật Một vũ khí chọn, trang bị cho nó (chúng tôi in các chỉ số của nó), sau đó thả nó xuống;
- Nhân vật B chọn cùng một vũ khí, trang bị cho nó (và chúng tôi in lại chỉ số của nó để cho thấy chúng khác nhau như thế nào).
Như thế này:
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()
Nó nên in:
Cho một lần đặt cược
Cải tiến: 2, Hiệu ứng trúng: [], Hiệu ứng khác: []
Đối với một antipaladin
Tăng cường: 5, Hiệu ứng hit: ['u ám'], Hiệu ứng khác: ['aurea aurea']