New collisions set scene component
This commit is contained in:
parent
8a3bd44659
commit
d5774e81f1
@ -0,0 +1,409 @@
|
|||||||
|
// Oleg Petruny proprietary.
|
||||||
|
|
||||||
|
|
||||||
|
#include "CollisionSceneComponent.h"
|
||||||
|
|
||||||
|
#include "CharacterMovementComponentAsync.h"
|
||||||
|
#include "GameFramework/CheatManager.h"
|
||||||
|
|
||||||
|
#define PERF_MOVECOMPONENT_STATS 0
|
||||||
|
#define LOCTEXT_NAMESPACE "PrimitiveComponent"
|
||||||
|
|
||||||
|
CSV_DEFINE_CATEGORY(CollisionSceneComponent, false);
|
||||||
|
|
||||||
|
namespace CollisionSceneComponentStatics
|
||||||
|
{
|
||||||
|
static const FText MobilityWarnText = LOCTEXT("InvalidMove", "move");
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace PrimitiveComponentCVars
|
||||||
|
{
|
||||||
|
int32 bAllowCachedOverlapsCVar = 1;
|
||||||
|
static FAutoConsoleVariableRef CVarAllowCachedOverlaps(
|
||||||
|
TEXT("p.AllowCachedOverlaps"),
|
||||||
|
bAllowCachedOverlapsCVar,
|
||||||
|
TEXT("Primitive Component physics\n")
|
||||||
|
TEXT("0: disable cached overlaps, 1: enable (default)"),
|
||||||
|
ECVF_Default);
|
||||||
|
|
||||||
|
float InitialOverlapToleranceCVar = 0.0f;
|
||||||
|
static FAutoConsoleVariableRef CVarInitialOverlapTolerance(
|
||||||
|
TEXT("p.InitialOverlapTolerance"),
|
||||||
|
InitialOverlapToleranceCVar,
|
||||||
|
TEXT("Tolerance for initial overlapping test in PrimitiveComponent movement.\n")
|
||||||
|
TEXT("Normals within this tolerance are ignored if moving out of the object.\n")
|
||||||
|
TEXT("Dot product of movement direction and surface normal."),
|
||||||
|
ECVF_Default);
|
||||||
|
|
||||||
|
float HitDistanceToleranceCVar = 0.0f;
|
||||||
|
static FAutoConsoleVariableRef CVarHitDistanceTolerance(
|
||||||
|
TEXT("p.HitDistanceTolerance"),
|
||||||
|
HitDistanceToleranceCVar,
|
||||||
|
TEXT("Tolerance for hit distance for overlap test in PrimitiveComponent movement.\n")
|
||||||
|
TEXT("Hits that are less than this distance are ignored."),
|
||||||
|
ECVF_Default);
|
||||||
|
|
||||||
|
int32 bEnableFastOverlapCheck = 1;
|
||||||
|
static FAutoConsoleVariableRef CVarEnableFastOverlapCheck(TEXT("p.EnableFastOverlapCheck"), bEnableFastOverlapCheck, TEXT("Enable fast overlap check against sweep hits, avoiding UpdateOverlaps (for the swept component)."));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class AllocatorType>
|
||||||
|
FORCEINLINE_DEBUGGABLE int32 IndexOfOverlapFast(const TArray<FOverlapInfo, AllocatorType>& OverlapArray, const FOverlapInfo& SearchItem)
|
||||||
|
{
|
||||||
|
return OverlapArray.IndexOfByPredicate(FFastOverlapInfoCompare(SearchItem));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class AllocatorType>
|
||||||
|
FORCEINLINE_DEBUGGABLE void AddUniqueOverlapFast(TArray<FOverlapInfo, AllocatorType>& OverlapArray, FOverlapInfo&& NewOverlap)
|
||||||
|
{
|
||||||
|
if(IndexOfOverlapFast(OverlapArray, NewOverlap) == INDEX_NONE)
|
||||||
|
{
|
||||||
|
OverlapArray.Add(NewOverlap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UCollisionSceneComponent::MoveComponentImpl(const FVector& Delta, const FQuat& NewRotationQuat, bool bSweep, FHitResult* OutHit, EMoveComponentFlags MoveFlags, ETeleportType Teleport)
|
||||||
|
{
|
||||||
|
// SCOPE_CYCLE_COUNTER(STAT_MoveComponentTime); // LINKER ERROR
|
||||||
|
CSV_SCOPED_TIMING_STAT(CollisionSceneComponent, MoveComponentTime);
|
||||||
|
|
||||||
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) && PERF_MOVECOMPONENT_STATS
|
||||||
|
FScopedMoveCompTimer MoveTimer(this, Delta);
|
||||||
|
#endif // !(UE_BUILD_SHIPPING || UE_BUILD_TEST) && PERF_MOVECOMPONENT_STATS
|
||||||
|
|
||||||
|
#if defined(PERF_SHOW_MOVECOMPONENT_TAKING_LONG_TIME) || LOOKING_FOR_PERF_ISSUES
|
||||||
|
uint32 MoveCompTakingLongTime = 0;
|
||||||
|
CLOCK_CYCLES(MoveCompTakingLongTime);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// static things can move before they are registered (e.g. immediately after streaming), but not after.
|
||||||
|
if(!IsValid(this) || CheckStaticMobilityAndWarn(CollisionSceneComponentStatics::MobilityWarnText))
|
||||||
|
{
|
||||||
|
if(OutHit)
|
||||||
|
{
|
||||||
|
OutHit->Init();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConditionalUpdateComponentToWorld();
|
||||||
|
|
||||||
|
// Set up
|
||||||
|
const FVector TraceStart = GetComponentLocation();
|
||||||
|
const FVector TraceEnd = TraceStart + Delta;
|
||||||
|
float DeltaSizeSq = (TraceEnd - TraceStart).SizeSquared(); // Recalc here to account for precision loss of float addition
|
||||||
|
const FQuat InitialRotationQuat = GetComponentTransform().GetRotation();
|
||||||
|
|
||||||
|
// ComponentSweepMulti does nothing if moving < KINDA_SMALL_NUMBER in distance, so it's important to not try to sweep distances smaller than that.
|
||||||
|
const float MinMovementDistSq = (bSweep ? FMath::Square(4.f * UE_KINDA_SMALL_NUMBER) : 0.f);
|
||||||
|
if(DeltaSizeSq <= MinMovementDistSq)
|
||||||
|
{
|
||||||
|
// Skip if no vector or rotation.
|
||||||
|
if(NewRotationQuat.Equals(InitialRotationQuat, SCENECOMPONENT_QUAT_TOLERANCE))
|
||||||
|
{
|
||||||
|
// copy to optional output param
|
||||||
|
if(OutHit)
|
||||||
|
{
|
||||||
|
OutHit->Init(TraceStart, TraceEnd);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
DeltaSizeSq = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool bSkipPhysicsMove = ((MoveFlags & MOVECOMP_SkipPhysicsMove) != MOVECOMP_NoFlags);
|
||||||
|
|
||||||
|
// WARNING: HitResult is only partially initialized in some paths. All data is valid only if bFilledHitResult is true.
|
||||||
|
FHitResult BlockingHit(NoInit);
|
||||||
|
BlockingHit.bBlockingHit = false;
|
||||||
|
BlockingHit.Time = 1.f;
|
||||||
|
bool bFilledHitResult = false;
|
||||||
|
bool bMoved = false;
|
||||||
|
bool bIncludesOverlapsAtEnd = false;
|
||||||
|
bool bRotationOnly = false;
|
||||||
|
TInlineOverlapInfoArray PendingOverlaps;
|
||||||
|
AActor* const Actor = GetOwner();
|
||||||
|
|
||||||
|
if(!bSweep)
|
||||||
|
{
|
||||||
|
// not sweeping, just go directly to the new transform
|
||||||
|
bMoved = InternalSetWorldLocationAndRotation(TraceEnd, NewRotationQuat, bSkipPhysicsMove, Teleport);
|
||||||
|
bRotationOnly = (DeltaSizeSq == 0);
|
||||||
|
bIncludesOverlapsAtEnd = bRotationOnly && (AreSymmetricRotations(InitialRotationQuat, NewRotationQuat, GetComponentScale())) && IsQueryCollisionEnabled();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TArray<FHitResult> Hits;
|
||||||
|
FVector NewLocation = TraceStart;
|
||||||
|
|
||||||
|
// Perform movement collision checking if needed for this actor.
|
||||||
|
const bool bCollisionEnabled = IsQueryCollisionEnabled();
|
||||||
|
UWorld* const MyWorld = GetWorld();
|
||||||
|
if(MyWorld && bCollisionEnabled && (DeltaSizeSq > 0.f))
|
||||||
|
{
|
||||||
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
||||||
|
if(!IsRegistered() && !MyWorld->bIsTearingDown)
|
||||||
|
{
|
||||||
|
if(Actor)
|
||||||
|
{
|
||||||
|
ensureMsgf(IsRegistered(), TEXT("%s MovedComponent %s not registered during sweep (IsValid %d)"), *Actor->GetName(), *GetName(), IsValid(Actor));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ //-V523
|
||||||
|
ensureMsgf(IsRegistered(), TEXT("Non-actor MovedComponent %s not registered during sweep"), *GetFullName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) && PERF_MOVECOMPONENT_STATS
|
||||||
|
MoveTimer.bDidLineCheck = true;
|
||||||
|
#endif
|
||||||
|
static const FName TraceTagName = TEXT("MoveComponent");
|
||||||
|
const bool bForceGatherOverlaps = !ShouldCheckOverlapFlagToQueueOverlaps(*this);
|
||||||
|
FComponentQueryParams Params(SCENE_QUERY_STAT(MoveComponent), Actor);
|
||||||
|
FCollisionResponseParams ResponseParam;
|
||||||
|
InitSweepCollisionParams(Params, ResponseParam);
|
||||||
|
Params.bIgnoreTouches |= !(GetGenerateOverlapEvents() || bForceGatherOverlaps);
|
||||||
|
Params.TraceTag = TraceTagName;
|
||||||
|
|
||||||
|
bool const bHadBlockingHit = MyWorld->ComponentSweepMulti(Hits, this, TraceStart, TraceEnd, InitialRotationQuat, Params);
|
||||||
|
|
||||||
|
if(Hits.Num() > 0)
|
||||||
|
{
|
||||||
|
const float DeltaSize = FMath::Sqrt(DeltaSizeSq);
|
||||||
|
for(int32 HitIdx = 0; HitIdx < Hits.Num(); HitIdx++)
|
||||||
|
{
|
||||||
|
FUpdatedComponentAsyncInput::PullBackHit(Hits[HitIdx], TraceStart, TraceEnd, DeltaSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we had a valid blocking hit, store it.
|
||||||
|
// If we are looking for overlaps, store those as well.
|
||||||
|
int32 FirstNonInitialOverlapIdx = INDEX_NONE;
|
||||||
|
if(bHadBlockingHit || (GetGenerateOverlapEvents() || bForceGatherOverlaps))
|
||||||
|
{
|
||||||
|
int32 BlockingHitIndex = INDEX_NONE;
|
||||||
|
float BlockingHitNormalDotDelta = UE_BIG_NUMBER;
|
||||||
|
for(int32 HitIdx = 0; HitIdx < Hits.Num(); HitIdx++)
|
||||||
|
{
|
||||||
|
const FHitResult& TestHit = Hits[HitIdx];
|
||||||
|
|
||||||
|
if(TestHit.bBlockingHit)
|
||||||
|
{
|
||||||
|
if(!FUpdatedComponentAsyncInput::ShouldIgnoreHitResult(MyWorld, TestHit, Delta, Actor, MoveFlags) && !ShouldComponentIgnoreHitResult(TestHit, MoveFlags))
|
||||||
|
{
|
||||||
|
if(TestHit.bStartPenetrating)
|
||||||
|
{
|
||||||
|
// We may have multiple initial hits, and want to choose the one with the normal most opposed to our movement.
|
||||||
|
const float NormalDotDelta = (TestHit.ImpactNormal | Delta);
|
||||||
|
if(NormalDotDelta < BlockingHitNormalDotDelta)
|
||||||
|
{
|
||||||
|
BlockingHitNormalDotDelta = NormalDotDelta;
|
||||||
|
BlockingHitIndex = HitIdx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(BlockingHitIndex == INDEX_NONE)
|
||||||
|
{
|
||||||
|
// First non-overlapping blocking hit should be used, if an overlapping hit was not.
|
||||||
|
// This should be the only non-overlapping blocking hit, and last in the results.
|
||||||
|
BlockingHitIndex = HitIdx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(GetGenerateOverlapEvents() || bForceGatherOverlaps)
|
||||||
|
{
|
||||||
|
UPrimitiveComponent* OverlapComponent = TestHit.Component.Get();
|
||||||
|
if(OverlapComponent && (OverlapComponent->GetGenerateOverlapEvents() || bForceGatherOverlaps))
|
||||||
|
{
|
||||||
|
if(!FUpdatedComponentAsyncInput::ShouldIgnoreOverlapResult(MyWorld, Actor, *this, TestHit.HitObjectHandle.FetchActor(), *OverlapComponent))
|
||||||
|
{
|
||||||
|
// don't process touch events after initial blocking hits
|
||||||
|
if(BlockingHitIndex >= 0 && TestHit.Time > Hits[BlockingHitIndex].Time)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(FirstNonInitialOverlapIdx == INDEX_NONE && TestHit.Time > 0.f)
|
||||||
|
{
|
||||||
|
// We are about to add the first non-initial overlap.
|
||||||
|
FirstNonInitialOverlapIdx = PendingOverlaps.Num();
|
||||||
|
}
|
||||||
|
|
||||||
|
// cache touches
|
||||||
|
AddUniqueOverlapFast(PendingOverlaps, FOverlapInfo(TestHit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update blocking hit, if there was a valid one.
|
||||||
|
if(BlockingHitIndex >= 0)
|
||||||
|
{
|
||||||
|
BlockingHit = Hits[BlockingHitIndex];
|
||||||
|
bFilledHitResult = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update NewLocation based on the hit result
|
||||||
|
if(!BlockingHit.bBlockingHit)
|
||||||
|
{
|
||||||
|
NewLocation = TraceEnd;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
check(bFilledHitResult);
|
||||||
|
NewLocation = TraceStart + (BlockingHit.Time * (TraceEnd - TraceStart));
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
const FVector ToNewLocation = (NewLocation - TraceStart);
|
||||||
|
if(ToNewLocation.SizeSquared() <= MinMovementDistSq)
|
||||||
|
{
|
||||||
|
// We don't want really small movements to put us on or inside a surface.
|
||||||
|
NewLocation = TraceStart;
|
||||||
|
BlockingHit.Time = 0.f;
|
||||||
|
|
||||||
|
// Remove any pending overlaps after this point, we are not going as far as we swept.
|
||||||
|
if(FirstNonInitialOverlapIdx != INDEX_NONE)
|
||||||
|
{
|
||||||
|
PendingOverlaps.SetNum(FirstNonInitialOverlapIdx, EAllowShrinking::No);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bIncludesOverlapsAtEnd = AreSymmetricRotations(InitialRotationQuat, NewRotationQuat, GetComponentScale());
|
||||||
|
|
||||||
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
||||||
|
if(UCheatManager::IsDebugCapsuleSweepPawnEnabled() && BlockingHit.bBlockingHit && !IsZeroExtent())
|
||||||
|
{
|
||||||
|
// this is sole debug purpose to find how capsule trace information was when hit
|
||||||
|
// to resolve stuck or improve our movement system - To turn this on, use DebugCapsuleSweepPawn
|
||||||
|
APawn const* const ActorPawn = (Actor ? Cast<APawn>(Actor) : NULL);
|
||||||
|
if(ActorPawn && ActorPawn->Controller && ActorPawn->Controller->IsLocalPlayerController())
|
||||||
|
{
|
||||||
|
APlayerController const* const PC = CastChecked<APlayerController>(ActorPawn->Controller);
|
||||||
|
if(PC->CheatManager)
|
||||||
|
{
|
||||||
|
FVector CylExtent = ActorPawn->GetSimpleCollisionCylinderExtent() * FVector(1.001f, 1.001f, 1.0f);
|
||||||
|
FCollisionShape CapsuleShape = FCollisionShape::MakeCapsule(CylExtent);
|
||||||
|
PC->CheatManager->AddCapsuleSweepDebugInfo(TraceStart, TraceEnd, BlockingHit.ImpactPoint, BlockingHit.Normal, BlockingHit.ImpactNormal, BlockingHit.Location, CapsuleShape.GetCapsuleHalfHeight(), CapsuleShape.GetCapsuleRadius(), true, (BlockingHit.bStartPenetrating && BlockingHit.bBlockingHit) ? true : false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if(DeltaSizeSq > 0.f)
|
||||||
|
{
|
||||||
|
// apply move delta even if components has collisions disabled
|
||||||
|
NewLocation += Delta;
|
||||||
|
bIncludesOverlapsAtEnd = false;
|
||||||
|
}
|
||||||
|
else if(DeltaSizeSq == 0.f && bCollisionEnabled)
|
||||||
|
{
|
||||||
|
bIncludesOverlapsAtEnd = AreSymmetricRotations(InitialRotationQuat, NewRotationQuat, GetComponentScale());
|
||||||
|
bRotationOnly = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the location. This will teleport any child components as well (not sweep).
|
||||||
|
bMoved = InternalSetWorldLocationAndRotation(NewLocation, NewRotationQuat, bSkipPhysicsMove, Teleport);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle overlap notifications.
|
||||||
|
if(bMoved)
|
||||||
|
{
|
||||||
|
if(IsDeferringMovementUpdates())
|
||||||
|
{
|
||||||
|
// Defer UpdateOverlaps until the scoped move ends.
|
||||||
|
FScopedMovementUpdate* ScopedUpdate = GetCurrentScopedMovement();
|
||||||
|
if(bRotationOnly && bIncludesOverlapsAtEnd)
|
||||||
|
{
|
||||||
|
ScopedUpdate->KeepCurrentOverlapsAfterRotation(bSweep);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ScopedUpdate->AppendOverlapsAfterMove(PendingOverlaps, bSweep, bIncludesOverlapsAtEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(bIncludesOverlapsAtEnd)
|
||||||
|
{
|
||||||
|
TInlineOverlapInfoArray OverlapsAtEndLocation;
|
||||||
|
bool bHasEndOverlaps = false;
|
||||||
|
if(bRotationOnly)
|
||||||
|
{
|
||||||
|
bHasEndOverlaps = ConvertRotationOverlapsToCurrentOverlaps(OverlapsAtEndLocation, OverlappingComponents);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bHasEndOverlaps = ConvertSweptOverlapsToCurrentOverlaps(OverlapsAtEndLocation, PendingOverlaps, 0, GetComponentLocation(), GetComponentQuat());
|
||||||
|
}
|
||||||
|
TOverlapArrayView PendingOverlapsView(PendingOverlaps);
|
||||||
|
TOverlapArrayView OverlapsAtEndView(OverlapsAtEndLocation);
|
||||||
|
UpdateOverlaps(&PendingOverlapsView, true, bHasEndOverlaps ? &OverlapsAtEndView : nullptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TOverlapArrayView PendingOverlapsView(PendingOverlaps);
|
||||||
|
UpdateOverlaps(&PendingOverlapsView, true, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle blocking hit notifications. Avoid if pending kill (which could happen after overlaps).
|
||||||
|
const bool bAllowHitDispatch = !BlockingHit.bStartPenetrating || !(MoveFlags & MOVECOMP_DisableBlockingOverlapDispatch);
|
||||||
|
if(BlockingHit.bBlockingHit && bAllowHitDispatch && IsValid(this))
|
||||||
|
{
|
||||||
|
check(bFilledHitResult);
|
||||||
|
if(IsDeferringMovementUpdates())
|
||||||
|
{
|
||||||
|
FScopedMovementUpdate* ScopedUpdate = GetCurrentScopedMovement();
|
||||||
|
ScopedUpdate->AppendBlockingHitAfterMove(BlockingHit);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DispatchBlockingHit(*Actor, BlockingHit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(PERF_SHOW_MOVECOMPONENT_TAKING_LONG_TIME) || LOOKING_FOR_PERF_ISSUES
|
||||||
|
UNCLOCK_CYCLES(MoveCompTakingLongTime);
|
||||||
|
const float MSec = FPlatformTime::ToMilliseconds(MoveCompTakingLongTime);
|
||||||
|
if(MSec > PERF_SHOW_MOVECOMPONENT_TAKING_LONG_TIME_AMOUNT)
|
||||||
|
{
|
||||||
|
if(GetOwner())
|
||||||
|
{
|
||||||
|
UE_LOG(LogPrimitiveComponent, Log, TEXT("%10f executing MoveComponent for %s owned by %s"), MSec, *GetName(), *GetOwner()->GetFullName());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogPrimitiveComponent, Log, TEXT("%10f executing MoveComponent for %s"), MSec, *GetFullName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// copy to optional output param
|
||||||
|
if(OutHit)
|
||||||
|
{
|
||||||
|
if(bFilledHitResult)
|
||||||
|
{
|
||||||
|
*OutHit = BlockingHit;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OutHit->Init(TraceStart, TraceEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return whether we moved at all.
|
||||||
|
return bMoved;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UCollisionSceneComponent::BeginPlay()
|
||||||
|
{
|
||||||
|
for(auto& child : GetAttachChildren())
|
||||||
|
if(auto primitive = Cast<UPrimitiveComponent>(child))
|
||||||
|
targetCollisions.Add(primitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef LOCTEXT_NAMESPACE
|
@ -0,0 +1,146 @@
|
|||||||
|
// Oleg Petruny proprietary.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Components/PrimitiveComponent.h"
|
||||||
|
|
||||||
|
#include "CollisionSceneComponent.generated.h"
|
||||||
|
|
||||||
|
// Modified implementation of the UPrimitiveComponent
|
||||||
|
UCLASS(ClassGroup = (Utility, Common), BlueprintType, hideCategories = (Trigger, PhysicsVolume), meta = (BlueprintSpawnableComponent, IgnoreCategoryKeywordsInSubclasses, ShortTooltip = "A Scene Component that use targeted collisions."), MinimalAPI)
|
||||||
|
class UCollisionSceneComponent : public UPrimitiveComponent
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual bool MoveComponentImpl(const FVector& Delta, const FQuat& NewRotation, bool bSweep, FHitResult* OutHit = NULL, EMoveComponentFlags MoveFlags = MOVECOMP_NoFlags, ETeleportType Teleport = ETeleportType::None) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void BeginPlay() override;
|
||||||
|
|
||||||
|
/** Convert a set of overlaps from a sweep to a subset that includes only those at the end location (filling in OverlapsAtEndLocation). */
|
||||||
|
template<typename AllocatorType>
|
||||||
|
bool ConvertSweptOverlapsToCurrentOverlaps(TArray<FOverlapInfo, AllocatorType>& OutOverlapsAtEndLocation, const TOverlapArrayView& SweptOverlaps, int32 SweptOverlapsIndex, const FVector& EndLocation, const FQuat& EndRotationQuat);
|
||||||
|
|
||||||
|
/** Convert a set of overlaps from a symmetric change in rotation to a subset that includes only those at the end location (filling in OverlapsAtEndLocation). */
|
||||||
|
template<typename AllocatorType>
|
||||||
|
bool ConvertRotationOverlapsToCurrentOverlaps(TArray<FOverlapInfo, AllocatorType>& OutOverlapsAtEndLocation, const TOverlapArrayView& CurrentOverlaps);
|
||||||
|
|
||||||
|
template<typename AllocatorType>
|
||||||
|
bool GetOverlapsWithActor_Template(const AActor* Actor, TArray<FOverlapInfo, AllocatorType>& OutOverlaps) const;
|
||||||
|
|
||||||
|
// FScopedMovementUpdate needs access to the above two functions.
|
||||||
|
friend FScopedMovementUpdate;
|
||||||
|
|
||||||
|
TArray<UPrimitiveComponent*> targetCollisions;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename AllocatorType>
|
||||||
|
bool UCollisionSceneComponent::ConvertSweptOverlapsToCurrentOverlaps(
|
||||||
|
TArray<FOverlapInfo, AllocatorType>& OverlapsAtEndLocation, const TOverlapArrayView& SweptOverlaps, int32 SweptOverlapsIndex,
|
||||||
|
const FVector& EndLocation, const FQuat& EndRotationQuat)
|
||||||
|
{
|
||||||
|
checkSlow(SweptOverlapsIndex >= 0);
|
||||||
|
|
||||||
|
bool bResult = false;
|
||||||
|
const bool bForceGatherOverlaps = !ShouldCheckOverlapFlagToQueueOverlaps(*this);
|
||||||
|
if((GetGenerateOverlapEvents() || bForceGatherOverlaps) && PrimitiveComponentCVars::bAllowCachedOverlapsCVar)
|
||||||
|
{
|
||||||
|
const AActor* Actor = GetOwner();
|
||||||
|
if(Actor && Actor->GetRootComponent() == this)
|
||||||
|
{
|
||||||
|
// We know we are not overlapping any new components at the end location. Children are ignored here (see note below).
|
||||||
|
if(PrimitiveComponentCVars::bEnableFastOverlapCheck)
|
||||||
|
{
|
||||||
|
SCOPE_CYCLE_COUNTER(STAT_MoveComponent_FastOverlap);
|
||||||
|
|
||||||
|
// Check components we hit during the sweep, keep only those still overlapping
|
||||||
|
const FCollisionQueryParams UnusedQueryParams(NAME_None, FCollisionQueryParams::GetUnknownStatId());
|
||||||
|
const int32 NumSweptOverlaps = SweptOverlaps.Num();
|
||||||
|
OverlapsAtEndLocation.Reserve(OverlapsAtEndLocation.Num() + NumSweptOverlaps);
|
||||||
|
for(int32 Index = SweptOverlapsIndex; Index < NumSweptOverlaps; ++Index)
|
||||||
|
{
|
||||||
|
const FOverlapInfo& OtherOverlap = SweptOverlaps[Index];
|
||||||
|
UPrimitiveComponent* OtherPrimitive = OtherOverlap.OverlapInfo.GetComponent();
|
||||||
|
if(OtherPrimitive && (OtherPrimitive->GetGenerateOverlapEvents() || bForceGatherOverlaps))
|
||||||
|
{
|
||||||
|
if(OtherPrimitive->bMultiBodyOverlap)
|
||||||
|
{
|
||||||
|
// Not handled yet. We could do it by checking every body explicitly and track each body index in the overlap test, but this seems like a rare need.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if(Cast<USkeletalMeshComponent>(OtherPrimitive) || Cast<USkeletalMeshComponent>(this))
|
||||||
|
{
|
||||||
|
// SkeletalMeshComponent does not support this operation, and would return false in the test when an actual query could return true.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if(OtherPrimitive->ComponentOverlapComponent(this, EndLocation, EndRotationQuat, UnusedQueryParams))
|
||||||
|
{
|
||||||
|
OverlapsAtEndLocation.Add(OtherOverlap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: we don't worry about adding any child components here, because they are not included in the sweep results.
|
||||||
|
// Children test for their own overlaps after we update our own, and we ignore children in our own update.
|
||||||
|
checkfSlow(OverlapsAtEndLocation.FindByPredicate(FPredicateOverlapHasSameActor(*Actor)) == nullptr,
|
||||||
|
TEXT("Child overlaps should not be included in the SweptOverlaps() array in UPrimitiveComponent::ConvertSweptOverlapsToCurrentOverlaps()."));
|
||||||
|
|
||||||
|
bResult = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(SweptOverlaps.Num() == 0 && AreAllCollideableDescendantsRelative())
|
||||||
|
{
|
||||||
|
// Add overlaps with components in this actor.
|
||||||
|
GetOverlapsWithActor_Template(Actor, OverlapsAtEndLocation);
|
||||||
|
bResult = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename AllocatorType>
|
||||||
|
bool UCollisionSceneComponent::ConvertRotationOverlapsToCurrentOverlaps(TArray<FOverlapInfo, AllocatorType>& OutOverlapsAtEndLocation, const TOverlapArrayView& CurrentOverlaps)
|
||||||
|
{
|
||||||
|
bool bResult = false;
|
||||||
|
const bool bForceGatherOverlaps = !ShouldCheckOverlapFlagToQueueOverlaps(*this);
|
||||||
|
if((GetGenerateOverlapEvents() || bForceGatherOverlaps) && PrimitiveComponentCVars::bAllowCachedOverlapsCVar)
|
||||||
|
{
|
||||||
|
const AActor* Actor = GetOwner();
|
||||||
|
if(Actor && Actor->GetRootComponent() == this)
|
||||||
|
{
|
||||||
|
if(PrimitiveComponentCVars::bEnableFastOverlapCheck)
|
||||||
|
{
|
||||||
|
// Add all current overlaps that are not children. Children test for their own overlaps after we update our own, and we ignore children in our own update.
|
||||||
|
OutOverlapsAtEndLocation.Reserve(OutOverlapsAtEndLocation.Num() + CurrentOverlaps.Num());
|
||||||
|
Algo::CopyIf(CurrentOverlaps, OutOverlapsAtEndLocation, FPredicateOverlapHasDifferentActor(*Actor));
|
||||||
|
bResult = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename AllocatorType>
|
||||||
|
bool UCollisionSceneComponent::GetOverlapsWithActor_Template(const AActor* Actor, TArray<FOverlapInfo, AllocatorType>& OutOverlaps) const
|
||||||
|
{
|
||||||
|
const int32 InitialCount = OutOverlaps.Num();
|
||||||
|
if(Actor)
|
||||||
|
{
|
||||||
|
for(int32 OverlapIdx = 0; OverlapIdx < OverlappingComponents.Num(); ++OverlapIdx)
|
||||||
|
{
|
||||||
|
UPrimitiveComponent const* const PrimComp = OverlappingComponents[OverlapIdx].OverlapInfo.Component.Get();
|
||||||
|
if(PrimComp && (PrimComp->GetOwner() == Actor))
|
||||||
|
{
|
||||||
|
OutOverlaps.Add(OverlappingComponents[OverlapIdx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return InitialCount != OutOverlaps.Num();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user