脚部IK(C++方式)

脚部IK(C++方式)

学习地址:UE4角色脚部IK教程_哔哩哔哩_bilibili

我们希望角色的脚部能够很好地贴合地面,以增加游戏的真实性,所以我们要对人物的脚部进行IK绑定。

创建变量

image-20240709155252664

首先是使用位置,我们希望脚部IK不影响其他动画的播放,所以我们应该在角色处于站立静止的状态时调用,如果角色有其他行为则不使用,并且这是属于角色下半身的一个行为。为此,我们在原有的下半身动画基础上添加即可。

image-20240709153228673

如图所示,在Idle动画之后添加如下节点,Two Bone IK,Transform Bone两种节点,Two Bone IK影响左腿右腿的运动,Transform Bone影响pelvis(相当于骨骼模型原点),左脚右脚的位置旋转。

Two Bone IK节点中设置的参数也需要注意,效果器中执行位置空间需要设置为骨骼空间,目标为脚部骨骼,关节目标中关节目标位置设置为骨骼空间,目标为大腿骨骼,并且将Jonint Target Location设置到膝盖正前方,让腿部弯曲是是向前弯折,而不是向后弯折,符合现实人体。

image-20240709153645663

image-20240709154445836

Transform Bone需要将影响pelvis的节点中平移这个设置一下,平移模式为添加至现有,平移空间为相对世界场景坐标(因为我们需要移动角色模型高度),脚部的修改旋转中的设置,修改内容相同,用于变换脚部与地面的贴合。

image-20240709154639027

image-20240709154910958

接下来就是如何计算这些参数了。

在character中创建如下变量以及方法

//脚部IK相关参数
	float IK_LeftFootOffset;
	float IK_RightFootOffset;
	float IK_MeshOffset;
	FRotator IK_LeftFootRotator;
	FRotator IK_RightFootRotator;
	//计算脚部IK相关参数
	struct FFootIKInfo
	{
		float FootOffset;
		FRotator FootRotator;
		bool TraceSuccess;
	};
	FFootIKInfo CalculateFootIKInfo(FName InSocketName);
	void CalculateFootIK();

如图所示,我们在两只脚的位置打射线检测,射线起始位置是胶囊体半高的高度,脚部的正上方,结束位置是我们设定的长度,让在下方的脚成为我们的MinOffset,在上方的脚就做出对应的偏移,如果射线没有检测到,则说明角色脚部离地面有较远距离,让它处于不变状态。脚部的旋转通过法线以及反正切函数转化为角度求得。

image-20240710103346224

实现代码

APlayerCharacter::FFootIKInfo APlayerCharacter::CalculateFootIKInfo(FName InSocketName)
{
	FFootIKInfo result;
	//射线检测开始结束点
	FVector StartVector;
	FVector EndVector;
	//计算脚部插槽位置
	FVector SocketLocation = GetMesh()->GetSocketLocation(InSocketName);
	//获取Actor的正中心的Z轴位置,即角色中心位置
	float ActorZ = GetActorLocation().Z;
	//获取胶囊体半高
	float CapsuleHalfHeight = GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
	//角色脚部上方差不多腰部位置
	StartVector = FVector(SocketLocation.X, SocketLocation.Y, ActorZ);
	//胶囊体半高1.5倍长度
	EndVector = FVector(SocketLocation.X, SocketLocation.Y,ActorZ-(CapsuleHalfHeight*1.5));
	//射线检测
	FHitResult HitResult;
	bool IsHit=UKismetSystemLibrary::LineTraceSingle(this,
		StartVector,
		EndVector,
		UEngineTypes::ConvertToTraceType(ECC_Visibility),
		false,
		TArray<AActor*>(),
		EDrawDebugTrace::None,
		HitResult,
		true,
		FLinearColor::Red,
		FLinearColor::Green,
		1.0f);
	//如果检测到则设置偏移量
	if(IsHit){
		result.TraceSuccess=true;
		result.FootOffset = CapsuleHalfHeight-HitResult.Distance;
	}
	else
	{
		result.TraceSuccess=false;
		result.FootOffset = 0;
	}
	//通过法线计算脚部旋转值,需要将弧度转化为角度
	FVector a=HitResult.Normal;
	result.FootRotator=FRotator(atan2(a.X,a.Z)*-1 * (180.0f / M_PI),0,atan2(a.Y,a.Z) * (180.0f / M_PI));
	return result;
}


void APlayerCharacter::CalculateFootIK()
{
	FFootIKInfo LeftFootIKInfo = CalculateFootIKInfo(TEXT("foot_l"));
	FFootIKInfo RightFootIKInfo = CalculateFootIKInfo(TEXT("foot_r"));
	float MinOffset = FMath::Min(LeftFootIKInfo.FootOffset,RightFootIKInfo.FootOffset);
	float MeshTargetOffset = MinOffset;
	float LeftFootTargetOffset;
	float RightFootTargetOffset;
	if(LeftFootIKInfo.TraceSuccess)
	{
		LeftFootTargetOffset=LeftFootIKInfo.FootOffset-MinOffset;
	}
	else
	{
		LeftFootTargetOffset=0;
	}
	if(RightFootIKInfo.TraceSuccess)
	{
			RightFootTargetOffset=RightFootIKInfo.FootOffset-MinOffset;
	}
	else
	{
		RightFootTargetOffset=0;
	}
	IK_MeshOffset=FMath::FInterpTo(IK_MeshOffset,MeshTargetOffset,GetWorld()->GetDeltaSeconds(),20.0f);
	IK_LeftFootOffset=FMath::FInterpTo(IK_LeftFootOffset,LeftFootTargetOffset,GetWorld()->GetDeltaSeconds(),20.0f);
	IK_RightFootOffset=FMath::FInterpTo(IK_RightFootOffset,RightFootTargetOffset,GetWorld()->GetDeltaSeconds(),20.0f);
	IK_LeftFootRotator=LeftFootIKInfo.FootRotator;
	IK_RightFootRotator=RightFootIKInfo.FootRotator;
	
}

使用接口将参数交给动画蓝图即可

//接口中声明
UFUNCTION(BlueprintNativeEvent,BlueprintCallable,Category="FootIKInfo")
	void GetFootIKInfo(float& LeftFootOffset,float& RightFootOffset,float& MeshOffset,FRotator& LeftFootRotator,FRotator& RightFootRotator);
	

//character实现获取脚部IK参数接口
	virtual void GetFootIKInfo_Implementation(float& LeftFootOffset,float& RightFootOffset,float& MeshOffset,FRotator& LeftFootRotator,FRotator& RightFootRotator) override;

void APlayerCharacter::GetFootIKInfo_Implementation(float& LeftFootOffset, float& RightFootOffset, float& MeshOffset,
	FRotator& LeftFootRotator, FRotator& RightFootRotator)
{
	LeftFootOffset=IK_LeftFootOffset;
	RightFootOffset=IK_RightFootOffset;
	MeshOffset=IK_MeshOffset;
	LeftFootRotator=IK_LeftFootRotator;
	RightFootRotator=IK_RightFootRotator;
	
}


动画蓝图创建好函数调用接口

image-20240710105349725

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值