EnHanced Input
EnHancedInput插件EnHancedInout插件增加了更加复杂的输入
实现了更多的输入功能
使用EnHancedInput插件
使用EnhancedInput插件需要再编辑器插件中打开
在项目设置里找到输入(Input) =>默认类(Default Classes) 设置为EnhancedInput
主要功能
输入操作(Input Actions)
是Enhanced Input系统和项目代码之间的通信桥梁。输入操作可以是角色的任何交互操作,例如跳跃、开门。输入操作还能表示用户的输入状态,例如按住按钮让角色从行走变为奔跑状态。输入操作与原始输入(raw input)是相互独立的;输入操作无需知道是什么触发了它,但它知道其当前状态,并且最多能将输入值反应在三个维度上(三个独立浮点型数值)。例如,"拾取道具"操作可能只需要一个开/关状态,用于指示用户是否希望角色捡起某个对象,而"行走"操作可能需要两个轴来描述角色行走时的方向和速度。
创建输入操作
创建输入操作
InputAction 编辑
输入操作说明
[*]Consume Input(输入穿透(是否影响更低优先级的输入))
[*]Trigger when Paused(游戏暂停状态下是否触发)
[*]Reserve All Mappings (输入不受高优先级的Mapping控制,需要先手动删除)
[*]Value Type(输入类型)
[*]Bool
[*]Float
[*]Vector2
[*]Vector3
[*]Triggers(输入触发器列表)
[*]Modifiers(输入修饰符列表)
输入操作使用
[*]Triggered:触发事件
[*]Started:开始事件(可能也会Triggered)
[*]OnGoing:处理中(需要Trigger列表有支持的Trigger)
[*]Cancel:取消(需要Trigger列表有支持的Trigger)
[*]Completed:完成 状态从Trigger->None
[*]Action Value:(输入值,和InputAction.ValueType对应)
[*]ElapsedTime:执行的时间
[*]Trigger Seconds:触发的时间
[*]Input Action:输入操作
输入映射上下文(Input Mapping Context)
负责将用户的输入映射成输入操作;它可以根据用户需求动态增减或调整优先级。你可以通过 增强输入本地玩家子系统(Enhanced Input Local Player Subsystem) 将一个或多个上下文应用到本地玩家身上,并调整它们的优先级,避免多个输入操作因为尝试读取同一输入而发生的冲突。一个常见情况是,某个按钮既可以用来打开场景中的门,又可以用来拾取背包中的道具。当角色靠近门时,你可以添加"开门"这个上下文。假如角色打开背包,你可以添加"选择道具"这个输入映射上下文,并且让它的优先级高于"开门"。这样,当角色站在门旁边时,仍然可以选择背包中的道具。当角色合上背包后,你可以删除"选择道具"上下文,让"开门"上下文生效。这样就能根据角色的环境来正确地解译用户的输入操作,不再需要在输入层面编写代码来区别对待开门和背包系统。
创建输入映射上下文
输入操作说明
[*]Mappings(映射列表)
[*]Input Action Asset(输入操作资产)
[*]TArray<FEnhancedActionKeyMapping> Mappings;(按键映射列表)
[*]FKey (按键绑定)
[*]Triggers(输入触发器,决定是否触发Input Action(输入操作))
[*]Modifiers(输入修饰符,将原始输入设备的值处理后发送到Triggers触发器)
[*]IsPlayerMappable(bool值,是否公开到玩家绑定的按键?)
[*]PlayerMappableOptions (上面的设置)
[*]Name (名字)
[*]Display Name (显示名字)
[*]Display Category (分类)
[*]Config
[*]Description(映射列表说明)
使用输入映射
触发器(Triggers)
输入触发器决定了用户输入后是否会激活输入操作(Input Actions)
创建自己的输入触发器
输入触发器的类型
[*]显示(Explicit): 如果输入触发器成功,该类型将使输入成功。
[*]隐式(Implicit):如果输入触发器和所有其他隐式类型输入触发器成功,该类型将使输入成功。
[*]拦截器(Blocker):如果输入触发器成功,该类型将使输入失败。
输入触发器的状态
在处理用户输入后,触发器返回三种状态之一
[*]无(None):触发器状态未满足,失败
[*]进行中(OnGoing):部分满足,还在处理
[*]已触发(Triggered):全部满足
常见的触发器
[*]按下(Pressed)
[*]Actuation Threshold (阈值)
[*]按住(Down)
[*]Actuation Threshold
[*]抬起(Release)
[*]Actuation Threshold
[*]按住多少时间(Hold)
[*]Hold Time Threshold: 按住多久触发
[*]Is One Shot:只触发一次?
[*]Affected By Time Dilation:时间膨胀是否影响
[*]每隔一段时间触发(Pluse)
[*]Trigger On Start:一开始就触发
[*]Interval:间隔
[*]Trigger Limit:最大次数
[*]Affected By TimeDilation:是否受时间膨胀影响
[*]Actuation Threshold
[*]按住并抬起(HoldAndRelease)
[*]Hold Time Threshold:多久触发
[*]Affected By TimeDilation:是否受时间膨胀影响
[*]Actuation Threshold
[*]按下并快速释放(Tap)
[*]Tap Release Time Threshold:按下抬起触发最长时间
[*]Affected By TimeDilation:是否受时间膨胀影响
[*]Actuation Threshold
[*]其他触发器触发了触发此触发器(Chorded Action)
[*]Chord Action :其他触发器
[*]Actuation Threshold
[*]用户自定义触发器(Others)
输入修饰(Modifiers)
调整来自用户设备的原始输入的值。在输入映射上下文中,每个输入操作的原始输入可以绑定的任意数量的修饰符。
顾名思义,就是用来改编输入设备获取的输入值,经过一层一层的处理后,传给Trigger
创建自己的输入修饰
常见的修饰符
[*]死区(DeadZone):限制值的范围
[*]计算:取 -1~1 之间的值 (如果小于LowerThreshold 则为0)
FMath::Min(1.f, (FMath::Max(0.f, FMath::Abs(AxisVal) - LowerThreshold) / (UpperThreshold - LowerThreshold))) * FMath::Sign(AxisVal);
[*]最小阈值(Lower Threshold):低于这个值就为0 (需要比Up Threshold小)
[*]最大阈值(Up Threshold):大于Lower Threshold 的值根据 值域映射到
[*]类型(Type):计算类型
[*]Axial:每个维度单独计算
[*]Radial:合并一起计算
[*]缩放(Scalar)
[*]Scalar:三维向量,每个维度的缩放比,bool类型不缩放
[*]反向(Negate)
[*]X: X方向反向
[*]Y: Y取反
[*]Z: Z取反
[*]多帧平滑(Smooth)
FInputActionValue UInputModifierSmooth::ModifyRaw_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime)
{
uint8 SampleCount = 1;//KeyState->SampleCountAccumulator; // TODO: Need access to axis sample count accumulator here.
// TODO: This could be fired multiple times if modifiers are badly set up, breaking sample count/deltatime updates.
if(AverageValue.GetMagnitudeSq() != 0.f)
{
TotalSampleTime += DeltaTime;
Samples += SampleCount;
}
if (DeltaTime < 0.25f)
{
if (Samples > 0 && TotalSampleTime > 0.0f)
{
// this is seconds/sample
const float AxisSamplingTime = TotalSampleTime / Samples;
check(AxisSamplingTime > 0.0f);
if (CurrentValue.GetMagnitudeSq() && SampleCount > 0)
{
ZeroTime = 0.0f;
if (AverageValue.GetMagnitudeSq())
{
// this isn&#39;t the first tick with non-zero mouse movement
if (DeltaTime < AxisSamplingTime * (SampleCount + 1))
{
// smooth mouse movement so samples/tick is constant
CurrentValue *=DeltaTime / (AxisSamplingTime * SampleCount);
SampleCount = 1;
}
}
AverageValue = CurrentValue * (1.f / SampleCount);
}
else
{
// no mouse movement received
if (ZeroTime < AxisSamplingTime)
{
// zero mouse movement is possibly because less than the mouse sampling interval has passed
CurrentValue = AverageValue.ConvertToType(CurrentValue) * (DeltaTime / AxisSamplingTime);
}
else
{
ClearSmoothedAxis();
}
ZeroTime += DeltaTime; // increment length of time we&#39;ve been at zero
}
}
}
else
{
// if we had an abnormally long frame, clear everything so it doesn&#39;t distort the results
ClearSmoothedAxis();
}
// TODO: FortPlayerInput clears the sample count accumulator here!
//KeyState->SampleCountAccumulator = 0;
return CurrentValue;
}
[*]指数(Exponential)
[*]三维的指数(Curve Exponent)
[*]三个维度的指数,bool类型不参与运算
FInputActionValue UInputModifierResponseCurveExponential::ModifyRaw_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime)
{
FVector ResponseValue = CurrentValue.Get<FVector>();
switch (CurrentValue.GetValueType())
{
case EInputActionValueType::Axis3D:
ResponseValue.Z = CurveExponent.Z != 1.f ? FMath::Sign(ResponseValue.Z) * FMath::Pow(FMath::Abs(ResponseValue.Z), CurveExponent.Z) : ResponseValue.Z;
//[];
case EInputActionValueType::Axis2D:
ResponseValue.Y = CurveExponent.Y != 1.f ? FMath::Sign(ResponseValue.Y) * FMath::Pow(FMath::Abs(ResponseValue.Y), CurveExponent.Y) : ResponseValue.Y;
//[];
case EInputActionValueType::Axis1D:
ResponseValue.X = CurveExponent.X != 1.f ? FMath::Sign(ResponseValue.X) * FMath::Pow(FMath::Abs(ResponseValue.X), CurveExponent.X) : ResponseValue.X;
break;
}
return ResponseValue;
};
[*]用户曲线(User Defined)
[*]X曲线: X轴二维曲线资源
[*]Y曲线: Y轴二维曲线资源
[*]Z曲线: Z轴二维曲线资源
[*]使用FOV 缩放(FOV Scaling)
[*]缩放比例(FOVScale)
[*]类型(FOVScaling Type)
[*]Standard
const float FOVAngle = PC->PlayerCameraManager ? PC->PlayerCameraManager->GetFOVAngle() : 1.f;
float Scale = FOVScale;
const float kPlayerInput_BaseFOV = 80.0f;
const float BaseHalfFOV = kPlayerInput_BaseFOV * 0.5f;
const float HalfFOV = FOVAngle * 0.5f;
const float BaseTanHalfFOV = FMath::Tan(FMath::DegreesToRadians(BaseHalfFOV));
const float TanHalfFOV = FMath::Tan(FMath::DegreesToRadians(HalfFOV));
check(BaseTanHalfFOV > 0.0f);
Scale *= (TanHalfFOV / BaseTanHalfFOV);
return CurrentValue * Scale;
[*]UE4_BackCompat
const float FOVAngle = PC->PlayerCameraManager ? PC->PlayerCameraManager->GetFOVAngle() : 1.f;
float Scale = FOVScale;
Scale *= FOVAngle;
return CurrentValue * Scale;
[*]转到世界空间坐标 (WorldSpace)
[*]简单的把输入改成了Unreal的世界坐标空间的值 (x,y,z)=>(z,x,y)
[*]交换轴输入(SwizzleAxis)
[*]YXZ: (x,y,z)=>(y,x,z)
[*]ZYX: (x,y,z)=>(z,y,x)
[*]XZY: (x,y,z)=>(x,z,y)
[*]YZX: (x,y,z)=>(y,z,x)
[*]ZXY: (x,y,z)=>(z,x,y)
[*]输入修饰集合(Collection)
[*]Modifiers:输入修饰集合
[*]Permit Value Type Modification:是否改变当前的输入(好像没啥软用)
[*]Other:用户自定义的输入修饰,由蓝图创建出来
页:
[1]