EnhancedPlayerInput

PlayerInput的子类。内部通过InputMappingContext管理按键响应。
一个IMC是一套按键和行为的集合。
而EnhancedPlayerInput可以按优先级有多个IMC。

当前使用的IMC存放在AppliedInputContexts。Value为优先级。
TMap<TObjectPtr, int32> AppliedInputContexts;

当它的内容发生变化时会触发RebuildControlMappings,这个函数在IEnhancedInputSubsystemInterface中定义。IEnhancedInputSubsystemInterface和EnhancedPlayerInput一一绑定。
RebuildControlMappings会确保相同按键按优先级只绑定一个Action。
最终的绑定结果存放在EnhancedActionMappings中。并为每一个绑定的Action创建一个实例数据ActionInstanceData。
TArray EnhancedActionMappings;
TMap<TObjectPtr, FInputActionInstance> ActionInstanceData;

Build过程

  1. 转移EnhancedActionMappings数据到另一个结构上。创建Set类型AppliedKeys,避免相同按键重复绑定。
  2. 对IMC进行排序,并一一迭代,将Mapping加入到EnhancedActionMappings中。通过AppliedKeys过滤重复的按键绑定。
  3. 确定是否立刻清空ActionInstanceData(相同的按键响应如果冲突了是否立刻被打断。)如果不是立刻,会根据EnhancedActionMappings删除ActionInstanceData中已经没有的Action数据。

主要做3件事情,循环往复:

  1. 接收按键事件;
  2. 更新按键状态;
  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重载。
过程:

  1. 遍历EnhancedActionMappings,根据KeyState信息确定这一帧触发的Action放在ActionsWithEventsThisTick。
  2. 更新所有的ActionInstanceData,如果Action在当前帧,则需要更新TriggerState信息,通过Action上的Modifier。
  3. 遍历所有的监听者EnhancedInputComponent。搜集要触发的Delgates
  4. 如果可以触发,则触发Delegates。

总结:
这里有两层的映射关系。Key->Action->Binding,其中Key->Action由IMC定义,并按优先级无重复Key的方式管理在EnhancedActionMappings中。
Action->Binding则由各自监听者EnhancedInputComponent维护,存放在FEnhancedInputActionEventBinding中。EnhancedInput负责在从Key合理地映射到Binding

EnhancedInputComponent

存储触发的delegates。内部称为EventBinding,各个地方可以监听自己关心的Action。触发自己的deletages。