[UE] 在虚幻中使用动画序列和分层骨骼混合简单实现角色看向

  • Post author:
  • Post category:其他


做ue demo 骨骼动画作业时想到的功能,记录一下。实现效果如下:角色会看向摄像机方向。NPC会看向玩家方向。本期动画素材使用了ue商店中的AdvancedLocomotionSystemV。

实现步骤

首先实现了身体部分的正常的动画状态机:包括站立,走路,下蹲,跳跃等。

因为角色头部转向是与身体运动分开的。所以需要再新建一个动画状态,专门用来处理头部转向,并使用骨骼分层混合将他们合并起来。在设置混合指定骨骼时,选中对应的骨骼(这里是“Head”),并设置对应的权重。

观察素材中给的人物转头动画。实际上是一个additive的动画序列(30帧1秒),动画播放位置的不同(float数值0到1)对应了人物从左向右看。那么我们只需要计算出对应的目标与自身角色的夹角,并将夹角大小映射为浮点型[0,1]的数值范围即可。



如何使用浮点数量化两个夹角大小呢?容易想到使用使用两个单位向量的点乘判断大小。我们。因为等于1时对应动画位置是向右看,点乘两个重叠的单位向量大小也正好为1,所以我们只需取角色的局部右方向对目标方向进行点乘即可。

新建一个C++组件,搭载到角色Actor上,负责计算自己与需要看向目标的方向夹角,lookAtPoint为需要播放的动画位置点(float数值0-1对应看向的左到右,0.5时代表玩家看向自己的正前方)

使用transform.right(即actor->GetActorLocation())与角色看的方向的单位向量的点乘判断夹角来赋值给动画.如果点乘结果更接近1,说明角色更看向右边,对应的动画序列位置为1.点乘结果更接近-1,说明角色更看向左边,对应的动画序列位置为0.得到关系: 动画序列位置 = (点乘结果 + 1) * 0.5f

同时,使用transform.forward,判断目标是否在角色前面.如果与方向向量点乘结果<0,说明目标在角色后面,则角色不需要进行看向操作(动画序列位置为0.5)

class SKEPROJECT_API UMyAnimCompoment : public UActorComponent
{
public:        
        UPROPERTY(EditAnywhere, BlueprintReadWrite)
                float lookAtPoint; //暴露给动画蓝图进行编辑
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Follow Target")
                AActor* target;    //需要看向的目标
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Follow Target")
                bool targetIsPlayer;
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Follow Target")
                bool targetIsCamera;

protected:
        virtual void BeginPlay() override;
        UActorComponent* cylinderCollider;
}
void UMyAnimCompoment::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
        Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

        if (target == nullptr)
        {
                GLog->Log("target Not Set");
                return;
        }

        //得到角色的胶囊碰撞体
        UPrimitiveComponent* collider = GetOwner()->FindComponentByClass<UPrimitiveComponent>(); //得到自己的胶囊碰撞体
        
        FVector selfpos = collider->GetComponentLocation(); //得到自己的位置
        FVector tarPos = target->GetActorLocation();        //得到目标的位置
        FVector direction = (tarPos - selfpos).GetSafeNormal(); //得到自己到目标方向的单位向量

        //使用局部右方向,使用点乘判断自己与目标方向的夹角,返回[-1,1]
        //这里的局部右方向要用GetForwardVector()??可能和原本组件自带的旋转有关
        float dir = FVector::DotProduct(-collider->GetForwardVector(), direction);
        //将点乘[-1,1]的结果映射回动画播放中需要用到的[0,1]的范围
        dir = (dir + 1) * 0.5f; 
        
        //判断目标是否在角色前,如果目标在角色后面就不需要角色看向了
        bool isForward = (FVector::DotProduct(collider->GetRightVector(), direction) > 0); 

        //得到动画混合结果(0为最左,0.5为中间,1为最右)
        lookAtPoint = isForward ? dir : 0.5f; 

}



版权声明:本文为m0_51776409原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。