需求
怪物有细长舌头,垂下。带舌头的怪物要把3p视角下的玩家拉上去,过程中舌头要表现的向绳子物理。玩家在拉的过程中,下半身开启物理配合拉的节奏。
舌头
物理资产
舌头物理表现向绳子,需要每根骨骼设置物理约束。物理形状要大一些,避免快速往上拉时,绳子摆动太快,乱飞。
可以写个工具,自动创建物理Shape和Constraint。
const FPhysAssetCreateParams& NewBodyData = GetDefault<UPhysicsAssetGenerationSettings>()->CreateParams;
const int32 NewBodySetupIndex = FPhysicsAssetUtils::CreateNewBody(SharedData->PhysicsAsset, BoneName, NewBodyData);
BodySetup->Modify();
// 创建Shape,设置BodySetup
...
// 给上下两个骨骼的BodySetup创建约束
// Create new constraint with a name not related to a bone, so it wont get auto managed in code that creates new bodies
const int32 NewConstraintIndex = FPhysicsAssetUtils::CreateNewConstraint(SharedData->PhysicsAsset, *ConstraintName);
UPhysicsConstraintTemplate* ConstraintSetup = SharedData->PhysicsAsset->ConstraintSetup[NewConstraintIndex];
ConstraintSetup->Modify(false);
UBodySetup* ChildBodySetup = SharedData->PhysicsAsset->SkeletalBodySetups[NewBodySetupIndex];
UBodySetup* ParentBodySetup = SharedData->PhysicsAsset->SkeletalBodySetups[NewBodySetupIndex - 1];
// Place joint at origin of child
ConstraintSetup->DefaultInstance.ConstraintBone1 = ChildBodySetup->BoneName;
ConstraintSetup->DefaultInstance.ConstraintBone2 = ParentBodySetup->BoneName;
const FTransform ParentTransform = ConstraintSetup->DefaultInstance.CalculateDefaultParentTransform(SharedData->PhysicsAsset);
const FTransform ChildTransform = ConstraintSetup->DefaultInstance.CalculateDefaultChildTransform();
ConstraintSetup->DefaultInstance.SetRefPosition(EConstraintFrame::Frame1, (ChildTransform.GetLocation() - ParentTransform.GetLocation()) * 0.5f);
ConstraintSetup->DefaultInstance.SetRefPosition(EConstraintFrame::Frame2, (ParentTransform.GetLocation() - ChildTransform.GetLocation()) * 0.5f);
ConstraintSetup->DefaultInstance.ProfileInstance.LinearLimit.XMotion = LCM_Locked;
ConstraintSetup->DefaultInstance.ProfileInstance.LinearLimit.YMotion = LCM_Locked;
ConstraintSetup->DefaultInstance.ProfileInstance.LinearLimit.ZMotion = LCM_Locked;
ConstraintSetup->DefaultInstance.ProfileInstance.ConeLimit.Swing1Motion = ACM_Limited;
ConstraintSetup->DefaultInstance.ProfileInstance.ConeLimit.Swing2Motion = ACM_Limited;
ConstraintSetup->DefaultInstance.ProfileInstance.TwistLimit.TwistMotion = ACM_Limited;
ConstraintSetup->DefaultInstance.ProfileInstance.ConeLimit.Swing1LimitDegrees = 5.0f;
ConstraintSetup->DefaultInstance.ProfileInstance.ConeLimit.Swing2LimitDegrees = 30.0f;
ConstraintSetup->DefaultInstance.ProfileInstance.TwistLimit.TwistLimitDegrees = 15.0f;
ConstraintSetup->DefaultInstance.SetDisableCollision(true);
ConstraintSetup->DefaultInstance.UpdateLinearLimit();
ConstraintSetup->DefaultInstance.UpdateAngularLimit();
ConstraintSetup->SetDefaultProfile(ConstraintSetup->DefaultInstance);
物理设置
舌头根部以下开启物理,设置物理Blend权重(避免绳子乱飞)
玩家
跟舌头同步吊上去
玩家是挂在舌头上吊上去的,上半身不开启物理,仅仅下半身开启物理。
让舌头固定在玩家模型的固定位置,但玩家要跟着舌头动:
目前固定在spine_01位置。每帧获取舌头挂点位置,将spine_01和挂点世界坐标重合,反向调整玩家Actor的位置。每帧设置玩家Actor的位置。
注:
直接将玩家Actor或Mesh Attach到舌头上还不够,因为Attach后,自身的RelativeTransform不会改变,当舌头挂点有旋转时,就会出现舌头挂点不在玩家身上的情况。所以上面要保持舌头始终在玩家身上。
相当于已知spine_01的world space的位置,计算玩家Actor的world space的transform。
spine_01 在 world space空间的transform,设为SpineWS。
spine_01 在MeshComponent的transform可以获取,设为SpineCS。
MeshComponent相对Actor的transform可以获取,设为MeshRelativeAS。
而Acotr的ComponentToWorld是我们要求的,设为ActorWS。
则。这里使用列向量右乘。SpineWS里的Location设置成舌头的位置,Rotation,Scale保持不变。
则
注:
设置Actor位置的时候,不要Reset或者Teleport 物理,表现会不正确。所以要使用RootComponent->SetWorldTransform(ActorWS, false,nullptr, ETeleportType::None);的方式。
关闭位置同步
3p视角里的位置不能受服务器同步影响。
需要将3p视角里的角色SetReplicatingMovement(false),这样网络位置同步过来也不会理会。
但主控端依然会上传位置给服务端,两者保持同步,3p下自由处理。
遗留问题
舌头的根部不开启物理,舌头拉上去是靠平移这根骨骼控制的。会出现拉上去时,如果舌头晃动,舌头会穿怪物的嘴巴模型。
尝试过在舌头的子开启物理骨骼上升到指定高度后就关闭物理,发现会对子骨骼有瞬变的影响,导致玩家跟着瞬变。也许需要关闭过程要缓一下