Desain Berbasis Data
Saya mengirimkan sesuatu seperti pertanyaan ini ke tinjauan kode baru-baru ini.
Setelah beberapa saran dan peningkatan, hasilnya adalah kode sederhana yang memungkinkan fleksibilitas relatif pada pembuatan senjata berdasarkan kamus (atau JSON). Data ditafsirkan pada saat runtime dan verifikasi sederhana dilakukan oleh Weapon
kelas itu sendiri, tanpa perlu bergantung pada seluruh juru bahasa skrip.
Desain Berbasis Data, meskipun Python menjadi bahasa yang ditafsirkan (baik sumber dan file data dapat diedit tanpa perlu mengkompilasi ulang), terdengar seperti hal yang benar untuk dilakukan dalam kasus-kasus seperti yang Anda sajikan. Pertanyaan ini menjelaskan lebih lanjut tentang konsep, pro dan kontra. Juga ada presentasi yang bagus tentang Universitas Cornell tentang hal itu.
Dibandingkan dengan bahasa lain, seperti C ++, yang mungkin akan menggunakan bahasa scripting (seperti LUA) untuk menangani data x interaksi mesin dan skrip pada umumnya, dan format data tertentu (seperti XML) untuk menyimpan data, Python sebenarnya bisa melakukan itu semua dengan sendirinya (mengingat standar dict
tetapi juga weakref
, yang terakhir khusus untuk pemuatan sumber daya dan caching).
Namun, pengembang independen mungkin tidak menggunakan pendekatan berbasis data secara ekstrim seperti yang disarankan pada artikel ini :
Seberapa banyak tentang desain berbasis data? Saya tidak berpikir mesin game harus berisi satu baris kode khusus game. Bukan satu. Tidak ada jenis senjata yang dikode dengan keras. Tidak ada tata letak HUD hardcoded. Tidak ada unit hardcoded AI. Nada. Zip. Zilch.
Mungkin, dengan Python, orang dapat mengambil manfaat dari pendekatan berorientasi objek dan data-driven, yang bertujuan untuk produktivitas dan ekstensibilitas.
Pemrosesan sampel sederhana
Dalam kasus khusus yang dibahas pada tinjauan kode, kamus akan menyimpan "atribut statis" dan logika yang akan ditafsirkan - jika senjata memiliki perilaku kondisional.
Pada contoh di bawah pedang harus memiliki beberapa kemampuan dan statistik di tangan karakter kelas 'antipaladin', dan tidak ada efek, dengan statistik lebih rendah ketika digunakan oleh karakter lain):
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]
}
]
}
}
Untuk tujuan pengujian, saya membuat kelas Player
dan sederhana Weapon
: yang pertama memegang / melengkapi senjata (dengan demikian memanggil pengaturan on_equip kondisional) dan yang terakhir sebagai kelas tunggal yang akan mengambil data dari kamus, berdasarkan pada nama item yang dilewatkan sebagai argumen selama Weapon
inisialisasi. Mereka tidak mencerminkan desain kelas game yang tepat, tetapi masih bisa berguna untuk menguji data:
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)
Dengan beberapa perbaikan di masa depan saya berharap bahwa ini bahkan akan memungkinkan saya untuk memiliki sistem kerajinan dinamis suatu hari nanti, memproses komponen senjata daripada seluruh senjata ...
Uji
- Character A mengambil senjata, melengkapinya (kami mencetak statistiknya), lalu menjatuhkannya;
- Karakter B mengambil senjata yang sama, melengkapinya (dan kami mencetak statistiknya lagi untuk menunjukkan perbedaannya).
Seperti ini:
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()
Itu harus mencetak:
Untuk seorang bard
Peningkatan: 2, efek Hit: [], Efek lainnya: []
Untuk antipaladin
Peningkatan: 5, efek Hit: ['tidak suci'], Efek lainnya: ['unholy aurea']