├── .gitignore ├── RMS.uplugin ├── Resources └── Icon128.png └── Source └── RMS ├── Private ├── AnimNotifyState_RMS.cpp ├── Experimental │ ├── RMSComponent.cpp │ └── Task │ │ ├── RMSTask_AnimWarping.cpp │ │ ├── RMSTask_Base.cpp │ │ └── RMSTask_MoveTo.cpp ├── RMSGroupEx.cpp ├── RMSLibrary.cpp ├── RMSModule.cpp └── RMSTypes.cpp ├── Public ├── AnimNotifyState_RMS.h ├── Experimental │ ├── RMSComponent.h │ └── Task │ │ ├── RMSTask_AnimWarping.h │ │ ├── RMSTask_Base.h │ │ └── RMSTask_MoveTo.h ├── RMSGroupEx.h ├── RMSLibrary.h ├── RMSModule.h └── RMSTypes.h └── RMS.Build.cs /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.vs 3 | /.svn 4 | /Binaries 5 | /Build 6 | /DerivedDataCache 7 | /Intermediate 8 | /Saved 9 | /Content -------------------------------------------------------------------------------- /RMS.uplugin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VJien/RootMotionSource/2a5db30c1b7daf256081aa80fabd408a2b6d35b5/RMS.uplugin -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VJien/RootMotionSource/2a5db30c1b7daf256081aa80fabd408a2b6d35b5/Resources/Icon128.png -------------------------------------------------------------------------------- /Source/RMS/Private/AnimNotifyState_RMS.cpp: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | 5 | #include "AnimNotifyState_RMS.h" 6 | 7 | -------------------------------------------------------------------------------- /Source/RMS/Private/Experimental/RMSComponent.cpp: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | 5 | #include "Experimental/RMSComponent.h" 6 | 7 | #include "Experimental/Task/RMSTask_MoveTo.h" 8 | 9 | // Sets default values for this component's properties 10 | URMSComponent::URMSComponent(const FObjectInitializer& Object):Super(Object) 11 | { 12 | // Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features 13 | // off to improve performance if you don't need them. 14 | PrimaryComponentTick.bCanEverTick = true; 15 | PrimaryComponentTick.bStartWithTickEnabled = true; 16 | // ... 17 | } 18 | 19 | 20 | // Called when the game starts 21 | void URMSComponent::BeginPlay() 22 | { 23 | Super::BeginPlay(); 24 | 25 | // ... 26 | 27 | } 28 | 29 | 30 | // Called every frame 31 | void URMSComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) 32 | { 33 | Super::TickComponent(DeltaTime, TickType, ThisTickFunction); 34 | if (bListenTaskEnd) 35 | { 36 | //@todo 除非改引擎, 不然没有更好的办法来监听RMS的结束, 只能暴力tick 37 | if (MovementComponent.IsValid()) 38 | { 39 | for (auto P: CurrentTasks) 40 | { 41 | auto RMS = MovementComponent->GetRootMotionSourceByID(P.Key); 42 | if (!RMS.IsValid()) 43 | { 44 | DirtIDs.Emplace(P.Key, true); 45 | } 46 | } 47 | 48 | } 49 | for (auto P: DirtIDs) 50 | { 51 | if(const auto task = CurrentTasks.Find(P.Key)) 52 | { 53 | OnTaskEnd.Broadcast(*task,P.Value); 54 | CurrentTasks.Remove(P.Key); 55 | } 56 | } 57 | DirtIDs.Empty(); 58 | // ... 59 | } 60 | 61 | } 62 | 63 | void URMSComponent::Init(UCharacterMovementComponent* InMovementComponent) 64 | { 65 | MovementComponent = InMovementComponent; 66 | } 67 | 68 | int32 URMSComponent::TryActivateTask(URMSTask_Base* Task) 69 | { 70 | // if (URMSTask_MoveTo* MoveTo = Cast(Task)) 71 | // { 72 | // if (!MoveTo->RootMotionComponent.IsValid()) 73 | // { 74 | // OnTaskEnd.Broadcast(MoveTo,false); 75 | // return -1; 76 | // } 77 | // if (bListenTaskEnd /*&& !MoveTo->Setting.bForce*/ && URMSLibrary::IsRootMotionSourceValid(MoveTo->RootMotionComponent->GetMovementComponent(), MoveTo->GetInstanceName())) 78 | // { 79 | // OnTaskEnd.Broadcast(MoveTo,false); 80 | // return -1; 81 | // } 82 | // int32 ID = URMSLibrary::ApplyRootMotionSource_MoveToForce(MoveTo->RootMotionComponent->GetMovementComponent(), MoveTo->GetInstanceName(), MoveTo->StartLocation,MoveTo->TargetLocation,MoveTo->Duration, 83 | // MoveTo->Priority,MoveTo->PathOffsetCurve,0,ERMSApplyMode::None,MoveTo->Setting); 84 | // MoveTo->Activate(); 85 | // if (bListenTaskEnd) 86 | // { 87 | // CurrentTasks.Emplace(ID, MoveTo); 88 | // } 89 | // OnTaskBegin.Broadcast(MoveTo); 90 | // return ID; 91 | // } 92 | return -1; 93 | } 94 | 95 | void URMSComponent::SetRms_TargetByLocation(FName Instance, FVector Location) 96 | { 97 | auto item = RMSMap.Find(Instance); 98 | if (item) 99 | { 100 | FTransform newTransform = *item; 101 | newTransform.SetLocation(Location); 102 | RMSMap.Add(Instance, newTransform); 103 | } 104 | } 105 | 106 | void URMSComponent::SetRms_TargetByRotation(FName Instance, FRotator Rotation) 107 | { 108 | auto item = RMSMap.Find(Instance); 109 | if (item) 110 | { 111 | FTransform newTransform = *item; 112 | newTransform.SetRotation(FQuat::MakeFromRotator(Rotation)); 113 | RMSMap.Add(Instance, newTransform); 114 | } 115 | } 116 | 117 | void URMSComponent::SetRms_Target(FName Instance, FVector Location, FRotator Rotation) 118 | { 119 | RMSMap.Add(Instance,FTransform(FQuat::MakeFromRotator(Rotation),Location )); 120 | } 121 | 122 | -------------------------------------------------------------------------------- /Source/RMS/Private/Experimental/Task/RMSTask_AnimWarping.cpp: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | 5 | #include "Experimental/Task/RMSTask_AnimWarping.h" 6 | 7 | #include "Experimental/RMSComponent.h" 8 | 9 | URMSTask_AnimWarping* URMSTask_AnimWarping::RootMotionSourceTask_AnimWarping(URMSComponent* RootMotionComponent, UAnimSequence* Anim, TMap InWarpingInfo) 10 | { 11 | if (!RootMotionComponent || RootMotionComponent->GetMovementComponent() || InWarpingInfo.Num() == 0) 12 | { 13 | return nullptr; 14 | } 15 | 16 | URMSTask_AnimWarping* Task = NewRootMotionSourceTask(RootMotionComponent,TEXT("AnimWarping"), 0); 17 | Task->RootMotionComponent = RootMotionComponent; 18 | Task->ID = RootMotionComponent->TryActivateTask(Task); 19 | Task->WarpingInfo = InWarpingInfo; 20 | Task->Anim = Anim; 21 | RootMotionComponent->OnTaskEnd.AddDynamic(Task, &URMSTask_AnimWarping::OnTaskFinished); 22 | return Task; 23 | } 24 | 25 | void URMSTask_AnimWarping::Activate() 26 | { 27 | Super::Activate(); 28 | } 29 | 30 | void URMSTask_AnimWarping::Pause() 31 | { 32 | Super::Pause(); 33 | } 34 | 35 | void URMSTask_AnimWarping::Resume() 36 | { 37 | Super::Resume(); 38 | } 39 | 40 | void URMSTask_AnimWarping::TickTask(float DeltaTime) 41 | { 42 | Super::TickTask(DeltaTime); 43 | } 44 | 45 | void URMSTask_AnimWarping::OnTaskFinished_Implementation(URMSTask_Base* TaskObject, bool bSuccess) 46 | { 47 | Super::OnTaskFinished_Implementation(TaskObject, bSuccess); 48 | if (TaskObject) 49 | { 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Source/RMS/Private/Experimental/Task/RMSTask_Base.cpp: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | 5 | #include "Experimental/Task/RMSTask_Base.h" 6 | 7 | 8 | void URMSTask_Base::OnTaskFinished_Implementation(URMSTask_Base* TaskObject, bool bSuccess) 9 | { 10 | } 11 | 12 | URMSTask_Base::URMSTask_Base(const FObjectInitializer& ObjectInitializer):Super(ObjectInitializer) 13 | { 14 | bTickingTask = true; 15 | } 16 | -------------------------------------------------------------------------------- /Source/RMS/Private/Experimental/Task/RMSTask_MoveTo.cpp: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | 5 | #include "Experimental/Task/RMSTask_MoveTo.h" 6 | 7 | #include "Experimental/RMSComponent.h" 8 | 9 | URMSTask_MoveTo* URMSTask_MoveTo::RootMotionSourceTask_MoveTo(URMSComponent* RootMotionComponent, FName InstanceName, 10 | FVector StartLocation, FVector TargetLocation, float Duration, 11 | int32 Priority, 12 | FRMSSetting_Move Setting, 13 | UCurveVector* PathOffsetCurve) 14 | { 15 | if (!RootMotionComponent->GetMovementComponent()) 16 | { 17 | return nullptr; 18 | } 19 | 20 | URMSTask_MoveTo* Task = NewRootMotionSourceTask(RootMotionComponent,InstanceName,Priority); 21 | Task->StartLocation = StartLocation; 22 | Task->TargetLocation = TargetLocation; 23 | Task->Duration = Duration; 24 | Task->Priority = Priority; 25 | Task->PathOffsetCurve = PathOffsetCurve; 26 | Task->Setting = Setting; 27 | Task->RootMotionComponent = RootMotionComponent; 28 | Task->ID = RootMotionComponent->TryActivateTask(Task); 29 | RootMotionComponent->OnTaskEnd.AddDynamic(Task, &URMSTask_MoveTo::OnTaskFinished); 30 | return Task; 31 | } 32 | 33 | 34 | 35 | void URMSTask_MoveTo::Activate() 36 | { 37 | Super::Activate(); 38 | } 39 | 40 | void URMSTask_MoveTo::Pause() 41 | { 42 | Super::Pause(); 43 | } 44 | 45 | void URMSTask_MoveTo::Resume() 46 | { 47 | Super::Resume(); 48 | } 49 | 50 | void URMSTask_MoveTo::TickTask(float DeltaTime) 51 | { 52 | Super::TickTask(DeltaTime); 53 | } 54 | 55 | void URMSTask_MoveTo::OnTaskFinished_Implementation(URMSTask_Base* TaskObject, bool bSuccess) 56 | { 57 | Super::OnTaskFinished_Implementation(TaskObject, bSuccess); 58 | if (RootMotionComponent.IsValid()) 59 | { 60 | RootMotionComponent->OnTaskEnd.RemoveDynamic(this, &URMSTask_Base::OnTaskFinished); 61 | } 62 | if (bSuccess) 63 | { 64 | OnSuccess.Broadcast(this); 65 | } 66 | else 67 | { 68 | OnFail.Broadcast(this); 69 | } 70 | 71 | EndTask(); 72 | } 73 | -------------------------------------------------------------------------------- /Source/RMS/Private/RMSGroupEx.cpp: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | 5 | #include "RMSGroupEx.h" 6 | #include "Curves/CurveVector.h" 7 | #include "Curves/CurveFloat.h" 8 | #include "DrawDebugHelpers.h" 9 | #include "RMSLibrary.h" 10 | #include "GameFramework/Character.h" 11 | #include "GameFramework/CharacterMovementComponent.h" 12 | #include "Components/SkeletalMeshComponent.h" 13 | #include "Components/CapsuleComponent.h" 14 | #include "GameFramework/CharacterMovementComponent.h" 15 | #include "Kismet/KismetMaterialLibrary.h" 16 | #include "Kismet/KismetMathLibrary.h" 17 | #include "Kismet/KismetSystemLibrary.h" 18 | 19 | UE_DISABLE_OPTIMIZATION 20 | 21 | 22 | #pragma region FRootMotionSource_PathMoveToForce 23 | FRootMotionSource_PathMoveToForce::FRootMotionSource_PathMoveToForce() 24 | { 25 | } 26 | 27 | FVector FRootMotionSource_PathMoveToForce::GetPathOffsetInWorldSpace(const float MoveFraction, FRMSPathMoveToData Data, 28 | FVector Start) const 29 | { 30 | if (Data.PathOffsetCurve) 31 | { 32 | // Calculate path offset 33 | const FVector PathOffsetInFacingSpace = URMSLibrary::EvaluateVectorCurveAtFraction( 34 | *Data.PathOffsetCurve, MoveFraction); 35 | FRotator FacingRotation((Data.Target - Start).Rotation()); 36 | FacingRotation.Pitch = 0.f; 37 | return FacingRotation.RotateVector(PathOffsetInFacingSpace); 38 | } 39 | return FVector::ZeroVector; 40 | } 41 | 42 | bool FRootMotionSource_PathMoveToForce::GetPathDataByTime(float InTime, FRMSPathMoveToData& OutCurrData, 43 | FRMSPathMoveToData& OutLastData) const 44 | { 45 | float Time = 0; 46 | if (Duration > 0) 47 | { 48 | for (auto i : Path) 49 | { 50 | if (InTime >= Time && InTime <= i.Duration + Time) 51 | { 52 | OutCurrData = i; 53 | return true; 54 | } 55 | else 56 | { 57 | OutLastData = i; 58 | Time += i.Duration; 59 | } 60 | } 61 | } 62 | return false; 63 | } 64 | 65 | bool FRootMotionSource_PathMoveToForce::UpdateStateFrom(const FRootMotionSource* SourceToTakeStateFrom, 66 | bool bMarkForSimulatedCatchup) 67 | { 68 | if (!FRootMotionSource::UpdateStateFrom(SourceToTakeStateFrom, bMarkForSimulatedCatchup)) 69 | { 70 | return false; 71 | } 72 | 73 | return true; 74 | } 75 | 76 | void FRootMotionSource_PathMoveToForce::PrepareRootMotion(float SimulationTime, float MovementTickTime, 77 | const ACharacter& Character, 78 | const UCharacterMovementComponent& MoveComponent) 79 | { 80 | RootMotionParams.Clear(); 81 | if (Path.Num() <= 0) 82 | { 83 | return; 84 | } 85 | if (Duration > SMALL_NUMBER && MovementTickTime > SMALL_NUMBER) 86 | { 87 | const float NextFrame = (GetTime() + SimulationTime); 88 | if (Index == -1) 89 | { 90 | LastData.Target = StartLocation; 91 | LastData.RotationSetting.TargetRotation = StartRotation; 92 | 93 | CurrData = Path[0]; 94 | Index = 0; 95 | } 96 | if (NextFrame > CurrData.Duration) 97 | { 98 | if (Index < Path.Num() - 1) 99 | { 100 | Index ++; 101 | LastData = CurrData; 102 | CurrData = Path[Index]; 103 | LastData.RotationSetting.TargetRotation = Character.GetActorRotation(); 104 | } 105 | } 106 | float MoveFraction = (NextFrame - LastData.Duration) / CurrData.Duration; 107 | if (CurrData.TimeMappingCurve) 108 | { 109 | MoveFraction = URMSLibrary::EvaluateFloatCurveAtFraction(*CurrData.TimeMappingCurve, MoveFraction); 110 | } 111 | FVector CurrentTargetLocation = FMath::Lerp(LastData.Target, CurrData.Target, MoveFraction); 112 | CurrentTargetLocation += GetPathOffsetInWorldSpace(MoveFraction, CurrData, LastData.Target); 113 | const FVector CurrentLocation = Character.GetActorLocation(); 114 | FVector Force = (CurrentTargetLocation - CurrentLocation) / MovementTickTime; 115 | 116 | FRotator RotationDt = FRotator::ZeroRotator; 117 | if (CurrData.RotationSetting.IsWarpRotation()) 118 | { 119 | FRotator TargetRotation; 120 | if (CurrData.RotationSetting.Mode == ERMSRotationMode::FaceToTarget) 121 | { 122 | TargetRotation = (CurrData.Target - LastData.Target).Rotation(); 123 | } 124 | else 125 | { 126 | TargetRotation = CurrData.RotationSetting.TargetRotation; 127 | } 128 | const float RotationFraction = FMath::Clamp(MoveFraction * CurrData.RotationSetting.WarpMultiplier,0,1); 129 | 130 | URMSLibrary::ExtractRotation(RotationDt, Character, LastData.RotationSetting.TargetRotation, TargetRotation, RotationFraction, 131 | CurrData.RotationSetting.Curve); 132 | } 133 | 134 | 135 | // Debug 136 | #if ROOT_MOTION_DEBUG 137 | if (RMS::CVarRMS_Debug.GetValueOnGameThread() > 0) 138 | { 139 | const FVector LocDiff = MoveComponent.UpdatedComponent->GetComponentLocation() - CurrentLocation; 140 | const float DebugLifetime = 5.0f; 141 | 142 | // Current 143 | DrawDebugCapsule(Character.GetWorld(), MoveComponent.UpdatedComponent->GetComponentLocation(), 144 | Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 145 | FQuat::Identity, FColor::Red, false, DebugLifetime); 146 | 147 | // Current Target 148 | DrawDebugCapsule(Character.GetWorld(), CurrentTargetLocation + LocDiff, 149 | Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 150 | FQuat::Identity, FColor::Green, false, DebugLifetime); 151 | 152 | // Target 153 | DrawDebugCapsule(Character.GetWorld(), CurrData.Target + LocDiff, Character.GetSimpleCollisionHalfHeight(), 154 | Character.GetSimpleCollisionRadius(), FQuat::Identity, FColor::Blue, false, DebugLifetime); 155 | 156 | // Force 157 | DrawDebugLine(Character.GetWorld(), CurrentLocation, CurrentLocation + Force, FColor::Blue, false, 158 | DebugLifetime); 159 | } 160 | #endif 161 | 162 | FTransform NewTransform(RotationDt, Force); 163 | RootMotionParams.Set(NewTransform); 164 | } 165 | else 166 | { 167 | checkf(Duration > SMALL_NUMBER, TEXT("FRootMotionSource_MoveToForce prepared with invalid duration.")); 168 | } 169 | 170 | SetTime(GetTime() + SimulationTime); 171 | } 172 | 173 | bool FRootMotionSource_PathMoveToForce::NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) 174 | { 175 | if (!FRootMotionSource::NetSerialize(Ar, Map, bOutSuccess)) 176 | { 177 | return false; 178 | } 179 | 180 | Ar << StartLocation; 181 | Ar << Path; 182 | Ar << CurrData; 183 | Ar << LastData; 184 | Ar << Index; 185 | 186 | bOutSuccess = true; 187 | return true; 188 | } 189 | 190 | FRootMotionSource* FRootMotionSource_PathMoveToForce::Clone() const 191 | { 192 | FRootMotionSource_PathMoveToForce* CopyPtr = new FRootMotionSource_PathMoveToForce(*this); 193 | return CopyPtr; 194 | } 195 | 196 | bool FRootMotionSource_PathMoveToForce::Matches(const FRootMotionSource* Other) const 197 | { 198 | if (!FRootMotionSource::Matches(Other)) 199 | { 200 | return false; 201 | } 202 | 203 | // We can cast safely here since in FRootMotionSource::Matches() we ensured ScriptStruct equality 204 | const FRootMotionSource_PathMoveToForce* OtherCast = static_cast(Other); 205 | 206 | return StartLocation == OtherCast->StartLocation && 207 | Path == OtherCast->Path && StartRotation == OtherCast->StartRotation; 208 | } 209 | 210 | bool FRootMotionSource_PathMoveToForce::MatchesAndHasSameState(const FRootMotionSource* Other) const 211 | { 212 | if (!FRootMotionSource::MatchesAndHasSameState(Other)) 213 | { 214 | return false; 215 | } 216 | 217 | return true; 218 | } 219 | 220 | UScriptStruct* FRootMotionSource_PathMoveToForce::GetScriptStruct() const 221 | { 222 | return FRootMotionSource_PathMoveToForce::StaticStruct(); 223 | } 224 | 225 | FString FRootMotionSource_PathMoveToForce::ToSimpleString() const 226 | { 227 | return FString::Printf( 228 | TEXT("[ID:%u]FRootMotionSource_PathMoveToForce %s"), LocalID, *InstanceName.GetPlainNameString()); 229 | } 230 | 231 | void FRootMotionSource_PathMoveToForce::AddReferencedObjects(FReferenceCollector& Collector) 232 | { 233 | for (auto i : Path) 234 | { 235 | Collector.AddReferencedObject(i.PathOffsetCurve); 236 | Collector.AddReferencedObject(i.TimeMappingCurve); 237 | } 238 | FRootMotionSource::AddReferencedObjects(Collector); 239 | } 240 | //******************************************************* 241 | #pragma region Jump 242 | FRootMotionSource_JumpForce_WithPoints::FRootMotionSource_JumpForce_WithPoints() 243 | { 244 | 245 | } 246 | 247 | bool FRootMotionSource_JumpForce_WithPoints::UpdateStateFrom(const FRootMotionSource* SourceToTakeStateFrom, bool bMarkForSimulatedCatchup) 248 | { 249 | if (!FRootMotionSource::UpdateStateFrom(SourceToTakeStateFrom, bMarkForSimulatedCatchup)) 250 | { 251 | return false; 252 | } 253 | 254 | return true; 255 | } 256 | 257 | void FRootMotionSource_JumpForce_WithPoints::PrepareRootMotion(float SimulationTime, float MovementTickTime, const ACharacter& Character, const UCharacterMovementComponent& MoveComponent) 258 | { 259 | if (!bIsInit) 260 | { 261 | bIsInit = true; 262 | InitPath(Character); 263 | } 264 | RootMotionParams.Clear(); 265 | 266 | if (Duration > SMALL_NUMBER && MovementTickTime > SMALL_NUMBER && SimulationTime > SMALL_NUMBER) 267 | { 268 | float CurrentTimeFraction = GetTime() / Duration; 269 | float TargetTimeFraction = (GetTime() + SimulationTime) / Duration; 270 | 271 | // If we're beyond specified duration, we need to re-map times so that 272 | // we continue our desired ending velocity 273 | if (TargetTimeFraction > 1.f) 274 | { 275 | float TimeFractionPastAllowable = TargetTimeFraction - 1.0f; 276 | TargetTimeFraction -= TimeFractionPastAllowable; 277 | CurrentTimeFraction -= TimeFractionPastAllowable; 278 | } 279 | 280 | float CurrentMoveFraction = CurrentTimeFraction; 281 | float TargetMoveFraction = TargetTimeFraction; 282 | 283 | if (TimeMappingCurve) 284 | { 285 | CurrentMoveFraction = URMSLibrary::EvaluateFloatCurveAtFraction(*TimeMappingCurve, CurrentMoveFraction); 286 | TargetMoveFraction = URMSLibrary::EvaluateFloatCurveAtFraction(*TimeMappingCurve, TargetMoveFraction); 287 | } 288 | if (RotationSetting.Mode == ERMSRotationMode::FaceToTarget) 289 | { 290 | float RotFraction = CurrentMoveFraction; 291 | if (RotationSetting.Curve) 292 | { 293 | RotFraction = FMath::Clamp(RotationSetting.WarpMultiplier * URMSLibrary::EvaluateFloatCurveAtFraction(*RotationSetting.Curve, RotFraction),0,1); 294 | } 295 | const FRotator TargetRotation = (TargetLocation - StartLocation).Rotation(); 296 | SavedRotation = UKismetMathLibrary::RLerp(StartRotation, TargetRotation, RotFraction, true); 297 | } 298 | else if(RotationSetting.Mode == ERMSRotationMode::Custom) 299 | { 300 | float RotFraction = CurrentMoveFraction; 301 | if (RotationSetting.Curve) 302 | { 303 | RotFraction = FMath::Clamp(RotationSetting.WarpMultiplier * URMSLibrary::EvaluateFloatCurveAtFraction(*RotationSetting.Curve, RotFraction),0,1); 304 | } 305 | const FRotator TargetRotation = RotationSetting.TargetRotation; 306 | SavedRotation = UKismetMathLibrary::RLerp(StartRotation, TargetRotation, RotFraction, true); 307 | } 308 | const FVector CurrentRelativeLocation = GetRelativeLocation(CurrentMoveFraction); 309 | const FVector TargetRelativeLocation = GetRelativeLocation(TargetMoveFraction); 310 | 311 | const FVector Force = (TargetRelativeLocation - CurrentRelativeLocation) / MovementTickTime; 312 | 313 | // Debug 314 | #if ROOT_MOTION_DEBUG 315 | if (RMS::CVarRMS_Debug.GetValueOnGameThread() > 0) 316 | { 317 | const FVector CurrentLocation = Character.GetActorLocation(); 318 | const FVector CurrentTargetLocation = CurrentLocation + (TargetRelativeLocation - CurrentRelativeLocation); 319 | const FVector LocDiff = MoveComponent.UpdatedComponent->GetComponentLocation() - CurrentLocation; 320 | const float DebugLifetime = 5.0f; 321 | 322 | // Current 323 | DrawDebugCapsule(Character.GetWorld(), MoveComponent.UpdatedComponent->GetComponentLocation(), Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), FQuat::Identity, FColor::Red, false, DebugLifetime); 324 | 325 | // Current Target 326 | DrawDebugCapsule(Character.GetWorld(), CurrentTargetLocation + LocDiff, Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), FQuat::Identity, FColor::Green, false, DebugLifetime); 327 | 328 | // Target 329 | DrawDebugCapsule(Character.GetWorld(), CurrentTargetLocation + LocDiff, Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), FQuat::Identity, FColor::Blue, false, DebugLifetime); 330 | 331 | // Force 332 | DrawDebugLine(Character.GetWorld(), CurrentLocation, CurrentLocation+Force, FColor::Blue, false, DebugLifetime); 333 | 334 | // Halfway point 335 | const FVector HalfwayLocation = CurrentLocation + (GetRelativeLocation(0.5f) - CurrentRelativeLocation); 336 | if (SavedHalfwayLocation.IsNearlyZero()) 337 | { 338 | SavedHalfwayLocation = HalfwayLocation; 339 | } 340 | if (FVector::DistSquared(SavedHalfwayLocation, HalfwayLocation) > 50.f*50.f) 341 | { 342 | UE_LOG(LogRootMotion, Verbose, TEXT("RootMotion JumpForce drifted from saved halfway calculation!")); 343 | SavedHalfwayLocation = HalfwayLocation; 344 | } 345 | DrawDebugCapsule(Character.GetWorld(), HalfwayLocation + LocDiff, Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), FQuat::Identity, FColor::White, true, DebugLifetime); 346 | 347 | // Destination point 348 | const FVector DestinationLocation = CurrentLocation + (GetRelativeLocation(1.0f) - CurrentRelativeLocation); 349 | DrawDebugCapsule(Character.GetWorld(), DestinationLocation + LocDiff, Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), FQuat::Identity, FColor::White, true, DebugLifetime); 350 | 351 | UE_LOG(LogRootMotion, VeryVerbose, TEXT("RootMotionJumpForce %s %s preparing from %f to %f from (%s) to (%s) resulting force %s"), 352 | Character.GetLocalRole() == ROLE_AutonomousProxy ? TEXT("AUTONOMOUS") : TEXT("AUTHORITY"), 353 | Character.bClientUpdating ? TEXT("UPD") : TEXT("NOR"), 354 | GetTime(), GetTime() + SimulationTime, 355 | *CurrentLocation.ToString(), *CurrentTargetLocation.ToString(), 356 | *Force.ToString()); 357 | 358 | { 359 | FString AdjustedDebugString = FString::Printf(TEXT(" FRootMotionSource_JumpForce::Prep Force(%s) SimTime(%.3f) MoveTime(%.3f) StartP(%.3f) EndP(%.3f)"), 360 | *Force.ToCompactString(), SimulationTime, MovementTickTime, CurrentMoveFraction, TargetMoveFraction); 361 | RootMotionSourceDebug::PrintOnScreen(Character, AdjustedDebugString); 362 | } 363 | } 364 | #endif 365 | 366 | const FTransform NewTransform(Force); 367 | RootMotionParams.Set(NewTransform); 368 | } 369 | else 370 | { 371 | checkf(Duration > SMALL_NUMBER, TEXT("FRootMotionSource_JumpForce prepared with invalid duration.")); 372 | } 373 | 374 | SetTime(GetTime() + SimulationTime); 375 | } 376 | 377 | bool FRootMotionSource_JumpForce_WithPoints::NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) 378 | { 379 | if (!FRootMotionSource::NetSerialize(Ar, Map, bOutSuccess)) 380 | { 381 | return false; 382 | } 383 | 384 | 385 | Ar << bDisableTimeout; 386 | Ar << TimeMappingCurve; 387 | 388 | bOutSuccess = true; 389 | return true; 390 | 391 | } 392 | 393 | FVector FRootMotionSource_JumpForce_WithPoints::GetPathOffset(float MoveFraction) const 394 | { 395 | FVector PathOffset(FVector::ZeroVector); 396 | if (PathOffsetCurve) 397 | { 398 | // Calculate path offset 399 | PathOffset = URMSLibrary::EvaluateVectorCurveAtFraction(*PathOffsetCurve, MoveFraction); 400 | } 401 | else 402 | { 403 | // Default to "jump parabola", a simple x^2 shifted to be upside-down and shifted 404 | // to get [0,1] X (MoveFraction/Distance) mapping to [0,1] Y (height) 405 | // Height = -(2x-1)^2 + 1 406 | const float Phi = 2.f*MoveFraction - 1; 407 | const float Z = -(Phi*Phi) + 1; 408 | PathOffset.Z = Z; 409 | } 410 | 411 | // Scale Z offset to height. If Height < 0, we use direct path offset values 412 | if (SavedHeight >= 0.f) 413 | { 414 | PathOffset.Z *= SavedHeight; 415 | } 416 | 417 | return PathOffset; 418 | } 419 | 420 | FVector FRootMotionSource_JumpForce_WithPoints::GetRelativeLocation(float MoveFraction) const 421 | { 422 | // Given MoveFraction, what relative location should a character be at? 423 | FRotator FacingRotation(SavedRotation); 424 | FacingRotation.Pitch = 0.f; // By default we don't include pitch, but an option could be added if necessary 425 | 426 | FVector RelativeLocationFacingSpace = FVector(MoveFraction * SavedDistance, 0.f, 0.f) + GetPathOffset(MoveFraction); 427 | 428 | return FacingRotation.RotateVector(RelativeLocationFacingSpace); 429 | } 430 | 431 | bool FRootMotionSource_JumpForce_WithPoints::IsTimeOutEnabled() const 432 | { 433 | if (bDisableTimeout) 434 | { 435 | return false; 436 | } 437 | return FRootMotionSource::IsTimeOutEnabled(); 438 | } 439 | 440 | FRootMotionSource* FRootMotionSource_JumpForce_WithPoints::Clone() const 441 | { 442 | FRootMotionSource_JumpForce_WithPoints* CopyPtr = new FRootMotionSource_JumpForce_WithPoints(*this); 443 | return CopyPtr; 444 | } 445 | 446 | bool FRootMotionSource_JumpForce_WithPoints::Matches(const FRootMotionSource* Other) const 447 | { 448 | return FRootMotionSource::Matches(Other); 449 | } 450 | 451 | bool FRootMotionSource_JumpForce_WithPoints::MatchesAndHasSameState(const FRootMotionSource* Other) const 452 | { 453 | if (!FRootMotionSource::MatchesAndHasSameState(Other)) 454 | { 455 | return false; 456 | } 457 | 458 | return true; 459 | } 460 | 461 | UScriptStruct* FRootMotionSource_JumpForce_WithPoints::GetScriptStruct() const 462 | { 463 | return FRootMotionSource_JumpForce_WithPoints::StaticStruct(); 464 | } 465 | 466 | FString FRootMotionSource_JumpForce_WithPoints::ToSimpleString() const 467 | { 468 | return FString::Printf( 469 | TEXT("[ID:%u]FRootMotionSource_JumpForce_WithPoints %s"), LocalID, *InstanceName.GetPlainNameString());; 470 | } 471 | 472 | void FRootMotionSource_JumpForce_WithPoints::AddReferencedObjects(FReferenceCollector& Collector) 473 | { 474 | Collector.AddReferencedObject(RotationSetting.Curve); 475 | Collector.AddReferencedObject(TimeMappingCurve); 476 | Collector.AddReferencedObject(PathOffsetCurve); 477 | FRootMotionSource::AddReferencedObjects(Collector); 478 | } 479 | void FRootMotionSource_JumpForce_WithPoints::InitPath(const ACharacter& Character) 480 | { 481 | if (RotationSetting.Mode == ERMSRotationMode::None) 482 | { 483 | SavedRotation = StartRotation; 484 | //(TargetLocation - StartLocation).Rotation(); 485 | } 486 | if (!PathOffsetCurve) 487 | { 488 | PathOffsetCurve = NewObject(); 489 | } 490 | const bool bDebug = RMS::CVarRMS_Debug->GetInt()>0; 491 | 492 | const float Dist1toMid = (HalfWayLocation - StartLocation).Size(); 493 | const float DistMidto2 = (HalfWayLocation - TargetLocation).Size(); 494 | const float Dist1to2 = (StartLocation - TargetLocation).Size(); 495 | FVector AvgMidPoint = (StartLocation + TargetLocation) * 0.5; 496 | AvgMidPoint.Z = HalfWayLocation.Z; 497 | const float Dist1toAvgMid = (AvgMidPoint - StartLocation).Size(); 498 | const float DistAvgMidto2 = (AvgMidPoint - TargetLocation).Size(); 499 | SavedDistance = Dist1to2; 500 | SavedHeight = FMath::Abs(HalfWayLocation.Z - StartLocation.Z); 501 | float Duration1toMid = Duration * (Dist1toMid / (Dist1toMid + DistMidto2)); 502 | float DurationMidto2 = Duration * (DistMidto2 / (Dist1toMid + DistMidto2)); 503 | if (bDebug) 504 | { 505 | DrawDebugSphere(Character.GetWorld(), HalfWayLocation,15.0,8,FColor::Green,false,5.0,0,0.1); 506 | DrawDebugSphere(Character.GetWorld(), AvgMidPoint,15.0,8,FColor::Green,false,5.0,0,0.1); 507 | } 508 | FRichCurve XCurve; 509 | FRichCurve ZCurve; 510 | //根据这个持续时间遍历 511 | for (float i = 0; i <= Duration; i += Duration / 64.0) 512 | { 513 | const float MoveFraction = i / Duration; 514 | const float Phi = 2.f * MoveFraction - 1; 515 | const float Z = -(Phi * Phi) + 1; 516 | 517 | ZCurve.AddKey(i / Duration, Z); 518 | //处理X 519 | const float MidFrac = Dist1toMid / (Dist1toMid + DistMidto2); 520 | const float CurrDist = MoveFraction * (Dist1toMid + DistMidto2); 521 | const float CurrDistAvg = MoveFraction * (Dist1toAvgMid + DistAvgMidto2); 522 | //在前半段 523 | float X, AvgX; 524 | if (CurrDist <= Dist1toMid) 525 | { 526 | FVector CurrPoint = StartLocation + (CurrDist / Dist1toMid) * (HalfWayLocation - StartLocation); 527 | X = (CurrPoint - StartLocation).Size2D(); 528 | if (bDebug) 529 | { 530 | DrawDebugSphere(Character.GetWorld(), CurrPoint, 5.0,4,FColor::Green,false,5.0,0,0.5); 531 | } 532 | } 533 | else 534 | { 535 | FVector CurrPoint = HalfWayLocation + ((CurrDist - Dist1toMid) / DistMidto2) * (TargetLocation - HalfWayLocation); 536 | X = (CurrPoint - StartLocation).Size2D(); 537 | if (bDebug) 538 | { 539 | DrawDebugSphere(Character.GetWorld(), CurrPoint, 5.0,4,FColor::Green,false,5.0,0,0.5); 540 | } 541 | } 542 | if (CurrDistAvg <= Dist1toAvgMid) 543 | { 544 | FVector CurrPoint = StartLocation + (CurrDistAvg / Dist1toAvgMid) * (AvgMidPoint - StartLocation); 545 | AvgX = (CurrPoint - StartLocation).Size2D(); 546 | if (bDebug) 547 | { 548 | DrawDebugSphere(Character.GetWorld(), CurrPoint, 5.0,4,FColor::Red,false,5.0,0,0.5); 549 | } 550 | } 551 | else 552 | { 553 | FVector CurrPoint = AvgMidPoint + ((CurrDistAvg - Dist1toAvgMid) / DistAvgMidto2) * (TargetLocation - AvgMidPoint); 554 | AvgX = (CurrPoint - StartLocation).Size2D(); 555 | if (bDebug) 556 | { 557 | DrawDebugSphere(Character.GetWorld(), CurrPoint, 5.0,4,FColor::Red,false,5.0,0,0.5); 558 | } 559 | } 560 | XCurve.AddKey(i / Duration, X - AvgX); 561 | } 562 | XCurve.AddKey(1.0, 0.0); 563 | ZCurve.AddKey(1.0, 0.0); 564 | PathOffsetCurve->FloatCurves[0] = XCurve; 565 | PathOffsetCurve->FloatCurves[2] = ZCurve; 566 | } 567 | 568 | 569 | #pragma endregion Jump 570 | 571 | 572 | //******************************************************************* 573 | FRootMotionSource_MoveToForce_WithRotation::FRootMotionSource_MoveToForce_WithRotation() 574 | { 575 | } 576 | 577 | void FRootMotionSource_MoveToForce_WithRotation::PrepareRootMotion(float SimulationTime, float MovementTickTime, 578 | const ACharacter& Character, 579 | const UCharacterMovementComponent& MoveComponent) 580 | { 581 | if (RotationSetting.IsWarpRotation()) 582 | { 583 | RootMotionParams.Clear(); 584 | 585 | if (Duration > SMALL_NUMBER && MovementTickTime > SMALL_NUMBER) 586 | { 587 | const float MoveFraction = (GetTime() + SimulationTime) / Duration; 588 | 589 | FVector CurrentTargetLocation = FMath::Lerp(StartLocation, TargetLocation, MoveFraction); 590 | CurrentTargetLocation += GetPathOffsetInWorldSpace(MoveFraction); 591 | const FVector CurrentLocation = Character.GetActorLocation(); 592 | FVector Force = (CurrentTargetLocation - CurrentLocation) / MovementTickTime; 593 | FRotator RotationDt; 594 | const FRotator TargetRotation = RotationSetting.Mode == ERMSRotationMode::Custom 595 | ? RotationSetting.TargetRotation 596 | : (TargetLocation - StartLocation).Rotation(); 597 | const float RotationFraction = FMath::Clamp(MoveFraction * RotationSetting.WarpMultiplier,0,1); 598 | URMSLibrary::ExtractRotation(RotationDt, Character, StartRotation, TargetRotation, RotationFraction, RotationSetting.Curve); 599 | 600 | if (bRestrictSpeedToExpected && !Force.IsNearlyZero(KINDA_SMALL_NUMBER)) 601 | { 602 | // Calculate expected current location (if we didn't have collision and moved exactly where our velocity should have taken us) 603 | const float PreviousMoveFraction = GetTime() / Duration; 604 | FVector CurrentExpectedLocation = FMath::Lerp( 605 | StartLocation, TargetLocation, PreviousMoveFraction); 606 | CurrentExpectedLocation += GetPathOffsetInWorldSpace(PreviousMoveFraction); 607 | 608 | // Restrict speed to the expected speed, allowing some small amount of error 609 | const FVector ExpectedForce = (CurrentTargetLocation - CurrentExpectedLocation) / MovementTickTime; 610 | const float ExpectedSpeed = ExpectedForce.Size(); 611 | const float CurrentSpeedSqr = Force.SizeSquared(); 612 | 613 | const float ErrorAllowance = 0.5f; // in cm/s 614 | if (CurrentSpeedSqr > FMath::Square(ExpectedSpeed + ErrorAllowance)) 615 | { 616 | Force.Normalize(); 617 | Force *= ExpectedSpeed; 618 | } 619 | } 620 | 621 | // Debug 622 | #if ROOT_MOTION_DEBUG 623 | if (RMS::CVarRMS_Debug.GetValueOnGameThread() > 0) 624 | { 625 | const FVector LocDiff = MoveComponent.UpdatedComponent->GetComponentLocation() - CurrentLocation; 626 | const float DebugLifetime = 2.0f; 627 | 628 | // Current 629 | DrawDebugCapsule(Character.GetWorld(), MoveComponent.UpdatedComponent->GetComponentLocation(), 630 | Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 631 | FQuat::Identity, FColor::Red, false, DebugLifetime); 632 | 633 | // Current Target 634 | DrawDebugCapsule(Character.GetWorld(), CurrentTargetLocation + LocDiff, 635 | Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 636 | FQuat::Identity, FColor::Green, false, DebugLifetime); 637 | 638 | // Target 639 | DrawDebugCapsule(Character.GetWorld(), TargetLocation + LocDiff, 640 | Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 641 | FQuat::Identity, FColor::Blue, false, DebugLifetime); 642 | 643 | // Force 644 | DrawDebugLine(Character.GetWorld(), CurrentLocation, CurrentLocation + Force, FColor::Blue, false, 645 | DebugLifetime); 646 | } 647 | #endif 648 | 649 | FTransform NewTransform(RotationDt, Force); 650 | RootMotionParams.Set(NewTransform); 651 | } 652 | SetTime(GetTime() + SimulationTime); 653 | } 654 | else 655 | { 656 | FRootMotionSource_MoveToForce::PrepareRootMotion(SimulationTime, MovementTickTime, Character, 657 | MoveComponent); 658 | } 659 | } 660 | 661 | UScriptStruct* FRootMotionSource_MoveToForce_WithRotation::GetScriptStruct() const 662 | { 663 | return FRootMotionSource_MoveToForce_WithRotation::StaticStruct(); 664 | } 665 | 666 | FString FRootMotionSource_MoveToForce_WithRotation::ToSimpleString() const 667 | { 668 | return FString::Printf( 669 | TEXT("[ID:%u]FRootMotionSource_MoveToForce_WithRotation %s"), LocalID, *InstanceName.GetPlainNameString()); 670 | } 671 | 672 | void FRootMotionSource_MoveToForce_WithRotation::AddReferencedObjects(FReferenceCollector& Collector) 673 | { 674 | 675 | Collector.AddReferencedObject(RotationSetting.Curve); 676 | 677 | 678 | FRootMotionSource::AddReferencedObjects(Collector); 679 | } 680 | 681 | bool FRootMotionSource_MoveToForce_WithRotation::NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) 682 | { 683 | if (!FRootMotionSource::NetSerialize(Ar, Map, bOutSuccess)) 684 | { 685 | return false; 686 | } 687 | 688 | Ar << StartLocation; 689 | Ar << RotationSetting; 690 | Ar << StartLocation; // TODO-RootMotionSource: Quantization 691 | Ar << TargetLocation; // TODO-RootMotionSource: Quantization 692 | Ar << bRestrictSpeedToExpected; 693 | //Ar << PathOffsetCurve; 694 | 695 | bOutSuccess = true; 696 | return true; 697 | } 698 | 699 | FRootMotionSource* FRootMotionSource_MoveToForce_WithRotation::Clone() const 700 | { 701 | FRootMotionSource_MoveToForce_WithRotation* CopyPtr = new FRootMotionSource_MoveToForce_WithRotation(*this); 702 | return CopyPtr; 703 | } 704 | 705 | bool FRootMotionSource_MoveToForce_WithRotation::Matches(const FRootMotionSource* Other) const 706 | { 707 | if (!FRootMotionSource::Matches(Other)) 708 | { 709 | return false; 710 | } 711 | const FRootMotionSource_MoveToForce_WithRotation* OtherCast = static_cast(Other); 712 | 713 | return RotationSetting == OtherCast->RotationSetting && StartRotation == OtherCast->StartRotation; 714 | } 715 | 716 | bool FRootMotionSource_MoveToForce_WithRotation::MatchesAndHasSameState(const FRootMotionSource* Other) const 717 | { 718 | if (!FRootMotionSource::MatchesAndHasSameState(Other)) 719 | { 720 | return false; 721 | } 722 | 723 | return true; 724 | } 725 | 726 | bool FRootMotionSource_MoveToForce_WithRotation::UpdateStateFrom(const FRootMotionSource* SourceToTakeStateFrom, bool bMarkForSimulatedCatchup) 727 | { 728 | if (!FRootMotionSource::UpdateStateFrom(SourceToTakeStateFrom, bMarkForSimulatedCatchup)) 729 | { 730 | return false; 731 | } 732 | 733 | return true; 734 | } 735 | 736 | void FRootMotionSource_MoveToDynamicForce_WithRotation::PrepareRootMotion(float SimulationTime, float MovementTickTime, 737 | const ACharacter& Character, 738 | const UCharacterMovementComponent& 739 | MoveComponent) 740 | { 741 | if (RotationSetting.IsWarpRotation()) 742 | { 743 | RootMotionParams.Clear(); 744 | 745 | if (Duration > SMALL_NUMBER && MovementTickTime > SMALL_NUMBER) 746 | { 747 | float MoveFraction = (GetTime() + SimulationTime) / Duration; 748 | 749 | if (TimeMappingCurve) 750 | { 751 | MoveFraction = URMSLibrary::EvaluateFloatCurveAtFraction(*TimeMappingCurve, MoveFraction); 752 | } 753 | 754 | FVector CurrentTargetLocation = FMath::Lerp(StartLocation, TargetLocation, MoveFraction); 755 | CurrentTargetLocation += GetPathOffsetInWorldSpace(MoveFraction); 756 | 757 | const FVector CurrentLocation = Character.GetActorLocation(); 758 | 759 | FVector Force = (CurrentTargetLocation - CurrentLocation) / MovementTickTime; 760 | 761 | FRotator RotationDt; 762 | FRotator TargetRotation = RotationSetting.Mode == ERMSRotationMode::Custom 763 | ? RotationSetting.TargetRotation 764 | : (TargetLocation - StartLocation).Rotation(); 765 | const float RotationFraction = FMath::Clamp(MoveFraction * RotationSetting.WarpMultiplier,0,1); 766 | URMSLibrary::ExtractRotation(RotationDt, Character, StartRotation, TargetRotation, RotationFraction, 767 | RotationSetting.Curve); 768 | 769 | 770 | if (bRestrictSpeedToExpected && !Force.IsNearlyZero(KINDA_SMALL_NUMBER)) 771 | { 772 | // Calculate expected current location (if we didn't have collision and moved exactly where our velocity should have taken us) 773 | float PreviousMoveFraction = GetTime() / Duration; 774 | if (TimeMappingCurve) 775 | { 776 | PreviousMoveFraction = URMSLibrary::EvaluateFloatCurveAtFraction( 777 | *TimeMappingCurve, PreviousMoveFraction); 778 | } 779 | 780 | FVector CurrentExpectedLocation = FMath::Lerp( 781 | StartLocation, TargetLocation, PreviousMoveFraction); 782 | CurrentExpectedLocation += GetPathOffsetInWorldSpace(PreviousMoveFraction); 783 | 784 | // Restrict speed to the expected speed, allowing some small amount of error 785 | const FVector ExpectedForce = (CurrentTargetLocation - CurrentExpectedLocation) / MovementTickTime; 786 | const float ExpectedSpeed = ExpectedForce.Size(); 787 | const float CurrentSpeedSqr = Force.SizeSquared(); 788 | 789 | const float ErrorAllowance = 0.5f; // in cm/s 790 | if (CurrentSpeedSqr > FMath::Square(ExpectedSpeed + ErrorAllowance)) 791 | { 792 | Force.Normalize(); 793 | Force *= ExpectedSpeed; 794 | } 795 | } 796 | 797 | // Debug 798 | #if ROOT_MOTION_DEBUG 799 | if (RMS::CVarRMS_Debug.GetValueOnGameThread() > 0) 800 | { 801 | const FVector LocDiff = MoveComponent.UpdatedComponent->GetComponentLocation() - CurrentLocation; 802 | const float DebugLifetime = 2.0f; 803 | 804 | // Current 805 | DrawDebugCapsule(Character.GetWorld(), MoveComponent.UpdatedComponent->GetComponentLocation(), 806 | Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 807 | FQuat::Identity, FColor::Red, false, DebugLifetime); 808 | 809 | // Current Target 810 | DrawDebugCapsule(Character.GetWorld(), CurrentTargetLocation + LocDiff, 811 | Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 812 | FQuat::Identity, FColor::Green, false, DebugLifetime); 813 | 814 | // Target 815 | DrawDebugCapsule(Character.GetWorld(), TargetLocation + LocDiff, 816 | Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 817 | FQuat::Identity, FColor::Blue, false, DebugLifetime); 818 | 819 | // Force 820 | DrawDebugLine(Character.GetWorld(), CurrentLocation, CurrentLocation + Force, FColor::Blue, false, 821 | DebugLifetime); 822 | } 823 | #endif 824 | 825 | FTransform NewTransform(RotationDt, Force); 826 | RootMotionParams.Set(NewTransform); 827 | } 828 | SetTime(GetTime() + SimulationTime); 829 | } 830 | else 831 | { 832 | FRootMotionSource_MoveToDynamicForce::PrepareRootMotion(SimulationTime, MovementTickTime, Character, 833 | MoveComponent); 834 | } 835 | } 836 | 837 | UScriptStruct* FRootMotionSource_MoveToDynamicForce_WithRotation::GetScriptStruct() const 838 | { 839 | return FRootMotionSource_MoveToDynamicForce_WithRotation::StaticStruct(); 840 | } 841 | 842 | bool FRootMotionSource_MoveToDynamicForce_WithRotation::NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) 843 | { 844 | 845 | if (!FRootMotionSource::NetSerialize(Ar, Map, bOutSuccess)) 846 | { 847 | return false; 848 | } 849 | Ar << StartRotation; 850 | Ar << StartLocation; // TODO-RootMotionSource: Quantization 851 | Ar << InitialTargetLocation; // TODO-RootMotionSource: Quantization 852 | Ar << TargetLocation; // TODO-RootMotionSource: Quantization 853 | Ar << bRestrictSpeedToExpected; 854 | Ar << RotationSetting; 855 | //Ar << PathOffsetCurve; 856 | //Ar << TimeMappingCurve; 857 | 858 | bOutSuccess = true; 859 | return true; 860 | 861 | //return FRootMotionSource_MoveToDynamicForce::NetSerialize(Ar, Map, bOutSuccess); 862 | } 863 | 864 | FRootMotionSource* FRootMotionSource_MoveToDynamicForce_WithRotation::Clone() const 865 | { 866 | FRootMotionSource_MoveToDynamicForce_WithRotation* CopyPtr = new FRootMotionSource_MoveToDynamicForce_WithRotation(*this); 867 | return CopyPtr; 868 | } 869 | 870 | bool FRootMotionSource_MoveToDynamicForce_WithRotation::Matches(const FRootMotionSource* Other) const 871 | { 872 | if (!FRootMotionSource::Matches(Other)) 873 | { 874 | return false; 875 | } 876 | const FRootMotionSource_MoveToDynamicForce_WithRotation* OtherCast = static_cast(Other); 877 | 878 | return RotationSetting == OtherCast->RotationSetting && StartRotation == OtherCast->StartRotation; 879 | } 880 | 881 | bool FRootMotionSource_MoveToDynamicForce_WithRotation::MatchesAndHasSameState(const FRootMotionSource* Other) const 882 | { 883 | if (!FRootMotionSource::MatchesAndHasSameState(Other)) 884 | { 885 | return false; 886 | } 887 | 888 | return true; 889 | } 890 | #pragma endregion FRootMotionSource_PathMoveToForce 891 | 892 | 893 | //********************FRootMotionSource_AnimWarping*********************** 894 | #pragma region FRootMotionSource_AnimWarping 895 | 896 | 897 | FTransform FRootMotionSource_AnimWarping::ProcessRootMotion(const ACharacter& Character, 898 | const FTransform& InRootMotion, float InPreviousTime, 899 | float InCurrentTime, float DeltaSeconds) 900 | { 901 | FTransform FinalRootMotion = InRootMotion; 902 | if (!Animation || !IsValid(&Character)) 903 | { 904 | return InRootMotion; 905 | } 906 | float EndTime = GetCurrentAnimEndTime(); 907 | if (EndTime < 0 || EndTime > Animation->GetPlayLength()) 908 | { 909 | EndTime = Animation->GetPlayLength(); 910 | } 911 | 912 | const FTransform RootMotionTotal = ExtractRootMotion(InPreviousTime, EndTime); 913 | const FTransform RootMotionDelta = ExtractRootMotion(InPreviousTime, FMath::Min(InCurrentTime, EndTime)); 914 | 915 | if (!RootMotionDelta.GetTranslation().IsNearlyZero()) 916 | { 917 | const FTransform CurrentTransform = FTransform( 918 | Character.GetActorQuat(), 919 | Character.GetActorLocation() - FVector( 920 | 0.f, 0.f, Character.GetCapsuleComponent()->GetScaledCapsuleHalfHeight())); 921 | //剩下的总共的RootMotion 922 | const FTransform RootMotionTotalWorldSpace = CurrentTransform * Character.GetMesh()-> 923 | ConvertLocalRootMotionToWorld(RootMotionTotal); 924 | //这一帧的RootMotion 925 | const FTransform RootMotionDeltaWorldSpace = Character.GetMesh()->ConvertLocalRootMotionToWorld( 926 | RootMotionDelta); 927 | const FVector CurrentLocation = CurrentTransform.GetLocation(); 928 | const FQuat CurrentRotation = CurrentTransform.GetRotation(); 929 | 930 | FVector TargetLocation = GetTargetLocation(); 931 | if (bIgnoreZAxis) 932 | { 933 | TargetLocation.Z = CurrentLocation.Z; 934 | } 935 | //这一帧的偏移 936 | const FVector Translation = RootMotionDeltaWorldSpace.GetTranslation(); 937 | //总共剩下的动画RootMotion的偏移 938 | const FVector FutureLocation = RootMotionTotalWorldSpace.GetLocation(); 939 | //当前位置到目标位置的偏移 940 | const FVector CurrentToWorldOffset = TargetLocation - CurrentLocation; 941 | //当前位置到动画RootMotion的偏移 942 | const FVector CurrentToRootOffset = FutureLocation - CurrentLocation; 943 | 944 | 945 | // 创建一个矩阵,我们可以用它把所有的东西放在一个空间中,直视RootMotionSyncPosition。“向前”应该是我们想要缩放的轴。 946 | //todo 实际上就是找到当前运动做接近的一个轴向作为向前的轴 947 | FVector ToRootNormalized = CurrentToRootOffset.GetSafeNormal(); 948 | float BestMatchDot = FMath::Abs(FVector::DotProduct(ToRootNormalized, CurrentRotation.GetAxisX())); 949 | FMatrix ToRootSyncSpace = FRotationMatrix::MakeFromXZ(ToRootNormalized, CurrentRotation.GetAxisZ()); 950 | 951 | float ZDot = FMath::Abs(FVector::DotProduct(ToRootNormalized, CurrentRotation.GetAxisZ())); 952 | if (ZDot > BestMatchDot) 953 | { 954 | ToRootSyncSpace = FRotationMatrix::MakeFromXZ(ToRootNormalized, CurrentRotation.GetAxisX()); 955 | BestMatchDot = ZDot; 956 | } 957 | 958 | float YDot = FMath::Abs(FVector::DotProduct(ToRootNormalized, CurrentRotation.GetAxisY())); 959 | if (YDot > BestMatchDot) 960 | { 961 | ToRootSyncSpace = FRotationMatrix::MakeFromXZ(ToRootNormalized, CurrentRotation.GetAxisZ()); 962 | } 963 | 964 | // 把所有偏移信息都放入这个空间中 965 | const FVector RootMotionInSyncSpace = ToRootSyncSpace.InverseTransformVector(Translation); 966 | const FVector CurrentToWorldSync = ToRootSyncSpace.InverseTransformVector(CurrentToWorldOffset); 967 | const FVector CurrentToRootMotionSync = ToRootSyncSpace.InverseTransformVector(CurrentToRootOffset); 968 | 969 | FVector CurrentToWorldSyncNorm = CurrentToWorldSync; 970 | CurrentToWorldSyncNorm.Normalize(); 971 | 972 | FVector CurrentToRootMotionSyncNorm = CurrentToRootMotionSync; 973 | CurrentToRootMotionSyncNorm.Normalize(); 974 | 975 | // 计算偏斜的角度Yaw 976 | FVector FlatToWorld = FVector(CurrentToWorldSyncNorm.X, CurrentToWorldSyncNorm.Y, 0.0f); 977 | FlatToWorld.Normalize(); 978 | FVector FlatToRoot = FVector(CurrentToRootMotionSyncNorm.X, CurrentToRootMotionSyncNorm.Y, 0.0f); 979 | FlatToRoot.Normalize(); 980 | float AngleAboutZ = FMath::Acos(FVector::DotProduct(FlatToWorld, FlatToRoot)); 981 | float AngleAboutZNorm = FMath::DegreesToRadians( 982 | FRotator::NormalizeAxis(FMath::RadiansToDegrees(AngleAboutZ))); 983 | if (FlatToWorld.Y < 0.0f) 984 | { 985 | AngleAboutZNorm *= -1.0f; 986 | } 987 | 988 | // 计算偏斜的角度Pitch 989 | FVector ToWorldNoY = FVector(CurrentToWorldSyncNorm.X, 0.0f, CurrentToWorldSyncNorm.Z); 990 | ToWorldNoY.Normalize(); 991 | FVector ToRootNoY = FVector(CurrentToRootMotionSyncNorm.X, 0.0f, CurrentToRootMotionSyncNorm.Z); 992 | ToRootNoY.Normalize(); 993 | const float AngleAboutY = FMath::Acos(FVector::DotProduct(ToWorldNoY, ToRootNoY)); 994 | float AngleAboutYNorm = FMath::DegreesToRadians( 995 | FRotator::NormalizeAxis(FMath::RadiansToDegrees(AngleAboutY))); 996 | if (ToWorldNoY.Z < 0.0f) 997 | { 998 | AngleAboutYNorm *= -1.0f; 999 | } 1000 | 1001 | FVector SkewedRootMotion = FVector::ZeroVector; 1002 | float ProjectedScale = FVector::DotProduct(CurrentToWorldSync, CurrentToRootMotionSyncNorm) / 1003 | CurrentToRootMotionSync.Size(); 1004 | if (ProjectedScale != 0.0f) 1005 | { 1006 | FMatrix ScaleMatrix; 1007 | ScaleMatrix.SetIdentity(); 1008 | ScaleMatrix.SetAxis(0, FVector(ProjectedScale, 0.0f, 0.0f)); 1009 | ScaleMatrix.SetAxis(1, FVector(0.0f, 1.0f, 0.0f)); 1010 | ScaleMatrix.SetAxis(2, FVector(0.0f, 0.0f, 1.0f)); 1011 | 1012 | FMatrix ShearXAlongYMatrix; 1013 | ShearXAlongYMatrix.SetIdentity(); 1014 | ShearXAlongYMatrix.SetAxis(0, FVector(1.0f, FMath::Tan(AngleAboutZNorm), 0.0f)); 1015 | ShearXAlongYMatrix.SetAxis(1, FVector(0.0f, 1.0f, 0.0f)); 1016 | ShearXAlongYMatrix.SetAxis(2, FVector(0.0f, 0.0f, 1.0f)); 1017 | 1018 | FMatrix ShearXAlongZMatrix; 1019 | ShearXAlongZMatrix.SetIdentity(); 1020 | ShearXAlongZMatrix.SetAxis(0, FVector(1.0f, 0.0f, FMath::Tan(AngleAboutYNorm))); 1021 | ShearXAlongZMatrix.SetAxis(1, FVector(0.0f, 1.0f, 0.0f)); 1022 | ShearXAlongZMatrix.SetAxis(2, FVector(0.0f, 0.0f, 1.0f)); 1023 | 1024 | FMatrix ScaledSkewMatrix = ScaleMatrix * ShearXAlongYMatrix * ShearXAlongZMatrix; 1025 | 1026 | // Skew and scale the Root motion. 1027 | SkewedRootMotion = ScaledSkewMatrix.TransformVector(RootMotionInSyncSpace); 1028 | } 1029 | else if (!CurrentToRootMotionSync.IsZero() && !CurrentToWorldSync.IsZero() && !RootMotionInSyncSpace. 1030 | IsZero()) 1031 | { 1032 | // Figure out ratio between remaining Root and remaining World. Then project scaled length of current Root onto World. 1033 | const float Scale = CurrentToWorldSync.Size() / CurrentToRootMotionSync.Size(); 1034 | const float StepTowardTarget = RootMotionInSyncSpace.ProjectOnTo(RootMotionInSyncSpace).Size(); 1035 | SkewedRootMotion = CurrentToWorldSyncNorm * (Scale * StepTowardTarget); 1036 | } 1037 | 1038 | // Put our result back in world space. 1039 | FinalRootMotion.SetTranslation(ToRootSyncSpace.TransformVector(SkewedRootMotion)); 1040 | 1041 | 1042 | 1043 | } 1044 | if (RotationSetting.IsWarpRotation()) 1045 | { 1046 | float TimeRemaining = EndTime - PreviousTime; 1047 | const float Fraction = 1 - (EndTime - PreviousTime) / EndTime; 1048 | if (RotationSetting.Curve) 1049 | { 1050 | TimeRemaining = EndTime - FMath::Clamp(RotationSetting.Curve->GetFloatValue(Fraction), 0, 1) * EndTime; 1051 | } 1052 | const FQuat Qt = this->WarpRotation( Character, InRootMotion, RootMotionTotal, TimeRemaining , DeltaSeconds); 1053 | FinalRootMotion.SetRotation(Qt); 1054 | } 1055 | return FinalRootMotion; 1056 | } 1057 | 1058 | bool FRootMotionSource_AnimWarping::UpdateStateFrom(const FRootMotionSource* SourceToTakeStateFrom, 1059 | bool bMarkForSimulatedCatchup) 1060 | { 1061 | if (!FRootMotionSource::UpdateStateFrom(SourceToTakeStateFrom, bMarkForSimulatedCatchup)) 1062 | { 1063 | return false; 1064 | } 1065 | 1066 | return true; 1067 | } 1068 | 1069 | void FRootMotionSource_AnimWarping::PrepareRootMotion(float SimulationTime, float MovementTickTime, 1070 | const ACharacter& Character, 1071 | const UCharacterMovementComponent& MoveComponent) 1072 | { 1073 | RootMotionParams.Clear(); 1074 | 1075 | if (Animation && Duration 1076 | > 1077 | SMALL_NUMBER && MovementTickTime > SMALL_NUMBER 1078 | ) 1079 | { 1080 | const float CurrEndTime = (AnimEndTime < 0 || AnimEndTime > Animation->GetPlayLength()) 1081 | ? Animation->GetPlayLength() 1082 | : AnimEndTime; 1083 | const float CalcDuration = CurrEndTime - StartTime; 1084 | const float TimeScale = CalcDuration / Duration; 1085 | const FTransform StartFootTransform = FTransform(StartRotation, 1086 | StartLocation - FVector( 1087 | 0.f, 0.f, 1088 | Character.GetCapsuleComponent()-> 1089 | GetScaledCapsuleHalfHeight())); 1090 | const FTransform CurrChacterFootTransform = FTransform(StartRotation, 1091 | Character.GetActorLocation() - FVector( 1092 | 0.f, 0.f, 1093 | Character.GetCapsuleComponent()-> 1094 | GetScaledCapsuleHalfHeight())); 1095 | FTransform MeshTransformWS = Character.GetMesh()->GetComponentTransform(); 1096 | FTransform Mesh2CharInverse = StartFootTransform.GetRelativeTransform(MeshTransformWS); 1097 | 1098 | FTransform TargetTransform = ExtractRootMotion(AnimStartTime, CurrEndTime); 1099 | TargetTransform = TargetTransform * MeshTransformWS; //模型世界空间的RM 1100 | //通过逆矩阵把模型空间转换成actor空间 1101 | const FTransform TargetTransformWS = Mesh2CharInverse * TargetTransform; 1102 | 1103 | if (!bInit) 1104 | { 1105 | bInit = true; 1106 | SetTargetLocation(TargetTransformWS.GetLocation()); 1107 | } 1108 | 1109 | 1110 | const float PrevTime = GetTime() * TimeScale; 1111 | const float CurrTime = (GetTime() + SimulationTime) * TimeScale; 1112 | const FTransform CurrRootMotion = ExtractRootMotion(PrevTime, CurrTime); 1113 | //这个是世界空间的偏移 1114 | FTransform WarpTransform = ProcessRootMotion(Character, CurrRootMotion, PrevTime, CurrTime, SimulationTime); 1115 | //因为是世界空间的,所以是右乘 1116 | FTransform WarpTransformWS = CurrChacterFootTransform * WarpTransform; 1117 | const FVector CurrentLocation = Character.GetActorLocation() - FVector( 1118 | 0, 0, Character.GetCapsuleComponent()->GetScaledCapsuleHalfHeight()); 1119 | 1120 | FVector Force = (WarpTransformWS.GetLocation() - CurrentLocation) / MovementTickTime; 1121 | 1122 | // Debug 1123 | #if ROOT_MOTION_DEBUG 1124 | if (RMS::CVarRMS_Debug.GetValueOnGameThread() > 0) 1125 | { 1126 | const FVector LocDiff = MoveComponent.UpdatedComponent->GetComponentLocation() - CurrentLocation; 1127 | const float DebugLifetime = 5; 1128 | UE_LOG(LogTemp, Log, TEXT("Target = %s"), *TargetTransform.ToString()); 1129 | // Current 1130 | DrawDebugCapsule(Character.GetWorld(), MoveComponent.UpdatedComponent->GetComponentLocation(), 1131 | Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 1132 | FQuat::Identity, FColor::Red, false, DebugLifetime); 1133 | 1134 | // Current Target 1135 | DrawDebugCapsule(Character.GetWorld(), WarpTransformWS.GetLocation() + LocDiff, 1136 | Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 1137 | FQuat::Identity, FColor::Green, false, DebugLifetime); 1138 | 1139 | // Target 1140 | DrawDebugCapsule(Character.GetWorld(), TargetTransformWS.GetLocation() + LocDiff, 1141 | Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 1142 | FQuat::Identity, FColor::Blue, false, DebugLifetime); 1143 | 1144 | // Force 1145 | DrawDebugLine(Character.GetWorld(), CurrentLocation, CurrentLocation + Force, FColor::Blue, false, 1146 | DebugLifetime); 1147 | } 1148 | #endif 1149 | 1150 | FTransform NewTransform(Force); 1151 | RootMotionParams.Set(NewTransform); 1152 | } 1153 | else 1154 | { 1155 | checkf(Duration > SMALL_NUMBER, TEXT("FRootMotionSource_MoveToForce prepared with invalid duration.")); 1156 | } 1157 | 1158 | SetTime(GetTime() + SimulationTime); 1159 | } 1160 | 1161 | FTransform FRootMotionSource_AnimWarping::ExtractRootMotion(float InStartTime, float InEndTime) const 1162 | { 1163 | FTransform OutTransform; 1164 | if (UAnimSequence* Seq = Cast(Animation)) 1165 | { 1166 | OutTransform = Seq->ExtractRootMotionFromRange(InStartTime, InEndTime); 1167 | } 1168 | else if (UAnimMontage* Montage = Cast(Animation)) 1169 | { 1170 | OutTransform = Montage->ExtractRootMotionFromTrackRange(InStartTime, InEndTime); 1171 | } 1172 | return OutTransform; 1173 | } 1174 | 1175 | FQuat FRootMotionSource_AnimWarping::WarpRotation(const ACharacter& Character, const FTransform& RootMotionDelta, 1176 | const FTransform& RootMotionTotal, float TimeRemaining, 1177 | float DeltaSeconds) 1178 | { 1179 | if (!IsValid(&Character)) 1180 | { 1181 | return FQuat::Identity; 1182 | } 1183 | 1184 | const FTransform& CharacterTransform = Character.GetActorTransform(); 1185 | const FQuat CurrentRotation = CharacterTransform.GetRotation(); 1186 | const FQuat TargetRotation = GetTargetRotation().Quaternion(); 1187 | const FQuat RemainingRootRotationInWorld = RootMotionTotal.GetRotation(); 1188 | const FQuat CurrentPlusRemainingRootMotion = RemainingRootRotationInWorld * CurrentRotation; 1189 | 1190 | const float PercentThisStep = FMath::Clamp(DeltaSeconds / TimeRemaining * FMath::Max(0.01, RotationSetting.WarpMultiplier), 0.f, 1.f); 1191 | const FQuat TargetRotThisFrame = FQuat::Slerp(CurrentPlusRemainingRootMotion, TargetRotation, PercentThisStep); 1192 | const FQuat DeltaOut = TargetRotThisFrame * CurrentPlusRemainingRootMotion.Inverse(); 1193 | 1194 | return (DeltaOut * RootMotionDelta.GetRotation()); 1195 | } 1196 | 1197 | bool FRootMotionSource_AnimWarping::NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) 1198 | { 1199 | if (!FRootMotionSource::NetSerialize(Ar, Map, bOutSuccess)) 1200 | { 1201 | return false; 1202 | } 1203 | Ar << StartLocation; // TODO-RootMotionSource: Quantization 1204 | Ar << StartRotation; 1205 | Ar << AnimStartTime; 1206 | Ar << AnimEndTime; 1207 | Ar << Animation; 1208 | Ar << bIgnoreZAxis; 1209 | Ar << CachedEndTime; 1210 | Ar << RotationSetting; 1211 | 1212 | bOutSuccess = true; 1213 | return true; 1214 | } 1215 | 1216 | FRootMotionSource* FRootMotionSource_AnimWarping::Clone() const 1217 | { 1218 | FRootMotionSource_AnimWarping* CopyPtr = new FRootMotionSource_AnimWarping(*this); 1219 | return CopyPtr; 1220 | } 1221 | 1222 | bool FRootMotionSource_AnimWarping::Matches(const FRootMotionSource* Other) const 1223 | { 1224 | if (!FRootMotionSource::Matches(Other)) 1225 | { 1226 | return false; 1227 | } 1228 | const FRootMotionSource_AnimWarping* OtherCast = static_cast(Other); 1229 | 1230 | return StartLocation == OtherCast->StartLocation && 1231 | StartRotation == OtherCast->StartRotation && 1232 | StartTime == OtherCast->StartTime && 1233 | AnimEndTime == OtherCast->AnimEndTime && 1234 | Animation == OtherCast->Animation && 1235 | bIgnoreZAxis == OtherCast->bIgnoreZAxis && 1236 | RotationSetting == OtherCast->RotationSetting; 1237 | } 1238 | 1239 | bool FRootMotionSource_AnimWarping::MatchesAndHasSameState(const FRootMotionSource* Other) const 1240 | { 1241 | if (!FRootMotionSource::MatchesAndHasSameState(Other)) 1242 | { 1243 | return false; 1244 | } 1245 | 1246 | return true; 1247 | } 1248 | 1249 | UScriptStruct* FRootMotionSource_AnimWarping::GetScriptStruct() const 1250 | { 1251 | return FRootMotionSource_AnimWarping::StaticStruct(); 1252 | } 1253 | 1254 | FString FRootMotionSource_AnimWarping::ToSimpleString() const 1255 | { 1256 | return FString::Printf( 1257 | TEXT("[ID:%u]FRootMotionSource_AnimWarping %s"), LocalID, *InstanceName.GetPlainNameString()); 1258 | } 1259 | 1260 | void FRootMotionSource_AnimWarping::AddReferencedObjects(FReferenceCollector& Collector) 1261 | { 1262 | Collector.AddReferencedObject(Animation); 1263 | FRootMotionSource::AddReferencedObjects(Collector); 1264 | } 1265 | 1266 | #pragma endregion FRootMotionSource_AnimWarping 1267 | //*******************************FRootMotionSource_AnimWarping_FinalPoint********************************************* 1268 | 1269 | #pragma region FRootMotionSource_AnimWarping_FinalPoint 1270 | 1271 | void FRootMotionSource_AnimWarping_FinalPoint::PrepareRootMotion(float SimulationTime, 1272 | float MovementTickTime, 1273 | const ACharacter& Character, 1274 | const UCharacterMovementComponent& MoveComponent) 1275 | { 1276 | RootMotionParams.Clear(); 1277 | 1278 | if (Animation && Duration 1279 | > 1280 | SMALL_NUMBER && MovementTickTime > SMALL_NUMBER 1281 | ) 1282 | { 1283 | const float CurrEndTime = (AnimEndTime < 0 || AnimEndTime > Animation->GetPlayLength()) 1284 | ? Animation->GetPlayLength() 1285 | : AnimEndTime; 1286 | const float CalcDuration = CurrEndTime - StartTime; 1287 | const float TimeScale = CalcDuration / Duration; 1288 | const FTransform StartFootTransform = FTransform(StartRotation, 1289 | StartLocation - FVector( 1290 | 0.f, 0.f, 1291 | Character.GetCapsuleComponent()-> 1292 | GetScaledCapsuleHalfHeight())); 1293 | const FTransform CurrChacterFootTransform = FTransform(StartRotation, 1294 | Character.GetActorLocation() - FVector( 1295 | 0.f, 0.f, 1296 | Character.GetCapsuleComponent()-> 1297 | GetScaledCapsuleHalfHeight())); 1298 | FTransform MeshTransformWS = Character.GetMesh()->GetComponentTransform(); 1299 | FTransform Mesh2CharInverse = StartFootTransform.GetRelativeTransform(MeshTransformWS); 1300 | 1301 | FTransform TargetTransform = ExtractRootMotion(AnimStartTime, CurrEndTime); 1302 | TargetTransform = TargetTransform * MeshTransformWS; //模型世界空间的RM 1303 | //通过逆矩阵把模型空间转换成actor空间 1304 | const FTransform TargetTransformWS = Mesh2CharInverse * TargetTransform; 1305 | 1306 | if (!bInit) 1307 | { 1308 | bInit = true; 1309 | SetTargetLocation(TargetLocation); 1310 | SetTargetRotation(TargetRotation); 1311 | } 1312 | 1313 | 1314 | const float PrevTime = GetTime() * TimeScale; 1315 | const float CurrTime = (GetTime() + SimulationTime) * TimeScale; 1316 | const FTransform CurrRootMotion = ExtractRootMotion(PrevTime, CurrTime); 1317 | //这个是世界空间的偏移 1318 | FTransform WarpTransform = ProcessRootMotion(Character, CurrRootMotion, PrevTime, CurrTime, SimulationTime); 1319 | //为了得到正确的偏移, 先要把Rotation拿出来, 同时我们只需要Yaw 1320 | FRotator WarpRot = WarpTransform.GetRotation().Rotator(); 1321 | WarpRot.Pitch = 0; 1322 | WarpRot.Roll = 0; 1323 | WarpTransform.SetRotation(FQuat::Identity); 1324 | //因为是世界空间的,所以是右乘 1325 | FTransform WarpTransformWS = CurrChacterFootTransform * WarpTransform; 1326 | const FVector CurrentLocation = Character.GetActorLocation() - FVector( 1327 | 0, 0, Character.GetCapsuleComponent()->GetScaledCapsuleHalfHeight()); 1328 | 1329 | FVector Force = (WarpTransformWS.GetLocation() - CurrentLocation) / MovementTickTime; 1330 | 1331 | // Debug 1332 | #if ROOT_MOTION_DEBUG 1333 | if (RMS::CVarRMS_Debug.GetValueOnGameThread() > 0) 1334 | { 1335 | const FVector LocDiff = MoveComponent.UpdatedComponent->GetComponentLocation() - CurrentLocation; 1336 | const float DebugLifetime = 5; 1337 | UE_LOG(LogTemp, Log, TEXT("Target = %s"), *TargetTransform.ToString()); 1338 | // Current 1339 | DrawDebugCapsule(Character.GetWorld(), MoveComponent.UpdatedComponent->GetComponentLocation(), 1340 | Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 1341 | FQuat::Identity, FColor::Red, false, DebugLifetime); 1342 | 1343 | // Current Target 1344 | DrawDebugCapsule(Character.GetWorld(), WarpTransformWS.GetLocation() + LocDiff, 1345 | Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 1346 | FQuat::Identity, FColor::Green, false, DebugLifetime); 1347 | 1348 | // Target 1349 | // DrawDebugCapsule(Character.GetWorld(), TargetTransformWS.GetLocation() + LocDiff, 1350 | // Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 1351 | // FQuat::Identity, FColor::Blue, false, DebugLifetime); 1352 | 1353 | // Force 1354 | DrawDebugLine(Character.GetWorld(), CurrentLocation, CurrentLocation + Force, FColor::Blue, false, 1355 | DebugLifetime); 1356 | } 1357 | #endif 1358 | 1359 | FTransform NewTransform(Force); 1360 | if (RotationSetting.IsWarpRotation()) 1361 | { 1362 | NewTransform.SetRotation(WarpRot.Quaternion()); 1363 | } 1364 | 1365 | 1366 | RootMotionParams.Set(NewTransform); 1367 | } 1368 | else 1369 | { 1370 | checkf(Duration > SMALL_NUMBER, TEXT("FRootMotionSource_MoveToForce prepared with invalid duration.")); 1371 | } 1372 | 1373 | SetTime(GetTime() + SimulationTime); 1374 | } 1375 | 1376 | bool FRootMotionSource_AnimWarping_FinalPoint::NetSerialize(FArchive& Ar, UPackageMap* Map, 1377 | bool& bOutSuccess) 1378 | { 1379 | if (!FRootMotionSource::NetSerialize(Ar, Map, bOutSuccess)) 1380 | { 1381 | return false; 1382 | } 1383 | Ar << TargetLocation; 1384 | bOutSuccess = true; 1385 | return bOutSuccess; 1386 | } 1387 | 1388 | FRootMotionSource* FRootMotionSource_AnimWarping_FinalPoint::Clone() const 1389 | { 1390 | FRootMotionSource_AnimWarping_FinalPoint* CopyPtr = new FRootMotionSource_AnimWarping_FinalPoint(*this); 1391 | return CopyPtr; 1392 | } 1393 | 1394 | bool FRootMotionSource_AnimWarping_FinalPoint::Matches(const FRootMotionSource* Other) const 1395 | { 1396 | if (!FRootMotionSource::Matches(Other)) 1397 | { 1398 | return false; 1399 | } 1400 | const FRootMotionSource_AnimWarping_FinalPoint* OtherCast = static_cast(Other); 1402 | 1403 | return TargetLocation == OtherCast->TargetLocation;; 1404 | } 1405 | 1406 | 1407 | UScriptStruct* FRootMotionSource_AnimWarping_FinalPoint::GetScriptStruct() const 1408 | { 1409 | return FRootMotionSource_AnimWarping_FinalPoint::StaticStruct(); 1410 | } 1411 | 1412 | FString FRootMotionSource_AnimWarping_FinalPoint::ToSimpleString() const 1413 | { 1414 | return FString::Printf( 1415 | TEXT("[ID:%u]FRootMotionSource_AnimWarping_FinalPoint %s"), LocalID, *InstanceName.GetPlainNameString()); 1416 | } 1417 | 1418 | #pragma endregion FRootMotionSource_AnimWarping_FinalPoint 1419 | 1420 | //***************************FRootMotionSource_AnimWarping_MultiTargets******************************** 1421 | #pragma region FRootMotionSource_AnimWarping_MultiTargets 1422 | 1423 | bool FRootMotionSource_AnimWarping_MultiTargets::UpdateTriggerTarget(float SimulationTime, float TimeScale) 1424 | { 1425 | const float CurrTime = (GetTime() + SimulationTime) * TimeScale; 1426 | const int32 idx = TriggerDatas.Find(CurrTriggerData); 1427 | if (idx >= 0 && idx < TriggerDatas.Num() - 1 && CurrTime > CurrTriggerData.EndTime) 1428 | { 1429 | LastTriggerData = CurrTriggerData; 1430 | CurrTriggerData = TriggerDatas[idx + 1 ]; 1431 | RotationSetting = CurrTriggerData.RotationSetting; 1432 | return true; 1433 | } 1434 | 1435 | return false; 1436 | } 1437 | 1438 | void FRootMotionSource_AnimWarping_MultiTargets::PrepareRootMotion(float SimulationTime, float MovementTickTime, 1439 | const ACharacter& Character, 1440 | const UCharacterMovementComponent& MoveComponent) 1441 | { 1442 | RootMotionParams.Clear(); 1443 | 1444 | if (TriggerDatas.Num() > 0 && Animation && Duration > SMALL_NUMBER && MovementTickTime > SMALL_NUMBER) 1445 | { 1446 | AnimEndTime = Animation->GetPlayLength(); 1447 | const float TimeScale = AnimEndTime / Duration; 1448 | float RMStartTime = 0; 1449 | float RMEndTime = 0; 1450 | if (!bInit) 1451 | { 1452 | bInit = true; 1453 | CurrTriggerData = TriggerDatas[0]; 1454 | RotationSetting = CurrTriggerData.RotationSetting; 1455 | SetTargetLocation(CurrTriggerData.Target); 1456 | 1457 | if (RotationSetting.IsWarpRotation()) 1458 | { 1459 | if (RotationSetting.Mode == ERMSRotationMode::Custom) 1460 | { 1461 | SetTargetRotation(RotationSetting.TargetRotation); 1462 | } 1463 | else 1464 | { 1465 | SetTargetRotation((GetTargetLocation() - StartLocation).Rotation()); 1466 | } 1467 | } 1468 | else 1469 | { 1470 | SetTargetRotation(StartRotation); 1471 | } 1472 | 1473 | 1474 | SetCurrentAnimEndTime(CurrTriggerData.EndTime); 1475 | RMEndTime = CurrTriggerData.EndTime; 1476 | } 1477 | else if (UpdateTriggerTarget(SimulationTime, TimeScale)) 1478 | { 1479 | SetCurrentAnimEndTime(CurrTriggerData.EndTime); 1480 | SetTargetLocation(CurrTriggerData.Target); 1481 | 1482 | if (RotationSetting.IsWarpRotation()) 1483 | { 1484 | if (RotationSetting.Mode == ERMSRotationMode::Custom) 1485 | { 1486 | SetTargetRotation(RotationSetting.TargetRotation); 1487 | } 1488 | else 1489 | { 1490 | SetTargetRotation((GetTargetLocation() - LastTriggerData.Target).Rotation()); 1491 | } 1492 | } 1493 | 1494 | RMStartTime = CurrTriggerData.StartTime; 1495 | RMEndTime = CurrTriggerData.EndTime; 1496 | } 1497 | 1498 | 1499 | const FTransform StartFootTransform = FTransform(StartRotation, 1500 | StartLocation - FVector( 1501 | 0.f, 0.f, 1502 | Character.GetCapsuleComponent()-> 1503 | GetScaledCapsuleHalfHeight())); 1504 | const FTransform CurrChacterFootTransform = FTransform(StartRotation, 1505 | Character.GetActorLocation() - FVector( 1506 | 0.f, 0.f, 1507 | Character.GetCapsuleComponent()-> 1508 | GetScaledCapsuleHalfHeight())); 1509 | FTransform MeshTransformWS = Character.GetMesh()->GetComponentTransform(); 1510 | FTransform Mesh2CharInverse = StartFootTransform.GetRelativeTransform(MeshTransformWS); 1511 | FTransform RootMotionTargetTransform = ExtractRootMotion(RMStartTime, RMEndTime); 1512 | RootMotionTargetTransform = RootMotionTargetTransform * MeshTransformWS; //模型世界空间的RM 1513 | //通过逆矩阵把模型空间转换成actor空间 1514 | const FTransform RootMotionTargetTransformWS = Mesh2CharInverse * RootMotionTargetTransform; 1515 | 1516 | 1517 | const float PrevTime = GetTime() * TimeScale; 1518 | const float CurrTime = (GetTime() + SimulationTime) * TimeScale; 1519 | 1520 | const FTransform CurrRootMotion = ExtractRootMotion(PrevTime, CurrTime); 1521 | //这个是世界空间的偏移 1522 | FTransform WarpTransform = ProcessRootMotion(Character, CurrRootMotion, PrevTime, CurrTime, SimulationTime); 1523 | FRotator WarpRot = WarpTransform.Rotator(); 1524 | WarpRot.Pitch = 0; 1525 | WarpRot.Roll = 0; 1526 | WarpTransform.SetRotation(FQuat::Identity); 1527 | 1528 | //因为是世界空间的,所以是右乘 1529 | FTransform WarpTransformWS = CurrChacterFootTransform * WarpTransform; 1530 | const FVector CurrentLocation = Character.GetActorLocation() - FVector( 1531 | 0, 0, Character.GetCapsuleComponent()->GetScaledCapsuleHalfHeight()); 1532 | 1533 | FVector Force = (WarpTransformWS.GetLocation() - CurrentLocation) / MovementTickTime; 1534 | 1535 | // Debug 1536 | #if ROOT_MOTION_DEBUG 1537 | if (RMS::CVarRMS_Debug.GetValueOnGameThread() > 0) 1538 | { 1539 | const FVector LocDiff = MoveComponent.UpdatedComponent->GetComponentLocation() - CurrentLocation; 1540 | const float DebugLifetime = 5; 1541 | const FVector LastPos = TriggerDatas[TriggerDatas.Num() - 1].Target; 1542 | UE_LOG(LogTemp, Log, TEXT("Target = %s"), *RootMotionTargetTransform.ToString()); 1543 | // Current 1544 | DrawDebugCapsule(Character.GetWorld(), MoveComponent.UpdatedComponent->GetComponentLocation(), 1545 | Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 1546 | FQuat::Identity, FColor::Red, false, DebugLifetime); 1547 | 1548 | // Current Target 1549 | DrawDebugCapsule(Character.GetWorld(), WarpTransformWS.GetLocation() + LocDiff, 1550 | Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 1551 | FQuat::Identity, FColor::Green, false, DebugLifetime); 1552 | 1553 | // Target 1554 | DrawDebugCapsule(Character.GetWorld(), LastPos + LocDiff, Character.GetSimpleCollisionHalfHeight(), 1555 | Character.GetSimpleCollisionRadius(), FQuat::Identity, FColor::Blue, false, 1556 | DebugLifetime); 1557 | 1558 | // Force 1559 | DrawDebugLine(Character.GetWorld(), CurrentLocation, CurrentLocation + Force, FColor::Blue, false, 1560 | DebugLifetime); 1561 | 1562 | DrawDebugCapsule(Character.GetWorld(), CurrTriggerData.Target + LocDiff, 1563 | Character.GetSimpleCollisionHalfHeight(), Character.GetSimpleCollisionRadius(), 1564 | FQuat::Identity, FColor::Purple, false, DebugLifetime); 1565 | } 1566 | #endif 1567 | 1568 | FTransform NewTransform(WarpRot, Force); 1569 | RootMotionParams.Set(NewTransform); 1570 | } 1571 | else 1572 | { 1573 | checkf(Duration > SMALL_NUMBER, TEXT("FRootMotionSource_MoveToForce prepared with invalid duration.")); 1574 | } 1575 | 1576 | SetTime(GetTime() + SimulationTime); 1577 | } 1578 | 1579 | bool FRootMotionSource_AnimWarping_MultiTargets::NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) 1580 | { 1581 | if (!FRootMotionSource::NetSerialize(Ar, Map, bOutSuccess)) 1582 | { 1583 | return false; 1584 | } 1585 | Ar << TriggerDatas; 1586 | bOutSuccess = true; 1587 | return bOutSuccess; 1588 | } 1589 | 1590 | FRootMotionSource* FRootMotionSource_AnimWarping_MultiTargets::Clone() const 1591 | { 1592 | FRootMotionSource_AnimWarping_MultiTargets* CopyPtr = new FRootMotionSource_AnimWarping_MultiTargets(*this); 1593 | return CopyPtr; 1594 | } 1595 | 1596 | bool FRootMotionSource_AnimWarping_MultiTargets::Matches(const FRootMotionSource* Other) const 1597 | { 1598 | if (!FRootMotionSource::Matches(Other)) 1599 | { 1600 | return false; 1601 | } 1602 | const FRootMotionSource_AnimWarping_MultiTargets* OtherCast = static_cast(Other); 1604 | 1605 | return TriggerDatas == OtherCast->TriggerDatas;; 1606 | } 1607 | 1608 | UScriptStruct* FRootMotionSource_AnimWarping_MultiTargets::GetScriptStruct() const 1609 | { 1610 | return FRootMotionSource_AnimWarping_MultiTargets::StaticStruct(); 1611 | } 1612 | 1613 | FString FRootMotionSource_AnimWarping_MultiTargets::ToSimpleString() const 1614 | { 1615 | return FString::Printf( 1616 | TEXT("[ID:%u]FRootMotionSource_AnimWarping_MultiTargets %s"), LocalID, *InstanceName.GetPlainNameString()); 1617 | } 1618 | #pragma endregion FRootMotionSource_AnimWarping_MultiTargets 1619 | UE_ENABLE_OPTIMIZATION 1620 | -------------------------------------------------------------------------------- /Source/RMS/Private/RMSLibrary.cpp: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | 5 | #include "RMSLibrary.h" 6 | 7 | #include "AnimNotifyState_RMS.h" 8 | #include "Experimental/RMSComponent.h" 9 | #include "RMSGroupEx.h" 10 | #include "Components/CapsuleComponent.h" 11 | #include "Curves/CurveVector.h" 12 | #include "GameFramework/Character.h" 13 | #include "GameFramework/CharacterMovementComponent.h" 14 | #include "Kismet/KismetMathLibrary.h" 15 | #include "Kismet/KismetSystemLibrary.h" 16 | 17 | class UAnimNotifyState_RMS_Warping; 18 | 19 | UE_DISABLE_OPTIMIZATION 20 | 21 | float URMSLibrary::EvaluateFloatCurveAtFraction(const UCurveFloat& Curve, const float Fraction) 22 | { 23 | float MinCurveTime(0.f); 24 | float MaxCurveTime(1.f); 25 | 26 | Curve.GetTimeRange(MinCurveTime, MaxCurveTime); 27 | return Curve.GetFloatValue(FMath::GetRangeValue(FVector2D(MinCurveTime, MaxCurveTime), Fraction)); 28 | } 29 | 30 | FVector URMSLibrary::EvaluateVectorCurveAtFraction(const UCurveVector& Curve, const float Fraction) 31 | { 32 | float MinCurveTime(0.f); 33 | float MaxCurveTime(1.f); 34 | 35 | Curve.GetTimeRange(MinCurveTime, MaxCurveTime); 36 | return Curve.GetVectorValue(FMath::GetRangeValue(FVector2D(MinCurveTime, MaxCurveTime), Fraction)); 37 | } 38 | 39 | 40 | int32 URMSLibrary::ApplyRootMotionSource_MoveToForce(UCharacterMovementComponent* MovementComponent, 41 | FName InstanceName, 42 | FVector StartLocation, FVector TargetLocation, 43 | float Duration, 44 | int32 Priority, 45 | UCurveVector* PathOffsetCurve, 46 | FRMSRotationSetting RotationSetting, 47 | float StartTime, 48 | ERMSApplyMode ApplyMode, 49 | FRMSSetting_Move Setting) 50 | { 51 | if (!MovementComponent) 52 | { 53 | return -1; 54 | } 55 | const int32 NewPriority = CalcPriorityByApplyMode(MovementComponent, InstanceName, Priority, ApplyMode); 56 | if (NewPriority < 0) 57 | { 58 | return -1; 59 | } 60 | 61 | TSharedPtr MoveToForce = MakeShared< 62 | FRootMotionSource_MoveToForce_WithRotation>(); 63 | MoveToForce->InstanceName = InstanceName == NAME_None ? TEXT("MoveToForce") : InstanceName; 64 | MoveToForce->AccumulateMode = Setting.AccumulateMod; 65 | MoveToForce->Settings.SetFlag( 66 | static_cast(static_cast(Setting.SourcesSetting))); 67 | MoveToForce->Priority = NewPriority; 68 | MoveToForce->TargetLocation = TargetLocation; 69 | MoveToForce->StartLocation = StartLocation; 70 | MoveToForce->Duration = Duration; 71 | MoveToForce->bRestrictSpeedToExpected = Setting.bRestrictSpeedToExpected; 72 | MoveToForce->PathOffsetCurve = PathOffsetCurve; 73 | MoveToForce->FinishVelocityParams.Mode = static_cast(static_cast(Setting. 74 | VelocityOnFinishMode)); 75 | MoveToForce->FinishVelocityParams.SetVelocity = Setting.FinishSetVelocity; 76 | MoveToForce->FinishVelocityParams.ClampVelocity = Setting.FinishClampVelocity; 77 | MoveToForce->SetTime(StartTime); 78 | MoveToForce->StartRotation = MovementComponent->GetOwner()->GetActorRotation(); 79 | MoveToForce->RotationSetting = RotationSetting; 80 | 81 | return MovementComponent->ApplyRootMotionSource(MoveToForce); 82 | } 83 | 84 | int32 URMSLibrary::ApplyRootMotionSource_JumpForce(UCharacterMovementComponent* MovementComponent, 85 | FName InstanceName, 86 | FRotator Rotation, float Duration, float Distance, 87 | float Height, int32 Priority, 88 | UCurveVector* PathOffsetCurve, 89 | UCurveFloat* TimeMappingCurve, 90 | float StartTime, 91 | ERMSApplyMode ApplyMode, 92 | FRMSSetting_Jump Setting) 93 | { 94 | if (!MovementComponent) 95 | { 96 | return -1; 97 | } 98 | const int32 NewPriority = CalcPriorityByApplyMode(MovementComponent, InstanceName, Priority, ApplyMode); 99 | if (NewPriority < 0) 100 | { 101 | return -1; 102 | } 103 | TSharedPtr JumpForce = MakeShared(); 104 | JumpForce->InstanceName = InstanceName == NAME_None ? TEXT("Jump") : InstanceName; 105 | JumpForce->AccumulateMode = Setting.AccumulateMod; 106 | JumpForce->Priority = NewPriority; 107 | JumpForce->Duration = Duration; 108 | JumpForce->Rotation = Rotation; 109 | JumpForce->Distance = Distance; 110 | JumpForce->Height = Height; 111 | JumpForce->bDisableTimeout = Setting.bFinishOnLanded; // If we finish on landed, we need to disable force's timeout 112 | JumpForce->PathOffsetCurve = PathOffsetCurve; 113 | JumpForce->TimeMappingCurve = TimeMappingCurve; 114 | JumpForce->FinishVelocityParams.Mode = static_cast(static_cast(Setting. 115 | VelocityOnFinishMode)); 116 | JumpForce->FinishVelocityParams.SetVelocity = Setting.FinishSetVelocity; 117 | JumpForce->FinishVelocityParams.ClampVelocity = Setting.FinishClampVelocity; 118 | JumpForce->SetTime(StartTime); 119 | return MovementComponent->ApplyRootMotionSource(JumpForce); 120 | } 121 | 122 | int32 URMSLibrary::ApplyRootMotionSource_JumpForce_WithPoints(UCharacterMovementComponent* MovementComponent, FName InstanceName, FRotator StartRotation, float Duration, FVector StartLocation, FVector TargetLocation, FVector HalfWayLocation, 123 | int32 Priority, FRMSRotationSetting RotationSetting, UCurveFloat* TimeMappingCurve, float StartTime, ERMSApplyMode ApplyMode, FRMSSetting_Jump Setting) 124 | { 125 | if (!MovementComponent) 126 | { 127 | return -1; 128 | } 129 | const int32 NewPriority = CalcPriorityByApplyMode(MovementComponent, InstanceName, Priority, ApplyMode); 130 | if (NewPriority < 0) 131 | { 132 | return -1; 133 | } 134 | TSharedPtr JumpForce = MakeShared(); 135 | JumpForce->InstanceName = InstanceName == NAME_None ? TEXT("Jump") : InstanceName; 136 | JumpForce->AccumulateMode = Setting.AccumulateMod; 137 | JumpForce->Priority = NewPriority; 138 | JumpForce->Duration = Duration; 139 | JumpForce->StartLocation = StartLocation; 140 | JumpForce->StartRotation = StartRotation; 141 | JumpForce->HalfWayLocation = HalfWayLocation; 142 | JumpForce->TargetLocation = TargetLocation; 143 | JumpForce->bDisableTimeout = Setting.bFinishOnLanded; // If we finish on landed, we need to disable force's timeout 144 | JumpForce->RotationSetting = RotationSetting; 145 | JumpForce->TimeMappingCurve = TimeMappingCurve; 146 | JumpForce->FinishVelocityParams.Mode = static_cast(static_cast(Setting. 147 | VelocityOnFinishMode)); 148 | JumpForce->FinishVelocityParams.SetVelocity = Setting.FinishSetVelocity; 149 | JumpForce->FinishVelocityParams.ClampVelocity = Setting.FinishClampVelocity; 150 | JumpForce->SetTime(StartTime); 151 | return MovementComponent->ApplyRootMotionSource(JumpForce); 152 | 153 | } 154 | 155 | int32 URMSLibrary::ApplyRootMotionSource_DynamicMoveToForce(UCharacterMovementComponent* MovementComponent, 156 | FName InstanceName, FVector StartLocation, 157 | FVector TargetLocation, float Duration, 158 | int32 Priority, 159 | UCurveVector* PathOffsetCurve, 160 | UCurveFloat* TimeMappingCurve, 161 | FRMSRotationSetting RotationSetting, 162 | float StartTime, 163 | ERMSApplyMode ApplyMode, 164 | FRMSSetting_Move Setting) 165 | { 166 | if (!MovementComponent) 167 | { 168 | return -1; 169 | } 170 | const int32 NewPriority = CalcPriorityByApplyMode(MovementComponent, InstanceName, Priority, ApplyMode); 171 | if (NewPriority < 0) 172 | { 173 | return -1; 174 | } 175 | TSharedPtr MoveToActorForce = MakeShared< 176 | FRootMotionSource_MoveToDynamicForce_WithRotation>(); 177 | MoveToActorForce->InstanceName = InstanceName == NAME_None ? TEXT("DynamicMoveTo") : InstanceName; 178 | MoveToActorForce->AccumulateMode = Setting.AccumulateMod; 179 | MoveToActorForce->Settings.SetFlag( 180 | static_cast(static_cast(Setting.SourcesSetting))); 181 | MoveToActorForce->Priority = NewPriority; 182 | MoveToActorForce->InitialTargetLocation = TargetLocation; 183 | MoveToActorForce->TargetLocation = TargetLocation; 184 | MoveToActorForce->StartLocation = StartLocation; 185 | MoveToActorForce->Duration = FMath::Max(Duration, KINDA_SMALL_NUMBER); 186 | MoveToActorForce->bRestrictSpeedToExpected = Setting.bRestrictSpeedToExpected; 187 | MoveToActorForce->PathOffsetCurve = PathOffsetCurve; 188 | MoveToActorForce->TimeMappingCurve = TimeMappingCurve; 189 | MoveToActorForce->FinishVelocityParams.Mode = static_cast(static_cast(Setting. 190 | VelocityOnFinishMode)); 191 | MoveToActorForce->FinishVelocityParams.SetVelocity = Setting.FinishSetVelocity; 192 | MoveToActorForce->FinishVelocityParams.ClampVelocity = Setting.FinishClampVelocity; 193 | MoveToActorForce->SetTime(StartTime); 194 | MoveToActorForce->RotationSetting = RotationSetting; 195 | MoveToActorForce->StartRotation = MovementComponent->GetOwner()->GetActorRotation(); 196 | return MovementComponent->ApplyRootMotionSource(MoveToActorForce); 197 | } 198 | 199 | int32 URMSLibrary::ApplyRootMotionSource_MoveToForce_Parabola( 200 | UCharacterMovementComponent* MovementComponent, 201 | FName InstanceName, 202 | FVector StartLocation, FVector TargetLocation, 203 | float Duration, 204 | int32 Priority, 205 | UCurveFloat* ParabolaCurve, 206 | UCurveFloat* TimeMappingCurve, 207 | FRMSRotationSetting RotationSetting, 208 | int32 Segment, 209 | float StartTime, 210 | ERMSApplyMode ApplyMode, 211 | FRMSSetting_Move Setting) 212 | { 213 | if (!MovementComponent || Duration <= 0) 214 | { 215 | return -1; 216 | } 217 | const int32 NewPriority = CalcPriorityByApplyMode(MovementComponent, InstanceName, Priority, ApplyMode); 218 | if (NewPriority < 0) 219 | { 220 | return -1; 221 | } 222 | TSharedPtr MoveToForce = MakeShared< 223 | FRootMotionSource_MoveToDynamicForce_WithRotation>(); 224 | MoveToForce->InstanceName = InstanceName == NAME_None ? TEXT("ParabolaMoveTo") : InstanceName; 225 | MoveToForce->AccumulateMode = Setting.AccumulateMod; 226 | MoveToForce->Settings.SetFlag( 227 | static_cast(static_cast(Setting.SourcesSetting))); 228 | MoveToForce->Priority = NewPriority; 229 | MoveToForce->StartLocation = StartLocation; 230 | MoveToForce->Duration = Duration; 231 | MoveToForce->StartRotation = MovementComponent->GetOwner()->GetActorRotation(); 232 | MoveToForce->bRestrictSpeedToExpected = Setting.bRestrictSpeedToExpected; 233 | FVector Target = TargetLocation; 234 | UCurveVector* PathCurve = NewObject(); 235 | const bool bUseTotalOffset = false; 236 | if (ParabolaCurve) 237 | { 238 | FRichCurve ZCurve; 239 | if (bUseTotalOffset) 240 | { 241 | const float OffsetZ = (TargetLocation - StartLocation).Z; 242 | Target.Z = StartLocation.Z; 243 | 244 | for (int32 i = 0; i <= Segment; i++) 245 | { 246 | const float Fraction = static_cast(i) / static_cast(Segment); 247 | const float FractionZ = FMath::Lerp(0, OffsetZ, Fraction); 248 | const float Value = ParabolaCurve->GetFloatValue(Fraction); 249 | ZCurve.AddKey(Fraction, OffsetZ * Value); 250 | } 251 | } 252 | else 253 | { 254 | const float OffsetZ = (TargetLocation - StartLocation).Z; 255 | 256 | for (int32 i = 0; i <= Segment; i++) 257 | { 258 | const float Fraction = static_cast(i) / static_cast(Segment); 259 | const float FractionZ = FMath::Lerp(0, TargetLocation.Z - StartLocation.Z, Fraction); 260 | const float Value = ParabolaCurve->GetFloatValue(Fraction); 261 | ZCurve.AddKey(Fraction, OffsetZ * Value - FractionZ); 262 | } 263 | 264 | } 265 | 266 | PathCurve->FloatCurves[2] = ZCurve; 267 | } 268 | 269 | MoveToForce->TargetLocation = Target; 270 | MoveToForce->TimeMappingCurve = TimeMappingCurve; 271 | MoveToForce->PathOffsetCurve = PathCurve; 272 | MoveToForce->FinishVelocityParams.Mode = static_cast(static_cast(Setting. 273 | VelocityOnFinishMode)); 274 | MoveToForce->FinishVelocityParams.SetVelocity = Setting.FinishSetVelocity; 275 | MoveToForce->FinishVelocityParams.ClampVelocity = Setting.FinishClampVelocity; 276 | MoveToForce->SetTime(StartTime); 277 | MoveToForce->RotationSetting = RotationSetting; 278 | return MovementComponent->ApplyRootMotionSource(MoveToForce); 279 | } 280 | 281 | int32 URMSLibrary::ApplyRootMotionSource_PathMoveToForce(UCharacterMovementComponent* MovementComponent, 282 | FName InstanceName, FVector StartLocation, 283 | FRotator StartRotation, 284 | TArray Path, 285 | int32 Priority, float StartTime, 286 | ERMSApplyMode ApplyMode, 287 | FRMSSetting_Move ExtraSetting) 288 | { 289 | if (!MovementComponent) 290 | { 291 | return -1; 292 | } 293 | if (Path.Num() < 0) 294 | { 295 | return -1; 296 | } 297 | float Duration = 0; 298 | for (auto P : Path) 299 | { 300 | if (P.Duration <= 0) 301 | { 302 | return -1; 303 | } 304 | Duration += P.Duration; 305 | } 306 | const int32 NewPriority = CalcPriorityByApplyMode(MovementComponent, InstanceName, Priority, ApplyMode); 307 | if (NewPriority < 0) 308 | { 309 | return -1; 310 | } 311 | 312 | TSharedPtr PathMoveTo = MakeShared< 313 | FRootMotionSource_PathMoveToForce>(); 314 | PathMoveTo->InstanceName = InstanceName == NAME_None ? TEXT("PathMoveTo") : InstanceName; 315 | PathMoveTo->AccumulateMode = ExtraSetting.AccumulateMod; 316 | PathMoveTo->Settings.SetFlag( 317 | static_cast(static_cast(ExtraSetting.SourcesSetting))); 318 | PathMoveTo->Priority = NewPriority; 319 | PathMoveTo->Path = Path; 320 | PathMoveTo->StartLocation = StartLocation; 321 | PathMoveTo->Duration = FMath::Max(Duration, KINDA_SMALL_NUMBER); 322 | PathMoveTo->FinishVelocityParams.Mode = static_cast(static_cast(ExtraSetting. 323 | VelocityOnFinishMode)); 324 | PathMoveTo->FinishVelocityParams.SetVelocity = ExtraSetting.FinishSetVelocity; 325 | PathMoveTo->FinishVelocityParams.ClampVelocity = ExtraSetting.FinishClampVelocity; 326 | PathMoveTo->SetTime(StartTime); 327 | PathMoveTo->StartRotation = StartRotation; 328 | return MovementComponent->ApplyRootMotionSource(PathMoveTo); 329 | } 330 | 331 | int32 URMSLibrary::ApplyRootMotionSource_PathMoveToForce_V2(UCharacterMovementComponent* MovementComponent, 332 | FName InstanceName, FRotator StartRotation, TArray Path, int32 Priority, float StartTime,float Duration, 333 | FRMSRotationSetting RotationSetting, ERMSApplyMode ApplyMode, FRMSSetting_Move Setting, ERichCurveTangentMode TangentMode, 334 | ERichCurveInterpMode InterpMode) 335 | { 336 | const int32 PathNum = Path.Num(); 337 | if (!MovementComponent || Duration <= 0 || PathNum<2) 338 | { 339 | return -1; 340 | } 341 | const int32 NewPriority = CalcPriorityByApplyMode(MovementComponent, InstanceName, Priority, ApplyMode); 342 | if (NewPriority < 0) 343 | { 344 | return -1; 345 | } 346 | TSharedPtr MoveToForce = MakeShared< 347 | FRootMotionSource_MoveToDynamicForce_WithRotation>(); 348 | MoveToForce->InstanceName = InstanceName == NAME_None ? TEXT("PathMoveToV2") : InstanceName; 349 | MoveToForce->AccumulateMode = Setting.AccumulateMod; 350 | MoveToForce->Settings.SetFlag( 351 | static_cast(static_cast(Setting.SourcesSetting))); 352 | MoveToForce->Priority = NewPriority; 353 | MoveToForce->StartLocation = Path[0]; 354 | MoveToForce->Duration = Duration; 355 | MoveToForce->StartRotation = MovementComponent->GetOwner()->GetActorRotation(); 356 | MoveToForce->bRestrictSpeedToExpected = Setting.bRestrictSpeedToExpected; 357 | MoveToForce->TargetLocation = Path.Last(); 358 | 359 | UCurveVector* PathCurve = NewObject(); 360 | const bool bUseTotalOffset = false; 361 | 362 | if (PathNum>2) 363 | { 364 | auto SetCurve_Lambda = [=](FRichCurve& Curve, float Time, float Value, ERichCurveTangentMode Tangent, ERichCurveInterpMode Interp) 365 | { 366 | auto Handle = Curve.AddKey(Time, Value); 367 | Curve.SetKeyTangentMode(Handle,Tangent); 368 | Curve.SetKeyInterpMode(Handle,Interp); 369 | }; 370 | FRichCurve XCurve; 371 | FRichCurve YCurve; 372 | FRichCurve ZCurve; 373 | FVector Start = Path[0]; 374 | FVector Target = Path.Last(); 375 | FVector Dir = Target - Start; 376 | 377 | SetCurve_Lambda(XCurve,0,0,TangentMode,InterpMode); 378 | SetCurve_Lambda(YCurve,0,0,TangentMode,InterpMode); 379 | SetCurve_Lambda(ZCurve,0,0,TangentMode,InterpMode); 380 | 381 | 382 | for (int32 i = 1; i < PathNum - 1; i ++) 383 | { 384 | const float Frac = float(i) / float(PathNum - 1); 385 | const FVector CurrPoint = Start + Frac * Dir; 386 | FVector Offset = Path[i] - CurrPoint; 387 | 388 | SetCurve_Lambda(XCurve,Frac,Offset.X,TangentMode,InterpMode); 389 | SetCurve_Lambda(YCurve,Frac,Offset.Y,TangentMode,InterpMode); 390 | SetCurve_Lambda(ZCurve,Frac,Offset.Z,TangentMode,InterpMode); 391 | 392 | } 393 | 394 | SetCurve_Lambda(XCurve,1,0,TangentMode,InterpMode); 395 | SetCurve_Lambda(YCurve,1,0,TangentMode,InterpMode); 396 | SetCurve_Lambda(ZCurve,1,0,TangentMode,InterpMode); 397 | 398 | 399 | PathCurve->FloatCurves[0] = XCurve; 400 | PathCurve->FloatCurves[1] = YCurve; 401 | PathCurve->FloatCurves[2] = ZCurve; 402 | } 403 | 404 | MoveToForce->PathOffsetCurve = PathCurve; 405 | MoveToForce->FinishVelocityParams.Mode = static_cast(static_cast(Setting. 406 | VelocityOnFinishMode)); 407 | MoveToForce->FinishVelocityParams.SetVelocity = Setting.FinishSetVelocity; 408 | MoveToForce->FinishVelocityParams.ClampVelocity = Setting.FinishClampVelocity; 409 | MoveToForce->SetTime(StartTime); 410 | MoveToForce->RotationSetting = RotationSetting; 411 | return MovementComponent->ApplyRootMotionSource(MoveToForce); 412 | 413 | } 414 | 415 | bool URMSLibrary::ApplyRootMotionSource_SimpleAnimation_BM(UCharacterMovementComponent* MovementComponent, 416 | UAnimSequence* DataAnimation, 417 | FName InstanceName, 418 | int32 Priority, 419 | float StartTime, float EndTime, float Rate, 420 | bool bIgnoreZAxis, 421 | ERMSApplyMode ApplyMode) 422 | { 423 | if (!MovementComponent || !DataAnimation || (StartTime > EndTime && EndTime > 0)) 424 | { 425 | return false; 426 | } 427 | ACharacter* Character = Cast(MovementComponent->GetOwner()); 428 | USkeletalMeshComponent* Mesh = Character->GetMesh(); 429 | if (!Character || !Mesh) 430 | { 431 | return false; 432 | } 433 | if (CalcPriorityByApplyMode(MovementComponent, InstanceName, Priority, ApplyMode) < 0) 434 | { 435 | return false; 436 | } 437 | float HalfHeight = Character->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(); 438 | //持续时间, 获取的是动画时长或者自定的时间 439 | const float Length = DataAnimation->GetPlayLength(); 440 | if (EndTime <= 0) 441 | { 442 | EndTime = Length; 443 | } 444 | const float Duration = EndTime - StartTime; 445 | int32 NumFrame = DataAnimation->GetNumberOfSampledKeys(); 446 | const float FrameTime = DataAnimation->GetPlayLength() / NumFrame; 447 | //开始位置 448 | FVector StartLocation = Character->GetActorLocation(); 449 | FRotator StartRotation = Character->GetActorRotation(); 450 | const FTransform StartFootTransform = FTransform(StartRotation, 451 | StartLocation - FVector( 452 | 0.f, 0.f, 453 | Character->GetCapsuleComponent()-> 454 | GetScaledCapsuleHalfHeight())); 455 | FTransform MeshTransformWS = Character->GetMesh()->GetComponentTransform(); 456 | FTransform Mesh2CharInverse = StartFootTransform.GetRelativeTransform(MeshTransformWS); 457 | FTransform RootMotion = DataAnimation->ExtractRootMotionFromRange(StartTime, EndTime); 458 | FTransform RootMotionWS = RootMotion * MeshTransformWS; //模型世界空间的RM 459 | //通过逆矩阵把模型空间转换成actor空间 460 | const FTransform TargetTransformWS = Mesh2CharInverse * RootMotionWS; 461 | 462 | 463 | //用动态曲线的方式 , 而非静态, 464 | UCurveVector* OffsetCV = NewObject(); 465 | FRichCurve CurveX, CurveY, CurveZ; 466 | /* 467 | *遍历每一帧获取当前动画的RootMotion位置, 468 | *减去线性当前帧的位置,得到了动画的曲线偏移值 469 | *计算动画与期望位置的比率 470 | *乘以之前的曲线偏移值得到最终的偏移值 471 | */ 472 | for (float CurrentTime = StartTime; CurrentTime <= EndTime; CurrentTime += FrameTime) 473 | { 474 | float Fraction = (CurrentTime - StartTime) / Duration; 475 | const FTransform CurrFrameRootMotion = DataAnimation->ExtractRootMotion(0, CurrentTime, false); 476 | const FTransform CurrFrameRootMotionWS = CurrFrameRootMotion * MeshTransformWS; 477 | const FTransform CurrFrameActorRootMotionWS = Mesh2CharInverse * CurrFrameRootMotionWS; 478 | FRotator RMSRotation = UKismetMathLibrary::MakeRotFromXZ( 479 | TargetTransformWS.GetLocation() - StartFootTransform.GetLocation(), FVector(0, 0, 1)); 480 | FTransform RMSSpaceTM{RMSRotation, StartFootTransform.GetLocation()}; 481 | const FVector FinalTargetRMS = RMSSpaceTM.InverseTransformPosition(TargetTransformWS.GetLocation()); 482 | const FVector CurrFrameActorRootMotionRMS = RMSSpaceTM.InverseTransformPosition( 483 | CurrFrameActorRootMotionWS.GetLocation()); 484 | 485 | FVector AnimRootMotionLinearFraction = FinalTargetRMS * Fraction; 486 | //动画位置与线性偏移位置的偏差 487 | FVector CurveOffset = CurrFrameActorRootMotionRMS - AnimRootMotionLinearFraction; 488 | 489 | CurveX.AddKey(CurrentTime / Duration / Rate, CurveOffset.X); 490 | CurveY.AddKey(CurrentTime / Duration / Rate, CurveOffset.Y); 491 | CurveZ.AddKey(CurrentTime / Duration / Rate, CurveOffset.Z); 492 | 493 | 494 | if (RMS::CVarRMS_Debug.GetValueOnGameThread() > 0) 495 | { 496 | //动画每一帧位置 497 | UKismetSystemLibrary::DrawDebugCapsule( 498 | Character, CurrFrameActorRootMotionWS.GetLocation() + FVector(0, 0, HalfHeight), 499 | Character->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(), 500 | Character->GetCapsuleComponent()->GetScaledCapsuleRadius(), 501 | FRotator::ZeroRotator, FColor::Red, 5, 0.1); 502 | } 503 | } 504 | OffsetCV->FloatCurves[0] = CurveX; 505 | OffsetCV->FloatCurves[1] = CurveY; 506 | if (!bIgnoreZAxis) 507 | { 508 | OffsetCV->FloatCurves[2] = CurveZ; 509 | } 510 | FVector WorldTarget = TargetTransformWS.GetLocation() + FVector(0, 0, HalfHeight); 511 | 512 | FRMSSetting_Move Setting; 513 | Setting.VelocityOnFinishMode = ERMSFinishVelocityMode::MaintainLastRootMotionVelocity; 514 | FName InsName = InstanceName == NAME_None ? TEXT("SimpleAnimation") : InstanceName; 515 | return ApplyRootMotionSource_MoveToForce(MovementComponent, InsName, StartLocation, WorldTarget, 516 | Duration / Rate, Priority, OffsetCV, FRMSRotationSetting(), StartTime, 517 | ERMSApplyMode::Replace, Setting) >= 0; 518 | } 519 | 520 | 521 | bool URMSLibrary::ApplyRootMotionSource_AnimationAdjustment_BM( 522 | UCharacterMovementComponent* MovementComponent, 523 | UAnimSequence* DataAnimation, 524 | FName InstanceName, 525 | int32 Priority, 526 | FVector TargetLocation, 527 | bool bLocalTarget, 528 | bool bTargetBasedOnFoot, 529 | float InStartTime, 530 | float InEndTime, 531 | float Rate, 532 | FRMSRotationSetting RotationSetting, 533 | ERMSApplyMode ApplyMode) 534 | { 535 | if (!MovementComponent || !DataAnimation || InStartTime < 0 || (InEndTime > 0 && InEndTime <= InStartTime) || Rate 536 | <= 0) 537 | { 538 | return false; 539 | } 540 | if (CalcPriorityByApplyMode(MovementComponent, InstanceName, Priority, ApplyMode) < 0) 541 | { 542 | return false; 543 | } 544 | ACharacter* Character = Cast(MovementComponent->GetOwner()); 545 | USkeletalMeshComponent* Mesh = Character->GetMesh(); 546 | if (!Character || !Mesh) 547 | { 548 | return false; 549 | } 550 | float HalfHeight = Character->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(); 551 | const float EndTime = (InEndTime < 0 || InEndTime > DataAnimation->GetPlayLength()) 552 | ? DataAnimation->GetPlayLength() 553 | : InEndTime; 554 | int32 NumFrame = DataAnimation->GetNumberOfSampledKeys(); 555 | const float FrameTime = DataAnimation->GetPlayLength() / NumFrame; 556 | 557 | FVector StartLocation = Character->GetActorLocation(); 558 | FRotator StartRotation = Character->GetActorRotation(); 559 | const FTransform StartFootTransform = FTransform(StartRotation, 560 | StartLocation - FVector( 561 | 0.f, 0.f, 562 | Character->GetCapsuleComponent()-> 563 | GetScaledCapsuleHalfHeight())); 564 | FTransform RootMotion = DataAnimation->ExtractRootMotionFromRange(0, EndTime); 565 | FTransform Mesh2Char = Mesh->GetComponentTransform().GetRelativeTransform(StartFootTransform); 566 | Mesh2Char.SetLocation(FVector::ZeroVector); 567 | const FVector TargetLocationActorSpace = (RootMotion * Mesh2Char).GetLocation(); 568 | //用动态曲线的方式 , 而非静态, 569 | UCurveVector* OffsetCV = NewObject(); 570 | FRichCurve CurveX, CurveY, CurveZ; 571 | FVector WorldFootTarget = bTargetBasedOnFoot ? TargetLocation : TargetLocation - FVector(0, 0, HalfHeight); 572 | WorldFootTarget = bLocalTarget ? StartFootTransform.TransformPosition(TargetLocation) : WorldFootTarget; 573 | 574 | /* 575 | *遍历每一帧获取当前动画的RootMotion位置, 576 | *减去线性当前帧的位置,得到了动画的曲线偏移值 577 | *计算动画与期望位置的比率 578 | *乘以之前的曲线偏移值得到最终的偏移值 579 | */ 580 | for (float CurrentTime = 0; CurrentTime <= EndTime; CurrentTime += FrameTime) 581 | { 582 | float Fraction = CurrentTime / EndTime; 583 | //获取当前时间的rootMotion 584 | const FTransform CurrFrameRootMotion = DataAnimation->ExtractRootMotion(0, CurrentTime, false); 585 | const FTransform CurrFrameActorRootMotionActorSpace = CurrFrameRootMotion * Mesh2Char; 586 | const FVector CurrFrameActorRootMotionWS = StartFootTransform.TransformPosition( 587 | CurrFrameActorRootMotionActorSpace.GetLocation());; 588 | //创建RMS空间矩阵 589 | FRotator RMSRotation = UKismetMathLibrary::MakeRotFromXZ(WorldFootTarget - StartFootTransform.GetLocation(), 590 | FVector(0, 0, 1)); 591 | FTransform RMSSpaceTM{RMSRotation, StartFootTransform.GetLocation()}; 592 | 593 | //将所需的位置信息转换至RMS空间 594 | const FVector FinalTargetRMS = RMSSpaceTM.InverseTransformPosition(WorldFootTarget); 595 | const FVector FinalActorRootMotionRMS = TargetLocationActorSpace; 596 | const FVector CurrFrameActorRootMotionRMS = (CurrFrameRootMotion * Mesh2Char).GetLocation(); 597 | 598 | FVector FinalTargetRMSLinearFraction = FinalTargetRMS * Fraction; 599 | FVector FinalAnimRootMotionRMSLinearFraction = FinalActorRootMotionRMS * Fraction; 600 | FVector CurrFrameRootMotion2Linear = CurrFrameActorRootMotionRMS - FinalAnimRootMotionRMSLinearFraction; 601 | 602 | const float Ratio = FinalAnimRootMotionRMSLinearFraction.Size() == 0 603 | ? 0 604 | : FinalTargetRMSLinearFraction.Size() / FinalAnimRootMotionRMSLinearFraction.Size(); 605 | 606 | //动画位置与线性偏移位置的偏差 607 | FVector CurveOffset = CurrFrameRootMotion2Linear * Ratio; 608 | 609 | 610 | CurveX.AddKey(CurrentTime / EndTime, CurveOffset.X); 611 | CurveY.AddKey(CurrentTime / EndTime, CurveOffset.Y); 612 | CurveZ.AddKey(CurrentTime / EndTime, CurveOffset.Z); 613 | } 614 | 615 | 616 | OffsetCV->FloatCurves[0] = CurveX; 617 | OffsetCV->FloatCurves[1] = CurveY; 618 | OffsetCV->FloatCurves[2] = CurveZ; 619 | FName InsName = InstanceName == NAME_None ? TEXT("AnimationAdjustment") : InstanceName; 620 | const float Duration = EndTime / Rate; 621 | if (RMS::CVarRMS_Debug.GetValueOnGameThread() > 0) 622 | { 623 | for (int32 i = 0; i <= 10; i++) 624 | { 625 | FVector PredictLoc; 626 | if (PredictRootMotionSourceLocation_MoveTo(PredictLoc, MovementComponent, StartLocation, 627 | WorldFootTarget + FVector(0, 0, HalfHeight), Duration, 628 | Duration * (i / 10.0f), OffsetCV)) 629 | { 630 | //动画每一帧位置 631 | UKismetSystemLibrary::DrawDebugCapsule(Character, PredictLoc, 632 | Character->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(), 633 | Character->GetCapsuleComponent()->GetScaledCapsuleRadius(), 634 | FRotator::ZeroRotator, 635 | FColor::Red, 5); 636 | } 637 | } 638 | //动画每一帧位置 639 | UKismetSystemLibrary::DrawDebugCapsule(Character, WorldFootTarget + FVector(0, 0, HalfHeight), 640 | Character->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(), 641 | Character->GetCapsuleComponent()->GetScaledCapsuleRadius(), 642 | FRotator::ZeroRotator, 643 | FColor::Blue, 5); 644 | } 645 | 646 | 647 | return ApplyRootMotionSource_MoveToForce(MovementComponent, InsName, StartLocation, 648 | WorldFootTarget + FVector(0, 0, HalfHeight), Duration, 649 | Priority, OffsetCV, RotationSetting, InStartTime) >= 0; 650 | } 651 | 652 | bool URMSLibrary::ApplyRootMotionSource_AnimationAdjustment(UCharacterMovementComponent* MovementComponent, 653 | UAnimSequence* DataAnimation, 654 | FName InstanceName, 655 | int32 Priority, 656 | FVector TargetLocation, 657 | bool bLocalTarget, 658 | bool bTargetBasedOnFoot, 659 | float StartTime, 660 | float EndTime, 661 | float Rate, 662 | FRMSRotationSetting RotationSetting, 663 | bool bUseForwardCalculation, 664 | ERMSApplyMode ApplyMode) 665 | { 666 | if (!MovementComponent || !DataAnimation) 667 | { 668 | return false; 669 | } 670 | ACharacter* Character = Cast(MovementComponent->GetOwner()); 671 | USkeletalMeshComponent* Mesh = Character->GetMesh(); 672 | if (!Character || !Mesh) 673 | { 674 | return false; 675 | } 676 | 677 | if (bUseForwardCalculation) 678 | { 679 | return ApplyRootMotionSource_AnimationAdjustment_BM(MovementComponent, DataAnimation, InstanceName, Priority, 680 | TargetLocation, bLocalTarget, bTargetBasedOnFoot, StartTime, 681 | EndTime, Rate, RotationSetting, ApplyMode); 682 | } 683 | 684 | const int32 NewPriority = CalcPriorityByApplyMode(MovementComponent, InstanceName, Priority, ApplyMode); 685 | if (NewPriority < 0) 686 | { 687 | return false; 688 | } 689 | const float CurrEndTime = (EndTime < 0 || EndTime > DataAnimation->GetPlayLength()) 690 | ? DataAnimation->GetPlayLength() 691 | : EndTime; 692 | const float Duration = (CurrEndTime - StartTime) / FMath::Max(Rate, 0.1f); 693 | const float HalfHeight = Character->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(); 694 | const FTransform FootTransform = FTransform(Character->GetActorQuat(), 695 | Character->GetActorLocation() - FVector(0, 0, HalfHeight)); 696 | const FTransform CharacterTransform = Character->GetActorTransform(); 697 | //获取本地偏移 698 | FVector WorldTarget = FVector::ZeroVector; 699 | if (bLocalTarget) 700 | { 701 | if (bTargetBasedOnFoot) 702 | { 703 | WorldTarget = FootTransform.TransformPosition(TargetLocation); 704 | } 705 | else 706 | { 707 | WorldTarget = CharacterTransform.TransformPosition(TargetLocation); 708 | } 709 | } 710 | else 711 | { 712 | WorldTarget = TargetLocation - (bTargetBasedOnFoot ? FVector::ZeroVector : FVector(0, 0, HalfHeight)); 713 | } 714 | TSharedPtr RMS = MakeShared(); 715 | RMS->InstanceName = InstanceName == NAME_None ? TEXT("MotioWarping") : InstanceName; 716 | 717 | RMS->AccumulateMode = ERootMotionAccumulateMode::Override; 718 | RMS->Priority = NewPriority; 719 | RMS->RotationSetting = RotationSetting; 720 | 721 | 722 | if (RotationSetting.Mode == ERMSRotationMode::FaceToTarget) 723 | { 724 | RMS->TargetRotation = (WorldTarget - CharacterTransform.GetLocation()).Rotation(); 725 | } 726 | else if (RotationSetting.Mode == ERMSRotationMode::Custom) 727 | { 728 | RMS->TargetRotation = RotationSetting.TargetRotation; 729 | } 730 | 731 | RMS->TargetLocation = WorldTarget; 732 | RMS->StartLocation = CharacterTransform.GetLocation(); 733 | RMS->StartRotation = CharacterTransform.GetRotation().Rotator(); 734 | RMS->Duration = Duration; 735 | RMS->bIgnoreZAxis = false; 736 | RMS->Animation = DataAnimation; 737 | RMS->AnimStartTime = StartTime; 738 | RMS->AnimEndTime = EndTime; 739 | RMS->SetTime(StartTime); 740 | return MovementComponent->ApplyRootMotionSource(RMS) > -1; 741 | } 742 | 743 | 744 | bool URMSLibrary::ApplyRootMotionSource_AnimationWarping_ForwardCalculation( 745 | UCharacterMovementComponent* MovementComponent, UAnimSequence* DataAnimation, 746 | TMap WarpingTarget, FName InstanceName, 747 | int32 Priority, bool bTargetBasedOnFoot, float Rate, 748 | float Tolerance, float AnimWarpingMulti, bool bExcludeEndAnimMotion, ERMSAnimWarpingAxis WarpingAxis, 749 | ERMSApplyMode ApplyMode) 750 | { 751 | if (!MovementComponent || !DataAnimation || WarpingTarget.Num() == 0 || Rate <= 0) 752 | { 753 | return false; 754 | } 755 | const int32 NewPriority = CalcPriorityByApplyMode(MovementComponent, InstanceName, Priority, ApplyMode); 756 | if (NewPriority < 0) 757 | { 758 | return false; 759 | } 760 | ACharacter* Character = Cast(MovementComponent->GetOwner()); 761 | USkeletalMeshComponent* Mesh = Character->GetMesh(); 762 | if (!Character || !Mesh) 763 | { 764 | return false; 765 | } 766 | //动画基本数据 767 | const int32 NumFrame = DataAnimation->GetNumberOfSampledKeys(); 768 | const float FrameTime = DataAnimation->GetPlayLength() / NumFrame; 769 | float AnimLength = DataAnimation->GetPlayLength(); 770 | float Duration = AnimLength / Rate; 771 | float HalfHeight = Character->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(); 772 | const FVector HalfHeightVec = FVector(0, 0, HalfHeight); 773 | const auto Notifies = DataAnimation->Notifies; 774 | TArray Instances; 775 | WarpingTarget.GetKeys(Instances); 776 | TArray Windows; 777 | if (!GetRootMotionSourceWindowsByInstanceList(DataAnimation, Instances, Windows)) 778 | { 779 | return false; 780 | } 781 | TArray TriggerDatas; 782 | float Time = 0; 783 | int32 idx = 0; 784 | while (Time <= AnimLength) 785 | { 786 | FRMSNotifyTriggerData TriggerData; 787 | auto EmplaceTriggerData_Lambda = [&]() 788 | { 789 | TriggerData.WindowData = Windows[idx]; 790 | const auto Target = WarpingTarget.Find(Windows[idx].AnimNotify->RootMotionSourceTarget); 791 | if (Target) 792 | { 793 | TriggerData.Target = *Target; 794 | if (bTargetBasedOnFoot) 795 | { 796 | TriggerData.Target += HalfHeightVec; 797 | } 798 | TriggerData.bHasTarget = true; 799 | } 800 | TriggerDatas.Emplace(TriggerData); 801 | }; 802 | //已经到最后一个通知, 但是通知的结尾不是动画结束点, 就添加一个默认数据; 803 | //这里先添加了最后一个无通知动画数据, 后续再处理 804 | if (idx > Windows.Num() - 1 && AnimLength - Windows[Windows.Num() - 1].EndTime > Tolerance) 805 | { 806 | TriggerData.bHasTarget = false; 807 | TriggerData.WindowData.StartTime = Windows[Windows.Num() - 1].EndTime; 808 | TriggerData.WindowData.EndTime = AnimLength; 809 | TriggerDatas.Emplace(TriggerData); 810 | break; 811 | } 812 | else if (idx > Windows.Num() - 1) 813 | { 814 | break; 815 | } 816 | //小于公差部分忽略, 都作为起始点 817 | if (Windows[idx].StartTime <= Tolerance) 818 | { 819 | EmplaceTriggerData_Lambda(); 820 | Time = Windows[idx].EndTime; 821 | idx++; 822 | TriggerData.Reset(); 823 | } 824 | else 825 | { 826 | //起始时间不是上一次的结束时间, 说明中间有空隙, 需要填充一个默认数据 827 | if (Windows[idx].StartTime - Time > Tolerance) 828 | { 829 | TriggerData.bHasTarget = false; 830 | TriggerData.WindowData.StartTime = Time; 831 | TriggerData.WindowData.EndTime = Windows[idx].StartTime; 832 | TriggerDatas.Emplace(TriggerData); 833 | TriggerData.Reset(); 834 | EmplaceTriggerData_Lambda(); 835 | Time = Windows[idx].EndTime; 836 | idx++; 837 | } 838 | //否则即使后一个窗口的起点早于前一个窗口的结束 也从结束点开始 839 | else 840 | { 841 | TriggerData.WindowData = Windows[idx]; 842 | TriggerData.WindowData.StartTime = Windows[idx - 1].EndTime; 843 | const auto Target = WarpingTarget.Find(Windows[idx].AnimNotify->RootMotionSourceTarget); 844 | if (Target) 845 | { 846 | TriggerData.Target = *Target; 847 | TriggerData.bHasTarget = true; 848 | } 849 | TriggerDatas.Emplace(TriggerData); 850 | Time = Windows[idx].EndTime; 851 | idx++; 852 | } 853 | TriggerData.Reset(); 854 | } 855 | } 856 | 857 | if (TriggerDatas.Num() == 0) 858 | { 859 | return false; 860 | } 861 | 862 | 863 | //用动态曲线的方式 , 而非静态, 864 | UCurveVector* OffsetCV = NewObject(); 865 | FRichCurve CurveX, CurveY, CurveZ; 866 | //开始位置 867 | FVector StartLocation = Character->GetActorLocation(); 868 | //模型与角色的相对变换矩阵,我们只需要Rotation 869 | FTransform Mesh2Char = Mesh->GetComponentTransform().GetRelativeTransform(Character->GetActorTransform()); 870 | Mesh2Char.SetLocation(FVector::ZeroVector); 871 | 872 | FVector LocalOffset = FVector::ZeroVector; 873 | //****************** 874 | FVector LastWarpingTarget = FVector::ZeroVector; 875 | 876 | float LastWindowEndTime = 0; 877 | float LastTime = 0; 878 | FTransform FinalTargetAnimRM, CurrFrameAnimRM, CurrTargetAnimRM, LastTargetAnimRM; 879 | 880 | FVector LastTargetWS = StartLocation; 881 | FVector CurrTargetWS = FVector::ZeroVector; 882 | 883 | //************************** 884 | FRMSNotifyTriggerData TrigData; 885 | //确定最后目标 886 | FVector WorldTarget = FVector::ZeroVector; 887 | if (TriggerDatas[TriggerDatas.Num() - 1].bHasTarget) 888 | { 889 | WorldTarget = TriggerDatas[TriggerDatas.Num() - 1].Target; 890 | } 891 | else 892 | { 893 | int32 lastTargetIdx = 0; 894 | //判断窗口是否有大于1个, 如果大于1, 那么反向查找到最后一个 895 | if (TriggerDatas.Num() > 1) 896 | { 897 | for (int32 i = TriggerDatas.Num() - 1; i >= 0; i--) 898 | { 899 | if (TriggerDatas[i].bHasTarget) 900 | { 901 | LastWarpingTarget = TriggerDatas[i].Target; 902 | lastTargetIdx = i; 903 | break; 904 | } 905 | } 906 | } 907 | if (LastWarpingTarget == FVector::ZeroVector) 908 | { 909 | UE_LOG(LogTemp, Warning, 910 | TEXT( 911 | "Can not find last warping target, maybe give wrong [WarpingTarget] or no RootMotionSource AnimNotifies" 912 | )); 913 | } 914 | //如果不排除末尾的动画位移, 那么需要把最后一个动画通知窗口以后的RootMotion都加进来 915 | if (!bExcludeEndAnimMotion) 916 | { 917 | auto TM = DataAnimation->ExtractRootMotion(TriggerDatas[lastTargetIdx].WindowData.EndTime, AnimLength, 918 | false); 919 | TM = TM * Mesh2Char; 920 | auto ActorRM = TM.GetLocation(); 921 | FVector WorldRM = ActorRM.X * Character->GetActorForwardVector() + ActorRM.Y * Character-> 922 | GetActorRightVector() 923 | + ActorRM.Z * Character->GetActorUpVector(); 924 | WorldTarget = LastWarpingTarget + WorldRM; 925 | } 926 | else 927 | { 928 | WorldTarget = LastWarpingTarget; 929 | //todo 这里处理如果排除最后一个动画的情况, Duration和AnimLength需要重新设置 930 | Duration = TriggerDatas[lastTargetIdx].WindowData.EndTime / Rate; 931 | AnimLength = TriggerDatas[lastTargetIdx].WindowData.EndTime; 932 | } 933 | } 934 | 935 | //获取整体RootMotion数据 936 | FTransform TotalAnimRM = DataAnimation->ExtractRootMotionFromRange(0, AnimLength); 937 | TotalAnimRM = TotalAnimRM * Mesh2Char; 938 | 939 | 940 | if (RMS::CVarRMS_Debug.GetValueOnGameThread() > 0) 941 | { 942 | //绘制最后目标 943 | UKismetSystemLibrary::DrawDebugSphere( 944 | Mesh, WorldTarget, 20.f, 12, FColor::Purple, 5, 1); 945 | } 946 | 947 | //最终目标的RootMotion 948 | FinalTargetAnimRM = DataAnimation->ExtractRootMotion(0, AnimLength, false); 949 | FinalTargetAnimRM = FinalTargetAnimRM * Mesh2Char; 950 | 951 | FVector CurveOffset = FVector::ZeroVector; 952 | //逐帧遍历 953 | for (float CurrentTime = 0; CurrentTime <= AnimLength; CurrentTime += FrameTime) 954 | { 955 | //todo 需要区分缩放时间和真实的时间, 真实时间用于提取动画RM数据 956 | const float TimeScaled = CurrentTime / Rate; 957 | bool bNeedUpdateTarget = false; 958 | //如果当前时间已经大于上一次数据的最后时间, 说明换了一个窗口期, 记录上一次的时间和目标 959 | if (TimeScaled > TrigData.WindowData.EndTime / Rate) 960 | { 961 | LastWindowEndTime = LastTime; 962 | LastTargetWS = CurrTargetWS; 963 | bNeedUpdateTarget = true; 964 | LastTargetAnimRM = CurrTargetAnimRM; 965 | } 966 | //查询窗口数据需要用原始时间 967 | if (!FindTriggerDataByTime(TriggerDatas, CurrentTime, TrigData)) 968 | { 969 | continue; 970 | } 971 | if (TrigData == TriggerDatas.Last() && TrigData.bHasTarget == false && bExcludeEndAnimMotion) 972 | { 973 | break; 974 | } 975 | 976 | //当前分段内的百分比 977 | float WindowFraction = (TimeScaled - TrigData.WindowData.StartTime / Rate) / (TrigData.WindowData.EndTime / Rate 978 | - TrigData. 979 | WindowData.StartTime / Rate); 980 | //整体百分比, 都是缩放过的数据 981 | float Fraction = TimeScaled / Duration; 982 | 983 | //获取当前时间段的rootMotion 984 | CurrFrameAnimRM = DataAnimation->ExtractRootMotion(0, CurrentTime, false); 985 | CurrFrameAnimRM = CurrFrameAnimRM * Mesh2Char; 986 | 987 | bool bHasWarpingTarget = false; 988 | 989 | //****************************************** 990 | //查找当前窗口的目标数据, 只在窗口改变以后的时候刷新 991 | if (bNeedUpdateTarget || CurrentTime == 0) 992 | { 993 | if (TrigData.bHasTarget && IsValid(TrigData.WindowData.AnimNotify)) 994 | { 995 | const auto target = WarpingTarget.Find(TrigData.WindowData.AnimNotify->RootMotionSourceTarget); 996 | if (target) 997 | { 998 | CurrTargetWS = *target; 999 | if (bTargetBasedOnFoot) 1000 | { 1001 | CurrTargetWS += HalfHeightVec; 1002 | } 1003 | LocalOffset = UKismetMathLibrary::InverseTransformLocation( 1004 | Character->GetActorTransform(), CurrTargetWS); 1005 | bHasWarpingTarget = true; 1006 | } 1007 | //找不到直接返回失败, 必须匹配 1008 | else 1009 | { 1010 | UE_LOG(LogTemp, Warning, TEXT(" wrong [WarpingTarget], need matching animation notifies ")); 1011 | return false; 1012 | } 1013 | } 1014 | //否则就使用动画本身的位移 1015 | if (!bHasWarpingTarget) 1016 | { 1017 | const auto T = DataAnimation->ExtractRootMotion(LastWindowEndTime, TrigData.WindowData.EndTime, false); 1018 | LocalOffset = (T * Mesh2Char).GetLocation(); 1019 | FVector WorldOffset = LocalOffset.X * Character->GetActorForwardVector() + LocalOffset.Y * Character-> 1020 | GetActorRightVector() + LocalOffset.Z * Character->GetActorUpVector(); 1021 | CurrTargetWS = LastTargetWS + WorldOffset; 1022 | } 1023 | CurrTargetAnimRM = DataAnimation->ExtractRootMotion(0, TrigData.WindowData.EndTime, false); 1024 | CurrTargetAnimRM = CurrTargetAnimRM * Mesh2Char; 1025 | } 1026 | //****************************************** 1027 | 1028 | 1029 | //*****************计算关键数据************************* 1030 | //当前窗口的线性位移 1031 | FVector WindowLinearOffset = (CurrTargetWS - LastTargetWS) * WindowFraction; 1032 | //全局线性偏移 1033 | FVector FinalLinearOffset = (WorldTarget - StartLocation) * Fraction; 1034 | //todo 这里有个坑, 曲线信息是start到target的朝向空间的, 即X的方向是target-start的向量朝向; 所以需要转换成相对空间 1035 | FVector Offset_LinearBase = LastTargetWS + WindowLinearOffset - (StartLocation + FinalLinearOffset); 1036 | ConvWorldOffsetToRmsSpace(Offset_LinearBase, StartLocation, WorldTarget); 1037 | 1038 | FVector WindowAnimLinearOffset = (CurrTargetAnimRM.GetLocation() - LastTargetAnimRM.GetLocation()) * 1039 | WindowFraction; 1040 | FVector Offset_Anim = CurrFrameAnimRM.GetLocation() - (LastTargetAnimRM.GetLocation() + WindowAnimLinearOffset); 1041 | ConvWorldOffsetToRmsSpace(Offset_Anim, StartLocation, WorldTarget); 1042 | 1043 | //计算目标线性位移与动画RM线性位移的比值 1044 | const float WarpRatio = FMath::IsNearlyZero(WindowAnimLinearOffset.Size(), Tolerance) 1045 | ? 0 1046 | : WindowLinearOffset.Size() / WindowAnimLinearOffset.Size(); 1047 | Offset_Anim *= WarpRatio * AnimWarpingMulti; 1048 | FiltAnimCurveOffsetAxisData(Offset_Anim, WarpingAxis); 1049 | CurveOffset = Offset_LinearBase + Offset_Anim; 1050 | 1051 | 1052 | CurveX.AddKey(TimeScaled / Duration, CurveOffset.X); 1053 | CurveY.AddKey(TimeScaled / Duration, CurveOffset.Y); 1054 | CurveZ.AddKey(TimeScaled / Duration, CurveOffset.Z); 1055 | 1056 | 1057 | if (RMS::CVarRMS_Debug.GetValueOnGameThread() > 0) 1058 | { 1059 | //动画每一帧位置 1060 | UKismetSystemLibrary::DrawDebugSphere( 1061 | Mesh, UKismetMathLibrary::TransformLocation(Character->GetActorTransform(), 1062 | CurrFrameAnimRM.GetLocation()) - FVector(0, 0, HalfHeight), 1063 | 5.0, 4, FColor::Yellow, 5.0); 1064 | //当前窗口动画线性位置 1065 | UKismetSystemLibrary::DrawDebugSphere( 1066 | Mesh, UKismetMathLibrary::TransformLocation(Character->GetActorTransform(), 1067 | WindowAnimLinearOffset + LastTargetAnimRM.GetLocation()) - 1068 | FVector( 1069 | 0, 0, HalfHeight), 5.0, 4, FColor::Red, 5.0); 1070 | //最终目标的线性位置 1071 | UKismetSystemLibrary::DrawDebugSphere( 1072 | Mesh, StartLocation + FinalLinearOffset - 1073 | FVector(0, 0, HalfHeight), 5.0, 4, FColor::Green, 5.0); 1074 | FRotator FacingRot = (WorldTarget - StartLocation).Rotation(); 1075 | FacingRot.Pitch = 0; 1076 | FVector Fwd = UKismetMathLibrary::GetForwardVector(FacingRot); 1077 | FVector Rt = UKismetMathLibrary::GetRightVector(FacingRot); 1078 | FVector Up = UKismetMathLibrary::GetUpVector(FacingRot); 1079 | FVector Offset = Fwd * CurveOffset.X + Rt * CurveOffset.Y + Up * CurveOffset.Z; 1080 | 1081 | //当前窗口的线性位置 1082 | UKismetSystemLibrary::DrawDebugSphere( 1083 | Mesh, StartLocation + FinalLinearOffset + Offset - FVector(0, 0, HalfHeight), 1084 | 5.0, 4, FColor::Blue, 5.0); 1085 | } 1086 | LastTime = CurrentTime; 1087 | } 1088 | OffsetCV->FloatCurves[0] = CurveX; 1089 | OffsetCV->FloatCurves[1] = CurveY; 1090 | OffsetCV->FloatCurves[2] = CurveZ; 1091 | 1092 | InstanceName = InstanceName == NAME_None ? TEXT("AnimWarping") : InstanceName; 1093 | //用MoveToForce来计算路径, 尝试过用Jump+OffsetCv, 但是Jump的CV不好用 1094 | return ApplyRootMotionSource_MoveToForce(MovementComponent, InstanceName, StartLocation, WorldTarget, Duration, 1095 | Priority, 1096 | OffsetCV) >= 0; 1097 | } 1098 | 1099 | bool URMSLibrary::ApplyRootMotionSource_AnimationWarping(UCharacterMovementComponent* MovementComponent, 1100 | UAnimSequence* DataAnimation, 1101 | TMap WarpingTarget, 1102 | float StartTime, 1103 | FName InstanceName, 1104 | int32 Priority, 1105 | bool bTargetBasedOnFoot, 1106 | float Rate, 1107 | float Tolerance, 1108 | bool bExcludeEndAnimMotion, 1109 | ERMSApplyMode ApplyMode) 1110 | { 1111 | auto EmplaceTriggerDataTargetWithAnimRootMotion_Lambda = [&](ACharacter* Character, 1112 | FRMSTarget& Data, 1113 | FVector StartLocation, FRotator StartRotation) 1114 | { 1115 | auto RM = ExtractRootMotion(DataAnimation, Data.StartTime, Data.EndTime); 1116 | const FTransform CurrentTransform = FTransform(Character->GetActorQuat(), 1117 | StartLocation - FVector( 1118 | 0.f, 0.f, 1119 | Character->GetCapsuleComponent()-> 1120 | GetScaledCapsuleHalfHeight())); 1121 | const FTransform StartFootTransform = FTransform(StartRotation, 1122 | StartLocation - FVector( 1123 | 0.f, 0.f, 1124 | Character->GetCapsuleComponent()-> 1125 | GetScaledCapsuleHalfHeight())); 1126 | const FTransform CurrChacterFootTransform = FTransform(StartRotation, 1127 | Character->GetActorLocation() - FVector( 1128 | 0.f, 0.f, 1129 | Character->GetCapsuleComponent()-> 1130 | GetScaledCapsuleHalfHeight())); 1131 | FTransform MeshTransformWS = Character->GetMesh()->GetComponentTransform(); 1132 | FTransform Mesh2CharInverse = StartFootTransform.GetRelativeTransform(MeshTransformWS); 1133 | 1134 | RM = RM * MeshTransformWS; //模型世界空间的RM 1135 | //通过逆矩阵把模型空间转换成actor空间 1136 | const FTransform TargetTransformWS = Mesh2CharInverse * RM; 1137 | Data.Target = TargetTransformWS.GetLocation(); 1138 | }; 1139 | 1140 | if (!MovementComponent || !DataAnimation || WarpingTarget.Num() == 0 || Rate <= 0 || StartTime > DataAnimation-> 1141 | GetPlayLength()) 1142 | { 1143 | return false; 1144 | } 1145 | const int32 NewPriority = CalcPriorityByApplyMode(MovementComponent, InstanceName, Priority, ApplyMode); 1146 | if (NewPriority < 0) 1147 | { 1148 | return false; 1149 | } 1150 | ACharacter* Character = Cast(MovementComponent->GetOwner()); 1151 | USkeletalMeshComponent* Mesh = Character->GetMesh(); 1152 | if (!Character || !Mesh) 1153 | { 1154 | return false; 1155 | } 1156 | //动画基本数据 1157 | const int32 NumFrame = DataAnimation->GetNumberOfSampledKeys(); 1158 | const float FrameTime = DataAnimation->GetPlayLength() / NumFrame; 1159 | float AnimLength = DataAnimation->GetPlayLength(); 1160 | float Duration = AnimLength / Rate; 1161 | float HalfHeight = Character->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(); 1162 | const FVector HalfHeightVec = FVector(0, 0, HalfHeight); 1163 | const auto Notifies = DataAnimation->Notifies; 1164 | TArray TargetNames; 1165 | WarpingTarget.GetKeys(TargetNames); 1166 | TArray Windows; 1167 | if (!GetRootMotionSourceWindowsByInstanceList(DataAnimation, TargetNames, Windows)) 1168 | { 1169 | return false; 1170 | } 1171 | TArray TriggerDatas; 1172 | float Time = StartTime; 1173 | int32 idx = 0; 1174 | while (Time <= AnimLength) 1175 | { 1176 | FRMSTarget TriggerData; 1177 | //已经到最后一个通知, 但是通知的结尾不是动画结束点, 就添加一个默认数据; 1178 | if (!bExcludeEndAnimMotion && idx > Windows.Num() - 1 && AnimLength - Windows[Windows.Num() - 1].EndTime > 1179 | Tolerance) 1180 | { 1181 | TriggerData.StartTime = Windows[Windows.Num() - 1].EndTime; 1182 | TriggerData.EndTime = AnimLength; 1183 | if (const FRMSAnimWarppingConfig* Target = WarpingTarget.Find( 1184 | Windows[Windows.Num() - 1].AnimNotify->RootMotionSourceTarget)) 1185 | { 1186 | EmplaceTriggerDataTargetWithAnimRootMotion_Lambda(Character, TriggerData, (*Target).TargetLocation, 1187 | Character->GetActorRotation()); 1188 | TriggerDatas.Emplace(TriggerData); 1189 | break; 1190 | } 1191 | else 1192 | { 1193 | break; 1194 | } 1195 | } 1196 | else if (idx > Windows.Num() - 1) 1197 | { 1198 | break; 1199 | } 1200 | //小于公差部分忽略, 都作为起始点 1201 | if (idx == 0 && Windows[0].StartTime <= Tolerance) 1202 | { 1203 | TriggerData.StartTime = 0; 1204 | TriggerData.EndTime = Windows[0].EndTime; 1205 | if (const FRMSAnimWarppingConfig* Target = WarpingTarget.Find( 1206 | Windows[idx].AnimNotify->RootMotionSourceTarget)) 1207 | { 1208 | TriggerData.Target = (*Target).TargetLocation; 1209 | if (!bTargetBasedOnFoot) 1210 | { 1211 | TriggerData.Target -= HalfHeightVec; 1212 | } 1213 | TriggerData.RotationSetting = Target->RotationSetting; 1214 | } 1215 | else 1216 | { 1217 | EmplaceTriggerDataTargetWithAnimRootMotion_Lambda(Character, TriggerData, Character->GetActorLocation(), 1218 | Character->GetActorRotation()); 1219 | } 1220 | TriggerDatas.Emplace(TriggerData); 1221 | Time = Windows[0].EndTime; 1222 | idx++; 1223 | TriggerData.Reset(); 1224 | } 1225 | else 1226 | { 1227 | //起始时间不是上一次的结束时间, 说明中间有空隙, 需要填充一个默认数据 1228 | if (Windows[idx].StartTime - Time > Tolerance) 1229 | { 1230 | //填充一个默认数据 1231 | TriggerData.StartTime = Time; 1232 | TriggerData.EndTime = Windows[idx].StartTime; 1233 | const FRMSAnimWarppingConfig* Target = WarpingTarget.Find( 1234 | Windows[idx - 1].AnimNotify->RootMotionSourceTarget); 1235 | if (Target) 1236 | { 1237 | EmplaceTriggerDataTargetWithAnimRootMotion_Lambda(Character, TriggerData, (*Target).TargetLocation, 1238 | Character->GetActorRotation()); 1239 | } 1240 | 1241 | TriggerDatas.Emplace(TriggerData); 1242 | TriggerData.Reset(); 1243 | //填充有目标的 1244 | TriggerData.StartTime = Windows[idx].StartTime; 1245 | TriggerData.EndTime = Windows[idx].EndTime; 1246 | Target = WarpingTarget.Find(Windows[idx].AnimNotify->RootMotionSourceTarget); 1247 | if (Target) 1248 | { 1249 | TriggerData.Target = (*Target).TargetLocation; 1250 | if (!bTargetBasedOnFoot) 1251 | { 1252 | TriggerData.Target -= HalfHeightVec; 1253 | } 1254 | TriggerData.RotationSetting = Target->RotationSetting; 1255 | } 1256 | 1257 | TriggerDatas.Emplace(TriggerData); 1258 | Time = Windows[idx].EndTime; 1259 | idx++; 1260 | } 1261 | //否则即使后一个窗口的起点早于前一个窗口的结束 也从结束点开始 1262 | else 1263 | { 1264 | TriggerData.StartTime = Windows[idx - 1].EndTime; 1265 | TriggerData.EndTime = Windows[idx].EndTime; 1266 | if (const auto Target = WarpingTarget.Find(Windows[idx].AnimNotify->RootMotionSourceTarget)) 1267 | { 1268 | TriggerData.Target = (*Target).TargetLocation; 1269 | TriggerData.RotationSetting = Target->RotationSetting; 1270 | } 1271 | TriggerDatas.Emplace(TriggerData); 1272 | Time = Windows[idx].EndTime; 1273 | idx++; 1274 | } 1275 | TriggerData.Reset(); 1276 | } 1277 | } 1278 | 1279 | if (TriggerDatas.Num() == 0) 1280 | { 1281 | return false; 1282 | } 1283 | 1284 | if (RMS::CVarRMS_Debug.GetValueOnGameThread() > 0) 1285 | { 1286 | for (int32 i = 0; i < TriggerDatas.Num(); i++) 1287 | { 1288 | DrawDebugCapsule(Character->GetWorld(), 1289 | TriggerDatas[i].Target + FVector( 1290 | 0, 0, Character->GetCapsuleComponent()->GetScaledCapsuleHalfHeight()), 1291 | Character->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(), 1292 | Character->GetCapsuleComponent()->GetScaledCapsuleRadius(), 1293 | Character->GetActorQuat(), 1294 | FColor::Green, 1295 | false, 1296 | 5, 1297 | 0, 1298 | 1); 1299 | } 1300 | } 1301 | TSharedPtr RMS = MakeShared< 1302 | FRootMotionSource_AnimWarping_MultiTargets>(); 1303 | RMS->InstanceName = InstanceName == NAME_None ? TEXT("MotioWarping") : InstanceName; 1304 | 1305 | RMS->AccumulateMode = ERootMotionAccumulateMode::Override; 1306 | RMS->Priority = NewPriority; 1307 | //这个目标必须是脚底位置 1308 | RMS->TriggerDatas = TriggerDatas; 1309 | RMS->StartLocation = Character->GetActorLocation(); 1310 | RMS->StartRotation = Character->GetActorRotation(); 1311 | RMS->Duration = Duration; 1312 | RMS->bIgnoreZAxis = false; 1313 | RMS->Animation = DataAnimation; 1314 | RMS->AnimStartTime = StartTime; 1315 | RMS->AnimEndTime = DataAnimation->GetPlayLength(); 1316 | RMS->SetTime(StartTime); 1317 | return MovementComponent->ApplyRootMotionSource(RMS) > -1; 1318 | } 1319 | 1320 | bool URMSLibrary::GetRootMotionSourceWindow(UAnimSequence* DataAnimation, FName InstanceName, 1321 | FRMSWindowData& Window) 1322 | { 1323 | if (!DataAnimation) 1324 | { 1325 | return false; 1326 | } 1327 | const auto Notifies = DataAnimation->Notifies; 1328 | for (auto Noti : Notifies) 1329 | { 1330 | if (UAnimNotifyState_RMS_Warping* RMSNoti = Cast(Noti.NotifyStateClass)) 1331 | { 1332 | if (RMSNoti->RootMotionSourceTarget == InstanceName) 1333 | { 1334 | Window.AnimNotify = RMSNoti; 1335 | Window.StartTime = Noti.GetTriggerTime(); 1336 | Window.EndTime = Noti.GetEndTriggerTime(); 1337 | return true; 1338 | } 1339 | } 1340 | } 1341 | return false; 1342 | } 1343 | 1344 | bool URMSLibrary::GetRootMotionSourceWindows(UAnimSequence* DataAnimation, 1345 | TArray& Windows) 1346 | { 1347 | if (!DataAnimation) 1348 | { 1349 | return false; 1350 | } 1351 | const auto Notifies = DataAnimation->Notifies; 1352 | for (auto Noti : Notifies) 1353 | { 1354 | if (UAnimNotifyState_RMS_Warping* RMSNoti = Cast(Noti.NotifyStateClass)) 1355 | { 1356 | FRMSWindowData Window; 1357 | Window.AnimNotify = RMSNoti; 1358 | Window.StartTime = Noti.GetTriggerTime(); 1359 | Window.EndTime = Noti.GetEndTriggerTime(); 1360 | Windows.Emplace(Window); 1361 | } 1362 | } 1363 | return Windows.Num() > 0; 1364 | } 1365 | 1366 | bool URMSLibrary::GetRootMotionSourceWindowsByInstanceList(UAnimSequence* DataAnimation, 1367 | TArray Instances, 1368 | TArray& Windows) 1369 | { 1370 | if (!DataAnimation) 1371 | { 1372 | return false; 1373 | } 1374 | for (auto Ins : Instances) 1375 | { 1376 | FRMSWindowData Window; 1377 | if (GetRootMotionSourceWindow(DataAnimation, Ins, Window)) 1378 | { 1379 | Windows.Emplace(Window); 1380 | } 1381 | } 1382 | Windows.Sort([](FRMSWindowData A, FRMSWindowData B) 1383 | { 1384 | return A.StartTime < B.StartTime; 1385 | }); 1386 | return Windows.Num() > 0; 1387 | } 1388 | 1389 | bool URMSLibrary::FindTriggerDataByTime(const TArray& TriggerData, 1390 | float Time, FRMSNotifyTriggerData& OutData) 1391 | { 1392 | if (TriggerData.Num() == 0) 1393 | { 1394 | return false; 1395 | } 1396 | for (auto T : TriggerData) 1397 | { 1398 | if (Time >= T.WindowData.StartTime && Time < T.WindowData.EndTime) 1399 | { 1400 | OutData = T; 1401 | return true; 1402 | } 1403 | } 1404 | return false; 1405 | } 1406 | 1407 | FTransform URMSLibrary::ExtractRootMotion(UAnimSequenceBase* Anim, float StartTime, 1408 | float EndTime) 1409 | { 1410 | FTransform OutTransform; 1411 | if (UAnimSequence* Seq = Cast(Anim)) 1412 | { 1413 | OutTransform = Seq->ExtractRootMotionFromRange(StartTime, EndTime); 1414 | } 1415 | else if (UAnimMontage* Montage = Cast(Anim)) 1416 | { 1417 | OutTransform = Montage->ExtractRootMotionFromTrackRange(StartTime, EndTime); 1418 | } 1419 | return OutTransform; 1420 | } 1421 | 1422 | 1423 | bool URMSLibrary::ApplyRootMotionSource_SimpleAnimation(UCharacterMovementComponent* MovementComponent, 1424 | UAnimSequence* DataAnimation, 1425 | FName InstanceName, 1426 | int32 Priority, 1427 | float StartTime, 1428 | float EndTime, 1429 | float Rate, 1430 | bool bIgnoreZAxis, 1431 | bool bUseForwardCalculation, 1432 | ERMSApplyMode ApplyMode) 1433 | { 1434 | if (!MovementComponent || !DataAnimation) 1435 | { 1436 | return false; 1437 | } 1438 | const int32 NewPriority = CalcPriorityByApplyMode(MovementComponent, InstanceName, Priority, ApplyMode); 1439 | if (NewPriority < 0) 1440 | { 1441 | return false; 1442 | } 1443 | if (bUseForwardCalculation) 1444 | { 1445 | return ApplyRootMotionSource_SimpleAnimation_BM(MovementComponent, DataAnimation, InstanceName, Priority, 1446 | StartTime, EndTime, Rate, bIgnoreZAxis, ApplyMode); 1447 | } 1448 | const float CurrEndTime = (EndTime < 0 || EndTime > DataAnimation->GetPlayLength()) 1449 | ? DataAnimation->GetPlayLength() 1450 | : EndTime; 1451 | const float Duration = (CurrEndTime - StartTime) / FMath::Max(Rate, 0.1f); 1452 | TSharedPtr RMS = MakeShared(); 1453 | RMS->InstanceName = InstanceName == NAME_None ? TEXT("MotioWarping") : InstanceName; 1454 | RMS->AccumulateMode = ERootMotionAccumulateMode::Override; 1455 | RMS->Priority = NewPriority; 1456 | RMS->StartLocation = MovementComponent->GetOwner()->GetActorLocation(); 1457 | RMS->StartRotation = MovementComponent->GetOwner()->GetActorRotation(); 1458 | RMS->Duration = Duration; 1459 | RMS->bIgnoreZAxis = bIgnoreZAxis; 1460 | RMS->Animation = DataAnimation; 1461 | RMS->AnimStartTime = StartTime; 1462 | RMS->AnimEndTime = EndTime; 1463 | RMS->SetTime(StartTime); 1464 | return MovementComponent->ApplyRootMotionSource(RMS) > -1; 1465 | } 1466 | 1467 | 1468 | int32 URMSLibrary::ApplyRootMotionSource_ConstantForece(UCharacterMovementComponent* MovementComponent, 1469 | FName InstanceName, 1470 | ERootMotionAccumulateMode AccumulateMod, 1471 | int32 Priority, FVector WorldDirection, 1472 | float Strength, 1473 | UCurveFloat* StrengthOverTime, float Duration, 1474 | ERMSFinishVelocityMode VelocityOnFinishMode, 1475 | FVector FinishSetVelocity, 1476 | float FinishClampVelocity, bool bEnableGravity, 1477 | ERMSApplyMode ApplyMode) 1478 | { 1479 | const int32 NewPriority = CalcPriorityByApplyMode(MovementComponent, InstanceName, Priority, ApplyMode); 1480 | if (NewPriority < 0) 1481 | { 1482 | return -1; 1483 | } 1484 | TSharedPtr ConstantForce = MakeShared(); 1485 | ConstantForce->InstanceName = InstanceName; 1486 | ConstantForce->AccumulateMode = AccumulateMod; 1487 | ConstantForce->Priority = NewPriority; 1488 | ConstantForce->Force = WorldDirection * Strength; 1489 | ConstantForce->Duration = Duration; 1490 | ConstantForce->StrengthOverTime = StrengthOverTime; 1491 | ConstantForce->FinishVelocityParams.Mode = static_cast(static_cast( 1492 | VelocityOnFinishMode)); 1493 | ConstantForce->FinishVelocityParams.SetVelocity = FinishSetVelocity; 1494 | ConstantForce->FinishVelocityParams.ClampVelocity = FinishClampVelocity; 1495 | if (bEnableGravity) 1496 | { 1497 | ConstantForce->Settings.SetFlag(ERootMotionSourceSettingsFlags::IgnoreZAccumulate); 1498 | } 1499 | return MovementComponent->ApplyRootMotionSource(ConstantForce); 1500 | } 1501 | 1502 | int32 URMSLibrary::ApplyRootMotionSource_RadialForece(UCharacterMovementComponent* MovementComponent, 1503 | FName InstanceName, 1504 | ERootMotionAccumulateMode AccumulateMod, 1505 | int32 Priority, AActor* LocationActor, 1506 | FVector Location, float Strength, 1507 | float Radius, bool bNoZForce, 1508 | UCurveFloat* StrengthDistanceFalloff, 1509 | UCurveFloat* StrengthOverTime, bool bIsPush, 1510 | float Duration, bool bUseFixedWorldDirection, 1511 | FRotator FixedWorldDirection, 1512 | ERMSFinishVelocityMode VelocityOnFinishMode, 1513 | FVector FinishSetVelocity, 1514 | float FinishClampVelocity, 1515 | ERMSApplyMode ApplyMode) 1516 | { 1517 | const int32 NewPriority = CalcPriorityByApplyMode(MovementComponent, InstanceName, Priority, ApplyMode); 1518 | if (NewPriority < 0) 1519 | { 1520 | return -1; 1521 | } 1522 | TSharedPtr RadialForce = MakeShared(); 1523 | RadialForce->InstanceName = InstanceName; 1524 | RadialForce->AccumulateMode = AccumulateMod; 1525 | RadialForce->Priority = NewPriority; 1526 | RadialForce->Location = Location; 1527 | RadialForce->LocationActor = LocationActor; 1528 | RadialForce->Duration = Duration; 1529 | RadialForce->Radius = Radius; 1530 | RadialForce->Strength = Strength; 1531 | RadialForce->bIsPush = bIsPush; 1532 | RadialForce->bNoZForce = bNoZForce; 1533 | RadialForce->StrengthDistanceFalloff = StrengthDistanceFalloff; 1534 | RadialForce->StrengthOverTime = StrengthOverTime; 1535 | RadialForce->bUseFixedWorldDirection = bUseFixedWorldDirection; 1536 | RadialForce->FixedWorldDirection = FixedWorldDirection; 1537 | RadialForce->FinishVelocityParams.Mode = static_cast(static_cast( 1538 | VelocityOnFinishMode)); 1539 | RadialForce->FinishVelocityParams.SetVelocity = FinishSetVelocity; 1540 | RadialForce->FinishVelocityParams.ClampVelocity = FinishClampVelocity; 1541 | return MovementComponent->ApplyRootMotionSource(RadialForce); 1542 | } 1543 | 1544 | 1545 | void URMSLibrary::UpdateDynamicMoveToTarget(UCharacterMovementComponent* MovementComponent, 1546 | FName InstanceName, FVector NewTarget) 1547 | { 1548 | if (MovementComponent && !NewTarget.ContainsNaN()) 1549 | { 1550 | auto RMS = MovementComponent->GetRootMotionSource(InstanceName); 1551 | if (!RMS) 1552 | { 1553 | return; 1554 | } 1555 | auto RMS_Dy = StaticCastSharedPtr(RMS); 1556 | if (!RMS_Dy) 1557 | { 1558 | return; 1559 | } 1560 | if (NewTarget.Equals(RMS_Dy->TargetLocation, 0.1)) 1561 | { 1562 | return; 1563 | } 1564 | //为了防止跳跃, 我们需要重新设定Start和Time 1565 | const float Time = RMS_Dy->GetTime(); 1566 | const float Duration = RMS_Dy->GetDuration(); 1567 | RMS_Dy->SetTime(0); 1568 | RMS_Dy->Duration = Duration - Time; 1569 | RMS_Dy->StartLocation = MovementComponent->GetOwner()->GetActorLocation(); 1570 | RMS_Dy->StartRotation = MovementComponent->GetOwner()->GetActorRotation(); 1571 | RMS_Dy->SetTargetLocation(NewTarget); 1572 | } 1573 | } 1574 | 1575 | 1576 | void URMSLibrary::RemoveRootMotionSource(UCharacterMovementComponent* MovementComponent, 1577 | FName InstanceName) 1578 | { 1579 | if (MovementComponent) 1580 | { 1581 | MovementComponent->RemoveRootMotionSource(InstanceName); 1582 | } 1583 | } 1584 | 1585 | void URMSLibrary::UpdateDynamicMoveDuration(UCharacterMovementComponent* MovementComponent, 1586 | FName InstanceName, float NewDuration) 1587 | { 1588 | if (MovementComponent && NewDuration > 0) 1589 | { 1590 | auto RMS = MovementComponent->GetRootMotionSource(InstanceName); 1591 | if (!RMS) 1592 | { 1593 | return; 1594 | } 1595 | auto RMS_Dy = StaticCastSharedPtr(RMS); 1596 | if (!RMS_Dy) 1597 | { 1598 | return; 1599 | } 1600 | const float CurrTime = RMS_Dy->GetTime(); 1601 | if (NewDuration <= CurrTime) 1602 | { 1603 | return; 1604 | } 1605 | RMS_Dy->Duration = NewDuration - CurrTime; 1606 | RMS_Dy->SetTime(0); 1607 | RMS_Dy->StartLocation = MovementComponent->GetOwner()->GetActorLocation(); 1608 | RMS_Dy->StartRotation = MovementComponent->GetOwner()->GetActorRotation(); 1609 | } 1610 | } 1611 | 1612 | void URMSLibrary::GetCurrentRootMotionSourceTime(UCharacterMovementComponent* MovementComponent, 1613 | FName InstanceName, float& Time, float& Duration) 1614 | { 1615 | if (auto RMS = GetRootMotionSource(MovementComponent, InstanceName)) 1616 | { 1617 | Time = RMS->GetTime(); 1618 | Duration = RMS->GetDuration(); 1619 | } 1620 | } 1621 | 1622 | bool URMSLibrary::IsRootMotionSourceValid(UCharacterMovementComponent* MovementComponent, 1623 | FName InstanceName) 1624 | { 1625 | return GetRootMotionSource(MovementComponent, InstanceName).IsValid(); 1626 | } 1627 | 1628 | bool URMSLibrary::IsRootMotionSourceIdValid(UCharacterMovementComponent* MovementComponent, int32 ID) 1629 | { 1630 | return GetRootMotionSourceByID(MovementComponent, ID).IsValid(); 1631 | } 1632 | 1633 | TSharedPtr URMSLibrary::GetRootMotionSource( 1634 | UCharacterMovementComponent* MovementComponent, FName InstanceName) 1635 | { 1636 | if (MovementComponent) 1637 | { 1638 | return MovementComponent->GetRootMotionSource(InstanceName); 1639 | } 1640 | return nullptr; 1641 | } 1642 | 1643 | TSharedPtr URMSLibrary::GetDynamicMoveToRootMotionSource( 1644 | UCharacterMovementComponent* MovementComponent, FName InstanceName) 1645 | { 1646 | if (MovementComponent) 1647 | { 1648 | auto RMS = MovementComponent->GetRootMotionSource(InstanceName); 1649 | if (RMS) 1650 | { 1651 | return StaticCastSharedPtr(RMS); 1652 | } 1653 | } 1654 | return nullptr; 1655 | } 1656 | 1657 | TSharedPtr URMSLibrary::GetRootMotionSourceByID( 1658 | UCharacterMovementComponent* MovementComponent, int32 ID) 1659 | { 1660 | if (MovementComponent) 1661 | { 1662 | auto RMS = MovementComponent->GetRootMotionSourceByID(ID); 1663 | if (RMS) 1664 | { 1665 | return StaticCastSharedPtr(RMS); 1666 | } 1667 | } 1668 | return nullptr; 1669 | } 1670 | 1671 | void URMSLibrary::CalcAnimWarpingScale(FVector& OriginOffset, ERMSAnimWarpingType Type, 1672 | FVector AnimRootMotionLinear, FVector RMSTargetLinearOffset, 1673 | float Scale, float Tolerance) 1674 | { 1675 | switch (Type) 1676 | { 1677 | case ERMSAnimWarpingType::BasedOnLength: 1678 | { 1679 | const float WarpRatio = FMath::IsNearlyZero(AnimRootMotionLinear.Size(), Tolerance) 1680 | ? 0 1681 | : RMSTargetLinearOffset.Size() / AnimRootMotionLinear.Size(); 1682 | OriginOffset *= WarpRatio * Scale; 1683 | break; 1684 | } 1685 | case ERMSAnimWarpingType::BasedOn3Axis: 1686 | { 1687 | FVector WarpRatio; 1688 | WarpRatio.X = FMath::IsNearlyZero(AnimRootMotionLinear.X, Tolerance) 1689 | ? 0 1690 | : RMSTargetLinearOffset.X / AnimRootMotionLinear.X; 1691 | WarpRatio.Y = FMath::IsNearlyZero(AnimRootMotionLinear.Y, Tolerance) 1692 | ? 0 1693 | : RMSTargetLinearOffset.Y / AnimRootMotionLinear.Y; 1694 | WarpRatio.Z = FMath::IsNearlyZero(AnimRootMotionLinear.Z, Tolerance) 1695 | ? 0 1696 | : RMSTargetLinearOffset.Z / AnimRootMotionLinear.Z; 1697 | OriginOffset *= WarpRatio * Scale; 1698 | break; 1699 | } 1700 | default: 1701 | break; 1702 | } 1703 | } 1704 | 1705 | void URMSLibrary::ConvWorldOffsetToRmsSpace(FVector& OffsetWS, FVector Start, FVector Target) 1706 | { 1707 | FRotator FacingRot = (Target - Start).Rotation(); 1708 | FacingRot.Pitch = 0; 1709 | //UKismetMathLibrary::InverseTransformDirection(FTransform(FacingRot,StartLocation - HalfHeightVec,FVector::OneVector),Offset_LinearBase); //这样不行吗??? 1710 | const FVector ForwardVec = UKismetMathLibrary::GetForwardVector(FacingRot); 1711 | const FVector RightVec = UKismetMathLibrary::GetRightVector(FacingRot); 1712 | //const FVector UpVec = UKismetMathLibrary::GetUpVector(FacingRot); 1713 | FVector ProjFwd = OffsetWS.ProjectOnTo(ForwardVec); 1714 | FVector ProjRt = OffsetWS.ProjectOnTo(RightVec); 1715 | //FVector ProjUp = OffsetWS.ProjectOnTo(UpVec); 1716 | OffsetWS = FVector(ProjFwd.Size() * (ProjFwd.GetSafeNormal() | ForwardVec.GetSafeNormal()), 1717 | ProjRt.Size() * (ProjRt.GetSafeNormal() | RightVec.GetSafeNormal()), 1718 | OffsetWS.Z); 1719 | } 1720 | 1721 | void URMSLibrary::FiltAnimCurveOffsetAxisData(FVector& AnimOffset, ERMSAnimWarpingAxis Axis) 1722 | { 1723 | switch (Axis) 1724 | { 1725 | case ERMSAnimWarpingAxis::None: 1726 | { 1727 | AnimOffset.X = 0; 1728 | AnimOffset.Y = 0; 1729 | AnimOffset.Z = 0; 1730 | break; 1731 | } 1732 | case ERMSAnimWarpingAxis::X: 1733 | { 1734 | AnimOffset.Y = 0; 1735 | AnimOffset.Z = 0; 1736 | break; 1737 | } 1738 | case ERMSAnimWarpingAxis::Y: 1739 | { 1740 | AnimOffset.X = 0; 1741 | AnimOffset.Z = 0; 1742 | break; 1743 | } 1744 | case ERMSAnimWarpingAxis::Z: 1745 | { 1746 | AnimOffset.X = 0; 1747 | AnimOffset.Y = 0; 1748 | break; 1749 | } 1750 | case ERMSAnimWarpingAxis::XY: 1751 | { 1752 | AnimOffset.Z = 0; 1753 | break; 1754 | } 1755 | case ERMSAnimWarpingAxis::XZ: 1756 | { 1757 | AnimOffset.Y = 0; 1758 | break; 1759 | } 1760 | case ERMSAnimWarpingAxis::YZ: 1761 | { 1762 | AnimOffset.X = 0; 1763 | 1764 | break; 1765 | } 1766 | case ERMSAnimWarpingAxis::XYZ: 1767 | { 1768 | break; 1769 | } 1770 | default: 1771 | break; 1772 | } 1773 | } 1774 | 1775 | int32 URMSLibrary::CalcPriorityByApplyMode(UCharacterMovementComponent* MovementComponent, 1776 | FName PendingInstanceName, int32 PendingPriorioty, 1777 | ERMSApplyMode ApplyMode) 1778 | { 1779 | if (!MovementComponent) 1780 | { 1781 | return -1; 1782 | } 1783 | if (auto RMS = MovementComponent->GetRootMotionSource(PendingInstanceName)) 1784 | { 1785 | const int32 OldPriority = RMS->Priority; 1786 | switch (ApplyMode) 1787 | { 1788 | case ERMSApplyMode::ApplyHigherPriority: 1789 | { 1790 | return FMath::Max(OldPriority + 1, PendingPriorioty); 1791 | } 1792 | case ERMSApplyMode::Replace: 1793 | { 1794 | MovementComponent->RemoveRootMotionSource(PendingInstanceName); 1795 | return OldPriority; 1796 | } 1797 | case ERMSApplyMode::Block: 1798 | { 1799 | return -1; 1800 | } 1801 | default: 1802 | return PendingPriorioty; 1803 | } 1804 | } 1805 | return PendingPriorioty; 1806 | } 1807 | 1808 | bool URMSLibrary::ExtractRotation(FRotator& OutRotation, const ACharacter& Character, FRotator StartRotation, 1809 | FRotator TargetRotation, 1810 | float Fraction, UCurveFloat* RotationCurve) 1811 | { 1812 | if (IsValid(&Character)) 1813 | { 1814 | float RotationFraction = Fraction; 1815 | if (RotationCurve) 1816 | { 1817 | RotationFraction = FMath::Clamp(RotationCurve->GetFloatValue(RotationFraction), 0.f, 1.f); 1818 | } 1819 | const float TargetYaw = TargetRotation.Yaw < 0 ? TargetRotation.Yaw + 360 : TargetRotation.Yaw; 1820 | const float StartYaw = StartRotation.Yaw < 0 ? StartRotation.Yaw + 360 : StartRotation.Yaw; 1821 | const float CurrentTargetYaw = FMath::Lerp(StartYaw, TargetYaw, RotationFraction); 1822 | 1823 | const FRotator CurrentRotation = Character.GetActorRotation(); 1824 | OutRotation = FRotator{0, (CurrentTargetYaw - CurrentRotation.Yaw), 0}; 1825 | return true; 1826 | } 1827 | return false; 1828 | } 1829 | 1830 | bool URMSLibrary::PredictRootMotionSourceLocation_Runtime(UCharacterMovementComponent* MovementComponent, 1831 | FName InstanceName, float Time, FVector& OutLocation) 1832 | { 1833 | if (!MovementComponent) 1834 | { 1835 | return false; 1836 | } 1837 | auto RMS = GetRootMotionSource(MovementComponent, InstanceName); 1838 | if (!RMS.IsValid()) 1839 | { 1840 | return false; 1841 | } 1842 | 1843 | if (auto RMS_MoveToDy = StaticCastSharedPtr(RMS)) 1844 | { 1845 | float Fraction = Time / RMS_MoveToDy->GetDuration(); 1846 | if (RMS_MoveToDy->TimeMappingCurve) 1847 | { 1848 | Fraction = EvaluateFloatCurveAtFraction(*RMS_MoveToDy->TimeMappingCurve, Fraction); 1849 | } 1850 | const FVector PathOffset = RMS_MoveToDy->GetPathOffsetInWorldSpace(Fraction); 1851 | const FVector CurrentTargetLocation = FMath::Lerp( 1852 | RMS_MoveToDy->StartLocation, RMS_MoveToDy->TargetLocation, Fraction); 1853 | OutLocation = CurrentTargetLocation + PathOffset; 1854 | return true; 1855 | } 1856 | else if (auto RMS_MoveTo = StaticCastSharedPtr(RMS)) 1857 | { 1858 | const float Fraction = Time / RMS_MoveTo->GetDuration(); 1859 | FVector PathOffset = FVector::ZeroVector; 1860 | if (RMS_MoveTo->PathOffsetCurve) 1861 | { 1862 | PathOffset = RMS_MoveTo->GetPathOffsetInWorldSpace(Fraction); 1863 | } 1864 | const FVector CurrentTargetLocation = FMath::Lerp( 1865 | RMS_MoveTo->StartLocation, RMS_MoveTo->TargetLocation, Fraction); 1866 | OutLocation = CurrentTargetLocation + PathOffset; 1867 | return true; 1868 | } 1869 | else if (auto RMS_Jump = StaticCastSharedPtr(RMS)) 1870 | { 1871 | float Fraction = Time / RMS_Jump->GetDuration(); 1872 | if (RMS_Jump->TimeMappingCurve) 1873 | { 1874 | float MinCurveTime(0.f); 1875 | float MaxCurveTime(1.f); 1876 | // 1877 | // RMS_Jump->TimeMappingCurve->GetTimeRange(MinCurveTime, MaxCurveTime); 1878 | // 1879 | // Fraction = RMS_Jump->TimeMappingCurve->GetFloatValue(FMath::GetRangeValue(FVector2D(MinCurveTime, MaxCurveTime), Fraction)); 1880 | 1881 | Fraction = EvaluateFloatCurveAtFraction(*RMS_Jump->TimeMappingCurve, Fraction); 1882 | const FVector TargetRelativeLocation = RMS_Jump->GetRelativeLocation(Fraction); 1883 | if (MovementComponent->GetOwner()) 1884 | { 1885 | OutLocation = MovementComponent->GetOwner()->GetActorLocation() + TargetRelativeLocation; 1886 | return true; 1887 | } 1888 | } 1889 | } 1890 | 1891 | return false; 1892 | } 1893 | 1894 | 1895 | bool URMSLibrary::PredictRootMotionSourceLocation_MoveTo(FVector& OutLocation, 1896 | UCharacterMovementComponent* MovementComponent, 1897 | FVector StartLocation, FVector TargetLocation, 1898 | float Duration, 1899 | float Time, UCurveVector* PathOffsetCurve) 1900 | { 1901 | if (!MovementComponent || Duration <= 0 || Time < 0 || Time > Duration) 1902 | { 1903 | return false; 1904 | } 1905 | const float Fraction = Time / Duration; 1906 | FVector CurrentTargetLocation = FMath::Lerp(StartLocation, TargetLocation, Fraction); 1907 | FVector PathOffset = FVector::ZeroVector; 1908 | if (PathOffsetCurve) 1909 | { 1910 | PathOffset = EvaluateVectorCurveAtFraction(*PathOffsetCurve, Fraction); 1911 | FRotator FacingRotation((TargetLocation - StartLocation).Rotation()); 1912 | FacingRotation.Pitch = 0.f; 1913 | PathOffset = FacingRotation.RotateVector(PathOffset); 1914 | } 1915 | OutLocation = CurrentTargetLocation + PathOffset; 1916 | 1917 | return true; 1918 | } 1919 | 1920 | bool URMSLibrary::PredictRootMotionSourceLocation_Jump(FVector& OutLocation, 1921 | UCharacterMovementComponent* MovementComponent, 1922 | FVector StartLocation, float Distance, float Height, 1923 | FRotator Rotation, 1924 | float Duration, float Time, 1925 | UCurveVector* PathOffsetCurve, 1926 | UCurveFloat* TimeMappingCurve) 1927 | { 1928 | if (!MovementComponent || Duration <= 0 || Time < 0 || Time > Duration) 1929 | { 1930 | return false; 1931 | } 1932 | FVector PathOffset(FVector::ZeroVector); 1933 | float Fraction = Time / Duration; 1934 | if (TimeMappingCurve) 1935 | { 1936 | Fraction = EvaluateFloatCurveAtFraction(*TimeMappingCurve, Fraction); 1937 | } 1938 | if (PathOffsetCurve) 1939 | { 1940 | PathOffset = EvaluateVectorCurveAtFraction(*PathOffsetCurve, Fraction); 1941 | } 1942 | else 1943 | { 1944 | const float Phi = 2.f * Fraction - 1; 1945 | const float Z = -(Phi * Phi) + 1; 1946 | PathOffset.Z = Z; 1947 | } 1948 | if (Height >= 0.f) 1949 | { 1950 | PathOffset.Z *= Height; 1951 | } 1952 | 1953 | 1954 | FRotator FacingRotation(Rotation); 1955 | FacingRotation.Pitch = 0.f; 1956 | FVector RelativeLocationFacingSpace = FVector(Fraction * Distance, 0.f, 0.f) + PathOffset; 1957 | RelativeLocationFacingSpace = FacingRotation.RotateVector(RelativeLocationFacingSpace); 1958 | OutLocation = StartLocation + RelativeLocationFacingSpace; 1959 | return true; 1960 | } 1961 | 1962 | bool URMSLibrary::PredictRootMotionSourceLocation_MoveToParabola(FVector& OutLocation 1963 | , UCharacterMovementComponent* 1964 | MovementComponent 1965 | , FVector StartLocation 1966 | , FVector TargetLocation 1967 | , float Duration 1968 | , float CurrentTime 1969 | , UCurveFloat* ParabolaCurve 1970 | , UCurveFloat* TimeMappingCurve) 1971 | { 1972 | if (!MovementComponent || Duration <= 0 || CurrentTime < 0 || CurrentTime > Duration) 1973 | { 1974 | return false; 1975 | } 1976 | float Fraction = CurrentTime / Duration; 1977 | FVector CurrentTargetLocation = FMath::Lerp(StartLocation, TargetLocation, Fraction); 1978 | if (TimeMappingCurve) 1979 | { 1980 | Fraction = EvaluateFloatCurveAtFraction(*TimeMappingCurve, Fraction); 1981 | } 1982 | if (ParabolaCurve) 1983 | { 1984 | const float Z = (TargetLocation - StartLocation).Z; 1985 | const float CurveValue = EvaluateFloatCurveAtFraction(*ParabolaCurve, Fraction); 1986 | CurrentTargetLocation.Z = StartLocation.Z + CurveValue * Z; 1987 | } 1988 | OutLocation = CurrentTargetLocation; 1989 | 1990 | return true; 1991 | } 1992 | 1993 | UE_ENABLE_OPTIMIZATION 1994 | -------------------------------------------------------------------------------- /Source/RMS/Private/RMSModule.cpp: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | #include "RMSModule.h" 5 | 6 | #define LOCTEXT_NAMESPACE "FRMSModule" 7 | 8 | void FRMSModule::StartupModule() 9 | { 10 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 11 | 12 | } 13 | 14 | void FRMSModule::ShutdownModule() 15 | { 16 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 17 | // we call this function before unloading the module. 18 | } 19 | 20 | #undef LOCTEXT_NAMESPACE 21 | 22 | IMPLEMENT_MODULE(FRMSModule, RMS) -------------------------------------------------------------------------------- /Source/RMS/Private/RMSTypes.cpp: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | 5 | #include "RMSTypes.h" 6 | 7 | 8 | namespace RMS 9 | { 10 | TAutoConsoleVariable CVarRMS_Debug(TEXT("b.RMS.Debug"), 0, TEXT("0: Disable 1: Enable "), ECVF_Cheat); 11 | } -------------------------------------------------------------------------------- /Source/RMS/Public/AnimNotifyState_RMS.h: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | #pragma once 5 | 6 | #include "CoreMinimal.h" 7 | #include "Animation/AnimNotifies/AnimNotifyState.h" 8 | #include "AnimNotifyState_RMS.generated.h" 9 | 10 | /** 11 | * 12 | */ 13 | UCLASS() 14 | class RMS_API UAnimNotifyState_RMS_Warping : public UAnimNotifyState 15 | { 16 | GENERATED_BODY() 17 | public: 18 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 19 | FName RootMotionSourceTarget = NAME_None; 20 | }; 21 | -------------------------------------------------------------------------------- /Source/RMS/Public/Experimental/RMSComponent.h: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | #pragma once 5 | 6 | #include "CoreMinimal.h" 7 | #include "GameplayTasksComponent.h" 8 | 9 | #include "RMSLibrary.h" 10 | 11 | #include "RMSComponent.generated.h" 12 | 13 | 14 | 15 | class URMSTask_Base; 16 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FRMSTaskDlg2p, URMSTask_Base*, TaskObject, bool, bSuccess); 17 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FRMSTaskDlg1p, URMSTask_Base*, TaskObject); 18 | 19 | UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent), BlueprintType, Blueprintable) 20 | class RMS_API URMSComponent : public UGameplayTasksComponent 21 | { 22 | GENERATED_UCLASS_BODY() 23 | public: 24 | protected: 25 | // Called when the game starts 26 | virtual void BeginPlay() override; 27 | 28 | public: 29 | // Called every frame 30 | virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; 31 | 32 | UFUNCTION(BlueprintCallable, Category=RootMotionSourceComponent) 33 | void Init(UCharacterMovementComponent* MovementComponent); 34 | UFUNCTION(BlueprintCallable, Category=RootMotionSourceComponent) 35 | int32 TryActivateTask(URMSTask_Base* Task); 36 | 37 | UFUNCTION(BlueprintCallable, Category=RootMotionSourceComponent) 38 | void SetRms_TargetByLocation(FName Instance, FVector Location); 39 | UFUNCTION(BlueprintCallable, Category=RootMotionSourceComponent) 40 | void SetRms_TargetByRotation(FName Instance, FRotator Rotation); 41 | UFUNCTION(BlueprintCallable, Category=RootMotionSourceComponent) 42 | void SetRms_Target(FName Instance, FVector Location, FRotator Rotation); 43 | 44 | public: 45 | UPROPERTY(BlueprintCallable, BlueprintAssignable) 46 | FRMSTaskDlg1p OnTaskBegin; 47 | UPROPERTY(BlueprintCallable, BlueprintAssignable) 48 | FRMSTaskDlg2p OnTaskEnd; 49 | 50 | UCharacterMovementComponent* GetMovementComponent() const 51 | { 52 | return MovementComponent.Get(); 53 | } 54 | 55 | protected: 56 | 57 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category=RootMotionSource) 58 | bool bListenTaskEnd = false; 59 | 60 | UPROPERTY() 61 | TMap DirtIDs; 62 | 63 | UPROPERTY(BlueprintReadWrite) 64 | TMap CurrentTasks; 65 | 66 | UPROPERTY(BlueprintReadWrite) 67 | TWeakObjectPtr MovementComponent = nullptr; 68 | 69 | 70 | UPROPERTY(BlueprintReadWrite) 71 | TMap RMSMap; 72 | }; 73 | -------------------------------------------------------------------------------- /Source/RMS/Public/Experimental/Task/RMSTask_AnimWarping.h: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | #pragma once 5 | 6 | #include "CoreMinimal.h" 7 | #include "RMSLibrary.h" 8 | #include "RMSTask_Base.h" 9 | #include "RMSTask_AnimWarping.generated.h" 10 | 11 | 12 | class URMSComponent; 13 | UCLASS() 14 | class RMS_API URMSTask_AnimWarping : public URMSTask_Base 15 | { 16 | GENERATED_BODY() 17 | public: 18 | UFUNCTION(BlueprintCallable, Category=RootMotionSource, meta = (BlueprintInternalUseOnly = "TRUE")) 19 | static URMSTask_AnimWarping* RootMotionSourceTask_AnimWarping(URMSComponent* RootMotionComponent, UAnimSequence*Anim, TMap WarpingInfo); 20 | 21 | 22 | virtual void Activate() override; 23 | virtual void Pause() override; 24 | virtual void Resume() override; 25 | virtual void TickTask(float DeltaTime) override; 26 | 27 | virtual void OnTaskFinished_Implementation(URMSTask_Base* TaskObject, bool bSuccess) override;; 28 | 29 | 30 | 31 | 32 | UPROPERTY() 33 | TWeakObjectPtr MovementComp = nullptr; 34 | 35 | TMap WarpingInfo; 36 | TWeakObjectPtr Anim = nullptr; 37 | 38 | }; 39 | -------------------------------------------------------------------------------- /Source/RMS/Public/Experimental/Task/RMSTask_Base.h: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | #pragma once 5 | 6 | #include "CoreMinimal.h" 7 | #include "GameplayTask.h" 8 | #include "Experimental/RMSComponent.h" 9 | #include "RMSTask_Base.generated.h" 10 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FRMSDlg, URMSTask_Base*, Object); 11 | 12 | class URMSComponent; 13 | UCLASS() 14 | class RMS_API URMSTask_Base : public UGameplayTask 15 | { 16 | GENERATED_BODY() 17 | public: 18 | URMSTask_Base(const FObjectInitializer& ObjectInitializer); 19 | UFUNCTION(Blueprintcallable, BlueprintNativeEvent) 20 | void OnTaskFinished(URMSTask_Base* TaskObject, bool bSuccess); 21 | 22 | UPROPERTY(BlueprintReadWrite) 23 | int32 ID = -1; 24 | 25 | TWeakObjectPtr RootMotionComponent = nullptr; 26 | 27 | template 28 | static T* NewRootMotionSourceTask(URMSComponent* RootMotionComponent, FName InstanceName = FName(), int32 Priority = 0) 29 | { 30 | check(RootMotionComponent); 31 | 32 | T* MyObj = NewObject(); 33 | MyObj->InitTask(*RootMotionComponent, Priority); 34 | MyObj->InstanceName = InstanceName; 35 | return MyObj; 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /Source/RMS/Public/Experimental/Task/RMSTask_MoveTo.h: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | #pragma once 5 | 6 | #include "CoreMinimal.h" 7 | #include "RMSLibrary.h" 8 | #include "RMSTask_Base.h" 9 | #include "RMSTask_MoveTo.generated.h" 10 | 11 | 12 | class URMSComponent; 13 | UCLASS() 14 | class RMS_API URMSTask_MoveTo : public URMSTask_Base 15 | { 16 | GENERATED_BODY() 17 | public: 18 | UFUNCTION(BlueprintCallable, Category=RootMotionSource, meta = (BlueprintInternalUseOnly = "TRUE")) 19 | static URMSTask_MoveTo* RootMotionSourceTask_MoveTo(URMSComponent* RootMotionComponent,FName RmsInstanceName, 20 | FVector StartLocation, FVector TargetLocation, float Duration, 21 | int32 Priority, 22 | FRMSSetting_Move Setting, 23 | UCurveVector* PathOffsetCurve = nullptr); 24 | 25 | 26 | virtual void Activate() override; 27 | virtual void Pause() override; 28 | virtual void Resume() override; 29 | virtual void TickTask(float DeltaTime) override; 30 | 31 | virtual void OnTaskFinished_Implementation(URMSTask_Base* TaskObject, bool bSuccess) override;; 32 | 33 | 34 | 35 | UPROPERTY(BlueprintCallable, BlueprintAssignable) 36 | FRMSDlg OnSuccess; 37 | UPROPERTY(BlueprintCallable, BlueprintAssignable) 38 | FRMSDlg OnFail; 39 | 40 | UPROPERTY() 41 | TWeakObjectPtr MovementComp = nullptr; 42 | UPROPERTY(BlueprintReadOnly) 43 | FVector StartLocation; 44 | UPROPERTY(BlueprintReadOnly) 45 | FVector TargetLocation; 46 | UPROPERTY(BlueprintReadOnly) 47 | float Duration; 48 | UPROPERTY(BlueprintReadOnly) 49 | int32 Priority; 50 | UPROPERTY(BlueprintReadOnly) 51 | UCurveVector* PathOffsetCurve = nullptr; 52 | UPROPERTY(BlueprintReadOnly) 53 | FRMSSetting_Move Setting; 54 | 55 | }; 56 | -------------------------------------------------------------------------------- /Source/RMS/Public/RMSGroupEx.h: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | #pragma once 5 | 6 | #include "CoreMinimal.h" 7 | #include "RMSTypes.h" 8 | #include "GameFramework/RootMotionSource.h" 9 | #include "RMSGroupEx.generated.h" 10 | 11 | USTRUCT() 12 | struct RMS_API FRootMotionSource_PathMoveToForce : public FRootMotionSource 13 | { 14 | GENERATED_USTRUCT_BODY() 15 | FRootMotionSource_PathMoveToForce(); 16 | 17 | virtual ~FRootMotionSource_PathMoveToForce() 18 | { 19 | } 20 | 21 | UPROPERTY() 22 | FVector StartLocation = FVector::ZeroVector; 23 | UPROPERTY() 24 | FRotator StartRotation = FRotator::ZeroRotator; 25 | UPROPERTY() 26 | TArray Path; 27 | 28 | 29 | FRMSPathMoveToData CurrData; 30 | FRMSPathMoveToData LastData; 31 | int32 Index = -1; 32 | 33 | 34 | FVector GetPathOffsetInWorldSpace(const float MoveFraction, FRMSPathMoveToData Data, FVector Start) const; 35 | 36 | bool GetPathDataByTime(float Time, FRMSPathMoveToData& OutCurrData, FRMSPathMoveToData& OutLastData) const; 37 | 38 | virtual bool UpdateStateFrom(const FRootMotionSource* SourceToTakeStateFrom, bool bMarkForSimulatedCatchup = false) override; 39 | 40 | virtual void PrepareRootMotion( 41 | float SimulationTime, 42 | float MovementTickTime, 43 | const ACharacter& Character, 44 | const UCharacterMovementComponent& MoveComponent 45 | ) override; 46 | 47 | virtual bool NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) override; 48 | virtual FRootMotionSource* Clone() const override; 49 | 50 | virtual bool Matches(const FRootMotionSource* Other) const override; 51 | 52 | virtual bool MatchesAndHasSameState(const FRootMotionSource* Other) const override; 53 | virtual UScriptStruct* GetScriptStruct() const override; 54 | virtual FString ToSimpleString() const override; 55 | virtual void AddReferencedObjects(class FReferenceCollector& Collector) override; 56 | }; 57 | 58 | template <> 59 | struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 60 | { 61 | enum 62 | { 63 | WithNetSerializer = true, 64 | WithCopy = true 65 | }; 66 | }; 67 | 68 | 69 | USTRUCT() 70 | struct RMS_API FRootMotionSource_JumpForce_WithPoints : public FRootMotionSource 71 | { 72 | GENERATED_USTRUCT_BODY() 73 | FRootMotionSource_JumpForce_WithPoints(); 74 | 75 | virtual ~FRootMotionSource_JumpForce_WithPoints() 76 | { 77 | } 78 | 79 | UPROPERTY() 80 | FRotator StartRotation = FRotator::ZeroRotator; 81 | 82 | UPROPERTY() 83 | FVector StartLocation = FVector::ZeroVector; 84 | 85 | UPROPERTY() 86 | FVector HalfWayLocation = FVector::ZeroVector; 87 | 88 | UPROPERTY() 89 | FVector TargetLocation = FVector::ZeroVector; 90 | 91 | UPROPERTY() 92 | FRMSRotationSetting RotationSetting; 93 | 94 | UPROPERTY() 95 | bool bDisableTimeout = false; 96 | 97 | 98 | UPROPERTY() 99 | TObjectPtr TimeMappingCurve = nullptr; 100 | protected: 101 | UPROPERTY() 102 | TObjectPtr PathOffsetCurve = nullptr; 103 | 104 | 105 | FVector SavedHalfwayLocation; 106 | float SavedHeight = 0; 107 | float SavedDistance = 0; 108 | FRotator SavedRotation = FRotator::ZeroRotator; 109 | bool bIsInit = false; 110 | 111 | public: 112 | FVector GetPathOffset(float MoveFraction) const; 113 | 114 | FVector GetRelativeLocation(float MoveFraction) const; 115 | 116 | virtual bool IsTimeOutEnabled() const override; 117 | 118 | virtual FRootMotionSource* Clone() const override; 119 | 120 | virtual bool Matches(const FRootMotionSource* Other) const override; 121 | 122 | virtual bool MatchesAndHasSameState(const FRootMotionSource* Other) const override; 123 | 124 | virtual bool UpdateStateFrom(const FRootMotionSource* SourceToTakeStateFrom, bool bMarkForSimulatedCatchup = false) override; 125 | 126 | virtual void PrepareRootMotion( 127 | float SimulationTime, 128 | float MovementTickTime, 129 | const ACharacter& Character, 130 | const UCharacterMovementComponent& MoveComponent 131 | ) override; 132 | 133 | virtual bool NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) override; 134 | 135 | virtual UScriptStruct* GetScriptStruct() const override; 136 | 137 | virtual FString ToSimpleString() const override; 138 | 139 | virtual void AddReferencedObjects(class FReferenceCollector& Collector) override; 140 | 141 | void InitPath(const ACharacter& Character); 142 | }; 143 | 144 | 145 | template <> 146 | struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 147 | { 148 | enum 149 | { 150 | WithNetSerializer = true, 151 | WithCopy = true 152 | }; 153 | }; 154 | 155 | 156 | USTRUCT() 157 | struct RMS_API FRootMotionSource_MoveToForce_WithRotation : public FRootMotionSource_MoveToForce 158 | { 159 | GENERATED_USTRUCT_BODY() 160 | FRootMotionSource_MoveToForce_WithRotation(); 161 | 162 | virtual ~FRootMotionSource_MoveToForce_WithRotation() 163 | { 164 | } 165 | 166 | virtual void PrepareRootMotion( 167 | float SimulationTime, 168 | float MovementTickTime, 169 | const ACharacter& Character, 170 | const UCharacterMovementComponent& MoveComponent 171 | ) override; 172 | virtual bool UpdateStateFrom(const FRootMotionSource* SourceToTakeStateFrom, bool bMarkForSimulatedCatchup = false) override; 173 | 174 | virtual bool NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) override; 175 | virtual FRootMotionSource* Clone() const override; 176 | 177 | virtual bool Matches(const FRootMotionSource* Other) const override; 178 | 179 | virtual bool MatchesAndHasSameState(const FRootMotionSource* Other) const override; 180 | virtual UScriptStruct* GetScriptStruct() const override; 181 | virtual FString ToSimpleString() const override; 182 | virtual void AddReferencedObjects(class FReferenceCollector& Collector) override; 183 | 184 | 185 | UPROPERTY() 186 | FRMSRotationSetting RotationSetting; 187 | UPROPERTY() 188 | FRotator StartRotation = FRotator::ZeroRotator; 189 | protected: 190 | // UPROPERTY() 191 | // FRotator TargetRotation = FRotator::ZeroRotator; 192 | }; 193 | 194 | template <> 195 | struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 196 | { 197 | enum 198 | { 199 | WithNetSerializer = true, 200 | WithCopy = true 201 | }; 202 | }; 203 | 204 | 205 | USTRUCT() 206 | struct RMS_API FRootMotionSource_MoveToDynamicForce_WithRotation : public FRootMotionSource_MoveToDynamicForce 207 | { 208 | GENERATED_USTRUCT_BODY() 209 | FRootMotionSource_MoveToDynamicForce_WithRotation() 210 | { 211 | }; 212 | 213 | virtual ~FRootMotionSource_MoveToDynamicForce_WithRotation() 214 | { 215 | } 216 | 217 | virtual void PrepareRootMotion( 218 | float SimulationTime, 219 | float MovementTickTime, 220 | const ACharacter& Character, 221 | const UCharacterMovementComponent& MoveComponent 222 | ) override; 223 | virtual UScriptStruct* GetScriptStruct() const override; 224 | virtual bool NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) override; 225 | virtual FRootMotionSource* Clone() const override; 226 | 227 | virtual bool Matches(const FRootMotionSource* Other) const override; 228 | 229 | virtual bool MatchesAndHasSameState(const FRootMotionSource* Other) const override; 230 | UPROPERTY() 231 | FRMSRotationSetting RotationSetting; 232 | UPROPERTY() 233 | FRotator StartRotation = FRotator::ZeroRotator; 234 | }; 235 | 236 | template <> 237 | struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 238 | { 239 | enum 240 | { 241 | WithNetSerializer = true, 242 | WithCopy = true 243 | }; 244 | }; 245 | 246 | USTRUCT() 247 | struct RMS_API FRootMotionSource_AnimWarping : public FRootMotionSource 248 | { 249 | GENERATED_USTRUCT_BODY() 250 | FRootMotionSource_AnimWarping() 251 | { 252 | }; 253 | 254 | virtual ~FRootMotionSource_AnimWarping() 255 | { 256 | } 257 | 258 | 259 | UPROPERTY() 260 | TObjectPtr Animation = nullptr; 261 | UPROPERTY() 262 | FVector StartLocation = FVector::ZeroVector; 263 | UPROPERTY() 264 | FRotator StartRotation = FRotator::ZeroRotator; 265 | UPROPERTY() 266 | FRMSRotationSetting RotationSetting; 267 | 268 | UPROPERTY() 269 | float AnimStartTime = 0; 270 | UPROPERTY() 271 | float AnimEndTime = -1; 272 | UPROPERTY() 273 | bool bIgnoreZAxis = false; 274 | protected: 275 | UPROPERTY() 276 | FRotator CachedRotation = FRotator::ZeroRotator; 277 | UPROPERTY() 278 | FVector CachedTarget = FVector::ZeroVector; 279 | UPROPERTY() 280 | bool bInit = false; 281 | UPROPERTY() 282 | float CachedEndTime = -1; 283 | 284 | FORCEINLINE float GetCurrentAnimEndTime() const 285 | { 286 | return CachedEndTime <= 0 ? AnimEndTime : CachedEndTime; 287 | }; 288 | FORCEINLINE FRotator GetTargetRotation() const 289 | { 290 | return CachedRotation; 291 | }; 292 | 293 | void SetCurrentAnimEndTime(float NewEndTime) 294 | { 295 | CachedEndTime = NewEndTime; 296 | } 297 | 298 | void SetTargetRotation(FRotator Rotation) 299 | { 300 | CachedRotation = Rotation; 301 | } 302 | 303 | public: 304 | void SetTargetLocation(FVector Target) 305 | { 306 | CachedTarget = Target; 307 | } 308 | 309 | FORCEINLINE FVector GetTargetLocation() const 310 | { 311 | return CachedTarget; 312 | }; 313 | 314 | virtual FTransform ProcessRootMotion(const ACharacter& Character, const FTransform& InRootMotion, float InPreviousTime, float InCurrentTime, float DeltaSeconds); 315 | FQuat WarpRotation(const ACharacter& Character, const FTransform& RootMotionDelta, const FTransform& RootMotionTotal, float TimeRemaining, float DeltaSeconds); 316 | 317 | 318 | virtual bool UpdateStateFrom(const FRootMotionSource* SourceToTakeStateFrom, bool bMarkForSimulatedCatchup = false) override; 319 | 320 | virtual void PrepareRootMotion( 321 | float SimulationTime, 322 | float MovementTickTime, 323 | const ACharacter& Character, 324 | const UCharacterMovementComponent& MoveComponent 325 | ) override; 326 | 327 | FTransform ExtractRootMotion(float StartTime, float EndTime) const; 328 | 329 | virtual bool NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) override; 330 | virtual FRootMotionSource* Clone() const override; 331 | 332 | virtual bool Matches(const FRootMotionSource* Other) const override; 333 | 334 | virtual bool MatchesAndHasSameState(const FRootMotionSource* Other) const override; 335 | virtual UScriptStruct* GetScriptStruct() const override; 336 | virtual FString ToSimpleString() const override; 337 | virtual void AddReferencedObjects(class FReferenceCollector& Collector) override; 338 | 339 | protected: 340 | }; 341 | 342 | template <> 343 | struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 344 | { 345 | enum 346 | { 347 | WithNetSerializer = true, 348 | WithCopy = true 349 | }; 350 | }; 351 | 352 | USTRUCT() 353 | struct RMS_API FRootMotionSource_AnimWarping_FinalPoint : public FRootMotionSource_AnimWarping 354 | { 355 | GENERATED_USTRUCT_BODY() 356 | FRootMotionSource_AnimWarping_FinalPoint() 357 | { 358 | }; 359 | 360 | virtual ~FRootMotionSource_AnimWarping_FinalPoint() 361 | { 362 | } 363 | 364 | 365 | UPROPERTY() 366 | FVector TargetLocation = FVector::ZeroVector; 367 | UPROPERTY() 368 | FRotator TargetRotation = FRotator::ZeroRotator; 369 | 370 | public: 371 | virtual void PrepareRootMotion( 372 | float SimulationTime, 373 | float MovementTickTime, 374 | const ACharacter& Character, 375 | const UCharacterMovementComponent& MoveComponent 376 | ) override; 377 | 378 | 379 | virtual bool NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) override; 380 | virtual FRootMotionSource* Clone() const override; 381 | 382 | virtual bool Matches(const FRootMotionSource* Other) const override; 383 | 384 | virtual UScriptStruct* GetScriptStruct() const override; 385 | virtual FString ToSimpleString() const override; 386 | }; 387 | 388 | template <> 389 | struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 390 | { 391 | enum 392 | { 393 | WithNetSerializer = true, 394 | WithCopy = true 395 | }; 396 | }; 397 | 398 | USTRUCT() 399 | struct RMS_API FRootMotionSource_AnimWarping_MultiTargets : public FRootMotionSource_AnimWarping 400 | { 401 | GENERATED_USTRUCT_BODY() 402 | FRootMotionSource_AnimWarping_MultiTargets() 403 | { 404 | }; 405 | 406 | virtual ~FRootMotionSource_AnimWarping_MultiTargets() 407 | { 408 | } 409 | 410 | 411 | UPROPERTY() 412 | TArray TriggerDatas; 413 | protected: 414 | UPROPERTY() 415 | FRMSTarget CurrTriggerData; 416 | UPROPERTY() 417 | FRMSTarget LastTriggerData; 418 | 419 | 420 | bool UpdateTriggerTarget(float SimulationTime, float TimeScale); 421 | public: 422 | virtual void PrepareRootMotion( 423 | float SimulationTime, 424 | float MovementTickTime, 425 | const ACharacter& Character, 426 | const UCharacterMovementComponent& MoveComponent 427 | ) override; 428 | 429 | 430 | virtual bool NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) override; 431 | virtual FRootMotionSource* Clone() const override; 432 | 433 | virtual bool Matches(const FRootMotionSource* Other) const override; 434 | 435 | virtual UScriptStruct* GetScriptStruct() const override; 436 | virtual FString ToSimpleString() const override; 437 | }; 438 | 439 | template <> 440 | struct TStructOpsTypeTraits : public TStructOpsTypeTraitsBase2 441 | { 442 | enum 443 | { 444 | WithNetSerializer = true, 445 | WithCopy = true 446 | }; 447 | }; -------------------------------------------------------------------------------- /Source/RMS/Public/RMSLibrary.h: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | #pragma once 5 | 6 | #include "CoreMinimal.h" 7 | #include "RMSTypes.h" 8 | #include "GameFramework/Character.h" 9 | #include "GameFramework/RootMotionSource.h" 10 | #include "Kismet/BlueprintFunctionLibrary.h" 11 | #include "RMSLibrary.generated.h" 12 | 13 | 14 | class URMSComponent; 15 | class UCurveVector; 16 | enum class ERootMotionAccumulateMode : uint8; 17 | class UCharacterMovementComponent; 18 | 19 | 20 | UCLASS() 21 | class RMS_API URMSLibrary : public UBlueprintFunctionLibrary 22 | { 23 | GENERATED_BODY() 24 | public: 25 | /** 26 | * 移动到一个点, 一般情况下 StartLocation和TargetLocation都是已角色中心点为参考点 27 | * 28 | * @param StartLocation 角色会基于此开始移动,所以请确保是Actor当前的Location 29 | * @param TargetLocation 参考StartLocation的目标位置(要考虑HalfHeight) 30 | * @param RotationSetting 旋转设置, 具体看每个参数自己的注释 31 | * @param ApplyMode RMS的应用模式, 默认是队列, 即存在同名RMS的情况下会在后台执行直到前一个运行完 32 | * 33 | */ 34 | UFUNCTION(BlueprintCallable, Category="RMS", 35 | meta = (AdvancedDisplay = "6", AutoCreateRefTerm = "ExtraSetting", CPP_Default_ExtraSetting, 36 | CPP_Default_RotationSetting)) 37 | static int32 ApplyRootMotionSource_MoveToForce(UCharacterMovementComponent* MovementComponent, 38 | FName InstanceName, 39 | FVector StartLocation, 40 | FVector TargetLocation, 41 | float Duration, 42 | int32 Priority, 43 | UCurveVector* PathOffsetCurve = nullptr, 44 | FRMSRotationSetting RotationSetting = {}, 45 | float StartTime = 0, 46 | ERMSApplyMode ApplyMode = 47 | ERMSApplyMode::None, 48 | FRMSSetting_Move ExtraSetting = {}); 49 | //通过高度和距离适配一个抛物线跳跃运动 50 | UFUNCTION(BlueprintCallable, Category="RMS", 51 | meta = (AdvancedDisplay = "7", AutoCreateRefTerm = "ExtraSetting", CPP_Default_ExtraSetting)) 52 | static int32 ApplyRootMotionSource_JumpForce(UCharacterMovementComponent* MovementComponent, 53 | FName InstanceName, 54 | FRotator Rotation, 55 | float Duration, 56 | float Distance, 57 | float Height, 58 | int32 Priority, 59 | UCurveVector* PathOffsetCurve = nullptr, 60 | UCurveFloat* TimeMappingCurve = nullptr, 61 | float StartTime = 0, 62 | ERMSApplyMode ApplyMode = 63 | ERMSApplyMode::None, 64 | FRMSSetting_Jump ExtraSetting = {}); 65 | 66 | //通过高度和距离适配一个抛物线跳跃运动 67 | UFUNCTION(BlueprintCallable, Category="RMS", 68 | meta = (AdvancedDisplay = "7", AutoCreateRefTerm = "ExtraSetting", CPP_Default_ExtraSetting)) 69 | static int32 ApplyRootMotionSource_JumpForce_WithPoints(UCharacterMovementComponent* MovementComponent, 70 | FName InstanceName, 71 | FRotator StartRotation, 72 | float Duration, 73 | FVector StartLocation, 74 | FVector TargetLocation, 75 | FVector HalfWayLocation, 76 | int32 Priority, 77 | FRMSRotationSetting RotationSetting, 78 | UCurveFloat* TimeMappingCurve = nullptr, 79 | float StartTime = 0, 80 | ERMSApplyMode ApplyMode = 81 | ERMSApplyMode::None, 82 | FRMSSetting_Jump ExtraSetting = {}); 83 | 84 | 85 | 86 | /** 87 | * 移动到一个动态目标, 需要通过UpdateDynamicMoveToTarget设置目标 88 | * @param StartLocation 角色会基于此开始移动,所以请确保是Actor当前的Location 89 | * @param TargetLocation 参考StartLocation的目标位置(要考虑HalfHeight) 90 | * 91 | */ 92 | UFUNCTION(BlueprintCallable, Category="RMS", 93 | meta = (AdvancedDisplay = "6", AutoCreateRefTerm = "ExtraSetting", CPP_Default_ExtraSetting, 94 | CPP_Default_RotationSetting)) 95 | static int32 ApplyRootMotionSource_DynamicMoveToForce(UCharacterMovementComponent* MovementComponent, 96 | FName InstanceName, 97 | FVector StartLocation, 98 | FVector TargetLocation, 99 | float Duration, 100 | int32 Priority, 101 | UCurveVector* PathOffsetCurve = nullptr, 102 | UCurveFloat* TimeMappingCurve = nullptr, 103 | FRMSRotationSetting RotationSetting = {}, 104 | float StartTime = 0, 105 | ERMSApplyMode ApplyMode = 106 | ERMSApplyMode::None, 107 | FRMSSetting_Move ExtraSetting = {}); 108 | 109 | /** 110 | * 抛物线的形式移动到一个点, 通过一个曲线来设定运动轨迹 111 | * ParabolaCurve X轴定义时间曲线, Z轴定义抛物线形态曲线 112 | * @param StartLocation 角色会基于此开始移动,所以请确保是Actor当前的Location 113 | * @param TargetLocation 参考StartLocation的目标位置(要考虑HalfHeight) 114 | * @param ParabolaCurve 定义抛物线形态 115 | * @param RotationSetting 旋转设置, 具体看每个参数自己的注释 116 | * @param ApplyMode RMS的应用模式, 默认是队列, 即存在同名RMS的情况下会在后台执行直到前一个运行完 117 | * 118 | */ 119 | UFUNCTION(BlueprintCallable, Category="RMS", 120 | meta = (AdvancedDisplay = "6", AutoCreateRefTerm = "ExtraSetting", CPP_Default_ExtraSetting, 121 | CPP_Default_RotationSetting)) 122 | static int32 ApplyRootMotionSource_MoveToForce_Parabola(UCharacterMovementComponent* MovementComponent, 123 | FName InstanceName, 124 | FVector StartLocation, 125 | FVector TargetLocation, 126 | float Duration, 127 | int32 Priority, 128 | UCurveFloat* ParabolaCurve = nullptr, 129 | UCurveFloat* TimeMappingCurve = nullptr, 130 | FRMSRotationSetting RotationSetting = {}, 131 | int32 Segment = 8, 132 | float StartTime = 0, 133 | ERMSApplyMode ApplyMode = 134 | ERMSApplyMode::None, 135 | FRMSSetting_Move ExtraSetting = {}); 136 | 137 | 138 | /** 139 | * 多路径版本的MoveToForce 140 | * 141 | */ 142 | UFUNCTION(BlueprintCallable, Category="RMS", 143 | meta = (AdvancedDisplay = "6", AutoCreateRefTerm = "ExtraSetting", CPP_Default_ExtraSetting)) 144 | static int32 ApplyRootMotionSource_PathMoveToForce(UCharacterMovementComponent* MovementComponent, 145 | FName InstanceName, 146 | FVector StartLocation, 147 | FRotator StartRotation, 148 | TArray Path, 149 | int32 Priority, 150 | float StartTime = 0, 151 | ERMSApplyMode ApplyMode = 152 | ERMSApplyMode::None, 153 | FRMSSetting_Move ExtraSetting = {}); 154 | 155 | /** 156 | * 按照路径点的曲线平滑移动 157 | * 158 | */ 159 | UFUNCTION(BlueprintCallable, Category="RMS", 160 | meta = (AdvancedDisplay = "6", AutoCreateRefTerm = "ExtraSetting", CPP_Default_ExtraSetting,CPP_Default_RotationSetting)) 161 | static int32 ApplyRootMotionSource_PathMoveToForce_V2(UCharacterMovementComponent* MovementComponent, 162 | FName InstanceName, 163 | FRotator StartRotation, 164 | TArray Path, 165 | int32 Priority, 166 | float StartTime = 0, 167 | float Duration = 1.0f, 168 | FRMSRotationSetting RotationSetting = {}, 169 | ERMSApplyMode ApplyMode = 170 | ERMSApplyMode::None, 171 | FRMSSetting_Move ExtraSetting = {}, 172 | ERichCurveTangentMode TangentMode = ERichCurveTangentMode::RCTM_Auto, 173 | ERichCurveInterpMode InterpMode = ERichCurveInterpMode::RCIM_Cubic); 174 | 175 | 176 | 177 | #pragma region Animation 178 | 179 | static bool ApplyRootMotionSource_SimpleAnimation_BM(UCharacterMovementComponent* MovementComponent, 180 | UAnimSequence* DataAnimation, 181 | FName InstanceName, 182 | int32 Priority, 183 | float StartTime = 0, 184 | float EndTime = -1, 185 | float Rate = 1, 186 | bool bIgnoreZAxis = false, 187 | ERMSApplyMode ApplyMode = 188 | ERMSApplyMode::None); 189 | 190 | /** 191 | * 直接使用动画的RootMotion数据,效果等同于播放RootMotion蒙太奇动画 192 | * @param DataAnimation 参考RootMotion数据的动画, 该节点本身不负责播放动画 193 | * @param EndTime 播放的结束时间, 如果小于0,那么就使用最终的动画长度 194 | * @param bIgnoreZAxis 忽略Z方向的RootMotion 195 | * @param ApplyMode RMS的应用模式, 默认是队列, 即存在同名RMS的情况下会在后台执行直到前一个运行完 196 | */ 197 | UFUNCTION(BlueprintCallable, Category="RMS|Animation", meta = (AdvancedDisplay = "3")) 198 | static bool ApplyRootMotionSource_SimpleAnimation(UCharacterMovementComponent* MovementComponent, 199 | UAnimSequence* DataAnimation, 200 | FName InstanceName, 201 | int32 Priority, 202 | float StartTime = 0, 203 | float EndTime = -1, 204 | float Rate = 1, 205 | bool bIgnoreZAxis = false, 206 | bool bUseForwardCalculation = false, 207 | ERMSApplyMode ApplyMode = 208 | ERMSApplyMode::None); 209 | 210 | 211 | static bool ApplyRootMotionSource_AnimationAdjustment_BM(UCharacterMovementComponent* MovementComponent, 212 | UAnimSequence* DataAnimation, 213 | FName InstanceName, 214 | int32 Priority, 215 | FVector TargetLocation, 216 | bool bLocalTarget, 217 | bool bTargetBasedOnFoot = true, 218 | float StartTime = 0, 219 | float EndTime = -1, 220 | float Rate = 1.0, 221 | FRMSRotationSetting RotationSetting = {}, 222 | ERMSApplyMode ApplyMode = ERMSApplyMode::None); 223 | 224 | /** 225 | * 依据动画RootMotion数据适配目标点的运动,效果类似MotionWarping, 只需要设置一个最终的目标位置 226 | * <请确认位置是基于脚底还是角色中心> 227 | * @param DataAnimation 参考RootMotion数据的动画, 该节点本身不负责播放动画 228 | * @param bLocalTarget 如果为true,那么偏移信息是本地空间的 229 | * @param bTargetBasedOnFoot 目标位置是基于脚底还是胶囊体中心 230 | * @param StartTime 开始时间, 即动画数据计算的开始时间 231 | * @param EndTime 动画数据计算结束的时间, 小于0意味着使用整个动画时长 232 | * @param Rate 动画速率,同时也会影响整个RMS的速度,如动画时长1秒, Rate=2, 那么整个RMS时长就是0.5秒 233 | * @param TargetLocation 最终的目标位置, 整个RootMotion运动会去适配这个位置 234 | * @param RotationSetting 旋转设置, 具体看每个参数自己的注释 235 | * @param bUseForwardCalculation 前向计算, 会把数据提前烘培到Curve上, Runtime开销会减少, 但是精确度略低 236 | * @param ApplyMode RMS的应用模式, 默认是队列, 即存在同名RMS的情况下会在后台执行直到前一个运行完 237 | */ 238 | UFUNCTION(BlueprintCallable, Category="RMS|Animation", meta = (AdvancedDisplay = "6", CPP_Default_RotationSetting)) 239 | static bool ApplyRootMotionSource_AnimationAdjustment(UCharacterMovementComponent* MovementComponent, 240 | UAnimSequence* DataAnimation, 241 | FName InstanceName, 242 | int32 Priority, 243 | FVector TargetLocation, 244 | bool bLocalTarget, 245 | bool bTargetBasedOnFoot = true, 246 | float StartTime = 0, 247 | float EndTime = -1.0, 248 | float Rate = 1.0, 249 | FRMSRotationSetting RotationSetting = {}, 250 | bool bUseForwardCalculation = false, 251 | ERMSApplyMode ApplyMode = 252 | ERMSApplyMode::None); 253 | 254 | 255 | /** 256 | * 需要配置动画通知窗口, 通过WarpingTarget配置对应窗口的目标点信息,做到分阶段的运动适配,类似MotionWarping 257 | * WarpingTarget数据需要与动画通知严格匹配, 否则可能出错 258 | * 前向计算, 把数据烘培到曲线上, 对比ApplyRootMotionSource_AnimationWarping(), 此方式精确度差一点, 适合稍微简单点的运动 259 | * 注意: 没有旋转 260 | * 261 | * <请确认位置是基于脚底还是角色中心> 262 | * @param DataAnimation 参考RootMotion数据的动画, 该节点本身不负责播放动画 263 | * @param WarpingTarget 需要与动画通知严格匹配 264 | * @param Tolerance 允许动画通知窗口之间的公差, 小于此值即忽略不计 265 | * @param AnimWarpingScale 动画信息的缩放, 如果是0代表使用线性位移 266 | * @param bExcludeEndAnimMotion 排除末尾的动画位移 267 | * @param bTargetBasedOnFoot 位置是基于脚底还是胶囊体中心 268 | */ 269 | UFUNCTION(BlueprintCallable, Category="RMS|Animation", meta = (AdvancedDisplay = "4")) 270 | static bool ApplyRootMotionSource_AnimationWarping_ForwardCalculation( 271 | UCharacterMovementComponent* MovementComponent, 272 | UAnimSequence* DataAnimation, 273 | TMap WarpingTarget, FName InstanceName, 274 | int32 Priority, 275 | bool bTargetBasedOnFoot = true, float Rate = 1, 276 | float Tolerance = 0.01, float AnimWarpingScale = 1.0, 277 | bool bExcludeEndAnimMotion = false, 278 | ERMSAnimWarpingAxis WarpingAxis = 279 | ERMSAnimWarpingAxis::XYZ, 280 | ERMSApplyMode ApplyMode = 281 | ERMSApplyMode::None); 282 | 283 | /** 284 | ** 285 | * 需要配置动画通知窗口, 通过WarpingTarget配置对应窗口的目标点信息,做到分阶段的运动适配,类似MotionWarping 286 | * <注意: 此RMS是基于当前实时位置计算, 所以重新调用新的同类RMS只会从当前实时位置继续执行而非跳转到之前的开始位置> 287 | * <请确认位置是基于脚底还是角色中心> 288 | * @param DataAnimation 参考RootMotion数据的动画, 该节点本身不负责播放动画 289 | * @param WarpingTarget 需要与动画通知严格匹配 290 | * @param Tolerance 允许动画通知窗口之间的公差, 小于此值即忽略不计 291 | * @param StartTime 开始时间, 即动画数据计算的开始时间 292 | * @param Rate 动画速率,同时也会影响整个RMS的速度,如动画时长1秒, Rate=2, 那么整个RMS时长就是0.5秒 293 | * @param bTargetBasedOnFoot 位置是基于脚底还是胶囊体中心 294 | */ 295 | UFUNCTION(BlueprintCallable, Category="RMS|Animation", meta = (AdvancedDisplay = "4")) 296 | static bool ApplyRootMotionSource_AnimationWarping(UCharacterMovementComponent* MovementComponent, 297 | UAnimSequence* DataAnimation, 298 | TMap WarpingTarget, 299 | float StartTime, 300 | FName InstanceName, 301 | int32 Priority, 302 | bool bTargetBasedOnFoot = true, 303 | float Rate = 1, 304 | float Tolerance = 0.01, 305 | bool bExcludeEndAnimMotion = false, 306 | ERMSApplyMode ApplyMode = 307 | ERMSApplyMode::None); 308 | 309 | static bool GetRootMotionSourceWindow(UAnimSequence* DataAnimation, FName InstanceName, 310 | FRMSWindowData& Window); 311 | static bool GetRootMotionSourceWindows(UAnimSequence* DataAnimation, TArray& Windows); 312 | static bool GetRootMotionSourceWindowsByInstanceList(UAnimSequence* DataAnimation, TArray Instances, 313 | TArray& Windows); 314 | 315 | static bool FindTriggerDataByTime(const TArray& TriggerData, float Time, 316 | FRMSNotifyTriggerData& OutData); 317 | 318 | UFUNCTION(BlueprintCallable, BlueprintPure, Category="RMS", meta = (AdvancedDisplay = "7")) 319 | static FTransform ExtractRootMotion(UAnimSequenceBase* Anim, float StartTime, float EndTime); 320 | 321 | #pragma endregion Animation 322 | 323 | //模拟力的RootMotion效果,类似AddForce 324 | UFUNCTION(BlueprintCallable, Category="RMS", meta = (AdvancedDisplay = "7")) 325 | static int32 ApplyRootMotionSource_ConstantForece(UCharacterMovementComponent* MovementComponent, 326 | FName InstanceName, ERootMotionAccumulateMode AccumulateMod, 327 | int32 Priority, FVector WorldDirection, float Strength, 328 | UCurveFloat* StrengthOverTime, float Duration, 329 | ERMSFinishVelocityMode VelocityOnFinishMode, 330 | FVector FinishSetVelocity, float FinishClampVelocity = 0, 331 | bool bEnableGravity = false, 332 | ERMSApplyMode ApplyMode = 333 | ERMSApplyMode::None); 334 | //模拟范围力的RootMotion效果, 类似AddRadialForce 335 | UFUNCTION(BlueprintCallable, Category="RMS", meta = (AdvancedDisplay = "7")) 336 | static int32 ApplyRootMotionSource_RadialForece(UCharacterMovementComponent* MovementComponent, FName InstanceName, 337 | ERootMotionAccumulateMode AccumulateMod, int32 Priority, 338 | AActor* LocationActor, FVector Location, float Strength, 339 | float Radius, 340 | bool bNoZForce, UCurveFloat* StrengthDistanceFalloff, 341 | UCurveFloat* StrengthOverTime, bool bIsPush, float Duration, 342 | bool bUseFixedWorldDirection, FRotator FixedWorldDirection, 343 | ERMSFinishVelocityMode VelocityOnFinishMode, 344 | FVector FinishSetVelocity, float FinishClampVelocity = 0, 345 | ERMSApplyMode ApplyMode = 346 | ERMSApplyMode::None); 347 | 348 | //刷新动态目标的位置 349 | UFUNCTION(BlueprintCallable, Category="RMS", meta = (AdvancedDisplay = "7")) 350 | static void UpdateDynamicMoveToTarget(UCharacterMovementComponent* MovementComponent, FName InstanceName, 351 | FVector NewTarget); 352 | /* 353 | * 刷新DynamicMoveTo的持续时间 354 | * 新的时间不能小于当前已经运行的时间 355 | */ 356 | UFUNCTION(BlueprintCallable, Category="RMS", meta = (AdvancedDisplay = "7")) 357 | static void UpdateDynamicMoveDuration(UCharacterMovementComponent* MovementComponent, FName InstanceName, 358 | float NewDuration); 359 | 360 | //移除RMS 361 | UFUNCTION(BlueprintCallable, Category="RMS", meta = (AdvancedDisplay = "7")) 362 | static void RemoveRootMotionSource(UCharacterMovementComponent* MovementComponent, FName InstanceName); 363 | 364 | //获取RMS的时间信息 365 | UFUNCTION(BlueprintCallable, BlueprintPure, Category="RMS", meta = (AdvancedDisplay = "7")) 366 | static void GetCurrentRootMotionSourceTime(UCharacterMovementComponent* MovementComponent, FName InstanceName, 367 | float& CurrentTime, float& Duration); 368 | 369 | UFUNCTION(BlueprintCallable, BlueprintPure, Category="RMS", meta = (AdvancedDisplay = "7"), 370 | BlueprintPure) 371 | static bool IsRootMotionSourceValid(UCharacterMovementComponent* MovementComponent, FName InstanceName); 372 | UFUNCTION(BlueprintCallable, BlueprintPure, Category="RMS", meta = (AdvancedDisplay = "7"), 373 | BlueprintPure) 374 | static bool IsRootMotionSourceIdValid(UCharacterMovementComponent* MovementComponent, int32 ID); 375 | 376 | static TSharedPtr GetRootMotionSource(UCharacterMovementComponent* MovementComponent, 377 | FName InstanceName); 378 | static TSharedPtr GetDynamicMoveToRootMotionSource( 379 | UCharacterMovementComponent* MovementComponent, FName InstanceName); 380 | 381 | static TSharedPtr GetRootMotionSourceByID(UCharacterMovementComponent* MovementComponent, 382 | int32 ID); 383 | 384 | static void CalcAnimWarpingScale(FVector& OriginOffset, ERMSAnimWarpingType Type, 385 | FVector AnimRootMotionLinear, FVector RMSTargetLinearOffset, float Scale = 1, 386 | float Tolerance = 0.1); 387 | 388 | static void ConvWorldOffsetToRmsSpace(FVector& Offset, FVector Start, FVector Target); 389 | 390 | static void FiltAnimCurveOffsetAxisData(FVector& AnimOffset, ERMSAnimWarpingAxis Axis); 391 | 392 | static int32 CalcPriorityByApplyMode(UCharacterMovementComponent* MovementComponent, FName PendingInstanceName, 393 | int32 PendingPriorioty, ERMSApplyMode ApplyMode); 394 | 395 | static bool ExtractRotation(FRotator& OutRotation, const ACharacter& Character, FRotator StartRotation, 396 | FRotator TargetRotation, float Fraction, UCurveFloat* RotationCurve = nullptr); 397 | 398 | static float EvaluateFloatCurveAtFraction(const UCurveFloat& Curve, const float Fraction); 399 | static FVector EvaluateVectorCurveAtFraction(const UCurveVector& Curve, const float Fraction); 400 | /* 401 | * 根据时间预测正在运行的RMS的实时位置 402 | */ 403 | UFUNCTION(BlueprintCallable, Category="RMS", BlueprintPure) 404 | static bool PredictRootMotionSourceLocation_Runtime(UCharacterMovementComponent* MovementComponent 405 | , FName InstanceName 406 | , float CurrentTime 407 | , FVector& OutLocation); 408 | /** 409 | * 根据时间预测MoveTo的位置 410 | * <位置是角色中心> 411 | */ 412 | UFUNCTION(BlueprintCallable, Category="RMS", BlueprintPure) 413 | static bool PredictRootMotionSourceLocation_MoveTo(FVector& OutLocation, 414 | UCharacterMovementComponent* MovementComponent, 415 | FVector StartLocation, FVector TargetLocation, 416 | float Duration, 417 | float CurrentTime, UCurveVector* PathOffsetCurve = nullptr); 418 | /** 419 | * 根据时间预测Jump的位置 420 | * <位置是角色中心> 421 | */ 422 | UFUNCTION(BlueprintCallable, Category="RMS", BlueprintPure) 423 | static bool PredictRootMotionSourceLocation_Jump(FVector& OutLocation, 424 | UCharacterMovementComponent* MovementComponent, 425 | FVector StartLocation, float Distance, float Height, 426 | FRotator Rotation, 427 | float Duration, float CurrentTime, 428 | UCurveVector* PathOffsetCurve = nullptr, 429 | UCurveFloat* TimeMappingCurve = nullptr); 430 | /** 431 | * 根据时间预测位置 432 | * <位置是角色中心> 433 | */ 434 | UFUNCTION(BlueprintCallable, Category="RMS", BlueprintPure) 435 | static bool PredictRootMotionSourceLocation_MoveToParabola(FVector& OutLocation, 436 | UCharacterMovementComponent* MovementComponent, 437 | FVector StartLocation, FVector TargetLocation, 438 | float Duration, 439 | float CurrentTime, UCurveFloat* ParabolaCurve = nullptr, 440 | UCurveFloat* TimeMappingCurve = nullptr); 441 | }; 442 | -------------------------------------------------------------------------------- /Source/RMS/Public/RMSModule.h: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | #pragma once 5 | 6 | #include "CoreMinimal.h" 7 | #include "Modules/ModuleManager.h" 8 | 9 | class FRMSModule : public IModuleInterface 10 | { 11 | public: 12 | 13 | /** IModuleInterface implementation */ 14 | virtual void StartupModule() override; 15 | virtual void ShutdownModule() override; 16 | }; 17 | -------------------------------------------------------------------------------- /Source/RMS/Public/RMSTypes.h: -------------------------------------------------------------------------------- 1 | // Copyright. VJ All Rights Reserved. 2 | // https://supervj.top/2022/03/24/RootMotionSource/ 3 | 4 | #pragma once 5 | 6 | #include "CoreMinimal.h" 7 | #include "GameFramework/CharacterMovementComponent.h" 8 | #include "RMSTypes.generated.h" 9 | 10 | namespace RMS 11 | { 12 | RMS_API extern TAutoConsoleVariable CVarRMS_Debug; 13 | } 14 | 15 | 16 | UENUM(BlueprintType) 17 | enum class ERMSApplyMode :uint8 18 | { 19 | //不做处理 20 | None = 0, 21 | //取代同名RMS, 会先把同名RMS移除掉 22 | Replace, 23 | //用同名的RMS优先级+1应用 24 | ApplyHigherPriority, 25 | //如果有同名的RMS,那就取消应用 26 | Block, 27 | //排队 28 | Queue 29 | }; 30 | 31 | UENUM(BlueprintType) 32 | enum class ERMSAnimWarpingAxis :uint8 33 | { 34 | None = 0, 35 | X, 36 | Y, 37 | Z, 38 | XY, 39 | XZ, 40 | YZ, 41 | XYZ 42 | }; 43 | 44 | UENUM(BlueprintType) 45 | enum class ERMSAnimWarpingType :uint8 46 | { 47 | // compared with total length 48 | BasedOnLength = 0, 49 | // compared with 3 axis 50 | BasedOn3Axis, 51 | }; 52 | 53 | /** 54 | * 55 | */ 56 | UENUM(BlueprintType) 57 | enum class ERMSFinishVelocityMode :uint8 58 | { 59 | // Maintain the last velocity root motion gave to the character 60 | MaintainLastRootMotionVelocity = 0, 61 | // Set Velocity to the specified value (for example, 0,0,0 to stop the character) 62 | SetVelocity, 63 | // Clamp velocity magnitude to the specified value. Note that it will not clamp Z if negative (falling). it will clamp Z positive though. 64 | ClampVelocity, 65 | }; 66 | 67 | UENUM(BlueprintType) 68 | enum class ERMSSourceSettingsFlags : uint8 69 | { 70 | None, 71 | // Source will switch character to Falling mode with any "Z up" velocity added. 72 | // Use this for jump-like root motion. If not enabled, uses default jump impulse 73 | // detection (which keeps you stuck on ground in Walking fairly strongly) 74 | UseSensitiveLiftoffCheck, 75 | // If Duration of Source would end partway through the last tick it is active, 76 | // do not reduce SimulationTime. Disabling this is useful for sources that 77 | // are more about providing velocity (like jumps), vs. sources that need 78 | // the precision of partial ticks for say ending up at an exact location (MoveTo) 79 | DisablePartialEndTick, 80 | // Whether to ignore impact to Z when accumulating output to Velocity 81 | // Setting this flag on override sources provides the same behavior as 82 | // animation root motion 83 | IgnoreZAccumulate 84 | }; 85 | 86 | 87 | UENUM(BlueprintType) 88 | enum class ERMSRotationMode : uint8 89 | { 90 | None, 91 | FaceToTarget, 92 | Custom 93 | }; 94 | 95 | USTRUCT(BlueprintType) 96 | struct FRMSRotationSetting 97 | { 98 | GENERATED_BODY() 99 | public: 100 | friend FArchive& operator <<(FArchive& Ar, FRMSRotationSetting& D) 101 | { 102 | return Ar << D.Mode << D.WarpMultiplier << D.TargetRotation; 103 | } 104 | 105 | bool operator==(const FRMSRotationSetting& Other) const 106 | { 107 | return Mode == Other.Mode && Curve == Other.Curve && WarpMultiplier == Other.WarpMultiplier && 108 | TargetRotation == Other.TargetRotation; 109 | } 110 | 111 | bool operator!=(const FRMSRotationSetting& Other) const 112 | { 113 | return !(*this == Other); 114 | } 115 | 116 | FORCEINLINE bool IsWarpRotation() const 117 | { 118 | return Mode != ERMSRotationMode::None; 119 | } 120 | 121 | //旋转模式, 如果是None就没有旋转 122 | UPROPERTY(BlueprintReadWrite) 123 | ERMSRotationMode Mode = ERMSRotationMode::None; 124 | //旋转的时间对应的百分比曲线 125 | UPROPERTY(BlueprintReadWrite) 126 | UCurveFloat* Curve = nullptr; 127 | //旋转速度加成,如果是1那么整个旋转过程是从Start点到Target点, 一般与曲线只用其中一个即可 128 | UPROPERTY(BlueprintReadWrite) 129 | float WarpMultiplier = 1.0f; 130 | //如果RotationMode == Custom, 那么此Rotator就是最终的目标旋转量 131 | UPROPERTY(BlueprintReadWrite) 132 | FRotator TargetRotation = FRotator::ZeroRotator; 133 | }; 134 | 135 | 136 | USTRUCT(BlueprintType) 137 | struct FRMSPathMoveToData 138 | { 139 | GENERATED_BODY() 140 | public: 141 | UPROPERTY(BlueprintReadWrite) 142 | FVector Target = FVector::ZeroVector; 143 | UPROPERTY(BlueprintReadWrite) 144 | float Duration = 0; 145 | UPROPERTY(BlueprintReadWrite) 146 | TObjectPtr PathOffsetCurve = nullptr; 147 | UPROPERTY(BlueprintReadWrite) 148 | TObjectPtr TimeMappingCurve = nullptr; 149 | 150 | UPROPERTY(BlueprintReadWrite) 151 | FRMSRotationSetting RotationSetting; 152 | 153 | friend FArchive& operator <<(FArchive& Ar, FRMSPathMoveToData& D) 154 | { 155 | return Ar << D.Duration << D.Target << D.PathOffsetCurve << D.TimeMappingCurve << D.RotationSetting; 156 | } 157 | 158 | bool operator==(const FRMSPathMoveToData& Other) const 159 | { 160 | return Target == Other.Target && Duration == Other.Duration && PathOffsetCurve == Other.PathOffsetCurve && 161 | TimeMappingCurve == Other.TimeMappingCurve && RotationSetting == Other.RotationSetting; 162 | } 163 | 164 | bool operator!=(const FRMSPathMoveToData& Other) const 165 | { 166 | return !(*this == Other); 167 | } 168 | 169 | bool IsValid() const 170 | { 171 | return Duration > 0; 172 | } 173 | }; 174 | 175 | 176 | USTRUCT(BlueprintType) 177 | struct FRMSSetting 178 | { 179 | GENERATED_BODY() 180 | public: 181 | UPROPERTY(BlueprintReadWrite) 182 | ERootMotionAccumulateMode AccumulateMod = ERootMotionAccumulateMode::Override; 183 | UPROPERTY(BlueprintReadWrite) 184 | ERMSFinishVelocityMode VelocityOnFinishMode = ERMSFinishVelocityMode::ClampVelocity; 185 | UPROPERTY(BlueprintReadWrite) 186 | FVector FinishSetVelocity = FVector::ZeroVector; 187 | UPROPERTY(BlueprintReadWrite) 188 | float FinishClampVelocity = 20; 189 | }; 190 | 191 | USTRUCT(BlueprintType) 192 | struct FRMSSetting_Move : public FRMSSetting 193 | { 194 | GENERATED_BODY() 195 | public: 196 | UPROPERTY(BlueprintReadWrite) 197 | bool bRestrictSpeedToExpected = false; 198 | UPROPERTY(BlueprintReadWrite) 199 | ERMSSourceSettingsFlags SourcesSetting = ERMSSourceSettingsFlags::UseSensitiveLiftoffCheck; 200 | }; 201 | 202 | USTRUCT(BlueprintType) 203 | struct FRMSSetting_Jump : public FRMSSetting 204 | { 205 | GENERATED_BODY() 206 | public: 207 | UPROPERTY(BlueprintReadWrite) 208 | float MinimumLandedTriggerTime = 0; 209 | UPROPERTY(BlueprintReadWrite) 210 | bool bFinishOnLanded = false; 211 | }; 212 | 213 | USTRUCT(BlueprintType) 214 | struct FRMSAnimWarppingConfig 215 | { 216 | GENERATED_BODY() 217 | public: 218 | UPROPERTY(BlueprintReadWrite) 219 | FVector TargetLocation = FVector::ZeroVector; 220 | UPROPERTY(BlueprintReadWrite) 221 | FRMSRotationSetting RotationSetting; 222 | }; 223 | 224 | 225 | USTRUCT(BlueprintType) 226 | struct FRMSWindowData 227 | { 228 | GENERATED_BODY() 229 | 230 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Defaults") 231 | TObjectPtr AnimNotify = nullptr; 232 | 233 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Defaults") 234 | float StartTime = 0.f; 235 | 236 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Defaults") 237 | float EndTime = 0.f; 238 | 239 | 240 | FORCEINLINE bool IsValid() const 241 | { 242 | return AnimNotify != nullptr; 243 | } 244 | }; 245 | 246 | USTRUCT(BlueprintType) 247 | struct FRMSNotifyTriggerData 248 | { 249 | GENERATED_BODY() 250 | 251 | 252 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Defaults") 253 | FRMSWindowData WindowData; 254 | 255 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Defaults") 256 | FVector Target = FVector::ZeroVector; 257 | 258 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Defaults") 259 | bool bHasTarget = false; 260 | 261 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Defaults") 262 | FRMSRotationSetting RotationSetting; 263 | 264 | bool operator ==(const FRMSNotifyTriggerData& other) const 265 | { 266 | return WindowData.AnimNotify == other.WindowData.AnimNotify && 267 | WindowData.EndTime == other.WindowData.EndTime && 268 | WindowData.StartTime == other.WindowData.StartTime && 269 | Target == other.Target && 270 | bHasTarget == other.bHasTarget && 271 | RotationSetting == other.RotationSetting; 272 | } 273 | 274 | bool operator !=(const FRMSNotifyTriggerData& other) const 275 | { 276 | return !((*this) == other); 277 | } 278 | 279 | FORCEINLINE void Reset() 280 | { 281 | WindowData.AnimNotify = nullptr; 282 | WindowData.EndTime = 0; 283 | WindowData.StartTime = 0; 284 | Target = FVector::ZeroVector; 285 | bHasTarget = false; 286 | RotationSetting = FRMSRotationSetting(); 287 | } 288 | }; 289 | 290 | 291 | USTRUCT(BlueprintType) 292 | struct FRMSTarget 293 | { 294 | GENERATED_BODY() 295 | FRMSTarget() 296 | { 297 | } 298 | 299 | virtual ~FRMSTarget() 300 | { 301 | } 302 | 303 | friend FArchive& operator <<(FArchive& Ar, FRMSTarget& D) 304 | { 305 | return Ar << D.Target << D.StartTime << D.EndTime; 306 | } 307 | 308 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Defaults") 309 | float StartTime = 0; 310 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Defaults") 311 | float EndTime = 0; 312 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Defaults") 313 | FVector Target = FVector::ZeroVector; 314 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Defaults") 315 | FRMSRotationSetting RotationSetting; 316 | 317 | virtual bool operator ==(const FRMSTarget& other) const 318 | { 319 | return 320 | StartTime == other.StartTime && 321 | EndTime == other.EndTime && 322 | Target == other.Target && 323 | RotationSetting == other.RotationSetting; 324 | } 325 | 326 | virtual bool operator !=(const FRMSTarget& other) const 327 | { 328 | return !((*this) == other); 329 | } 330 | 331 | FORCEINLINE void Reset() 332 | { 333 | EndTime = 0; 334 | StartTime = 0; 335 | Target = FVector::ZeroVector; 336 | RotationSetting = FRMSRotationSetting(); 337 | } 338 | }; -------------------------------------------------------------------------------- /Source/RMS/RMS.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class RMS : ModuleRules 6 | { 7 | public RMS(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicIncludePaths.AddRange( 12 | new string[] { 13 | // ... add public include paths required here ... 14 | } 15 | ); 16 | 17 | 18 | PrivateIncludePaths.AddRange( 19 | new string[] { 20 | // ... add other private include paths required here ... 21 | } 22 | ); 23 | 24 | 25 | PublicDependencyModuleNames.AddRange( 26 | new string[] 27 | { 28 | "Core", "Engine", 29 | // ... add other public dependencies that you statically link with here ... 30 | } 31 | ); 32 | 33 | 34 | PrivateDependencyModuleNames.AddRange( 35 | new string[] 36 | { 37 | "CoreUObject", 38 | "Engine", 39 | "Slate", 40 | "SlateCore", 41 | "GameplayTasks" 42 | // ... add private dependencies that you statically link with here ... 43 | } 44 | ); 45 | 46 | 47 | DynamicallyLoadedModuleNames.AddRange( 48 | new string[] 49 | { 50 | // ... add any modules that your module loads dynamically here ... 51 | } 52 | ); 53 | } 54 | } 55 | --------------------------------------------------------------------------------