State
动机(Motivation)
- 对象状态如果改变,其行为也会随之而发生变化,比如文档处于只读状态,其支持的行为和读写状态支持的行为就可能完全不同。
- 如何在运行时根据对象的状态来透明地改变对象的行为?
模式定义
允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为。
——《设计模式》GoF
要点总结
- State模式将所有与一个特定状态相关的行为都放入一个State的子对象中,在对象状态切换时,切换相应的对象;但同时维持State的接口,这样实现了具体操作与状态转换之间的解耦。
- 转换是原子性的
- 与Strategy模式类似
例子
# -*- coding:utf-8 -*-unique
from enum import Enum
class AimStateAction(Enum):
"""状态机行为类型,是状态机接受的外部指令"""
IDLE = 1
MOVE = 2
JUMP = 3
CROUCH = 4
FIRE = 5
SHOOT_AIM = 6
class AimStateType(Enum):
"""状态类型"""
IDLE = 1
MOVE = 2
JUMP = 3
CROUCH = 4
FIRE = 5
SHOOT_AIM = 6
# 每个状态对应的准星大小
StateAimSizeMap = {
AimStateType.IDLE: 0,
AimStateType.MOVE: 2,
AimStateType.JUMP: 3,
AimStateType.CROUCH: -1,
AimStateType.FIRE: 1,
AimStateType.SHOOT_AIM: -3,
}
# 状态机表,行为和状态之间的关系
AVATAR_ACTION_MAP = {
AimStateAction.IDLE: {'enter': (AimStateType.IDLE,),
'break': (AimStateType.MOVE, AimStateType.JUMP, AimStateType.CROUCH, AimStateType.SHOOT_AIM),
'cancel': ()},
AimStateAction.MOVE: {'enter': (AimStateType.MOVE,),
'break': (),
'cancel': ( AimStateType.MOVE,)},
AimStateAction.JUMP: {'enter': (AimStateType.JUMP,),
'break': (AimStateType.CROUCH, AimStateType.SHOOT_AIM,),
'cancel': ( AimStateType.JUMP,)},
AimStateAction.CROUCH: {'enter': (AimStateType.CROUCH,),
'break': (),
'cancel': (AimStateType.CROUCH, AimStateType.JUMP,)},
AimStateAction.SHOOT_AIM: {'enter': (AimStateType.SHOOT_AIM,),
'break': (),
'cancel': (AimStateType.SHOOT_AIM, AimStateType.JUMP,)},
}
class ShootAim(object):
def __init__(self):
self.aim_state_map = {}
def handle_action(self, aim_action):
print('-'*30)
print('handle_action:%s', aim_action)
if not self.check_aim_action(aim_action):
return False
for cancel_type in AVATAR_ACTION_MAP[aim_action]['cancel']:
if self.get_cur_aim_state(cancel_type):
self.cancel_action(cancel_type)
for break_type in AVATAR_ACTION_MAP[aim_action]['break']:
self.break_state(break_type)
for enter_type in AVATAR_ACTION_MAP[aim_action]['enter']:
self.enter_state(enter_type)
# 状态更新后,更新准星大小
self._update_aim_size()
def check_aim_action(self, aim_action):
if aim_action not in AVATAR_ACTION_MAP:
print('check_aim_action failed: aim_action:%s not in AVATAR_ACTION_MAP')
return False
return True
def get_cur_aim_state(self, aim_type):
return self.aim_state_map.get(aim_type, False)
def enter_state(self, aim_type):
if self.get_cur_aim_state(aim_type):
return True
self.aim_state_map[aim_type] = True
print('enter state:%s' % aim_type)
return True
def break_state(self, aim_type):
if not self.get_cur_aim_state(aim_type):
return True
self.aim_state_map[aim_type] = False
print('break state:%s' % aim_type)
return True
def cancel_action(self, aim_type):
print('cancel_action, because can not break state:%s' % aim_type)
def _update_aim_size(self):
# 根据当前状态计算准星大小
aim_size = 0
for state, value in self.aim_state_map.items():
if value:
aim_size += StateAimSizeMap[state]
print('aim_state_map:', self.aim_state_map)
print('aim_size:', aim_size)
if __name__ == '__main__':
shoot_aim = ShootAim()
shoot_aim.handle_action(AimStateAction.MOVE)
shoot_aim.handle_action(AimStateAction.CROUCH)
shoot_aim.handle_action(AimStateAction.JUMP)
shoot_aim.handle_action(AimStateAction.SHOOT_AIM)
- 射击游戏中,玩家状态会影响射击的准星大小,而准星大小决定子弹的散射范围。
- 准星大小和玩家的行为相关,比如移动、跳跃、爬、射击等。
-
AVATAR_ACTION_MAP
配置了玩家的行为和状态之间的关系
(1)AVATAR_ACTION_MAP
--enter
配置了行为进入哪个状态。比如:跳跃行为AimStateAction. JUMP
可以进入跳跃状态AimStateType. JUMP
(2)AVATAR_ACTION_MAP
--break
配置了行为可以打断当前状态。比如:跳跃行为AimStateAction. JUMP
可以打断匍匐状态AimStateType. CROUCH
(3)AVATAR_ACTION_MAP
--cancel
配置了行为不能打断的状态。比如:开镜行为AimStateAction. SHOOT_AIM
不能打断跳跃状态AimStateType. JUMP
。即玩家不能一边跳跃一边开倍镜。 - 代码中通过接口
enter_state()
、break_state()
、cancel_action()
来处理状态的进入、状态的打断、状态不能打断时的处理逻辑。 - 状态可以并存,比如可以在开倍镜后,一边移动,一边开抢
- 最后根据当前状态计算准星大小
网友评论