데이터 중심 설계
최근 에 코드 검토 를 위해이 질문과 같은 것을 제출 했습니다.
몇 가지 제안과 개선 후 결과는 사전 (또는 JSON)을 기반으로하는 무기 생성에 대한 상대적 유연성을 허용하는 간단한 코드였습니다. 데이터는 런타임에 해석되며 Weapon
전체 스크립트 인터프리터에 의존 할 필요없이 클래스 자체에서 간단한 검증을 수행합니다 .
데이터 중심 디자인, 파이썬은 해석 언어 (소스 및 데이터 파일 모두를 다시 컴파일 할 필요없이 편집 할 수 있음) 임에도 불구하고 제시 한 것과 같은 경우에 옳은 것처럼 들립니다. 이 질문 은 개념, 장단점에 대해 자세히 설명합니다. 코넬 대학 에 관한 멋진 프레젠테이션 도 있습니다.
C ++과 같은 다른 언어와 비교하여 아마도 LUA와 같은 스크립팅 언어를 사용하여 일반적으로 데이터 x 엔진 상호 작용 및 스크립팅을 처리하고 데이터를 저장하기 위해 특정 데이터 형식 (XML과 같은)을 처리 할 수 있습니다. 그것은 모두 자체적으로 (표준을 고려 dict
하지만 weakref
후자는 특히 리소스로드 및 캐싱을 위해) 고려합니다.
그러나 독립 개발자 는이 기사에서 제안한대로 데이터 중심 접근 방식을 최대한 활용하지 못할 수 있습니다 .
데이터 중심 디자인은 어느 정도입니까? 게임 엔진에 한 줄의 게임 특정 코드가 포함되어 있다고 생각하지 않습니다. 하나도 아니야 하드 코딩 된 무기 유형이 없습니다. 하드 코드 된 HUD 레이아웃이 없습니다. 하드 코딩 된 유닛 AI가 없습니다. 나다. 지퍼. 제로.
파이썬을 사용하면 생산성과 확장 성을 모두 목표로 객체 지향 및 데이터 중심 접근 방식 중 가장 좋은 이점을 얻을 수 있습니다.
간단한 시료 처리
코드 검토에서 논의 된 특정 사례에서 사전은 "정적 속성"과 해석 할 논리를 저장합니다. 무기에 조건부 동작이있는 경우.
아래 예에서 칼은 '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, 적중 효과 : [ '불량'], 기타 효과 : [ '불량'