通用 3C 框架
国庆前开的一个坑,在 Unity 中实现一个基于状态的通用 3C 框架
Character - Controller - Camera
开发施工中,文章可读性为 0 (没法解析UML可读性更低了) ,待整理
class InputManager {
-currentInput: InputData
+Update(): void
}
class StateManager {
-currentState: State
+ChangeState(newState: State): void
+OnStateChanged(newState: State)
}
abstract class BaseController {
-inputData: InputData
-currentState: State
+HandleController(): void
}
abstract class PlayerControllerBase {
+HandleController(): void
}
abstract class BaseTranslator {
-rawInput: InputData
+translatedInput: InputData
+TranslateInput(): void
}
class CharacterBase {
-characterState: State
-currentAnimation: Animation
+Update(): void
}
InputManager --> BaseController : provides input data
BaseController --> StateManager : interacts with
BaseController --> BaseTranslator : uses
BaseController --> CharacterBase : controls
@enduml
interface IInputModule {
+playerMovement: Vector2
}
class InputManager {
+InitializeInputSystem(): void
+RegisterInputModule(module: IInputModule): void
+UnregisterInputModule(module: IInputModule): void
-inputModules: List<IInputModule>
}
class ThirdPersonCameraInputModule implements IInputModule {
+playerMovement: Vector2 { get }
}
class BasicMovementInputModule implements IInputModule {
+playerMovement: Vector2 { get }
}
class ClimbingInputModule implements IInputModule {
+playerMovement: Vector2 { get }
}
@enduml
interface IInputModule {
+playerMovement: Vector2
}
interface IController {
+HandleControl(): void
}
class State {
+Enter(): void
+Exit(): void
-inputModule: IInputModule
-controller: IController
}
class StateManager {
+ChangeState(newState: State): void
-currentState: State
}
class InputManager {
+RegisterInputModule(module: IInputModule): void
+UnregisterInputModule(module: IInputModule): void
}
class ThirdPersonMovementState extends State {
-inputModule: ThirdPersonCameraInputModule
-controller: ThirdPersonMovementController
}
@enduml
接下来我们考虑一个角色State下会需要什么:
-
状态迁移字典
- 前往下一个状态的<条件,状态>对
- 其中状态是一个新的IState资源,而条件是一个被封装好的东西,可能使用ScriptableObject
-
State需要注册的Controller和InputModule
-
将Controller和InputModule注册至ControllerManager和InputManager的方法,在进入状态时调用
之后我希望有一个视窗类,用于收集并可视化角色所有State的信息和它们之间的迁移关系。
interface IInputModule {
+playerMovement: Vector2
}
interface IController {
+HandleControl(): void
}
class Condition {
+IsMet(): bool
}
class State <<ScriptableObject>> {
+Enter(): void
+Exit(): void
+RegisterModules(): void
-inputModule: IInputModule
-controller: IController
-transitions: Dictionary<Condition, State>
}
class StateManager {
+ChangeState(newState: State): void
-currentState: State
}
class InputManager {
+RegisterInputModule(module: IInputModule): void
+UnregisterInputModule(module: IInputModule): void
}
class ControllerManager {
+RegisterController(controller: IController): void
+UnregisterController(controller: IController): void
}
class StateWindow {
+DisplayStates(states: List<State>): void
+DisplayTransitions(transitions: Dictionary<Condition, State>): void
}
State --> Condition : contains
State --> IInputModule : uses
State --> IController : uses
StateManager --> State : uses
InputManager --> IInputModule : uses
ControllerManager --> IController : uses
StateWindow --> State : uses
@enduml
State 伪代码
另外,在StateManager中需要补充一个可用State列表,可能会用于管理特定游戏模式下可用是State
这样避免State超出集合
[CreateAssetMenu]
public class State : ScriptableObject
{
public IInputModule inputModule;
public IController controller;
public List<Transition> transitions;
public void Enter()
{
RegisterModules();
}
public void Exit()
{
InputManager.UnregisterInputModule(inputModule);
ControllerManager.UnregisterController(controller);
}
public void RegisterModules()
{
InputManager.RegisterInputModule(inputModule);
ControllerManager.RegisterController(controller);
}
public State CheckTransitions()
{
foreach (var transition in transitions)
{
if (transition.condition.IsMet())
{
return transition.nextState;
}
}
return this;
}
}
[System.Serializable]
public class Transition
{
public Condition condition;
public State nextState;
}
举个例子,在StateManager中需要补充一个可用State列表,可能会用于管理特定游戏模式下可用的State,以避免状态转移超出集合。
对于这种情况,就可能需要为不同游戏模式设置不同的State转移关系,并对转移关系进行统一管理。这时,State本身管理的更多只是对IInputModule、IController的引用关系。你觉得对于这种情况,将State视为SO是否太累赘了呢?
另外,对于State可能有着更多的子类,比方说移动类型的State其状态转移更多为进入不同状态后移动方式的切换,而对于连招之类的状态,就可能需要处理动作完成的回调,从而切换自身状态迁移条件。对于这种复杂的情景,SO是否有无法较好的处理了?
interface IInputModule {
+playerMovement: Vector2
+Initialize(): void
}
interface IController {
+HandleControl(): void
+Initialize(): void
}
abstract class Condition {
+IsMet(): bool
}
class State {
+Enter(): void
+Exit(): void
+RegisterModules(): void
+CheckTransitions(): void
-inputModule: IInputModule
-controller: IController
-transitions: Dictionary<Condition, State>
}
class StateManager {
+ChangeState(newState: State): void
-currentState: State
}
class InputManager {
+RegisterInputModule(module: IInputModule): void
+UnregisterInputModule(module: IInputModule): void
+ActiveInputModule: IInputModule { get; set; }
}
class ControllerManager {
+RegisterController(controller: IController): void
+UnregisterController(controller: IController): void
+ActiveController: IController { get; set; }
}
class StateWindow {
+DisplayStates(states: List<State>): void
+DisplayTransitions(transitions: Dictionary<Condition, State>): void
}
State --> Condition : contains
State --> IInputModule : uses
State --> IController : uses
StateManager --> State : uses
InputManager --> IInputModule : uses
ControllerManager --> IController : uses
StateWindow --> State : uses
@enduml
interface IInputModule {
+ProcessInput(input: Vector2): void
}
interface IController {
+HandleControl(): void
}
class Condition {
+IsMet(): bool
}
class State {
+Enter(): void
+Exit(): void
+RegisterModules(characterManager: CharacterManager): void
-inputModule: IInputModule
-controller: IController
-transitions: Dictionary<Condition, State>
}
class StateManager {
+ChangeState(newState: State): void
-currentState: State
}
class InputManager {
+RegisterInputModule(module: IInputModule): void
+UnregisterInputModule(module: IInputModule): void
+CurrentInputModule: IInputModule
}
class ControllerManager {
+RegisterController(controller: IController): void
+UnregisterController(controller: IController): void
-currentController: IController
}
class GameManager {
+ActiveCharacter: CharacterManager
+InputManager : InputManager
}
class CharacterManager {
+HandleInput(input: Vector2): void
-stateManager: StateManager
-inputManager: InputManager
-controllerManager: ControllerManager
}
State --> Condition : contains
State --> IInputModule : uses
State --> IController : uses
StateManager --> State : uses
CharacterManager --> StateManager : uses
CharacterManager --> InputManager : uses
CharacterManager --> ControllerManager : uses
InputManager --> IInputModule : manages
ControllerManager --> IController : manages
GameManager --> CharacterManager : uses
GameManager --> InputManager : uses
@enduml
interface IInputModule {
+ProcessInput(): void
}
interface ICharacterController {
+HandleControl(): void
+RegisterInputModule(): void
-inputModule: IInputModule
}
interface ICameraController {
+HandleControl(): void
+RegisterInputModule(): void
-inputModule: IInputModule
}
class Condition {
+IsMet(): bool
}
class State <<ScriptableObject>> {
+Enter(): void
+Exit(): void
+RegisterControllers(characterManager: CharacterManager): void
-characterController: ICharacterController
-cameraController: ICameraController
}
class StateManager {
+ChangeState(newState: State): void
-currentState: State
}
class InputManager {
+RegisterInputModule(module: IInputModule): void
+UnregisterInputModule(module: IInputModule): void
}
class CharacterManager {
-stateManager: StateManager
-inputManager: InputManager
}
State --> Condition : contains
State --> ICharacterController : uses
State --> ICameraController : uses
StateManager --> State : uses
CharacterManager --> StateManager : uses
CharacterManager --> InputManager : uses
ICharacterController --> IInputModule : uses
ICameraController --> IInputModule : uses
InputManager --> IInputModule : manages
@enduml
/Assets
/Scripts
/Managers
GameManager.cs
CharacterManager.cs
StateManager.cs
InputManager.cs
/States
State.cs
ConcreteState1.cs
ConcreteState2.cs
...
/Conditions
Condition.cs
ConcreteCondition1.cs
ConcreteCondition2.cs
...
/Controllers
IController.cs
PlayerController.cs
EnemyController.cs
...
/InputModules
IInputModule.cs
BasicMovementInputModule.cs
ThirdPersonCameraInputModule.cs
...
/Prefabs
PlayerPrefab.prefab
EnemyPrefab.prefab
/Resources
/States
ConcreteState1.asset
ConcreteState2.asset
...
类名 | 类型 | 完成 | 描述 |
---|---|---|---|
InputActions | InputSystem的输入Map,用于接收信息 | ||
InputModule | 用于翻译输入至Controller可读 | ||
InputManager | 管理器 | 管理InputModule的装载与卸载 | |
GameManager | 管理器 | 管理游戏逻辑 | |
CharacterManager | 管理器 | 待定管理器,创建角色的中介层 | |
Character | 定义游戏中的角色,通常与Gameplay关联 | ||
CharacterBase | 角色基类,主要管理状态、Pawn、Camera | ||
StateManager | 所属Character的状态管理器,储存状态迁移相关信息,决定了角色可用的状态 | ||
CharacterState | FSM状态类型,描述角色状态特供。 会储存一系列状态相关Controller |
||
Controller | Mono | 被State所指定,用于读取玩家输入信号,创建Pawn运动和状态转移 | |
Pawn | Mono | 用于标识可控制角色的基类。 装载 Pawn 时会自动装载 Animator / CC 等模组,实现角色相关操控和表现接口 |
|
基于连招状态表,储存抽象State对象
包含如硬直恢复时间,恢复操作等特殊
开辟镜头和角色状态两组状态机,分别用于管理镜头控制状态和角色操控状态
Comments NOTHING