A Demo Project for the UnrealEngineSDK
Loading...
Searching...
No Matches
VRBaseCharacterMovementComponent.cpp
Go to the documentation of this file.
1// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
2
3/*=============================================================================
4 Movement.cpp: Character movement implementation
5
6=============================================================================*/
7
9#include "VRBPDatatypes.h"
12#include "VRBaseCharacter.h"
13#include "VRRootComponent.h"
14#include "VRPlayerController.h"
15#include "GameFramework/PhysicsVolume.h"
16
17DEFINE_LOG_CATEGORY(LogVRBaseCharacterMovement);
18
20 : Super(ObjectInitializer)
21{
22 PrimaryComponentTick.TickGroup = TG_PrePhysics;
23
24
25 //#TODO: Might not be ready to make this change globally yet....
26
27 // Set Acceleration and braking deceleration walking to high values to avoid ramp up on speed
28 // Realized that I wasn't doing this here for people to default to no acceleration.
29 /*this->bRequestedMoveUseAcceleration = false;
30 this->MaxAcceleration = 200048.0f;
31 this->BrakingDecelerationWalking = 200048.0f;
32 */
33
34 AdditionalVRInputVector = FVector::ZeroVector;
35 CustomVRInputVector = FVector::ZeroVector;
36 TrackingLossThreshold = 6000.f;
38 bHadExtremeInput = false;
40
46
50
52
53 VRWallSlideScaler = 1.0f;
56
57 VREdgeRejectDistance = 0.01f; // Rounded minimum of root movement
58
60
61 NetworkSmoothingMode = ENetworkSmoothingMode::Exponential;
62
63 bWasInPushBack = false;
64 bIsInPushBack = false;
65
67
68 // Allow merging dual movements, generally this is wanted for the perf increase
69 bEnableServerDualMoveScopedMovementUpdates = true;
70
71 bNotifyTeleported = false;
72
73 bJustUnseated = false;
74
78
79 SetNetworkMoveDataContainer(VRNetworkMoveDataContainer);
80 SetMoveResponseDataContainer(VRMoveResponseDataContainer);
81}
82
83void UVRBaseCharacterMovementComponent::OnMovementModeChanged(EMovementMode PreviousMovementMode, uint8 PreviousCustomMode)
84{
85 if (!HasValidData())
86 {
87 return;
88 }
89
90 // Clear out the old custom input vector, it will pollute the pool now that all modes allow it.
91 CustomVRInputVector = FVector::ZeroVector;
92
93 if (PreviousMovementMode == EMovementMode::MOVE_Custom && PreviousCustomMode == (uint8)EVRCustomMovementMode::VRMOVE_Seated)
94 {
95 if (MovementMode != EMovementMode::MOVE_Custom || CustomMovementMode != (uint8)EVRCustomMovementMode::VRMOVE_Seated)
96 {
97 if (AVRBaseCharacter * BaseOwner = Cast<AVRBaseCharacter>(CharacterOwner))
98 {
99 BaseOwner->InitSeatedModeTransition();
100 }
101 }
102 }
103
104 if (MovementMode == EMovementMode::MOVE_Custom)
105 {
106 if (CustomMovementMode == (uint8)EVRCustomMovementMode::VRMOVE_Climbing || CustomMovementMode == (uint8)EVRCustomMovementMode::VRMOVE_Seated)
107 {
108 // Kill velocity and clear queued up events
109 StopMovementKeepPathing();
110 CharacterOwner->ResetJumpState();
111 ClearAccumulatedForces();
112
113 if (CustomMovementMode == (uint8)EVRCustomMovementMode::VRMOVE_Seated)
114 {
115 if (AVRBaseCharacter * BaseOwner = Cast<AVRBaseCharacter>(CharacterOwner))
116 {
117 BaseOwner->InitSeatedModeTransition();
118 }
119 }
120 }
121 }
122
123 Super::OnMovementModeChanged(PreviousMovementMode, PreviousCustomMode);
124}
125
127{
128 // Skip force updating position if we are seated.
129 if ((MovementMode == EMovementMode::MOVE_Custom && CustomMovementMode == (uint8)EVRCustomMovementMode::VRMOVE_Seated))
130 {
131 return false;
132 }
133
134 return Super::ForcePositionUpdate(DeltaTime);
135}
136
137void UVRBaseCharacterMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
138{
139
140 // Skip calling into BP if we aren't locally controlled
141 if (CharacterOwner->IsLocallyControlled() && GetReplicatedMovementMode() == EVRConjoinedMovementModes::C_VRMOVE_Climbing)
142 {
143 // Allow the player to run updates on the climb logic for CustomVRInputVector
145 {
147 }
148 }
149
150 // Scope all of the movements, including PRC
151 {
152 UParentRelativeAttachmentComponent* OuterScopePRC = nullptr;
154 {
156 }
157
158 FScopedMovementUpdate ScopedPRCMovementUpdate(OuterScopePRC, EScopedUpdate::DeferredUpdates);
159
160 {
161 UReplicatedVRCameraComponent* OuterScopeCamera = nullptr;
163 {
164 OuterScopeCamera = BaseVRCharacterOwner->VRReplicatedCamera;
165 }
166
167 FScopedMovementUpdate ScopedCameraMovementUpdate(OuterScopeCamera, EScopedUpdate::DeferredUpdates);
168
169 // Scope in the character movements first
170 {
171 // Scope these, they nest with Outer references so it should work fine
172 FVRCharacterScopedMovementUpdate ScopedMovementUpdate(UpdatedComponent, bEnableScopedMovementUpdates ? EScopedUpdate::DeferredUpdates : EScopedUpdate::ImmediateUpdates);
173
174 if (MovementMode == MOVE_Custom && CustomMovementMode == (uint8)EVRCustomMovementMode::VRMOVE_Seated)
175 {
176 const FVector InputVector = ConsumeInputVector();
177 if (!HasValidData() || ShouldSkipUpdate(DeltaTime))
178 {
179 return;
180 }
181
182 // Skip the perform movement logic, run the re-seat logic instead - running base movement component tick instead
183 Super::Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
184
185 // See if we fell out of the world.
186 const bool bIsSimulatingPhysics = UpdatedComponent->IsSimulatingPhysics();
187 if (CharacterOwner->GetLocalRole() == ROLE_Authority && (!bCheatFlying || bIsSimulatingPhysics) && !CharacterOwner->CheckStillInWorld())
188 {
189 return;
190 }
191
192 // If we are the owning client or the server then run the re-basing
193 if (CharacterOwner->GetLocalRole() > ROLE_SimulatedProxy)
194 {
195 // Run offset logic here, the server will update simulated proxies with the movement replication
196 if (AVRBaseCharacter* BaseChar = Cast<AVRBaseCharacter>(CharacterOwner))
197 {
198 BaseChar->TickSeatInformation(DeltaTime);
199 }
200
201 if (CharacterOwner && !CharacterOwner->IsLocallyControlled() && DeltaTime > 0.0f)
202 {
203 // If not playing root motion, tick animations after physics. We do this here to keep events, notifies, states and transitions in sync with client updates.
204 if (!CharacterOwner->bClientUpdating && !CharacterOwner->IsPlayingRootMotion() && CharacterOwner->GetMesh())
205 {
206 TickCharacterPose(DeltaTime);
207 // TODO: SaveBaseLocation() in case tick moves us?
208
209 // Trigger Events right away, as we could be receiving multiple ServerMoves per frame.
210 CharacterOwner->GetMesh()->ConditionallyDispatchQueuedAnimEvents();
211 }
212 }
213
214 }
215 else
216 {
217 if (bNetworkUpdateReceived)
218 {
219 if (bNetworkMovementModeChanged)
220 {
221 ApplyNetworkMovementMode(CharacterOwner->GetReplicatedMovementMode());
222 bNetworkMovementModeChanged = false;
223 }
224 }
225 }
226 }
227 else
228 Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
229
230
231 // This should be valid for both Simulated and owning clients as well as the server
232 // Better here than in perform movement
233 if (UVRRootComponent* VRRoot = Cast<UVRRootComponent>(CharacterOwner->GetCapsuleComponent()))
234 {
235 // If we didn't move the capsule, have it update itself here so the visual and physics representation is correct
236 // We do this specifically to avoid double calling into the render / physics threads.
237 if (!VRRoot->bCalledUpdateTransform)
238 VRRoot->OnUpdateTransform_Public(EUpdateTransformFlags::None, ETeleportType::None);
239 }
240
241 // Make sure these are cleaned out for the next frame
242 AdditionalVRInputVector = FVector::ZeroVector;
243 CustomVRInputVector = FVector::ZeroVector;
244 }
245
246 if (bRunControlRotationInMovementComponent && CharacterOwner->IsLocallyControlled())
247 {
249 {
251 {
252 const AController* OwningController = BaseVRCharacterOwner->GetController();
253 if (OwningController)
254 {
255 const FRotator PawnViewRotation = BaseVRCharacterOwner->GetViewRotation();
256 if (!PawnViewRotation.Equals(BaseVRCharacterOwner->VRReplicatedCamera->GetComponentRotation()))
257 {
258 BaseVRCharacterOwner->VRReplicatedCamera->SetWorldRotation(PawnViewRotation);
259 }
260 }
261 }
262 }
263 }
264
265 // If some of our important components run inside the cmc updates then lets update them now
266 if (OuterScopeCamera)
267 {
268 OuterScopeCamera->UpdateTracking(DeltaTime);
269 }
270
271 if (OuterScopePRC)
272 {
273 OuterScopePRC->UpdateTracking(DeltaTime);
274 }
275 }
276 }
277
279 {
281 {
283 bNotifyTeleported = false;
284 }
285 }
286}
287
289{
290 // Server is auth on seated mode and we want to ignore incoming pending movements after we have decided to set the client to seated mode
291 if (MovementMode == MOVE_Custom && CustomMovementMode == (uint8)EVRCustomMovementMode::VRMOVE_Seated)
292 return false;
293
294 return Super::VerifyClientTimeStamp(TimeStamp, ServerData);
295}
296
298{
299 bIsInPushBack = true;
300
301 if (bWasInPushBack)
302 return;
303
304 bWasInPushBack = true;
305
306 if (AVRBaseCharacter * OwningCharacter = Cast<AVRBaseCharacter>(GetCharacterOwner()))
307 {
308 OwningCharacter->OnBeginWallPushback(HitResult, !Acceleration.Equals(FVector::ZeroVector), AdditionalVRInputVector);
309 }
310}
311
313{
315 return;
316
317 bIsInPushBack = false;
318 bWasInPushBack = false;
319
320 if (AVRBaseCharacter * OwningCharacter = Cast<AVRBaseCharacter>(GetCharacterOwner()))
321 {
322 OwningCharacter->OnEndWallPushback();
323 }
324}
325
327{
328 if (AVRBaseCharacter * BaseCharacter = Cast<AVRBaseCharacter>(GetCharacterOwner()))
329 {
330 return UpdatedComponent ? (BaseCharacter->OffsetComponentToWorld.GetLocation() - FVector(0, 0, UpdatedComponent->Bounds.BoxExtent.Z)) : FNavigationSystem::InvalidLocation;
331 }
332 else
333 {
334 return UpdatedComponent ? (UpdatedComponent->GetComponentLocation() - FVector(0, 0, UpdatedComponent->Bounds.BoxExtent.Z)) : FNavigationSystem::InvalidLocation;
335 }
336}
337
338void UVRBaseCharacterMovementComponent::OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Result)
339{
340 if (AVRBaseCharacter* vrOwner = Cast<AVRBaseCharacter>(GetCharacterOwner()))
341 {
342 vrOwner->NavigationMoveCompleted(RequestID, Result);
343 }
344}
345
346void UVRBaseCharacterMovementComponent::ComputeFloorDist(const FVector& CapsuleLocation, float LineDistance, float SweepDistance, FFindFloorResult& OutFloorResult, float SweepRadius, const FHitResult* DownwardSweepResult) const
347{
348 UE_LOG(LogVRBaseCharacterMovement, VeryVerbose, TEXT("[Role:%d] ComputeFloorDist: %s at location %s"), (int32)CharacterOwner->GetLocalRole(), *GetNameSafe(CharacterOwner), *CapsuleLocation.ToString());
349 OutFloorResult.Clear();
350
351 float PawnRadius, PawnHalfHeight;
352 CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnRadius, PawnHalfHeight);
353
354 bool bSkipSweep = false;
355 if (DownwardSweepResult != NULL && DownwardSweepResult->IsValidBlockingHit())
356 {
357 // Only if the supplied sweep was vertical and downward.
358 if ((DownwardSweepResult->TraceStart.Z > DownwardSweepResult->TraceEnd.Z) &&
359 (DownwardSweepResult->TraceStart - DownwardSweepResult->TraceEnd).SizeSquared2D() <= KINDA_SMALL_NUMBER)
360 {
361 // Reject hits that are barely on the cusp of the radius of the capsule
362 if (IsWithinEdgeTolerance(DownwardSweepResult->Location, DownwardSweepResult->ImpactPoint, PawnRadius))
363 {
364 // Don't try a redundant sweep, regardless of whether this sweep is usable.
365 bSkipSweep = true;
366
367 const bool bIsWalkable = IsWalkable(*DownwardSweepResult);
368 const float FloorDist = (CapsuleLocation.Z - DownwardSweepResult->Location.Z);
369 OutFloorResult.SetFromSweep(*DownwardSweepResult, FloorDist, bIsWalkable);
370
371 if (bIsWalkable)
372 {
373 // Use the supplied downward sweep as the floor hit result.
374 return;
375 }
376 }
377 }
378 }
379
380 // We require the sweep distance to be >= the line distance, otherwise the HitResult can't be interpreted as the sweep result.
381 if (SweepDistance < LineDistance)
382 {
383 ensure(SweepDistance >= LineDistance);
384 return;
385 }
386
387 bool bBlockingHit = false;
388 FCollisionQueryParams QueryParams(SCENE_QUERY_STAT(ComputeFloorDist), false, CharacterOwner);
389 FCollisionResponseParams ResponseParam;
390 InitCollisionParams(QueryParams, ResponseParam);
391 const ECollisionChannel CollisionChannel = UpdatedComponent->GetCollisionObjectType();
392
393 // Skip physics bodies for floor check if we are skipping simulated objects
395 ResponseParam.CollisionResponse.PhysicsBody = ECollisionResponse::ECR_Ignore;
396
397 // Sweep test
398 if (!bSkipSweep && SweepDistance > 0.f && SweepRadius > 0.f)
399 {
400 // Use a shorter height to avoid sweeps giving weird results if we start on a surface.
401 // This also allows us to adjust out of penetrations.
402 const float ShrinkScale = 0.9f;
403 const float ShrinkScaleOverlap = 0.1f;
404 float ShrinkHeight = (PawnHalfHeight - PawnRadius) * (1.f - ShrinkScale);
405 float TraceDist = SweepDistance + ShrinkHeight;
406 FCollisionShape CapsuleShape = FCollisionShape::MakeCapsule(SweepRadius, PawnHalfHeight - ShrinkHeight);
407
408 FHitResult Hit(1.f);
409 bBlockingHit = FloorSweepTest(Hit, CapsuleLocation, CapsuleLocation + FVector(0.f, 0.f, -TraceDist), CollisionChannel, CapsuleShape, QueryParams, ResponseParam);
410
411 if (bBlockingHit)
412 {
413 // Reject hits adjacent to us, we only care about hits on the bottom portion of our capsule.
414 // Check 2D distance to impact point, reject if within a tolerance from radius.
415 if (Hit.bStartPenetrating || !IsWithinEdgeTolerance(CapsuleLocation, Hit.ImpactPoint, CapsuleShape.Capsule.Radius))
416 {
417 // Use a capsule with a slightly smaller radius and shorter height to avoid the adjacent object.
418 // Capsule must not be nearly zero or the trace will fall back to a line trace from the start point and have the wrong length.
419 CapsuleShape.Capsule.Radius = FMath::Max(0.f, CapsuleShape.Capsule.Radius - SWEEP_EDGE_REJECT_DISTANCE - KINDA_SMALL_NUMBER);
420 if (!CapsuleShape.IsNearlyZero())
421 {
422 ShrinkHeight = (PawnHalfHeight - PawnRadius) * (1.f - ShrinkScaleOverlap);
423 TraceDist = SweepDistance + ShrinkHeight;
424 CapsuleShape.Capsule.HalfHeight = FMath::Max(PawnHalfHeight - ShrinkHeight, CapsuleShape.Capsule.Radius);
425 Hit.Reset(1.f, false);
426
427 bBlockingHit = FloorSweepTest(Hit, CapsuleLocation, CapsuleLocation + FVector(0.f, 0.f, -TraceDist), CollisionChannel, CapsuleShape, QueryParams, ResponseParam);
428 }
429 }
430
431 // Reduce hit distance by ShrinkHeight because we shrank the capsule for the trace.
432 // We allow negative distances here, because this allows us to pull out of penetrations.
433 const float MaxPenetrationAdjust = FMath::Max(MAX_FLOOR_DIST, PawnRadius);
434 const float SweepResult = FMath::Max(-MaxPenetrationAdjust, Hit.Time * TraceDist - ShrinkHeight);
435
436 OutFloorResult.SetFromSweep(Hit, SweepResult, false);
437 if (Hit.IsValidBlockingHit() && IsWalkable(Hit))
438 {
439 if (SweepResult <= SweepDistance)
440 {
441 // Hit within test distance.
442 OutFloorResult.bWalkableFloor = true;
443 return;
444 }
445 }
446 }
447 }
448
449 // Since we require a longer sweep than line trace, we don't want to run the line trace if the sweep missed everything.
450 // We do however want to try a line trace if the sweep was stuck in penetration.
451 if (!OutFloorResult.bBlockingHit && !OutFloorResult.HitResult.bStartPenetrating)
452 {
453 OutFloorResult.FloorDist = SweepDistance;
454 return;
455 }
456
457 // Line trace
458 if (LineDistance > 0.f)
459 {
460 const float ShrinkHeight = PawnHalfHeight;
461 const FVector LineTraceStart = CapsuleLocation;
462 const float TraceDist = LineDistance + ShrinkHeight;
463 const FVector Down = FVector(0.f, 0.f, -TraceDist);
464 QueryParams.TraceTag = SCENE_QUERY_STAT_NAME_ONLY(FloorLineTrace);
465
466 FHitResult Hit(1.f);
467 bBlockingHit = GetWorld()->LineTraceSingleByChannel(Hit, LineTraceStart, LineTraceStart + Down, CollisionChannel, QueryParams, ResponseParam);
468
469 if (bBlockingHit)
470 {
471 if (Hit.Time > 0.f)
472 {
473 // Reduce hit distance by ShrinkHeight because we started the trace higher than the base.
474 // We allow negative distances here, because this allows us to pull out of penetrations.
475 const float MaxPenetrationAdjust = FMath::Max(MAX_FLOOR_DIST, PawnRadius);
476 const float LineResult = FMath::Max(-MaxPenetrationAdjust, Hit.Time * TraceDist - ShrinkHeight);
477
478 OutFloorResult.bBlockingHit = true;
479 if (LineResult <= LineDistance && IsWalkable(Hit))
480 {
481 OutFloorResult.SetFromLineTrace(Hit, OutFloorResult.FloorDist, LineResult, true);
482 return;
483 }
484 }
485 }
486 }
487
488 // No hits were acceptable.
489 OutFloorResult.bWalkableFloor = false;
490}
491
492
493float UVRBaseCharacterMovementComponent::SlideAlongSurface(const FVector& Delta, float Time, const FVector& InNormal, FHitResult& Hit, bool bHandleImpact)
494{
495 // Am running the CharacterMovementComponents calculations manually here now prior to scaling down the delta
496
497 if (!Hit.bBlockingHit)
498 {
499 return 0.f;
500 }
501
502 FVector Normal(InNormal);
503 if (IsMovingOnGround())
504 {
505 // We don't want to be pushed up an unwalkable surface.
506 if (Normal.Z > 0.f)
507 {
508 if (!IsWalkable(Hit))
509 {
510 Normal = Normal.GetSafeNormal2D();
511 }
512 }
513 else if (Normal.Z < -KINDA_SMALL_NUMBER)
514 {
515 // Don't push down into the floor when the impact is on the upper portion of the capsule.
516 if (CurrentFloor.FloorDist < MIN_FLOOR_DIST && CurrentFloor.bBlockingHit)
517 {
518 const FVector FloorNormal = CurrentFloor.HitResult.Normal;
519 const bool bFloorOpposedToMovement = (Delta | FloorNormal) < 0.f && (FloorNormal.Z < 1.f - DELTA);
520 if (bFloorOpposedToMovement)
521 {
522 Normal = FloorNormal;
523 }
524
525 Normal = Normal.GetSafeNormal2D();
526 }
527 }
528 }
529
530
531 /*if ((Delta | InNormal) <= -0.2)
532 {
533
534 }*/
535
537
538 // If the movement mode is one where sliding is an issue in VR, scale the delta by the custom scaler now
539 // that we have already validated the floor normal.
540 // Otherwise just pass in as normal, either way skip the parents implementation as we are doing it now.
541 if (IsMovingOnGround() || (MovementMode == MOVE_Custom && CustomMovementMode == (uint8)EVRCustomMovementMode::VRMOVE_Climbing))
542 return Super::Super::SlideAlongSurface(Delta * VRWallSlideScaler, Time, Normal, Hit, bHandleImpact);
543 else
544 return Super::Super::SlideAlongSurface(Delta, Time, Normal, Hit, bHandleImpact);
545
546
547}
548
550{
551 this->CrouchedHalfHeight = NewCrouchedHalfHeight;
552}
553
555{
556 // if we are a client then lets round this to match what it will be after net Replication
557 if (GetNetMode() == NM_Client)
559 else
560 CustomVRInputVector += Movement; // If not a client, don't bother to round this down.
561}
562
567
569{
570 // If we are calling this on the server on a non owned character, there is no reason to wait around, just do the action now
571 // If we ARE locally controlled, keep the action inline with the movement component to maintain consistency
572 if (GetNetMode() < NM_Client)
573 {
574 ACharacter* OwningChar = GetCharacterOwner();
575 if (OwningChar && !OwningChar->IsLocallyControlled())
576 {
579 }
580 }
581}
582
584{
585 StoreSetTrackingPaused(bNewTrackingPaused);
586}
587
589{
590 FVRMoveActionContainer MoveAction;
592 MoveAction.MoveActionFlags = bNewTrackingPaused;
593 MoveActionArray.MoveActions.Add(MoveAction);
595}
596
597
598void UVRBaseCharacterMovementComponent::PerformMoveAction_SnapTurn(float DeltaYawAngle, EVRMoveActionVelocityRetention VelocityRetention, bool bFlagGripTeleport, bool bFlagCharacterTeleport)
599{
600 FVRMoveActionContainer MoveAction;
602
603 // Removed 2 decimal precision rounding in favor of matching the actual replicated short fidelity instead.
604 // MoveAction.MoveActionRot = FRotator(0.0f, FMath::RoundToFloat(((FRotator(0.f,DeltaYawAngle, 0.f).Quaternion() * UpdatedComponent->GetComponentQuat()).Rotator().Yaw) * 100.f) / 100.f, 0.0f);
605
606 // Setting to the exact same fidelity as the replicated value ends up being, losing some precision
607 MoveAction.MoveActionRot = FRotator(0.0f, FRotator::DecompressAxisFromShort(FRotator::CompressAxisToShort((FRotator(0.f, DeltaYawAngle, 0.f).Quaternion() * UpdatedComponent->GetComponentQuat()).Rotator().Yaw)), 0.0f);
608
609 if (bFlagCharacterTeleport)
610 MoveAction.MoveActionFlags = 0x02;// .MoveActionRot.Roll = 2.0f;
611 else if(bFlagGripTeleport)
612 MoveAction.MoveActionFlags = 0x01;//MoveActionRot.Roll = bFlagGripTeleport ? 1.0f : 0.0f;
613
615 {
616 //MoveAction.MoveActionRot.Pitch = FMath::RoundToFloat(DeltaYawAngle * 100.f) / 100.f;
617 MoveAction.MoveActionRot.Pitch = DeltaYawAngle;
618 }
619
620 MoveAction.VelRetentionSetting = VelocityRetention;
621
622 MoveActionArray.MoveActions.Add(MoveAction);
624}
625
626void UVRBaseCharacterMovementComponent::PerformMoveAction_SetRotation(float NewYaw, EVRMoveActionVelocityRetention VelocityRetention, bool bFlagGripTeleport, bool bFlagCharacterTeleport)
627{
628 FVRMoveActionContainer MoveAction;
630 MoveAction.MoveActionRot = FRotator(0.0f, FMath::RoundToFloat(NewYaw * 100.f) / 100.f, 0.0f);
631
632 if (bFlagCharacterTeleport)
633 MoveAction.MoveActionFlags = 0x02;// .MoveActionRot.Roll = 2.0f;
634 else if (bFlagGripTeleport)
635 MoveAction.MoveActionFlags = 0x01;//MoveActionRot.Roll = bFlagGripTeleport ? 1.0f : 0.0f;
636
638 {
639 float DeltaYawAngle = FMath::FindDeltaAngleDegrees(UpdatedComponent->GetComponentRotation().Yaw, NewYaw);
640 //MoveAction.MoveActionRot.Pitch = FMath::RoundToFloat(DeltaYawAngle * 100.f) / 100.f;
641 MoveAction.MoveActionRot.Pitch = DeltaYawAngle;
642 }
643
644 MoveAction.VelRetentionSetting = VelocityRetention;
645
646 MoveActionArray.MoveActions.Add(MoveAction);
648}
649
650void UVRBaseCharacterMovementComponent::PerformMoveAction_Teleport(FVector TeleportLocation, FRotator TeleportRotation, EVRMoveActionVelocityRetention VelocityRetention, bool bSkipEncroachmentCheck)
651{
652 FVRMoveActionContainer MoveAction;
654 MoveAction.MoveActionLoc = RoundDirectMovement(TeleportLocation);
655 MoveAction.MoveActionRot.Yaw = FMath::RoundToFloat(TeleportRotation.Yaw * 100.f) / 100.f;
656 MoveAction.MoveActionFlags |= (uint8)bSkipEncroachmentCheck;//.MoveActionRot.Roll = bSkipEncroachmentCheck ? 1.0f : 0.0f;
657
659 {
660 float DeltaYawAngle = FMath::FindDeltaAngleDegrees(UpdatedComponent->GetComponentRotation().Yaw, TeleportRotation.Yaw);
661 //MoveAction.MoveActionRot.Pitch = FMath::RoundToFloat(DeltaYawAngle * 100.f) / 100.f;
662 MoveAction.MoveActionRot.Pitch = DeltaYawAngle;
663 }
664
665 MoveAction.VelRetentionSetting = VelocityRetention;
666
667 MoveActionArray.MoveActions.Add(MoveAction);
669}
670
679
680void UVRBaseCharacterMovementComponent::PerformMoveAction_Custom(EVRMoveAction MoveActionToPerform, EVRMoveActionDataReq DataRequirementsForMoveAction, FVector MoveActionVector, FRotator MoveActionRotator, uint8 MoveActionFlags)
681{
682 FVRMoveActionContainer MoveAction;
683 MoveAction.MoveAction = MoveActionToPerform;
684
685 // Round the vector to 2 decimal precision
686 MoveAction.MoveActionLoc = RoundDirectMovement(MoveActionVector);
687 MoveAction.MoveActionRot = MoveActionRotator;
688 MoveAction.MoveActionDataReq = DataRequirementsForMoveAction;
689 MoveAction.MoveActionFlags = MoveActionFlags;
690 MoveActionArray.MoveActions.Add(MoveAction);
691
693}
694
696{
698 {
699 switch (MoveAction.MoveAction)
700 {
702 {
703 /*return */DoMASnapTurn(MoveAction);
704 }break;
706 {
707 /*return */DoMATeleport(MoveAction);
708 }break;
710 {
711 /*return */DoMAStopAllMovement(MoveAction);
712 }break;
714 {
715 /*return */DoMASetRotation(MoveAction);
716 }break;
718 {
719 /*return */DoMAPauseTracking(MoveAction);
720 }break;
722 {}break;
723 default: // All other move actions (CUSTOM)
724 {
725 if (AVRBaseCharacter * OwningCharacter = Cast<AVRBaseCharacter>(GetCharacterOwner()))
726 {
727 OwningCharacter->OnCustomMoveActionPerformed(MoveAction.MoveAction, MoveAction.MoveActionLoc, MoveAction.MoveActionRot, MoveAction.MoveActionFlags);
728 }
729 }break;
730 }
731 }
732
733 return true;
734}
735
737{
738 if (AVRBaseCharacter * OwningCharacter = Cast<AVRBaseCharacter>(GetCharacterOwner()))
739 {
740
741 FRotator TargetRot(0.f, MoveAction.MoveActionRot.Yaw, 0.f);
742
743 FQuat OrigRot = OwningCharacter->GetActorQuat();
744
745 if (this->BaseVRCharacterOwner && this->BaseVRCharacterOwner->IsLocallyControlled())
746 {
748 {
749 MoveAction.MoveActionLoc = OwningCharacter->SetActorRotationVR(TargetRot, true, false);
750 MoveAction.MoveActionFlags |= 0x04; // Flag that we are using loc only
751 }
752 else
753 {
754 OwningCharacter->SetActorRotationVR(TargetRot, true, false);
755 }
756 }
757 else
758 {
759 if (MoveAction.MoveActionFlags & 0x04)
760 {
761 OwningCharacter->SetActorLocation(OwningCharacter->GetActorLocation() + MoveAction.MoveActionLoc);
762 }
763 else
764 {
765 OwningCharacter->SetActorRotationVR(TargetRot, true, false);
766 }
767 }
768
769 switch (MoveAction.VelRetentionSetting)
770 {
772 {
773
774 }break;
776 {
777 this->Velocity = FVector::ZeroVector;
778 }break;
780 {
781 if (OwningCharacter->IsLocallyControlled())
782 {
783 MoveAction.MoveActionVel = RoundDirectMovement(FRotator(0.f, MoveAction.MoveActionRot.Pitch, 0.f).RotateVector(this->Velocity));
784 this->Velocity = MoveAction.MoveActionVel;
785 }
786 else
787 {
788 this->Velocity = MoveAction.MoveActionVel;
789 }
790 }break;
791 }
792
793 // If we are flagged to teleport the grips
794 if (MoveAction.MoveActionFlags & 0x01 || MoveAction.MoveActionFlags & 0x02)
795 {
796 OwningCharacter->NotifyOfTeleport(MoveAction.MoveActionFlags & 0x02);
797 }
798 }
799
800 return false;
801}
802
804{
805 if (AVRBaseCharacter * OwningCharacter = Cast<AVRBaseCharacter>(GetCharacterOwner()))
806 {
807 FRotator TargetRot(0.f, MoveAction.MoveActionRot.Yaw, 0.f);
808 if (this->BaseVRCharacterOwner && this->BaseVRCharacterOwner->IsLocallyControlled())
809 {
811 {
812 MoveAction.MoveActionLoc = OwningCharacter->SetActorRotationVR(TargetRot, true);
813 MoveAction.MoveActionFlags |= 0x04; // Flag that we are using loc only
814 }
815 else
816 {
817 OwningCharacter->SetActorRotationVR(TargetRot, true);
818 }
819 }
820 else
821 {
822 if (MoveAction.MoveActionFlags & 0x04)
823 {
824 OwningCharacter->SetActorLocation(OwningCharacter->GetActorLocation() + MoveAction.MoveActionLoc);
825 }
826 else
827 {
828 OwningCharacter->SetActorRotationVR(TargetRot, true);
829 }
830 }
831
832 switch (MoveAction.VelRetentionSetting)
833 {
835 {
836
837 }break;
839 {
840 this->Velocity = FVector::ZeroVector;
841 }break;
843 {
844 if (OwningCharacter->IsLocallyControlled())
845 {
846 MoveAction.MoveActionVel = RoundDirectMovement(FRotator(0.f, MoveAction.MoveActionRot.Pitch, 0.f).RotateVector(this->Velocity));
847 this->Velocity = MoveAction.MoveActionVel;
848 }
849 else
850 {
851 this->Velocity = MoveAction.MoveActionVel;
852 }
853 }break;
854 }
855
856 // If we are flagged to teleport the grips
857 if (MoveAction.MoveActionFlags & 0x01 || MoveAction.MoveActionFlags & 0x02)
858 {
859 OwningCharacter->NotifyOfTeleport(MoveAction.MoveActionFlags & 0x02);
860 }
861 }
862
863 return false;
864}
865
867{
868 if (AVRBaseCharacter * OwningCharacter = Cast<AVRBaseCharacter>(GetCharacterOwner()))
869 {
870 AController* OwningController = OwningCharacter->GetController();
871
872 if (!OwningController)
873 {
875 return false;
876 }
877
878 bool bSkipEncroachmentCheck = MoveAction.MoveActionFlags & 0x01; //MoveAction.MoveActionRot.Roll > 0.0f;
879 FRotator TargetRot(0.f, MoveAction.MoveActionRot.Yaw, 0.f);
880 OwningCharacter->TeleportTo(MoveAction.MoveActionLoc, TargetRot, false, bSkipEncroachmentCheck);
881
882 switch (MoveAction.VelRetentionSetting)
883 {
885 {
886
887 }break;
889 {
890 this->Velocity = FVector::ZeroVector;
891 }break;
893 {
894 if (OwningCharacter->IsLocallyControlled())
895 {
896 MoveAction.MoveActionVel = RoundDirectMovement(FRotator(0.f, MoveAction.MoveActionRot.Pitch, 0.f).RotateVector(this->Velocity));
897 this->Velocity = MoveAction.MoveActionVel;
898 }
899 else
900 {
901 this->Velocity = MoveAction.MoveActionVel;
902 }
903 }break;
904 }
905
906 if (OwningCharacter->bUseControllerRotationYaw)
907 OwningController->SetControlRotation(TargetRot);
908
909 return true;
910 }
911
912 return false;
913}
914
916{
917 if (AVRBaseCharacter * OwningCharacter = Cast<AVRBaseCharacter>(GetCharacterOwner()))
918 {
919 this->StopMovementImmediately();
920 return true;
921 }
922
923 return false;
924}
925
927{
928 if (AVRBaseCharacter* OwningCharacter = Cast<AVRBaseCharacter>(GetCharacterOwner()))
929 {
930 OwningCharacter->bTrackingPaused = MoveAction.MoveActionFlags > 0;
931 OwningCharacter->PausedTrackingLoc = MoveAction.MoveActionLoc;
932 OwningCharacter->PausedTrackingRot = MoveAction.MoveActionRot.Yaw;
933 return true;
934 }
935 return false;
936}
937
938void UVRBaseCharacterMovementComponent::PhysCustom(float deltaTime, int32 Iterations)
939{
940 switch (static_cast<EVRCustomMovementMode>(CustomMovementMode))
941 {
943 PhysCustom_Climbing(deltaTime, Iterations);
944 break;
946 PhysCustom_LowGrav(deltaTime, Iterations);
947 break;
949 break;
950 default:
951 Super::PhysCustom(deltaTime, Iterations);
952 break;
953 }
954}
955
956bool UVRBaseCharacterMovementComponent::VRClimbStepUp(const FVector& GravDir, const FVector& Delta, const FHitResult &InHit, FStepDownResult* OutStepDownResult)
957{
958 return StepUp(GravDir, Delta, InHit, OutStepDownResult);
959}
960
961void UVRBaseCharacterMovementComponent::PhysCustom_Climbing(float deltaTime, int32 Iterations)
962{
963 if (deltaTime < MIN_TICK_TIME)
964 {
965 return;
966 }
967
968 // Skip calling into BP if we aren't locally controlled - *EDIT* MOVED TO TICKCOMPONENT to avoid batched movement issues
969 /*if (CharacterOwner->IsLocallyControlled())
970 {
971 // Allow the player to run updates on the climb logic for CustomVRInputVector
972 if (AVRBaseCharacter * characterOwner = Cast<AVRBaseCharacter>(CharacterOwner))
973 {
974 characterOwner->UpdateClimbingMovement(deltaTime);
975 }
976 }*/
977
978
979 // I am forcing this to 0 to avoid some legacy velocity coming out of other movement modes, climbing should only be direct movement anyway.
980 Velocity = FVector::ZeroVector;
981
982 // Rewind the players position by the new capsule location
984
985 Iterations++;
986 bJustTeleported = false;
987
988 FVector OldLocation = UpdatedComponent->GetComponentLocation();
989 const FVector Adjusted = /*(Velocity * deltaTime) + */CustomVRInputVector;
990 FVector Delta = Adjusted + AdditionalVRInputVector;
991 bool bZeroDelta = Delta.IsNearlyZero();
992
993 FStepDownResult StepDownResult;
994
995 // Instead of remaking the step up function, temp assign a custom step height and then fall back to the old one afterward
996 // This isn't the "proper" way to do it, but it saves on re-making stepup() for both vr characters seperatly (due to different hmd injection)
997 float OldMaxStepHeight = MaxStepHeight;
998 MaxStepHeight = VRClimbingStepHeight;
999 bool bSteppedUp = false;
1000
1001 if (!bZeroDelta)
1002 {
1003 FHitResult Hit(1.f);
1004 SafeMoveUpdatedComponent(Delta, UpdatedComponent->GetComponentQuat(), true, Hit);
1005
1006 if (Hit.Time < 1.f)
1007 {
1008 const FVector GravDir = FVector(0.f, 0.f, -1.f);
1009 const FVector VelDir = (CustomVRInputVector).GetSafeNormal();//Velocity.GetSafeNormal();
1010 const float UpDown = GravDir | VelDir;
1011
1012 //bool bSteppedUp = false;
1013 if ((FMath::Abs(Hit.ImpactNormal.Z) < 0.2f) && (UpDown < 0.5f) && (UpDown > -0.2f) && CanStepUp(Hit))
1014 {
1015 // Scope our movement updates, and do not apply them until all intermediate moves are completed.
1016 FVRCharacterScopedMovementUpdate ScopedStepUpMovement(UpdatedComponent, EScopedUpdate::DeferredUpdates);
1017
1018 float stepZ = UpdatedComponent->GetComponentLocation().Z;
1019
1020 // Making it easier to step up here with the multiplier, helps avoid falling back off
1022 bSteppedUp = VRClimbStepUp(GravDir, ((Adjusted.GetClampedToMaxSize2D(VRClimbingStepUpMaxSize) * VRClimbingStepUpMultiplier) + AdditionalVRInputVector) * (1.f - Hit.Time), Hit, &StepDownResult);
1023 else
1024 bSteppedUp = VRClimbStepUp(GravDir, ((Adjusted * VRClimbingStepUpMultiplier) + AdditionalVRInputVector) * (1.f - Hit.Time), Hit, &StepDownResult);
1025
1026 if (bSteppedUp && OnPerformClimbingStepUp.IsBound())
1027 {
1028 FVector finalLoc = UpdatedComponent->GetComponentLocation();
1029
1030 // Rewind the step up, the end user wants to handle it instead
1031 ScopedStepUpMovement.RevertMove();
1032
1033 // Revert to old max step height
1034 MaxStepHeight = OldMaxStepHeight;
1035
1036 OnPerformClimbingStepUp.Broadcast(finalLoc);
1037 return;
1038 }
1039
1040 if (bSteppedUp)
1041 {
1042 OldLocation.Z = UpdatedComponent->GetComponentLocation().Z + (OldLocation.Z - stepZ);
1043 }
1044
1045 }
1046
1047 if (!bSteppedUp)
1048 {
1049 //adjust and try again
1050 HandleImpact(Hit, deltaTime, Adjusted);
1051 SlideAlongSurface(Adjusted, (1.f - Hit.Time), Hit.Normal, Hit, true);
1052 }
1053 }
1054 }
1055
1056 // Revert to old max step height
1057 MaxStepHeight = OldMaxStepHeight;
1058
1059 if (bSteppedUp)
1060 {
1061 if (AVRBaseCharacter * ownerCharacter = Cast<AVRBaseCharacter>(CharacterOwner))
1062 {
1064 {
1065 // Takes effect next frame, this allows server rollback to correctly handle auto step up
1067 // Before doing this the server could rollback the client from a bad step up and leave it hanging in climb mode
1068 // This way the rollback replay correctly sets the movement mode from the step up request
1069
1070 Velocity = FVector::ZeroVector;
1071 }
1072
1073 // Notify the end user that they probably want to stop gripping now
1074 ownerCharacter->OnClimbingSteppedUp();
1075 }
1076 }
1077
1078 // Update floor.
1079 // StepUp might have already done it for us.
1080 if (StepDownResult.bComputedFloor)
1081 {
1082 CurrentFloor = StepDownResult.FloorResult;
1083 }
1084 else
1085 {
1086 FindFloor(UpdatedComponent->GetComponentLocation(), CurrentFloor, bZeroDelta, NULL);
1087 }
1088
1089 if (CurrentFloor.IsWalkableFloor())
1090 {
1091 if(CurrentFloor.GetDistanceToFloor() < (MIN_FLOOR_DIST + MAX_FLOOR_DIST) / 2)
1092 AdjustFloorHeight();
1093
1094 // This was causing based movement to apply to climbing
1095 //SetBase(CurrentFloor.HitResult.Component.Get(), CurrentFloor.HitResult.BoneName);
1096 }
1097 else if (CurrentFloor.HitResult.bStartPenetrating)
1098 {
1099 // The floor check failed because it started in penetration
1100 // We do not want to try to move downward because the downward sweep failed, rather we'd like to try to pop out of the floor.
1101 FHitResult Hit(CurrentFloor.HitResult);
1102 Hit.TraceEnd = Hit.TraceStart + FVector(0.f, 0.f, MAX_FLOOR_DIST);
1103 const FVector RequestedAdjustment = GetPenetrationAdjustment(Hit);
1104 ResolvePenetration(RequestedAdjustment, Hit, UpdatedComponent->GetComponentQuat());
1105 bForceNextFloorCheck = true;
1106 }
1107
1108 if(!bSteppedUp || !SetDefaultPostClimbMovementOnStepUp)
1109 {
1110 if (!bJustTeleported && !HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
1111 {
1112 Velocity = (((UpdatedComponent->GetComponentLocation() - OldLocation) - AdditionalVRInputVector) / deltaTime).GetClampedToMaxSize(VRClimbingMaxReleaseVelocitySize);
1113 }
1114 }
1115}
1116
1117void UVRBaseCharacterMovementComponent::PhysCustom_LowGrav(float deltaTime, int32 Iterations)
1118{
1119
1120 if (deltaTime < MIN_TICK_TIME)
1121 {
1122 return;
1123 }
1124
1125 // Skip calling into BP if we aren't locally controlled
1126 if (CharacterOwner->IsLocallyControlled())
1127 {
1128 // Allow the player to run updates on the push logic for CustomVRInputVector
1129 if (AVRBaseCharacter * characterOwner = Cast<AVRBaseCharacter>(CharacterOwner))
1130 {
1131 characterOwner->UpdateLowGravMovement(deltaTime);
1132 }
1133 }
1134
1135 float Friction = 0.0f;
1136 // Rewind the players position by the new capsule location
1138
1139 //RestorePreAdditiveVRMotionVelocity();
1140
1141 // If we are not in the default physics volume then accept the custom fluid friction setting
1142 // I set this by default to be ignored as many will not alter the default fluid friction
1143 if(!VRLowGravIgnoresDefaultFluidFriction || GetWorld()->GetDefaultPhysicsVolume() != GetPhysicsVolume())
1144 Friction = 0.5f * GetPhysicsVolume()->FluidFriction;
1145
1146 CalcVelocity(deltaTime, Friction, true, 0.0f);
1147
1148 // Adding in custom VR input vector here, can be used for custom movement during it
1149 // AddImpulse is not multiplayer compatible client side
1150 //Velocity += CustomVRInputVector;
1151
1152 ApplyVRMotionToVelocity(deltaTime);
1153
1154 Iterations++;
1155 bJustTeleported = false;
1156
1157 FVector OldLocation = UpdatedComponent->GetComponentLocation();
1158 const FVector Adjusted = (Velocity * deltaTime);
1159 FHitResult Hit(1.f);
1160 SafeMoveUpdatedComponent(Adjusted/* + AdditionalVRInputVector*/, UpdatedComponent->GetComponentQuat(), true, Hit);
1161
1162 if (Hit.Time < 1.f)
1163 {
1164 // Still running step up with grav dir
1165 const FVector GravDir = FVector(0.f, 0.f, -1.f);
1166 const FVector VelDir = Velocity.GetSafeNormal();
1167 const float UpDown = GravDir | VelDir;
1168
1169 bool bSteppedUp = false;
1170 if ((FMath::Abs(Hit.ImpactNormal.Z) < 0.2f) && (UpDown < 0.5f) && (UpDown > -0.2f) && CanStepUp(Hit))
1171 {
1172 float stepZ = UpdatedComponent->GetComponentLocation().Z;
1173 bSteppedUp = StepUp(GravDir, Adjusted * (1.f - Hit.Time), Hit);
1174 if (bSteppedUp)
1175 {
1176 OldLocation.Z = UpdatedComponent->GetComponentLocation().Z + (OldLocation.Z - stepZ);
1177 }
1178 }
1179
1180 if (!bSteppedUp)
1181 {
1182 //adjust and try again
1183 HandleImpact(Hit, deltaTime, Adjusted);
1184 SlideAlongSurface(Adjusted, (1.f - Hit.Time), Hit.Normal, Hit, true);
1185 }
1186
1187 if (!bJustTeleported && !HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
1188 {
1189 Velocity = (((UpdatedComponent->GetComponentLocation() - OldLocation) /* - AdditionalVRInputVector*/) / deltaTime) * VRLowGravWallFrictionScaler;
1190 }
1191 }
1192 else
1193 {
1194 if (!bJustTeleported && !HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
1195 {
1196 Velocity = (((UpdatedComponent->GetComponentLocation() - OldLocation) /* - AdditionalVRInputVector*/) / deltaTime);
1197 }
1198 }
1199
1201}
1202
1203
1211
1213{
1214 // Only have up to 15 that it can go up to, the previous 7 index's are used up for std movement modes
1215 VRReplicatedMovementMode = NewMovementMode;
1216}
1217
1219{
1220 if (MovementMode == EMovementMode::MOVE_Custom)
1221 {
1222 return (EVRConjoinedMovementModes)((int8)CustomMovementMode + (int8)EVRConjoinedMovementModes::C_VRMOVE_Climbing);
1223 }
1224 else
1225 return (EVRConjoinedMovementModes)MovementMode.GetValue();
1226}
1227
1229{
1230 if (CharacterOwner->GetLocalRole() != ENetRole::ROLE_SimulatedProxy)
1231 {
1232 const uint8 CurrentPackedMovementMode = PackNetworkMovementMode();
1233 if (CurrentPackedMovementMode != ReceivedMode)
1234 {
1235 TEnumAsByte<EMovementMode> NetMovementMode(MOVE_None);
1236 TEnumAsByte<EMovementMode> NetGroundMode(MOVE_None);
1237 uint8 NetCustomMode(0);
1238 UnpackNetworkMovementMode(ReceivedMode, NetMovementMode, NetCustomMode, NetGroundMode);
1239
1240 // Custom movement modes aren't going to be rolled back as they are client authed for our pawns
1241 if (NetMovementMode == EMovementMode::MOVE_Custom || MovementMode == EMovementMode::MOVE_Custom)
1242 {
1243 if (NetCustomMode == (uint8)EVRCustomMovementMode::VRMOVE_Climbing || CustomMovementMode == (uint8)EVRCustomMovementMode::VRMOVE_Climbing)
1244 return; // Don't rollback custom movement modes, we set the server to trust the client on them now so the server should get corrected
1245 }
1246 }
1247 }
1248
1249 Super::ApplyNetworkMovementMode(ReceivedMode);
1250}
1251
1252/*void UVRBaseCharacterMovementComponent::SendClientAdjustment()
1253{
1254 if (!HasValidData())
1255 {
1256 return;
1257 }
1258
1259 FNetworkPredictionData_Server_Character* ServerData = GetPredictionData_Server_Character();
1260 check(ServerData);
1261
1262 if (ServerData->PendingAdjustment.TimeStamp <= 0.f)
1263 {
1264 return;
1265 }
1266
1267 if (ServerData->PendingAdjustment.bAckGoodMove == true)
1268 {
1269 // just notify client this move was received
1270 ClientAckGoodMove(ServerData->PendingAdjustment.TimeStamp);
1271 }
1272 else
1273 {
1274 const bool bIsPlayingNetworkedRootMotionMontage = CharacterOwner->IsPlayingNetworkedRootMotionMontage();
1275 if (HasRootMotionSources())
1276 {
1277 FRotator Rotation = ServerData->PendingAdjustment.NewRot.GetNormalized();
1278 FVector_NetQuantizeNormal CompressedRotation(Rotation.Pitch / 180.f, Rotation.Yaw / 180.f, Rotation.Roll / 180.f);
1279 ClientAdjustRootMotionSourcePosition
1280 (
1281 ServerData->PendingAdjustment.TimeStamp,
1282 CurrentRootMotion,
1283 bIsPlayingNetworkedRootMotionMontage,
1284 bIsPlayingNetworkedRootMotionMontage ? CharacterOwner->GetRootMotionAnimMontageInstance()->GetPosition() : -1.f,
1285 ServerData->PendingAdjustment.NewLoc,
1286 CompressedRotation,
1287 ServerData->PendingAdjustment.NewVel.Z,
1288 ServerData->PendingAdjustment.NewBase,
1289 ServerData->PendingAdjustment.NewBaseBoneName,
1290 ServerData->PendingAdjustment.NewBase != NULL,
1291 ServerData->PendingAdjustment.bBaseRelativePosition,
1292 PackNetworkMovementMode()
1293 );
1294 }
1295 else if (bIsPlayingNetworkedRootMotionMontage)
1296 {
1297 FRotator Rotation = ServerData->PendingAdjustment.NewRot.GetNormalized();
1298 FVector_NetQuantizeNormal CompressedRotation(Rotation.Pitch / 180.f, Rotation.Yaw / 180.f, Rotation.Roll / 180.f);
1299 ClientAdjustRootMotionPosition
1300 (
1301 ServerData->PendingAdjustment.TimeStamp,
1302 CharacterOwner->GetRootMotionAnimMontageInstance()->GetPosition(),
1303 ServerData->PendingAdjustment.NewLoc,
1304 CompressedRotation,
1305 ServerData->PendingAdjustment.NewVel.Z,
1306 ServerData->PendingAdjustment.NewBase,
1307 ServerData->PendingAdjustment.NewBaseBoneName,
1308 ServerData->PendingAdjustment.NewBase != NULL,
1309 ServerData->PendingAdjustment.bBaseRelativePosition,
1310 PackNetworkMovementMode()
1311 );
1312 }
1313 else if (ServerData->PendingAdjustment.NewVel.IsZero())
1314 {
1315 if (AVRBaseCharacter * VRC = Cast<AVRBaseCharacter>(GetOwner()))
1316 {
1317 FVector CusVec = VRC->GetVRLocation_Inline();
1318 GEngine->AddOnScreenDebugMessage(-1, 125.f, IsLocallyControlled() ? FColor::Red : FColor::Green, FString::Printf(TEXT("VrLoc: x: %f, y: %f, X: %f"), CusVec.X, CusVec.Y, CusVec.Z));
1319 }
1320 GEngine->AddOnScreenDebugMessage(-1, 125.f, FColor::Red, TEXT("Correcting Client Location!"));
1321 ClientVeryShortAdjustPosition
1322 (
1323 ServerData->PendingAdjustment.TimeStamp,
1324 ServerData->PendingAdjustment.NewLoc,
1325 ServerData->PendingAdjustment.NewBase,
1326 ServerData->PendingAdjustment.NewBaseBoneName,
1327 ServerData->PendingAdjustment.NewBase != NULL,
1328 ServerData->PendingAdjustment.bBaseRelativePosition,
1329 PackNetworkMovementMode()
1330 );
1331 }
1332 else
1333 {
1334 if (AVRBaseCharacter * VRC = Cast<AVRBaseCharacter>(GetOwner()))
1335 {
1336 FVector CusVec = VRC->GetVRLocation_Inline();
1337 GEngine->AddOnScreenDebugMessage(-1, 125.f, IsLocallyControlled() ? FColor::Red : FColor::Green, FString::Printf(TEXT("VrLoc: x: %f, y: %f, X: %f"), CusVec.X, CusVec.Y, CusVec.Z));
1338 }
1339 GEngine->AddOnScreenDebugMessage(-1, 125.f, FColor::Red, TEXT("Correcting Client Location!"));
1340 ClientAdjustPosition
1341 (
1342 ServerData->PendingAdjustment.TimeStamp,
1343 ServerData->PendingAdjustment.NewLoc,
1344 ServerData->PendingAdjustment.NewVel,
1345 ServerData->PendingAdjustment.NewBase,
1346 ServerData->PendingAdjustment.NewBaseBoneName,
1347 ServerData->PendingAdjustment.NewBase != NULL,
1348 ServerData->PendingAdjustment.bBaseRelativePosition,
1349 PackNetworkMovementMode()
1350 );
1351 }
1352 }
1353
1354 ServerData->PendingAdjustment.TimeStamp = 0;
1355 ServerData->PendingAdjustment.bAckGoodMove = false;
1356 ServerData->bForceClientUpdate = false;
1357}
1358*/
1359
1361{
1362 Super::SetUpdatedComponent(NewUpdatedComponent);
1363
1364 BaseVRCharacterOwner = Cast<AVRBaseCharacter>(CharacterOwner);
1365}
1366
1367
1369{
1370 // Scope these, they nest with Outer references so it should work fine
1371 FVRCharacterScopedMovementUpdate ScopedMovementUpdate(UpdatedComponent, bEnableScopedMovementUpdates ? EScopedUpdate::DeferredUpdates : EScopedUpdate::ImmediateUpdates);
1372
1373 // This moves it into update scope
1374 if (bRunControlRotationInMovementComponent && CharacterOwner->IsLocallyControlled())
1375 {
1377 {
1379 BaseVRCharacterOwner->OwningVRPlayerController->UpdateRotation(DeltaSeconds);
1381 BaseVRCharacterOwner->OwningVRPlayerController->RotationInput = FRotator::ZeroRotator;
1382 }
1383 }
1384
1385 // Apply any replicated movement modes that are pending
1387
1388 // Handle move actions here - Should be scoped
1390
1391 // Clear out this flag prior to movement so we can see if it gets changed
1392 bIsInPushBack = false;
1393
1394 Super::PerformMovement(DeltaSeconds);
1395
1396 EndPushBackNotification(); // Check if we need to notify of ending pushback
1397
1398 // Make sure these are cleaned out for the next frame
1399 //AdditionalVRInputVector = FVector::ZeroVector;
1400 //CustomVRInputVector = FVector::ZeroVector;
1401
1402 // Only clear it here if we are the server, the client clears it later
1403 if (CharacterOwner->GetLocalRole() == ROLE_Authority)
1404 {
1406 }
1407}
1408
1409void UVRBaseCharacterMovementComponent::OnClientCorrectionReceived(class FNetworkPredictionData_Client_Character& ClientData, float TimeStamp, FVector NewLocation, FVector NewVelocity, UPrimitiveComponent* NewBase, FName NewBaseBoneName, bool bHasBase, bool bBaseRelativePosition, uint8 ServerMovementMode)
1410{
1411 Super::OnClientCorrectionReceived(ClientData, TimeStamp, NewLocation, NewVelocity, NewBase, NewBaseBoneName, bHasBase, bBaseRelativePosition, ServerMovementMode);
1412
1413
1414 // If we got corrected then lets teleport our grips, this means that we were out of sync with the server or the server moved us
1416 {
1420 //BaseVRCharacterOwner->NotifyOfTeleport(false);
1421 }
1422}
1423
1425{
1426 //return Super::SimulatedTick(DeltaSeconds);
1427
1428 QUICK_SCOPE_CYCLE_COUNTER(STAT_Character_CharacterMovementSimulated);
1429 checkSlow(CharacterOwner != nullptr);
1430
1431 if (NetworkSmoothingMode == ENetworkSmoothingMode::Replay)
1432 {
1433 const FVector OldLocation = UpdatedComponent ? UpdatedComponent->GetComponentLocation() : FVector::ZeroVector;
1434 const FVector OldVelocity = Velocity;
1435
1436 // Interpolate between appropriate samples
1437 {
1438 QUICK_SCOPE_CYCLE_COUNTER(STAT_Character_CharacterMovementSmoothClientPosition);
1439 SmoothClientPosition(DeltaSeconds);
1440 }
1441
1442 // Update replicated movement mode
1444
1445 UpdateComponentVelocity();
1446 bJustTeleported = false;
1447
1448 if (CharacterOwner)
1449 {
1450 CharacterOwner->RootMotionRepMoves.Empty();
1451 CurrentRootMotion.Clear();
1452 CharacterOwner->SavedRootMotion.Clear();
1453 }
1454
1455 // Note: we do not call the Super implementation, that runs prediction.
1456 // We do still need to call these though
1457 OnMovementUpdated(DeltaSeconds, OldLocation, OldVelocity);
1458 CallMovementUpdateDelegate(DeltaSeconds, OldLocation, OldVelocity);
1459
1460 LastUpdateLocation = UpdatedComponent ? UpdatedComponent->GetComponentLocation() : FVector::ZeroVector;
1461 LastUpdateRotation = UpdatedComponent ? UpdatedComponent->GetComponentQuat() : FQuat::Identity;
1462 LastUpdateVelocity = Velocity;
1463
1464 //TickCharacterPose( DeltaSeconds );
1465 return;
1466 }
1467
1468 // If we are playing a RootMotion AnimMontage.
1469 if (CharacterOwner->IsPlayingNetworkedRootMotionMontage())
1470 {
1471 bWasSimulatingRootMotion = true;
1472 UE_LOG(LogRootMotion, Verbose, TEXT("UCharacterMovementComponent::SimulatedTick"));
1473
1474 // Tick animations before physics.
1475 if (CharacterOwner && CharacterOwner->GetMesh())
1476 {
1477 TickCharacterPose(DeltaSeconds);
1478
1479 // Make sure animation didn't trigger an event that destroyed us
1480 if (!HasValidData())
1481 {
1482 return;
1483 }
1484 }
1485
1486 if (RootMotionParams.bHasRootMotion)
1487 {
1488 const FQuat OldRotationQuat = UpdatedComponent->GetComponentQuat();
1489 const FVector OldLocation = UpdatedComponent->GetComponentLocation();
1490 SimulateRootMotion(DeltaSeconds, RootMotionParams.GetRootMotionTransform());
1491
1492#if !(UE_BUILD_SHIPPING)
1493 // debug
1494 /*if (CharacterOwner && false)
1495 {
1496 const FRotator OldRotation = OldRotationQuat.Rotator();
1497 const FRotator NewRotation = UpdatedComponent->GetComponentRotation();
1498 const FVector NewLocation = UpdatedComponent->GetComponentLocation();
1499 DrawDebugCoordinateSystem(GetWorld(), CharacterOwner->GetMesh()->GetComponentLocation() + FVector(0, 0, 1), NewRotation, 50.f, false);
1500 DrawDebugLine(GetWorld(), OldLocation, NewLocation, FColor::Red, false, 10.f);
1501
1502 UE_LOG(LogRootMotion, Log, TEXT("UCharacterMovementComponent::SimulatedTick DeltaMovement Translation: %s, Rotation: %s, MovementBase: %s"),
1503 *(NewLocation - OldLocation).ToCompactString(), *(NewRotation - OldRotation).GetNormalized().ToCompactString(), *GetNameSafe(CharacterOwner->GetMovementBase()));
1504 }*/
1505#endif // !(UE_BUILD_SHIPPING)
1506 }
1507
1508 // then, once our position is up to date with our animation,
1509 // handle position correction if we have any pending updates received from the server.
1510 if (CharacterOwner && (CharacterOwner->RootMotionRepMoves.Num() > 0))
1511 {
1512 CharacterOwner->SimulatedRootMotionPositionFixup(DeltaSeconds);
1513 }
1514 }
1515 else if (CurrentRootMotion.HasActiveRootMotionSources())
1516 {
1517 // We have root motion sources and possibly animated root motion
1518 bWasSimulatingRootMotion = true;
1519 UE_LOG(LogRootMotion, Verbose, TEXT("UCharacterMovementComponent::SimulatedTick"));
1520
1521 // If we have RootMotionRepMoves, find the most recent important one and set position/rotation to it
1522 bool bCorrectedToServer = false;
1523 const FVector OldLocation = UpdatedComponent->GetComponentLocation();
1524 const FQuat OldRotation = UpdatedComponent->GetComponentQuat();
1525 if (CharacterOwner->RootMotionRepMoves.Num() > 0)
1526 {
1527 // Move Actor back to position of that buffered move. (server replicated position).
1528 FSimulatedRootMotionReplicatedMove& RootMotionRepMove = CharacterOwner->RootMotionRepMoves.Last();
1529 if (CharacterOwner->RestoreReplicatedMove(RootMotionRepMove))
1530 {
1531 bCorrectedToServer = true;
1532 }
1533 Acceleration = RootMotionRepMove.RootMotion.Acceleration;
1534
1535 CharacterOwner->PostNetReceiveVelocity(RootMotionRepMove.RootMotion.LinearVelocity);
1536 LastUpdateVelocity = RootMotionRepMove.RootMotion.LinearVelocity;
1537
1538 // Convert RootMotionSource Server IDs -> Local IDs in AuthoritativeRootMotion and cull invalid
1539 // so that when we use this root motion it has the correct IDs
1540 ConvertRootMotionServerIDsToLocalIDs(CurrentRootMotion, RootMotionRepMove.RootMotion.AuthoritativeRootMotion, RootMotionRepMove.Time);
1541 RootMotionRepMove.RootMotion.AuthoritativeRootMotion.CullInvalidSources();
1542
1543 // Set root motion states to that of repped in state
1544 CurrentRootMotion.UpdateStateFrom(RootMotionRepMove.RootMotion.AuthoritativeRootMotion, true);
1545
1546 // Clear out existing RootMotionRepMoves since we've consumed the most recent
1547 UE_LOG(LogRootMotion, Log, TEXT("\tClearing old moves in SimulatedTick (%d)"), CharacterOwner->RootMotionRepMoves.Num());
1548 CharacterOwner->RootMotionRepMoves.Reset();
1549 }
1550
1551 // Perform movement
1552 PerformMovement(DeltaSeconds);
1553
1554 // After movement correction, smooth out error in position if any.
1555 if (bCorrectedToServer || CurrentRootMotion.NeedsSimulatedSmoothing())
1556 {
1557 SmoothCorrection(OldLocation, OldRotation, UpdatedComponent->GetComponentLocation(), UpdatedComponent->GetComponentQuat());
1558 }
1559 }
1560 // Not playing RootMotion AnimMontage
1561 else
1562 {
1563 // if we were simulating root motion, we've been ignoring regular ReplicatedMovement updates.
1564 // If we're not simulating root motion anymore, force us to sync our movement properties.
1565 // (Root Motion could leave Velocity out of sync w/ ReplicatedMovement)
1566 if (bWasSimulatingRootMotion)
1567 {
1568 bWasSimulatingRootMotion = false;
1569 CharacterOwner->RootMotionRepMoves.Empty();
1570 CharacterOwner->OnRep_ReplicatedMovement();
1571 CharacterOwner->OnRep_ReplicatedBasedMovement();
1573 }
1574
1575 if (CharacterOwner->IsReplicatingMovement() && UpdatedComponent)
1576 {
1577 //USkeletalMeshComponent* Mesh = CharacterOwner->GetMesh();
1578 //const FVector SavedMeshRelativeLocation = Mesh ? Mesh->GetRelativeLocation() : FVector::ZeroVector;
1579 //const FQuat SavedCapsuleRotation = UpdatedComponent->GetComponentQuat();
1580 const bool bPreventMeshMovement = !bNetworkSmoothingComplete;
1581
1582 // Avoid moving the mesh during movement if SmoothClientPosition will take care of it.
1583 if(NetworkSmoothingMode != ENetworkSmoothingMode::Disabled)
1584 {
1585 const FScopedPreventAttachedComponentMove PreventMeshMove(bPreventMeshMovement ? BaseVRCharacterOwner->NetSmoother : nullptr);
1586 //const FScopedPreventAttachedComponentMove PreventMeshMovement(bPreventMeshMovement ? Mesh : nullptr);
1587 if (CharacterOwner->IsMatineeControlled() || CharacterOwner->IsPlayingRootMotion())
1588 {
1589 PerformMovement(DeltaSeconds);
1590 }
1591 else
1592 {
1593 // Moved this var into the VRChar to control smoothing
1594 //if(!bDisableSimulatedTickWhenSmoothingMovement)
1595 SimulateMovement(DeltaSeconds);
1596 }
1597 }
1598 else
1599 {
1600 if (CharacterOwner->IsMatineeControlled() || CharacterOwner->IsPlayingRootMotion())
1601 {
1602 PerformMovement(DeltaSeconds);
1603 }
1604 else
1605 {
1606 SimulateMovement(DeltaSeconds);
1607 }
1608 }
1609
1610 // With Linear smoothing we need to know if the rotation changes, since the mesh should follow along with that (if it was prevented above).
1611 // This should be rare that rotation changes during simulation, but it can happen when ShouldRemainVertical() changes, or standing on a moving base.
1612 /*const bool bValidateRotation = bPreventMeshMovement && (NetworkSmoothingMode == ENetworkSmoothingMode::Linear);
1613 if (bValidateRotation && UpdatedComponent)
1614 {
1615 // Same mesh with different rotation?
1616 const FQuat NewCapsuleRotation = UpdatedComponent->GetComponentQuat();
1617 if (Mesh == CharacterOwner->GetMesh() && !NewCapsuleRotation.Equals(SavedCapsuleRotation, 1e-6f) && ClientPredictionData)
1618 {
1619 // Smoothing should lerp toward this new rotation target, otherwise it will just try to go back toward the old rotation.
1620 ClientPredictionData->MeshRotationTarget = NewCapsuleRotation;
1621 Mesh->SetRelativeLocationAndRotation(SavedMeshRelativeLocation, CharacterOwner->GetBaseRotationOffset());
1622 }
1623 }*/
1624 }
1625 }
1626
1627 // Smooth mesh location after moving the capsule above.
1628 if (!bNetworkSmoothingComplete)
1629 {
1630 QUICK_SCOPE_CYCLE_COUNTER(STAT_Character_CharacterMovementSmoothClientPosition);
1631 SmoothClientPosition(DeltaSeconds);
1632 }
1633 else
1634 {
1635 UE_LOG(LogVRBaseCharacterMovement, Verbose, TEXT("Skipping network smoothing for %s."), *GetNameSafe(CharacterOwner));
1636 }
1637}
1638
1640 float ClientTimeStamp,
1641 float DeltaTime,
1642 uint8 CompressedFlags,
1643 const FVector& NewAccel
1644)
1645{
1646 if (!HasValidData())
1647 {
1648 return;
1649 }
1650
1651 UpdateFromCompressedFlags(CompressedFlags);
1652 CharacterOwner->CheckJumpInput(DeltaTime);
1653
1654 Acceleration = ConstrainInputAcceleration(NewAccel);
1655 Acceleration = Acceleration.GetClampedToMaxSize(GetMaxAcceleration());
1656 AnalogInputModifier = ComputeAnalogInputModifier();
1657
1658 FVector OldLocation = UpdatedComponent->GetComponentLocation();
1659 FQuat OldRotation = UpdatedComponent->GetComponentQuat();
1660
1661 if (BaseVRCharacterOwner && NetworkSmoothingMode == ENetworkSmoothingMode::Exponential)
1662 {
1663 OldLocation = BaseVRCharacterOwner->OffsetComponentToWorld.GetTranslation();
1664 OldRotation = BaseVRCharacterOwner->OffsetComponentToWorld.GetRotation();
1665 }
1666
1667 const bool bWasPlayingRootMotion = CharacterOwner->IsPlayingRootMotion();
1668
1669 PerformMovement(DeltaTime);
1670
1671 // Check if data is valid as PerformMovement can mark character for pending kill
1672 if (!HasValidData())
1673 {
1674 return;
1675 }
1676
1677 // If not playing root motion, tick animations after physics. We do this here to keep events, notifies, states and transitions in sync with client updates.
1678 if (CharacterOwner && !CharacterOwner->bClientUpdating && !CharacterOwner->IsPlayingRootMotion() && CharacterOwner->GetMesh())
1679 {
1680 if (!bWasPlayingRootMotion) // If we were playing root motion before PerformMovement but aren't anymore, we're on the last frame of anim root motion and have already ticked character
1681 {
1682 TickCharacterPose(DeltaTime);
1683 }
1684 // TODO: SaveBaseLocation() in case tick moves us?
1685
1686 // Trigger Events right away, as we could be receiving multiple ServerMoves per frame.
1687 CharacterOwner->GetMesh()->ConditionallyDispatchQueuedAnimEvents();
1688 }
1689
1690 if (CharacterOwner && UpdatedComponent)
1691 {
1692 // Smooth local view of remote clients on listen servers
1693 static const auto CVarNetEnableListenServerSmoothing = IConsoleManager::Get().FindConsoleVariable(TEXT("p.NetEnableListenServerSmoothing"));
1694 if (CVarNetEnableListenServerSmoothing->GetInt() &&
1695 CharacterOwner->GetRemoteRole() == ROLE_AutonomousProxy &&
1696 IsNetMode(NM_ListenServer))
1697 {
1698 SmoothCorrection(OldLocation, OldRotation, UpdatedComponent->GetComponentLocation(), UpdatedComponent->GetComponentQuat());
1699 }
1700 }
1701}
1702
1703void UVRBaseCharacterMovementComponent::SmoothCorrection(const FVector& OldLocation, const FQuat& OldRotation, const FVector& NewLocation, const FQuat& NewRotation)
1704{
1705
1706 //SCOPE_CYCLE_COUNTER(STAT_CharacterMovementSmoothCorrection);
1707 if (!HasValidData())
1708 {
1709 return;
1710 }
1711
1713 Super::SmoothCorrection(OldLocation, OldRotation, NewLocation, NewRotation);
1714
1715 // We shouldn't be running this on a server that is not a listen server.
1716 checkSlow(GetNetMode() != NM_DedicatedServer);
1717 checkSlow(GetNetMode() != NM_Standalone);
1718
1719 // Only client proxies or remote clients on a listen server should run this code.
1720 const bool bIsSimulatedProxy = (CharacterOwner->GetLocalRole() == ROLE_SimulatedProxy);
1721 const bool bIsRemoteAutoProxy = (CharacterOwner->GetRemoteRole() == ROLE_AutonomousProxy);
1722 ensure(bIsSimulatedProxy || bIsRemoteAutoProxy);
1723
1724 // Getting a correction means new data, so smoothing needs to run.
1725 bNetworkSmoothingComplete = false;
1726
1727 // Handle selected smoothing mode.
1728 if (NetworkSmoothingMode == ENetworkSmoothingMode::Replay)
1729 {
1730 // Replays use pure interpolation in this mode, all of the work is done in SmoothClientPosition_Interpolate
1731 return;
1732 }
1733 else if (NetworkSmoothingMode == ENetworkSmoothingMode::Disabled)
1734 {
1735 UpdatedComponent->SetWorldLocationAndRotation(NewLocation, NewRotation, false, nullptr, ETeleportType::TeleportPhysics);
1736 bNetworkSmoothingComplete = true;
1737 }
1738 else if (FNetworkPredictionData_Client_Character* ClientData = GetPredictionData_Client_Character())
1739 {
1740 const UWorld* MyWorld = GetWorld();
1741 if (!ensure(MyWorld != nullptr))
1742 {
1743 return;
1744 }
1745
1746 // Handle my custom VR Offset
1747 FVector OldWorldLocation = OldLocation;
1748 FQuat OldWorldRotation = OldRotation;
1749 FVector NewWorldLocation = NewLocation;
1750 FQuat NewWorldRotation = NewRotation;
1751
1752 if (BaseVRCharacterOwner && NetworkSmoothingMode == ENetworkSmoothingMode::Exponential)
1753 {
1754 if (GetNetMode() < ENetMode::NM_Client)
1755 {
1756 NewWorldLocation = BaseVRCharacterOwner->OffsetComponentToWorld.GetTranslation();
1757 NewWorldRotation = BaseVRCharacterOwner->OffsetComponentToWorld.GetRotation();
1758 }
1759 else
1760 {
1761 FTransform NewWorldTransform(NewRotation, NewLocation, UpdatedComponent->GetRelativeScale3D());
1762 FTransform CurrentRelative = BaseVRCharacterOwner->OffsetComponentToWorld.GetRelativeTransform(UpdatedComponent->GetComponentTransform());
1763 FTransform NewWorld = CurrentRelative * NewWorldTransform;
1764 OldWorldLocation = BaseVRCharacterOwner->OffsetComponentToWorld.GetLocation();
1765 OldWorldRotation = BaseVRCharacterOwner->OffsetComponentToWorld.GetRotation();
1766 NewWorldLocation = NewWorld.GetLocation();
1767 NewWorldRotation = NewWorld.GetRotation();
1768 }
1769 }
1770
1771 // The mesh doesn't move, but the capsule does so we have a new offset.
1772 FVector NewToOldVector = (OldWorldLocation - NewWorldLocation);
1773 if (bIsNavWalkingOnServer && FMath::Abs(NewToOldVector.Z) < NavWalkingFloorDistTolerance)
1774 {
1775 // ignore smoothing on Z axis
1776 // don't modify new location (local simulation result), since it's probably more accurate than server data
1777 // and shouldn't matter as long as difference is relatively small
1778 NewToOldVector.Z = 0;
1779 }
1780
1781 const float DistSq = NewToOldVector.SizeSquared();
1782 if (DistSq > FMath::Square(ClientData->MaxSmoothNetUpdateDist))
1783 {
1784 ClientData->MeshTranslationOffset = (DistSq > FMath::Square(ClientData->NoSmoothNetUpdateDist))
1785 ? FVector::ZeroVector
1786 : ClientData->MeshTranslationOffset + ClientData->MaxSmoothNetUpdateDist * NewToOldVector.GetSafeNormal();
1787 }
1788 else
1789 {
1790 ClientData->MeshTranslationOffset = ClientData->MeshTranslationOffset + NewToOldVector;
1791 }
1792
1793 UE_LOG(LogVRBaseCharacterMovement, Verbose, TEXT("Proxy %s SmoothCorrection(%.2f)"), *GetNameSafe(CharacterOwner), FMath::Sqrt(DistSq));
1794 if (NetworkSmoothingMode == ENetworkSmoothingMode::Linear)
1795 {
1796 // #TODO: Get this working in the future?
1797 // I am currently skipping smoothing on rotation operations
1798 if ((!OldRotation.Equals(NewRotation, 1e-5f)))// || Velocity.IsNearlyZero()))
1799 {
1800 BaseVRCharacterOwner->NetSmoother->SetRelativeLocation(FVector::ZeroVector);
1801 UpdatedComponent->SetWorldLocationAndRotation(NewLocation, NewRotation, false, nullptr, GetTeleportType());
1802 ClientData->MeshTranslationOffset = FVector::ZeroVector;
1803 ClientData->MeshRotationOffset = ClientData->MeshRotationTarget;
1804 bNetworkSmoothingComplete = true;
1805 }
1806 else
1807 {
1808 ClientData->OriginalMeshTranslationOffset = ClientData->MeshTranslationOffset;
1809
1810 // Remember the current and target rotation, we're going to lerp between them
1811 ClientData->OriginalMeshRotationOffset = OldRotation;
1812 ClientData->MeshRotationOffset = OldRotation;
1813 ClientData->MeshRotationTarget = NewRotation;
1814
1815 // Move the capsule, but not the mesh.
1816 // Note: we don't change rotation, we lerp towards it in SmoothClientPosition.
1817 if (NewLocation != OldLocation)
1818 {
1819 const FScopedPreventAttachedComponentMove PreventMeshMove(BaseVRCharacterOwner->NetSmoother);
1820 UpdatedComponent->SetWorldLocation(NewLocation, false, nullptr, GetTeleportType());
1821 }
1822 }
1823 }
1824 else
1825 {
1826 // #TODO: Get this working in the future?
1827 // I am currently skipping smoothing on rotation operations
1828 /*if ((!OldRotation.Equals(NewRotation, 1e-5f)))// || Velocity.IsNearlyZero()))
1829 {
1830 BaseVRCharacterOwner->NetSmoother->SetRelativeLocation(FVector::ZeroVector);
1831 UpdatedComponent->SetWorldLocationAndRotation(NewLocation, NewRotation, false, nullptr, GetTeleportType());
1832 ClientData->MeshTranslationOffset = FVector::ZeroVector;
1833 ClientData->MeshRotationOffset = ClientData->MeshRotationTarget;
1834 bNetworkSmoothingComplete = true;
1835 }
1836 else*/
1837 {
1838 // Calc rotation needed to keep current world rotation after UpdatedComponent moves.
1839 // Take difference between where we were rotated before, and where we're going
1840 ClientData->MeshRotationOffset = FQuat::Identity;// (NewRotation.Inverse() * OldRotation) * ClientData->MeshRotationOffset;
1841 ClientData->MeshRotationTarget = FQuat::Identity;
1842
1843 const FScopedPreventAttachedComponentMove PreventMeshMove(BaseVRCharacterOwner->NetSmoother);
1844 UpdatedComponent->SetWorldLocationAndRotation(NewLocation, NewRotation, false, nullptr, GetTeleportType());
1845 }
1846 }
1847
1849 // Update smoothing timestamps
1850
1851 // If running ahead, pull back slightly. This will cause the next delta to seem slightly longer, and cause us to lerp to it slightly slower.
1852 if (ClientData->SmoothingClientTimeStamp > ClientData->SmoothingServerTimeStamp)
1853 {
1854 const double OldClientTimeStamp = ClientData->SmoothingClientTimeStamp;
1855 ClientData->SmoothingClientTimeStamp = FMath::LerpStable(ClientData->SmoothingServerTimeStamp, OldClientTimeStamp, 0.5);
1856
1857 UE_LOG(LogVRBaseCharacterMovement, VeryVerbose, TEXT("SmoothCorrection: Pull back client from ClientTimeStamp: %.6f to %.6f, ServerTimeStamp: %.6f for %s"),
1858 OldClientTimeStamp, ClientData->SmoothingClientTimeStamp, ClientData->SmoothingServerTimeStamp, *GetNameSafe(CharacterOwner));
1859 }
1860
1861 // Using server timestamp lets us know how much time actually elapsed, regardless of packet lag variance.
1862 double OldServerTimeStamp = ClientData->SmoothingServerTimeStamp;
1863
1864 if (bIsSimulatedProxy)
1865 {
1866 // This value is normally only updated on the server, however some code paths might try to read it instead of the replicated value so copy it for proxies as well.
1867 ServerLastTransformUpdateTimeStamp = CharacterOwner->GetReplicatedServerLastTransformUpdateTimeStamp();
1868 }
1869 ClientData->SmoothingServerTimeStamp = ServerLastTransformUpdateTimeStamp;
1870
1871 // Initial update has no delta.
1872 if (ClientData->LastCorrectionTime == 0)
1873 {
1874 ClientData->SmoothingClientTimeStamp = ClientData->SmoothingServerTimeStamp;
1875 OldServerTimeStamp = ClientData->SmoothingServerTimeStamp;
1876 }
1877
1878 // Don't let the client fall too far behind or run ahead of new server time.
1879 const double ServerDeltaTime = ClientData->SmoothingServerTimeStamp - OldServerTimeStamp;
1880 const double MaxOffset = ClientData->MaxClientSmoothingDeltaTime;
1881 const double MinOffset = FMath::Min(double(ClientData->SmoothNetUpdateTime), MaxOffset);
1882
1883 // MaxDelta is the farthest behind we're allowed to be after receiving a new server time.
1884 const double MaxDelta = FMath::Clamp(ServerDeltaTime * 1.25, MinOffset, MaxOffset);
1885 ClientData->SmoothingClientTimeStamp = FMath::Clamp(ClientData->SmoothingClientTimeStamp, ClientData->SmoothingServerTimeStamp - MaxDelta, ClientData->SmoothingServerTimeStamp);
1886
1887 // Compute actual delta between new server timestamp and client simulation.
1888 ClientData->LastCorrectionDelta = ClientData->SmoothingServerTimeStamp - ClientData->SmoothingClientTimeStamp;
1889 ClientData->LastCorrectionTime = MyWorld->GetTimeSeconds();
1890
1891 UE_LOG(LogVRBaseCharacterMovement, VeryVerbose, TEXT("SmoothCorrection: WorldTime: %.6f, ServerTimeStamp: %.6f, ClientTimeStamp: %.6f, Delta: %.6f for %s"),
1892 MyWorld->GetTimeSeconds(), ClientData->SmoothingServerTimeStamp, ClientData->SmoothingClientTimeStamp, ClientData->LastCorrectionDelta, *GetNameSafe(CharacterOwner));
1893 /*
1894 Visualize network smoothing was here, removed it
1895 */
1896 }
1897}
1898
1900{
1901 if (!HasValidData() || NetworkSmoothingMode == ENetworkSmoothingMode::Disabled)
1902 {
1903 return;
1904 }
1905
1906 // We shouldn't be running this on a server that is not a listen server.
1907 checkSlow(GetNetMode() != NM_DedicatedServer);
1908 checkSlow(GetNetMode() != NM_Standalone);
1909
1910 // Only client proxies or remote clients on a listen server should run this code.
1911 const bool bIsSimulatedProxy = (CharacterOwner->GetLocalRole() == ROLE_SimulatedProxy);
1912 const bool bIsRemoteAutoProxy = (CharacterOwner->GetRemoteRole() == ROLE_AutonomousProxy);
1913 if (!ensure(bIsSimulatedProxy || bIsRemoteAutoProxy))
1914 {
1915 return;
1916 }
1917
1918 SmoothClientPosition_Interpolate(DeltaSeconds);
1919
1920 //SmoothClientPosition_UpdateVisuals(); No mesh, don't bother to run this
1922}
1923
1925{
1926 //SCOPE_CYCLE_COUNTER(STAT_CharacterMovementSmoothClientPosition_Visual);
1927 FNetworkPredictionData_Client_Character* ClientData = GetPredictionData_Client_Character();
1928
1929 if (!BaseVRCharacterOwner || !ClientData)
1930 return;
1931
1932 if (ClientData)
1933 {
1934 if (NetworkSmoothingMode == ENetworkSmoothingMode::Linear)
1935 {
1936 // Erased most of the code here, check back in later
1937 const FVector NewRelLocation = ClientData->MeshRotationOffset.UnrotateVector(ClientData->MeshTranslationOffset);// + CharacterOwner->GetBaseTranslationOffset();
1938 BaseVRCharacterOwner->NetSmoother->SetRelativeLocation(NewRelLocation);
1939 }
1940 else if (NetworkSmoothingMode == ENetworkSmoothingMode::Exponential)
1941 {
1942 // Adjust mesh location and rotation
1943 const FVector NewRelTranslation = UpdatedComponent->GetComponentToWorld().InverseTransformVectorNoScale(ClientData->MeshTranslationOffset);// +CharacterOwner->GetBaseTranslationOffset();
1944 const FQuat NewRelRotation = ClientData->MeshRotationOffset;// *CharacterOwner->GetBaseRotationOffset();
1945 //Basechar->NetSmoother->SetRelativeLocation(NewRelTranslation);
1946
1947 BaseVRCharacterOwner->NetSmoother->SetRelativeLocationAndRotation(NewRelTranslation, NewRelRotation);
1948
1949 }
1950 else if (NetworkSmoothingMode == ENetworkSmoothingMode::Replay)
1951 {
1952 if (!UpdatedComponent->GetComponentQuat().Equals(ClientData->MeshRotationOffset, SCENECOMPONENT_QUAT_TOLERANCE))// || !UpdatedComponent->GetComponentLocation().Equals(ClientData->MeshTranslationOffset, KINDA_SMALL_NUMBER))
1953 {
1954 //UpdatedComponent->SetWorldLocation(ClientData->MeshTranslationOffset);
1955 UpdatedComponent->SetWorldLocationAndRotation(ClientData->MeshTranslationOffset, ClientData->MeshRotationOffset);
1956 }
1957 }
1958 else
1959 {
1960 // Unhandled mode
1961 }
1962 }
1963}
1964
1966{
1967 bHasRequestedVelocity = bNewHasRequestedVelocity;
1968}
1969
1971{
1972 return ((MovementMode == MOVE_Custom) && (CustomMovementMode == (uint8)EVRCustomMovementMode::VRMOVE_Climbing)) && UpdatedComponent;
1973}
1974
1980
1985
1987{
1988 // If is a custom or VR custom movement mode
1989 //int32 MovementFlags = (Flags >> 2) & 15;
1990 //VRReplicatedMovementMode = (EVRConjoinedMovementModes)MovementFlags;
1991
1992 //bWantsToSnapTurn = ((Flags & FSavedMove_VRBaseCharacter::FLAG_SnapTurn) != 0);
1993
1994 Super::UpdateFromCompressedFlags(Flags);
1995}
1996
1998{
1999 // Match FVector_NetQuantize100 (2 decimal place of precision).
2000 InMovement.X = FMath::RoundToFloat(InMovement.X * 100.f) / 100.f;
2001 InMovement.Y = FMath::RoundToFloat(InMovement.Y * 100.f) / 100.f;
2002 InMovement.Z = FMath::RoundToFloat(InMovement.Z * 100.f) / 100.f;
2003 return InMovement;
2004}
EVRMoveActionVelocityRetention
UENUM(Blueprintable)
EVRMoveActionDataReq
UENUM(Blueprintable)
EVRMoveAction
UENUM(Blueprintable)
@ VRMOVEACTION_StopAllMovement
@ VRMOVEACTION_PauseTracking
@ VRMOVEACTION_SetRotation
EVRCustomMovementMode
UENUM(BlueprintType)
EVRConjoinedMovementModes
UENUM(BlueprintType)
DEFINE_LOG_CATEGORY(LogVRBaseCharacterMovement)
FVRPlayerNetworkCorrectedSignature OnCharacterNetworkCorrected_Bind
UPROPERTY(BlueprintAssignable, Category = "VRMovement")
USceneComponent * NetSmoother
UPROPERTY(Category = VRBaseCharacter, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess ...
UGripMotionControllerComponent * RightMotionController
UPROPERTY(Category = VRBaseCharacter, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess ...
FVRPlayerTeleportedSignature OnCharacterTeleported_Bind
UPROPERTY(BlueprintAssignable, Category = "VRMovement")
FTransform OffsetComponentToWorld
UPROPERTY(BlueprintReadOnly, Transient, Category = "VRExpansionLibrary")
void UpdateClimbingMovement(float DeltaTime)
UFUNCTION(BlueprintNativeEvent, meta = (DisplayName = "UpdateClimbingMovement", ScriptName = "UpdateC...
UReplicatedVRCameraComponent * VRReplicatedCamera
UPROPERTY(Category = VRBaseCharacter, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess ...
UGripMotionControllerComponent * LeftMotionController
UPROPERTY(Category = VRBaseCharacter, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess ...
UParentRelativeAttachmentComponent * ParentRelativeAttachment
UPROPERTY(Category = VRBaseCharacter, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess ...
AVRPlayerController * OwningVRPlayerController
UPROPERTY(Transient, DuplicateTransient)
void TeleportMoveGrips(bool bTeleportPhysicsGrips=true, bool bIsForPostTeleport=false)
UFUNCTION(BlueprintCallable, Category = "GripMotionController")
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = VRExpansionLibrary)
bool bUpdateInCharacterMovement
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRExpansionLibrary")
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = VRExpansionLibrary)
float VRClimbingStepUpMaxSize
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement|Climbing")
void SetHasRequestedVelocity(bool bNewHasRequestedVelocity)
void PerformMoveAction_Custom(EVRMoveAction MoveActionToPerform, EVRMoveActionDataReq DataRequirementsForMoveAction, FVector MoveActionVector, FRotator MoveActionRotator, uint8 MoveActionFlags=0)
UFUNCTION(BlueprintCallable, Category = "VRMovement")
void PerformMoveAction_SetTrackingPaused(bool bNewTrackingPaused)
UFUNCTION(BlueprintCallable, Category = "VRMovement")
float VRClimbingEdgeRejectDistance
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement|Climbing")
virtual bool VRClimbStepUp(const FVector &GravDir, const FVector &Delta, const FHitResult &InHit, FStepDownResult *OutStepDownResult=nullptr)
virtual bool DoMASnapTurn(FVRMoveActionContainer &MoveAction)
float VRClimbingStepUpMultiplier
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement|Climbing")
virtual bool VerifyClientTimeStamp(float TimeStamp, FNetworkPredictionData_Server_Character &ServerData) override
virtual void PerformMovement(float DeltaSeconds) override
float VRLowGravWallFrictionScaler
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement|LowGrav", meta = (ClampMin = "0....
void OnMovementModeChanged(EMovementMode PreviousMovementMode, uint8 PreviousCustomMode) override
virtual void PhysCustom(float deltaTime, int32 Iterations) override
virtual bool ForcePositionUpdate(float DeltaTime) override
FVector RewindVRMovement()
UFUNCTION(BlueprintCallable, Category = "VRMovement")
virtual void OnClientCorrectionReceived(class FNetworkPredictionData_Client_Character &ClientData, float TimeStamp, FVector NewLocation, FVector NewVelocity, UPrimitiveComponent *NewBase, FName NewBaseBoneName, bool bHasBase, bool bBaseRelativePosition, uint8 ServerMovementMode) override
virtual void PhysCustom_Climbing(float deltaTime, int32 Iterations)
virtual void StoreSetTrackingPaused(bool bNewTrackingPaused)
virtual void SetUpdatedComponent(USceneComponent *NewUpdatedComponent)
virtual void PhysCustom_LowGrav(float deltaTime, int32 Iterations)
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override
void SetClimbingMode(bool bIsClimbing)
UFUNCTION(BlueprintCallable, Category = "VRMovement|Climbing")
virtual float SlideAlongSurface(const FVector &Delta, float Time, const FVector &Normal, FHitResult &Hit, bool bHandleImpact) override
bool bCapHMDMovementToMaxMovementSpeed
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement")
FVROnPerformClimbingStepUp OnPerformClimbingStepUp
UPROPERTY(BlueprintAssignable, Category = "VRMovement")
float VRClimbingStepHeight
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement|Climbing")
virtual void SmoothClientPosition(float DeltaSeconds) override
void PerformMoveAction_SnapTurn(float SnapTurnDeltaYaw, EVRMoveActionVelocityRetention VelocityRetention=EVRMoveActionVelocityRetention::VRMOVEACTION_Velocity_None, bool bFlagGripTeleport=false, bool bFlagCharacterTeleport=false)
UFUNCTION(BlueprintCallable, Category = "VRMovement")
FVector RoundDirectMovement(FVector InMovement) const
void SetCrouchedHalfHeight(float NewCrouchedHalfHeight)
UFUNCTION(BlueprintCallable, Category = "VRMovement")
bool SetDefaultPostClimbMovementOnStepUp
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement|Climbing")
void PerformMoveAction_Teleport(FVector TeleportLocation, FRotator TeleportRotation, EVRMoveActionVelocityRetention VelocityRetention=EVRMoveActionVelocityRetention::VRMOVEACTION_Velocity_None, bool bSkipEncroachmentCheck=false)
UFUNCTION(BlueprintCallable, Category = "VRMovement")
virtual void ComputeFloorDist(const FVector &CapsuleLocation, float LineDistance, float SweepDistance, FFindFloorResult &OutFloorResult, float SweepRadius, const FHitResult *DownwardSweepResult=NULL) const override
bool bIgnoreSimulatingComponentsInFloorCheck
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement")
virtual void MoveAutonomous(float ClientTimeStamp, float DeltaTime, uint8 CompressedFlags, const FVector &NewAccel) override
UVRBaseCharacterMovementComponent(const FObjectInitializer &ObjectInitializer=FObjectInitializer::Get())
FVRCharacterNetworkMoveDataContainer VRNetworkMoveDataContainer
virtual bool DoMAPauseTracking(FVRMoveActionContainer &MoveAction)
FVRCharacterMoveResponseDataContainer VRMoveResponseDataContainer
AVRBaseCharacter * BaseVRCharacterOwner
UPROPERTY(Transient, DuplicateTransient)
virtual void SimulatedTick(float DeltaSeconds) override
EVRConjoinedMovementModes GetReplicatedMovementMode()
UFUNCTION(BlueprintPure, Category = "VRMovement")
bool bHoldPositionOnTrackingLossThresholdHit
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement")
virtual bool DoMAStopAllMovement(FVRMoveActionContainer &MoveAction)
EVRConjoinedMovementModes DefaultPostClimbMovement
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement|Climbing")
FORCEINLINE void ApplyReplicatedMovementMode(EVRConjoinedMovementModes &NewMovementMode, bool bClearMovementMode=false)
void AddCustomReplicatedMovement(FVector Movement)
UFUNCTION(BlueprintCallable, Category = "BaseVRCharacterMovementComponent|VRLocations")
virtual void OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult &Result)
float VRWallSlideScaler
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement", meta = (ClampMin = "0....
bool bUseClientControlRotation
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRBaseCharacterMovementComponent")
bool bDisableSimulatedTickWhenSmoothingMovement
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRBaseCharacterMovementComponent|Smoothing")
bool VRLowGravIgnoresDefaultFluidFriction
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement|LowGrav")
FVector GetCustomInputVector()
UFUNCTION(BlueprintCallable, Category = "VRMovement")
float VREdgeRejectDistance
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement")
virtual void SmoothCorrection(const FVector &OldLocation, const FQuat &OldRotation, const FVector &NewLocation, const FQuat &NewRotation) override
void SetReplicatedMovementMode(EVRConjoinedMovementModes NewMovementMode)
UFUNCTION(BlueprintCallable, Category = "VRMovement")
bool bRunControlRotationInMovementComponent
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement")
bool bClampClimbingStepUp
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement|Climbing")
float VRClimbingMaxReleaseVelocitySize
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement|Climbing")
void PerformMoveAction_StopAllMovement()
UFUNCTION(BlueprintCallable, Category = "VRMovement")
float TrackingLossThreshold
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VRMovement", meta = (ClampMin = "0....
virtual bool DoMATeleport(FVRMoveActionContainer &MoveAction)
void PerformMoveAction_SetRotation(float NewYaw, EVRMoveActionVelocityRetention VelocityRetention=EVRMoveActionVelocityRetention::VRMOVEACTION_Velocity_None, bool bFlagGripTeleport=false, bool bFlagCharacterTeleport=false)
UFUNCTION(BlueprintCallable, Category = "VRMovement")
virtual bool DoMASetRotation(FVRMoveActionContainer &MoveAction)
void ClearCustomReplicatedMovement()
UFUNCTION(BlueprintCallable, Category = "BaseVRCharacterMovementComponent|VRLocations")
virtual void ApplyNetworkMovementMode(const uint8 ReceivedMode) override
UCLASS(Blueprintable, meta = (BlueprintSpawnableComponent), ClassGroup = VRExpansionLibrary)
TArray< FVRMoveActionContainer > MoveActions
UPROPERTY()
EVRMoveActionVelocityRetention VelRetentionSetting
UPROPERTY()
EVRMoveActionDataReq MoveActionDataReq
UPROPERTY()
EVRMoveAction MoveAction
UPROPERTY()