void UFlareSpacecraftNavigationSystem::PushCommandRotation(const FVector& RotationTarget, const FVector& LocalShipAxis) { if (RotationTarget.IsNearlyZero() || LocalShipAxis.IsNearlyZero()) { return; } FFlareShipCommandData Command; Command.Type = EFlareCommandDataType::CDT_Rotation; Command.RotationTarget = RotationTarget; Command.LocalShipAxis = LocalShipAxis; FLOGV("UFlareSpacecraftNavigationSystem::PushCommandRotation RotationTarget '%s'", *RotationTarget.ToString()); FLOGV("UFlareSpacecraftNavigationSystem::PushCommandRotation LocalShipAxis '%s'", *LocalShipAxis.ToString()); PushCommand(Command); }
void FAnimNode_HandIKRetargeting::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, const FBoneContainer& RequiredBones, FA2CSPose& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { checkSlow(OutBoneTransforms.Num() == 0); // Get component space transforms for all of our IK and FK bones. FTransform const RightHandFKTM = MeshBases.GetComponentSpaceTransform(RightHandFK.BoneIndex); FTransform const LeftHandFKTM = MeshBases.GetComponentSpaceTransform(LeftHandFK.BoneIndex); FTransform const RightHandIKTM = MeshBases.GetComponentSpaceTransform(RightHandIK.BoneIndex); FTransform const LeftHandIKTM = MeshBases.GetComponentSpaceTransform(LeftHandIK.BoneIndex); // Compute weight FK and IK hand location. And translation from IK to FK. FVector const FKLocation = FMath::Lerp<FVector>(LeftHandFKTM.GetTranslation(), RightHandFKTM.GetTranslation(), HandFKWeight); FVector const IKLocation = FMath::Lerp<FVector>(LeftHandIKTM.GetTranslation(), RightHandIKTM.GetTranslation(), HandFKWeight); FVector const IK_To_FK_Translation = FKLocation - IKLocation; // If we're not translating, don't send any bones to update. if (!IK_To_FK_Translation.IsNearlyZero()) { // Move desired bones for (int32 BoneIndex = 0; BoneIndex < IKBonesToMove.Num(); BoneIndex++) { FBoneReference const & BoneReference = IKBonesToMove[BoneIndex]; if (BoneReference.IsValid(RequiredBones)) { FTransform BoneTransform = MeshBases.GetComponentSpaceTransform(BoneReference.BoneIndex); BoneTransform.AddToTranslation(IK_To_FK_Translation); OutBoneTransforms.Add(FBoneTransform(BoneReference.BoneIndex, BoneTransform)); } } } }
void FAnimNode_HandIKRetargeting::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, FCSPose<FCompactPose>& MeshBases, TArray<FBoneTransform>& OutBoneTransforms) { checkSlow(OutBoneTransforms.Num() == 0); const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer(); // Get component space transforms for all of our IK and FK bones. const FTransform& RightHandFKTM = MeshBases.GetComponentSpaceTransform(RightHandFK.GetCompactPoseIndex(BoneContainer)); const FTransform& LeftHandFKTM = MeshBases.GetComponentSpaceTransform(LeftHandFK.GetCompactPoseIndex(BoneContainer)); const FTransform& RightHandIKTM = MeshBases.GetComponentSpaceTransform(RightHandIK.GetCompactPoseIndex(BoneContainer)); const FTransform& LeftHandIKTM = MeshBases.GetComponentSpaceTransform(LeftHandIK.GetCompactPoseIndex(BoneContainer)); // Compute weight FK and IK hand location. And translation from IK to FK. FVector const FKLocation = FMath::Lerp<FVector>(LeftHandFKTM.GetTranslation(), RightHandFKTM.GetTranslation(), HandFKWeight); FVector const IKLocation = FMath::Lerp<FVector>(LeftHandIKTM.GetTranslation(), RightHandIKTM.GetTranslation(), HandFKWeight); FVector const IK_To_FK_Translation = FKLocation - IKLocation; // If we're not translating, don't send any bones to update. if (!IK_To_FK_Translation.IsNearlyZero()) { // Move desired bones for (const FBoneReference& BoneReference : IKBonesToMove) { if (BoneReference.IsValid(BoneContainer)) { FCompactPoseBoneIndex BoneIndex = BoneReference.GetCompactPoseIndex(BoneContainer); FTransform BoneTransform = MeshBases.GetComponentSpaceTransform(BoneIndex); BoneTransform.AddToTranslation(IK_To_FK_Translation); OutBoneTransforms.Add(FBoneTransform(BoneIndex, BoneTransform)); } } } }
void UCollidingPawnMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); // some amazing coding here // Make sure that every thing is still valid and we are allowed to move if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime)) { return; } // Get (and then clear) the movement vector that we set in ACollidingPawn::Tick FVector DesiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f)*DeltaTime*150.0f; if (!DesiredMovementThisFrame.IsNearlyZero()) { FHitResult Hit; SafeMoveUpdatedComponent(DesiredMovementThisFrame, UpdatedComponent->GetComponentRotation(), true, Hit); // If we bumped into something, try to slide along it // this check is important because otherwise the pawn will simply stop /* if (Hit.IsValidBlockingHit()) { SlideAlongSurface(DesiredMovementThisFrame, 1.f - Hit.Time, Hit.Normal, Hit); }*/ } }
void FBasedPosition::Set(class AActor* InBase, const FVector& InPosition) { if( InPosition.IsNearlyZero() ) { Base = NULL; Position = FVector::ZeroVector; return; } Base = (InBase && InBase->GetRootComponent() && (InBase->GetRootComponent()->Mobility != EComponentMobility::Static)) ? InBase : NULL; if( Base ) { const FVector BaseLocation = Base->GetActorLocation(); const FRotator BaseRotation = Base->GetActorRotation(); CachedBaseLocation = BaseLocation; CachedBaseRotation = BaseRotation; CachedTransPosition = InPosition; Position = FTransform(BaseRotation).InverseTransformPosition(InPosition - BaseLocation); } else { Position = InPosition; } }
float UMovementComponent::SlideAlongSurface(const FVector& Delta, float Time, const FVector& Normal, FHitResult& Hit, bool bHandleImpact) { if (!Hit.bBlockingHit) { return 0.f; } float PercentTimeApplied = 0.f; const FVector OldHitNormal = Normal; FVector SlideDelta = ComputeSlideVector(Delta, Time, Normal, Hit); if ((SlideDelta | Delta) > 0.f) { const FQuat Rotation = UpdatedComponent->GetComponentQuat(); SafeMoveUpdatedComponent(SlideDelta, Rotation, true, Hit); const float FirstHitPercent = Hit.Time; PercentTimeApplied = FirstHitPercent; if (Hit.IsValidBlockingHit()) { // Notify first impact if (bHandleImpact) { HandleImpact(Hit, FirstHitPercent * Time, SlideDelta); } // Compute new slide normal when hitting multiple surfaces. TwoWallAdjust(SlideDelta, Hit, OldHitNormal); // Only proceed if the new direction is of significant length and not in reverse of original attempted move. if (!SlideDelta.IsNearlyZero(1e-3f) && (SlideDelta | Delta) > 0.f) { // Perform second move SafeMoveUpdatedComponent(SlideDelta, Rotation, true, Hit); const float SecondHitPercent = Hit.Time * (1.f - FirstHitPercent); PercentTimeApplied += SecondHitPercent; // Notify second impact if (bHandleImpact && Hit.bBlockingHit) { HandleImpact(Hit, SecondHitPercent * Time, SlideDelta); } } } return FMath::Clamp(PercentTimeApplied, 0.f, 1.f); } return 0.f; }
bool FVertexSnappingImpl::SnapDragLocationToNearestVertex( const FVector& BaseLocation, FVector& DragDelta, FLevelEditorViewportClient* ViewportClient ) { int32 MouseX = ViewportClient->Viewport->GetMouseX(); int32 MouseY = ViewportClient->Viewport->GetMouseY(); FVector2D MousePosition = FVector2D( MouseX, MouseY ) ; EAxisList::Type CurrentAxis = ViewportClient->GetCurrentWidgetAxis(); bool bSnapped = false; if( !DragDelta.IsNearlyZero() ) { FVector Direction = DragDelta.GetSafeNormal(); FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues( ViewportClient->Viewport, ViewportClient->GetScene(), ViewportClient->EngineShowFlags ) .SetRealtimeUpdate( ViewportClient->IsRealtime() )); FSceneView* View = ViewportClient->CalcSceneView( &ViewFamily ); FVector DesiredUnsnappedLocation = BaseLocation+DragDelta; FBoxSphereBounds SnappingAreaBox( FBox( DesiredUnsnappedLocation-VertexSnappingConstants::MaxSnappingDistance, DesiredUnsnappedLocation+VertexSnappingConstants::MaxSnappingDistance ) ); FBox AllowedSnappingBox = SnappingAreaBox.GetBox(); AllowedSnappingBox += DragDelta; FPlane ActorPlane( DesiredUnsnappedLocation, Direction ); TSet< TWeakObjectPtr<AActor> > NoActorsToIgnore; FVertexSnappingArgs Args ( ActorPlane, DesiredUnsnappedLocation, ViewportClient, View, MousePosition, CurrentAxis, true ); SnapDragDelta( Args, BaseLocation, AllowedSnappingBox, NoActorsToIgnore, DragDelta ); } return bSnapped; }
void UFloatingPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime)) { return; } const AController* Controller = PawnOwner->GetController(); if (Controller && Controller->IsLocalController()) { ApplyControlInputToVelocity(DeltaTime); LimitWorldBounds(); bPositionCorrected = false; // Move actor FVector Delta = Velocity * DeltaTime; if (!Delta.IsNearlyZero(1e-6f)) { const FVector OldLocation = UpdatedComponent->GetComponentLocation(); const FRotator Rotation = UpdatedComponent->GetComponentRotation(); FVector TraceStart = OldLocation; FHitResult Hit(1.f); SafeMoveUpdatedComponent(Delta, Rotation, true, Hit); if (Hit.IsValidBlockingHit()) { HandleImpact(Hit, DeltaTime, Delta); // Try to slide the remaining distance along the surface. SlideAlongSurface(Delta, 1.f-Hit.Time, Hit.Normal, Hit, true); } // Update velocity // We don't want position changes to vastly reverse our direction (which can happen due to penetration fixups etc) if (!bPositionCorrected) { const FVector NewLocation = UpdatedComponent->GetComponentLocation(); Velocity = ((NewLocation - OldLocation) / DeltaTime); } } // Finalize UpdateComponentVelocity(); } };
void SetupNonUniformHelper(FVector& Scale3D, float& MinScale, float& MinScaleAbs, FVector& Scale3DAbs) { // if almost zero, set min scale // @todo fixme if (Scale3D.IsNearlyZero()) { // set min scale Scale3D = FVector(0.1f); } Scale3DAbs = Scale3D.GetAbs(); MinScaleAbs = Scale3DAbs.GetMin(); MinScale = FMath::Max3(Scale3D.X, Scale3D.Y, Scale3D.Z) < 0.f ? -MinScaleAbs : MinScaleAbs; //if all three values are negative make minScale negative if (FMath::IsNearlyZero(MinScale)) { // only one of them can be 0, we make sure they have mini set up correctly MinScale = 0.1f; MinScaleAbs = 0.1f; } }
FVector GetMovementBaseTangentialVelocity(const UPrimitiveComponent* MovementBase, const FName BoneName, const FVector& WorldLocation) { if (MovementBaseUtility::IsDynamicBase(MovementBase)) { if (const FBodyInstance* BodyInstance = MovementBase->GetBodyInstance(BoneName)) { const FVector BaseAngVel = BodyInstance->GetUnrealWorldAngularVelocity(); if (!BaseAngVel.IsNearlyZero()) { FVector BaseLocation; FQuat BaseRotation; if (MovementBaseUtility::GetMovementBaseTransform(MovementBase, BoneName, BaseLocation, BaseRotation)) { const FVector RadialDistanceToBase = WorldLocation - BaseLocation; const FVector TangentialVel = FVector::DegreesToRadians(BaseAngVel) ^ RadialDistanceToBase; return TangentialVel; } } } } return FVector::ZeroVector; }
void UProjectileMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { QUICK_SCOPE_CYCLE_COUNTER( STAT_ProjectileMovementComponent_TickComponent ); // skip if don't want component updated when not rendered or updated component can't move if (HasStoppedSimulation() || ShouldSkipUpdate(DeltaTime)) { return; } Super::TickComponent(DeltaTime, TickType, ThisTickFunction); AActor* ActorOwner = UpdatedComponent->GetOwner(); if ( !ActorOwner || !CheckStillInWorld() ) { return; } if (UpdatedComponent->IsSimulatingPhysics()) { return; } float RemainingTime = DeltaTime; uint32 NumBounces = 0; int32 Iterations = 0; FHitResult Hit(1.f); while( RemainingTime >= MIN_TICK_TIME && (Iterations < MaxSimulationIterations) && !ActorOwner->IsPendingKill() && !HasStoppedSimulation() ) { Iterations++; // subdivide long ticks to more closely follow parabolic trajectory const float TimeTick = ShouldUseSubStepping() ? GetSimulationTimeStep(RemainingTime, Iterations) : RemainingTime; RemainingTime -= TimeTick; Hit.Time = 1.f; const FVector OldVelocity = Velocity; const FVector MoveDelta = ComputeMoveDelta(OldVelocity, TimeTick); const FRotator NewRotation = (bRotationFollowsVelocity && !OldVelocity.IsNearlyZero(0.01f)) ? OldVelocity.Rotation() : ActorOwner->GetActorRotation(); // Move the component if (bShouldBounce) { // If we can bounce, we are allowed to move out of penetrations, so use SafeMoveUpdatedComponent which does that automatically. SafeMoveUpdatedComponent( MoveDelta, NewRotation, true, Hit ); } else { // If we can't bounce, then we shouldn't adjust if initially penetrating, because that should be a blocking hit that causes a hit event and stop simulation. TGuardValue<EMoveComponentFlags> ScopedFlagRestore(MoveComponentFlags, MoveComponentFlags | MOVECOMP_NeverIgnoreBlockingOverlaps); MoveUpdatedComponent(MoveDelta, NewRotation, true, &Hit ); } // If we hit a trigger that destroyed us, abort. if( ActorOwner->IsPendingKill() || HasStoppedSimulation() ) { return; } // Handle hit result after movement if( !Hit.bBlockingHit ) { PreviousHitTime = 1.f; bIsSliding = false; // Only calculate new velocity if events didn't change it during the movement update. if (Velocity == OldVelocity) { Velocity = ComputeVelocity(Velocity, TimeTick); } } else { // Only calculate new velocity if events didn't change it during the movement update. if (Velocity == OldVelocity) { // re-calculate end velocity for partial time Velocity = (Hit.Time > KINDA_SMALL_NUMBER) ? ComputeVelocity(OldVelocity, TimeTick * Hit.Time) : OldVelocity; } // Handle blocking hit float SubTickTimeRemaining = TimeTick * (1.f - Hit.Time); const EHandleBlockingHitResult HandleBlockingResult = HandleBlockingHit(Hit, TimeTick, MoveDelta, SubTickTimeRemaining); if (HandleBlockingResult == EHandleBlockingHitResult::Abort || HasStoppedSimulation()) { break; } else if (HandleBlockingResult == EHandleBlockingHitResult::Deflect) { NumBounces++; HandleDeflection(Hit, OldVelocity, NumBounces, SubTickTimeRemaining); PreviousHitTime = Hit.Time; PreviousHitNormal = ConstrainNormalToPlane(Hit.Normal); } else if (HandleBlockingResult == EHandleBlockingHitResult::AdvanceNextSubstep) { // Reset deflection logic to ignore this hit PreviousHitTime = 1.f; } else { // Unhandled EHandleBlockingHitResult checkNoEntry(); } // A few initial bounces should add more time and iterations to complete most of the simulation. if (NumBounces <= 2 && SubTickTimeRemaining >= MIN_TICK_TIME) { RemainingTime += SubTickTimeRemaining; Iterations--; } } } UpdateComponentVelocity(); }
bool FInstancedStaticMeshSCSEditorCustomization::HandleViewportDrag(class USceneComponent* InSceneComponent, class USceneComponent* InComponentTemplate, const FVector& InDeltaTranslation, const FRotator& InDeltaRotation, const FVector& InDeltaScale, const FVector& InPivot) { check(InSceneComponent->IsA(UInstancedStaticMeshComponent::StaticClass())); UInstancedStaticMeshComponent* InstancedStaticMeshComponentScene = CastChecked<UInstancedStaticMeshComponent>(InSceneComponent); UInstancedStaticMeshComponent* InstancedStaticMeshComponentTemplate = CastChecked<UInstancedStaticMeshComponent>(InComponentTemplate); // transform pivot into component's space const FVector LocalPivot = InstancedStaticMeshComponentScene->GetComponentToWorld().InverseTransformPosition(InPivot); // Ensure that selected instances are up-to-date ValidateSelectedInstances(InstancedStaticMeshComponentScene); bool bMovedInstance = false; check(InstancedStaticMeshComponentScene->SelectedInstances.Num() == InstancedStaticMeshComponentScene->PerInstanceSMData.Num()); for(int32 InstanceIndex = 0; InstanceIndex < InstancedStaticMeshComponentScene->SelectedInstances.Num(); InstanceIndex++) { if (InstancedStaticMeshComponentScene->SelectedInstances[InstanceIndex] && InstancedStaticMeshComponentTemplate->PerInstanceSMData.IsValidIndex(InstanceIndex)) { FMatrix& MatrixScene = InstancedStaticMeshComponentScene->PerInstanceSMData[InstanceIndex].Transform; FMatrix& MatrixTemplate = InstancedStaticMeshComponentTemplate->PerInstanceSMData[InstanceIndex].Transform; FVector Translation = MatrixScene.GetOrigin(); FRotator Rotation = MatrixScene.Rotator(); FVector Scale = MatrixScene.GetScaleVector(); FVector NewTranslation = Translation; FRotator NewRotation = Rotation; FVector NewScale = Scale; if( !InDeltaRotation.IsZero() ) { NewRotation = FRotator( InDeltaRotation.Quaternion() * Rotation.Quaternion() ); NewTranslation -= LocalPivot; NewTranslation = FRotationMatrix( InDeltaRotation ).TransformPosition( NewTranslation ); NewTranslation += LocalPivot; } NewTranslation += InDeltaTranslation; if( !InDeltaScale.IsNearlyZero() ) { const FScaleMatrix ScaleMatrix( InDeltaScale ); FVector DeltaScale3D = ScaleMatrix.TransformPosition( Scale ); NewScale = Scale + DeltaScale3D; NewTranslation -= LocalPivot; NewTranslation += ScaleMatrix.TransformPosition( NewTranslation ); NewTranslation += LocalPivot; } MatrixScene = FScaleRotationTranslationMatrix(NewScale, NewRotation, NewTranslation); MatrixTemplate = FScaleRotationTranslationMatrix(NewScale, NewRotation, NewTranslation); bMovedInstance = true; } } return bMovedInstance; }
bool FVertexSnappingImpl::SnapDraggedActorsToNearestVertex( FVector& DragDelta, FLevelEditorViewportClient* ViewportClient ) { int32 MouseX = ViewportClient->Viewport->GetMouseX(); int32 MouseY = ViewportClient->Viewport->GetMouseY(); FVector2D MousePosition = FVector2D( MouseX, MouseY ) ; EAxisList::Type CurrentAxis = ViewportClient->GetCurrentWidgetAxis(); bool bSnapped = false; if( !DragDelta.IsNearlyZero() ) { // Are there selected actors? USelection* Selection = GEditor->GetSelectedActors(); FVector Direction = DragDelta.GetSafeNormal(); FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues( ViewportClient->Viewport, ViewportClient->GetScene(), ViewportClient->EngineShowFlags ) .SetRealtimeUpdate( ViewportClient->IsRealtime() )); FSceneView* View = ViewportClient->CalcSceneView( &ViewFamily ); FVector StartLocation = ViewportClient->GetModeTools()->PivotLocation; FVector DesiredUnsnappedLocation = StartLocation+DragDelta; // Plane facing in the direction of axis movement. This is for clipping actors which are behind the desired location (they should not be considered for snap) FPlane ActorPlane( DesiredUnsnappedLocation, Direction ); TSet< TWeakObjectPtr<AActor> > ActorsToIgnore; // Snap each selected actor for ( FSelectionIterator It( GEditor->GetSelectedActorIterator() ) ; It ; ++It ) { // Build up a region that actors must be in for snapping FBoxSphereBounds SnappingAreaBox( FBox( DesiredUnsnappedLocation-VertexSnappingConstants::MaxSnappingDistance, DesiredUnsnappedLocation+VertexSnappingConstants::MaxSnappingDistance ) ); AActor* Actor = CastChecked<AActor>( *It ); if( Actor->GetRootComponent() != NULL ) { // Get a bounding box around the actor const bool bNonColliding = true; FBoxSphereBounds ActorBounds = Actor->GetComponentsBoundingBox(bNonColliding); // The allowed snapping box is a box around the selected actor plus a region around the actor that other actors must be in for snapping FBox AllowedSnappingBox = ActorBounds.GetBox(); // Extend the box to include the drag point AllowedSnappingBox += SnappingAreaBox.GetBox(); GetActorsToIgnore( Actor, ActorsToIgnore ); FVertexSnappingArgs Args ( ActorPlane, DesiredUnsnappedLocation, ViewportClient, View, MousePosition, CurrentAxis, true ); // Snap the drag delta SnapDragDelta( Args, StartLocation, AllowedSnappingBox, ActorsToIgnore, DragDelta ); } } } return bSnapped; }
void UFlareSpacecraftNavigationSystem::PhysicSubTick(float DeltaSeconds) { TArray<UActorComponent*> Engines = Spacecraft->GetComponentsByClass(UFlareEngine::StaticClass()); if (Spacecraft->GetDamageSystem()->IsPowered()) { // Linear physics FVector DeltaV = LinearTargetVelocity - Spacecraft->GetLinearVelocity(); FVector DeltaVAxis = DeltaV; DeltaVAxis.Normalize(); bool Log = false; if (Spacecraft->GetParent()->GetCompany() == Spacecraft->GetGame()->GetPC()->GetCompany()) { //Log = true; } bool HasUsedOrbitalBoost = false; float LinearMasterAlpha = 0.f; float LinearMasterBoostAlpha = 0.f; if(Log) { FLOGV("PhysicSubTick DeltaSeconds=%f", DeltaSeconds); FLOGV("PhysicSubTick LinearTargetVelocity=%s", *LinearTargetVelocity.ToString()); FLOGV("PhysicSubTick Spacecraft->GetLinearVelocity()=%s", *Spacecraft->GetLinearVelocity().ToString()); FLOGV("PhysicSubTick DeltaV=%s", *DeltaV.ToString()); } if (!DeltaV.IsNearlyZero()) { // First, try without using the boost FVector Acceleration = DeltaVAxis * GetTotalMaxThrustInAxis(Engines, -DeltaVAxis, false).Size() / Spacecraft->GetSpacecraftMass(); float AccelerationDeltaV = Acceleration.Size() * DeltaSeconds; LinearMasterAlpha = FMath::Clamp(DeltaV.Size()/ AccelerationDeltaV, 0.0f, 1.0f); // Second, if the not enought trust check with the boost if (UseOrbitalBoost && AccelerationDeltaV < DeltaV.Size() ) { FVector AccelerationWithBoost = DeltaVAxis * GetTotalMaxThrustInAxis(Engines, -DeltaVAxis, true).Size() / Spacecraft->GetSpacecraftMass(); if (AccelerationWithBoost.Size() > Acceleration.Size()) { HasUsedOrbitalBoost = true; float BoostDeltaV = (AccelerationWithBoost.Size() - Acceleration.Size()) * DeltaSeconds; float DeltaVAfterClassicalAcceleration = DeltaV.Size() - AccelerationDeltaV; LinearMasterBoostAlpha = FMath::Clamp(DeltaVAfterClassicalAcceleration/ BoostDeltaV, 0.0f, 1.0f); Acceleration = AccelerationWithBoost; } } FVector ClampedAcceleration = Acceleration.GetClampedToMaxSize(DeltaV.Size() / DeltaSeconds); Spacecraft->Airframe->SetPhysicsLinearVelocity(ClampedAcceleration * DeltaSeconds * 100, true); // Multiply by 100 because UE4 works in cm } // Angular physics FVector DeltaAngularV = AngularTargetVelocity - Spacecraft->Airframe->GetPhysicsAngularVelocity(); FVector DeltaAngularVAxis = DeltaAngularV; DeltaAngularVAxis.Normalize(); if (!DeltaAngularV.IsNearlyZero()) { FVector SimpleAcceleration = DeltaAngularVAxis * AngularAccelerationRate; // Scale with damages float TotalMaxTorqueInAxis = GetTotalMaxTorqueInAxis(Engines, DeltaAngularVAxis, false); if (!FMath::IsNearlyZero(TotalMaxTorqueInAxis)) { float DamageRatio = GetTotalMaxTorqueInAxis(Engines, DeltaAngularVAxis, true) / TotalMaxTorqueInAxis; FVector DamagedSimpleAcceleration = SimpleAcceleration * DamageRatio; FVector ClampedSimplifiedAcceleration = DamagedSimpleAcceleration.GetClampedToMaxSize(DeltaAngularV.Size() / DeltaSeconds); Spacecraft->Airframe->SetPhysicsAngularVelocity(ClampedSimplifiedAcceleration * DeltaSeconds, true); } } // Update engine alpha for (int32 EngineIndex = 0; EngineIndex < Engines.Num(); EngineIndex++) { UFlareEngine* Engine = Cast<UFlareEngine>(Engines[EngineIndex]); FVector ThrustAxis = Engine->GetThrustAxis(); float LinearAlpha = 0; float AngularAlpha = 0; if (Spacecraft->IsPresentationMode()) { LinearAlpha = true; } else if (!DeltaV.IsNearlyZero() || !DeltaAngularV.IsNearlyZero()) { if(Engine->IsA(UFlareOrbitalEngine::StaticClass())) { if(HasUsedOrbitalBoost) { LinearAlpha = (-FVector::DotProduct(ThrustAxis, DeltaVAxis) + 0.2) * LinearMasterBoostAlpha; } AngularAlpha = 0; } else { LinearAlpha = -FVector::DotProduct(ThrustAxis, DeltaVAxis) * LinearMasterAlpha; FVector EngineOffset = (Engine->GetComponentLocation() - COM) / 100; FVector TorqueDirection = FVector::CrossProduct(EngineOffset, ThrustAxis); TorqueDirection.Normalize(); if (!DeltaAngularV.IsNearlyZero() && !Engine->IsA(UFlareOrbitalEngine::StaticClass())) { AngularAlpha = -FVector::DotProduct(TorqueDirection, DeltaAngularVAxis); } } } Engine->SetAlpha(FMath::Clamp(LinearAlpha + AngularAlpha, 0.0f, 1.0f)); } } else { // Shutdown engines for (int32 EngineIndex = 0; EngineIndex < Engines.Num(); EngineIndex++) { UFlareEngine* Engine = Cast<UFlareEngine>(Engines[EngineIndex]); Engine->SetAlpha(0); } } }
void UFlareSpacecraftNavigationSystem::UpdateAngularAttitudeAuto(float DeltaSeconds) { TArray<UActorComponent*> Engines = Spacecraft->GetComponentsByClass(UFlareEngine::StaticClass()); // Rotation data FFlareShipCommandData Command; CommandData.Peek(Command); FVector TargetAxis = Command.RotationTarget; FVector LocalShipAxis = Command.LocalShipAxis; FVector AngularVelocity = Spacecraft->Airframe->GetPhysicsAngularVelocity(); FVector WorldShipAxis = Spacecraft->Airframe->GetComponentToWorld().GetRotation().RotateVector(LocalShipAxis); WorldShipAxis.Normalize(); TargetAxis.Normalize(); FVector RotationDirection = FVector::CrossProduct(WorldShipAxis, TargetAxis); if (RotationDirection.IsNearlyZero()) { RotationDirection=FVector(0,0,1); } RotationDirection.Normalize(); float Dot = FVector::DotProduct(WorldShipAxis, TargetAxis); float angle = FMath::RadiansToDegrees(FMath::Acos(Dot)); FVector DeltaVelocity = -AngularVelocity; FVector DeltaVelocityAxis = DeltaVelocity; DeltaVelocityAxis.Normalize(); float TimeToFinalVelocity; if (FMath::IsNearlyZero(DeltaVelocity.SizeSquared())) { TimeToFinalVelocity = 0; } else { FVector SimpleAcceleration = DeltaVelocityAxis * AngularAccelerationRate; // Scale with damages float DamageRatio = GetTotalMaxTorqueInAxis(Engines, DeltaVelocityAxis, true) / GetTotalMaxTorqueInAxis(Engines, DeltaVelocityAxis, false); FVector DamagedSimpleAcceleration = SimpleAcceleration * DamageRatio; FVector Acceleration = DamagedSimpleAcceleration; float AccelerationInAngleAxis = FMath::Abs(FVector::DotProduct(DamagedSimpleAcceleration, RotationDirection)); TimeToFinalVelocity = (DeltaVelocity.Size() / AccelerationInAngleAxis); } float AngleToStop = (DeltaVelocity.Size() / 2) * (TimeToFinalVelocity + DeltaSeconds); FVector RelativeResultSpeed; if (AngleToStop > angle) { RelativeResultSpeed = FVector::ZeroVector; } else { float MaxPreciseSpeed = FMath::Min((angle - AngleToStop) / DeltaSeconds, AngularMaxVelocity); RelativeResultSpeed = RotationDirection; RelativeResultSpeed *= MaxPreciseSpeed; } // Under this angle we consider the variation negligible, and ensure null delta + null speed if (angle < AngularDeadAngle && DeltaVelocity.Size() < AngularDeadAngle) { Spacecraft->Airframe->SetPhysicsAngularVelocity(FVector::ZeroVector, false); // TODO remove ClearCurrentCommand(); RelativeResultSpeed = FVector::ZeroVector; } AngularTargetVelocity = RelativeResultSpeed; }
void UMainCharacterMovementComponent::TickComponent(float deltaTime, enum ELevelTick tickType, FActorComponentTickFunction *thisTickFunction) { UPawnMovementComponent::TickComponent(deltaTime, tickType, thisTickFunction); if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(deltaTime)) { return; } if (m_character) { const FVector posInRoom = m_character->GetRelativePositionInRoom(); // pos should be center of room if (auto* room = m_character->GetCurrentRoom()) { int ladderPosInRoom = room->GetLadderPos(); bool isInsideLadderRegion = abs(posInRoom.X - ladderPosInRoom) <= (ARoom::c_ladderWidth * 0.5); // handle climbing up if (isInsideLadderRegion) { if (m_wantsToClimbUp) { if (room->HasLadderUp()) { if (auto* neighbour = room->GetNeighbour(RoomNeighbour::Up)) { m_isMoving = false; m_character->TeleportToRoom(neighbour); } } } // handle climbing down if (m_wantsToClimbDown) { if (room->HasLadderDown()) { if (auto* neighbour = room->GetNeighbour(RoomNeighbour::Down)) { m_isMoving = false; m_character->TeleportToRoom(neighbour); } } } } // handle left and right FVector desiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f) * deltaTime * m_walkSpeed; if (!desiredMovementThisFrame.IsNearlyZero()) { m_isMoving = true; if (desiredMovementThisFrame.X < 0.0f) { m_direction = FacingDirection::Left; } else if (desiredMovementThisFrame.X > 0.0f) { m_direction = FacingDirection::Right; } const float leftWallPos = room->GetLeftWallXPos(); const float rightWallPos = room->GetRightWallXPos(); FVector desiredPosInRoom = posInRoom - desiredMovementThisFrame; // no neighbour == wall // poor collision detection that won't work in low FPS (but hey, who gives a f***s?) // hint: not me if (!room->GetNeighbour(RoomNeighbour::Left) && desiredMovementThisFrame.X < 0 && desiredPosInRoom.X < leftWallPos) { desiredMovementThisFrame.X = 0; m_isMoving = false; } if (!room->GetNeighbour(RoomNeighbour::Right) && desiredMovementThisFrame.X > 0 && desiredPosInRoom.X > rightWallPos) { desiredMovementThisFrame.X = 0; m_isMoving = false; } FHitResult hit; MoveUpdatedComponent(desiredMovementThisFrame, UpdatedComponent->GetComponentRotation().Quaternion(), true, &hit); } else { m_isMoving = false; } } } m_wantsToClimbUp = false; m_wantsToClimbDown = false; }