EnhancedPlayerInput
PlayerInput的子类。内部通过InputMappingContext管理按键响应。
一个IMC是一套按键和行为的集合。
而EnhancedPlayerInput可以按优先级有多个IMC。
当前使用的IMC存放在AppliedInputContexts。Value为优先级。
TMap<TObjectPtr
当它的内容发生变化时会触发RebuildControlMappings,这个函数在IEnhancedInputSubsystemInterface中定义。IEnhancedInputSubsystemInterface和EnhancedPlayerInput一一绑定。
RebuildControlMappings会确保相同按键按优先级只绑定一个Action。
最终的绑定结果存放在EnhancedActionMappings中。并为每一个绑定的Action创建一个实例数据ActionInstanceData。
TArray
TMap<TObjectPtr
Build过程
- 转移EnhancedActionMappings数据到另一个结构上。创建Set类型AppliedKeys,避免相同按键重复绑定。
- 对IMC进行排序,并一一迭代,将Mapping加入到EnhancedActionMappings中。通过AppliedKeys过滤重复的按键绑定。
- 确定是否立刻清空ActionInstanceData(相同的按键响应如果冲突了是否立刻被打断。)如果不是立刻,会根据EnhancedActionMappings删除ActionInstanceData中已经没有的Action数据。
主要做3件事情,循环往复:
- 接收按键事件;
- 更新按键状态;
- 触发按键行为;
接收按键事件
时机:这部分由WinPumpMessage触发,一帧的开头。最终在PlayerInput:InputKey() 中处理。
结果:更新PlayerInput中的KeyStateMap,以按键为Key。根据传入的事件类型(按下,重复,松开等)更新对应的数值(RawValueAccumulator)和事件计数(Pressed,Release)。
这部分信息会在第二部继续使用。
更新按键状态
第一部分拿到了原始按键数据,在这一步会更进一步处理。
时机:由PlayerController的TickActor调用到TickPlayerInput,接着由ProcessPlayerInput调用到EnhancedPlayerInput的ProcessInputStack,经过基类中调用PlayerInput::EvaluateKeyMapState.
结果:把上面RawValueAccumulator赋值给KeyState中的RawValue,根据事件计数(Pressed - Release > 0) 确定按键是否按下。同时将RawValueAccumulator,SampleCountAccumulator都清0。
触发按键行为
时机:和上面一致,发生在ProcessInputStack调用中,并由子类EnhancedPlayerInput重载。
过程:
- 遍历EnhancedActionMappings,根据KeyState信息确定这一帧触发的Action放在ActionsWithEventsThisTick。
- 更新所有的ActionInstanceData,如果Action在当前帧,则需要更新TriggerState信息,通过Action上的Modifier。
- 遍历所有的监听者EnhancedInputComponent。搜集要触发的Delgates
- 如果可以触发,则触发Delegates。
总结:
这里有两层的映射关系。Key->Action->Binding,其中Key->Action由IMC定义,并按优先级无重复Key的方式管理在EnhancedActionMappings中。
Action->Binding则由各自监听者EnhancedInputComponent维护,存放在FEnhancedInputActionEventBinding中。EnhancedInput负责在从Key合理地映射到Binding
EnhancedInputComponent
存储触发的delegates。内部称为EventBinding,各个地方可以监听自己关心的Action。触发自己的deletages。