Root Motion 移動功能。
void UCharacterMovementComponent::SimulatedTick(float DeltaSeconds)
{
SCOPE_CYCLE_COUNTER(STAT_CharacterMovementSimulated);
checkSlow(CharacterOwner != nullptr);
if (NetworkSmoothingMode == ENetworkSmoothingMode::Replay)
{
const FVector OldLocation = UpdatedComponent ? UpdatedComponent->GetComponentLocation() : FVector::ZeroVector;
const FVector OldVelocity = Velocity;
// Interpolate between appropriate samples
{
SCOPE_CYCLE_COUNTER( STAT_CharacterMovementSmoothClientPosition );
SmoothClientPosition( DeltaSeconds );
}
// Update replicated movement mode
ApplyNetworkMovementMode( GetCharacterOwner()->GetReplicatedMovementMode() );
UpdateComponentVelocity();
bJustTeleported = false;
CharacterOwner->RootMotionRepMoves.Empty();
CurrentRootMotion.Clear();
CharacterOwner->SavedRootMotion.Clear();
// Note: we do not call the Super implementation, that runs prediction.
// We do still need to call these though
OnMovementUpdated(DeltaSeconds, OldLocation, OldVelocity);
CallMovementUpdateDelegate(DeltaSeconds, OldLocation, OldVelocity);
LastUpdateLocation = UpdatedComponent ? UpdatedComponent->GetComponentLocation() : FVector::ZeroVector;
LastUpdateRotation = UpdatedComponent ? UpdatedComponent->GetComponentQuat() : FQuat::Identity;
LastUpdateVelocity = Velocity;
//TickCharacterPose( DeltaSeconds );
return;
}
// If we are playing a RootMotion AnimMontage.
if (CharacterOwner->IsPlayingNetworkedRootMotionMontage())
{
bWasSimulatingRootMotion = true;
UE_LOG(LogRootMotion, Verbose, TEXT("UCharacterMovementComponent::SimulatedTick"));
// Tick animations before physics.
if( CharacterOwner->GetMesh() )
{
TickCharacterPose(DeltaSeconds);
// Make sure animation didn't trigger an event that destroyed us
if (!HasValidData())
{
return;
}
}
if( RootMotionParams.bHasRootMotion )
{
const FQuat OldRotationQuat = UpdatedComponent->GetComponentQuat();
const FVector OldLocation = UpdatedComponent->GetComponentLocation();
SimulateRootMotion(DeltaSeconds, RootMotionParams.GetRootMotionTransform());
#if !(UE_BUILD_SHIPPING)
// debug
if (false)
{
const FRotator OldRotation = OldRotationQuat.Rotator();
const FRotator NewRotation = UpdatedComponent->GetComponentRotation();
const FVector NewLocation = UpdatedComponent->GetComponentLocation();
DrawDebugCoordinateSystem(GetWorld(), CharacterOwner->GetMesh()->GetComponentLocation() + FVector(0,0,1), NewRotation, 50.f, false);
DrawDebugLine(GetWorld(), OldLocation, NewLocation, FColor::Red, true, 10.f);
UE_LOG(LogRootMotion, Log, TEXT("UCharacterMovementComponent::SimulatedTick DeltaMovement Translation: %s, Rotation: %s, MovementBase: %s"),
*(NewLocation - OldLocation).ToCompactString(), *(NewRotation - OldRotation).GetNormalized().ToCompactString(), *GetNameSafe(CharacterOwner->GetMovementBase()) );
}
#endif // !(UE_BUILD_SHIPPING)
}
// then, once our position is up to date with our animation,
// handle position correction if we have any pending updates received from the server.
if( CharacterOwner && (CharacterOwner->RootMotionRepMoves.Num() > 0) )
{
CharacterOwner->SimulatedRootMotionPositionFixup(DeltaSeconds);//這裏調整位置跟隨動作運動
}
}
//...
}
這裏做了移動:
void ACharacter::SimulatedRootMotionPositionFixup(float DeltaSeconds)
{
const FAnimMontageInstance* ClientMontageInstance = GetRootMotionAnimMontageInstance();//這裏獲取了自帶Mesh
if( ClientMontageInstance && CharacterMovement && Mesh )
{
// Find most recent buffered move that we can use.
const int32 MoveIndex = FindRootMotionRepMove(*ClientMontageInstance);
if( MoveIndex != INDEX_NONE )
{
const FVector OldLocation = GetActorLocation();
const FQuat OldRotation = GetActorQuat();
// Move Actor back to position of that buffered move. (server replicated position).
const FSimulatedRootMotionReplicatedMove& RootMotionRepMove = RootMotionRepMoves[MoveIndex];
if( RestoreReplicatedMove(RootMotionRepMove) )
{
const float ServerPosition = RootMotionRepMove.RootMotion.Position;
const float ClientPosition = ClientMontageInstance->GetPosition();
const float DeltaPosition = (ClientPosition - ServerPosition);
if( FMath::Abs(DeltaPosition) > KINDA_SMALL_NUMBER )
{
// Find Root Motion delta move to get back to where we were on the client.
const FTransform LocalRootMotionTransform = ClientMontageInstance->Montage->ExtractRootMotionFromTrackRange(ServerPosition, ClientPosition);
// Simulate Root Motion for delta move.
if( CharacterMovement )
{
const float MontagePlayRate = ClientMontageInstance->GetPlayRate();
// Guess time it takes for this delta track position, so we can get falling physics accurate.
if (!FMath::IsNearlyZero(MontagePlayRate))
{
const float DeltaTime = DeltaPosition / MontagePlayRate;
// Even with negative playrate deltatime should be positive.
check(DeltaTime > 0.f);
CharacterMovement->SimulateRootMotion(DeltaTime, LocalRootMotionTransform);
// After movement correction, smooth out error in position if any.
CharacterMovement->bNetworkSmoothingComplete = false;
CharacterMovement->SmoothCorrection(OldLocation, OldRotation, GetActorLocation(), GetActorQuat());
}
}
}
}
// Delete this move and any prior one, we don't need them anymore.
UE_LOG(LogRootMotion, Log, TEXT("\tClearing old moves (%d)"), MoveIndex+1);
RootMotionRepMoves.RemoveAt(0, MoveIndex+1);
}
}
}
這裏獲取了Character自帶的Mesh,限制的Root Motion只能用在Character 自帶的Mesh。
/** Get FAnimMontageInstance playing RootMotion */
FAnimMontageInstance * ACharacter::GetRootMotionAnimMontageInstance() const
{
return (Mesh && Mesh->GetAnimInstance()) ? Mesh->GetAnimInstance()->GetRootMotionMontageInstance() : NULL;
}