490 lines
15 KiB
C++
490 lines
15 KiB
C++
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||
|
||
#include "Collision/GCS_TraceSystemComponent.h"
|
||
#include "GCS_LogChannels.h"
|
||
#include "Collision/DEPRECATED_GCS_CollisionTraceInstance.h"
|
||
#include "Collision/GCS_TraceSubsystem.h"
|
||
#include "Components/SkeletalMeshComponent.h"
|
||
#include "Components/PrimitiveComponent.h"
|
||
#include "Utility/GCS_CombatFunctionLibrary.h"
|
||
|
||
UGCS_TraceSystemComponent::UGCS_TraceSystemComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
|
||
{
|
||
PrimaryComponentTick.bCanEverTick = false;
|
||
SetIsReplicatedByDefault(false);
|
||
}
|
||
|
||
UGCS_TraceSystemComponent* UGCS_TraceSystemComponent::GetTraceSystemComponent(const AActor* Actor)
|
||
{
|
||
return IsValid(Actor) ? Actor->FindComponentByClass<UGCS_TraceSystemComponent>() : nullptr;
|
||
}
|
||
|
||
bool UGCS_TraceSystemComponent::FindTraceSystemComponent(const AActor* Actor, UGCS_TraceSystemComponent*& Component)
|
||
{
|
||
Component = GetTraceSystemComponent(Actor);
|
||
return Component != nullptr;
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::OnInitialize_Implementation()
|
||
{
|
||
if (UMeshComponent* PrimitiveComp = UGCS_CombatFunctionLibrary::GetMainMeshComponent(GetOwner()))
|
||
{
|
||
AddTraces(TraceDefinitions, PrimitiveComp, GetOwner());
|
||
}
|
||
|
||
bInitialized = true;
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::OnDeinitialize_Implementation()
|
||
{
|
||
if (bInitialized)
|
||
{
|
||
RemoveAllTraces();
|
||
bInitialized = false;
|
||
}
|
||
}
|
||
|
||
// Called when the game starts
|
||
void UGCS_TraceSystemComponent::BeginPlay()
|
||
{
|
||
if (bAutoInitialize)
|
||
{
|
||
OnInitialize();
|
||
}
|
||
Super::BeginPlay();
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||
{
|
||
Super::EndPlay(EndPlayReason);
|
||
OnDeinitialize();
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::OnComponentDestroyed(bool bDestroyingHierarchy)
|
||
{
|
||
Super::OnComponentDestroyed(bDestroyingHierarchy);
|
||
OnDestroyedEvent.Broadcast();
|
||
|
||
OnDeinitialize();
|
||
}
|
||
|
||
TArray<FGCS_TraceHandle> UGCS_TraceSystemComponent::AddTraces(const TArray<FGCS_TraceDefinition>& Definitions, UPrimitiveComponent* SourceComponent, UObject* SourceObject)
|
||
{
|
||
TArray<FGCS_TraceHandle> Handles;
|
||
for (const FGCS_TraceDefinition& Def : Definitions)
|
||
{
|
||
FGCS_TraceHandle Handle = AddTrace(Def, SourceComponent, SourceObject);
|
||
if (Handle.IsValidHandle())
|
||
{
|
||
Handles.Add(Handle);
|
||
}
|
||
}
|
||
return Handles;
|
||
}
|
||
|
||
TArray<FGCS_TraceHandle> UGCS_TraceSystemComponent::AddTraces(const TArray<FDataTableRowHandle>& DefinitionHandles, UPrimitiveComponent* SourceComponent, UObject* SourceObject)
|
||
{
|
||
TArray<FGCS_TraceHandle> Handles;
|
||
for (const FDataTableRowHandle& DefHandle : DefinitionHandles)
|
||
{
|
||
FGCS_TraceHandle Handle = AddTrace(DefHandle, SourceComponent, SourceObject);
|
||
if (Handle.IsValidHandle())
|
||
{
|
||
Handles.Add(Handle);
|
||
}
|
||
}
|
||
return Handles;
|
||
}
|
||
|
||
FGCS_TraceHandle UGCS_TraceSystemComponent::AddTrace(const FGCS_TraceDefinition& TraceDefinition, UPrimitiveComponent* SourceComponent, UObject* SourceObject)
|
||
{
|
||
if (!TraceDefinition.IsValidDefinition())
|
||
{
|
||
GCS_CLOG(Warning, "Try adding invalid trace definition!")
|
||
return FGCS_TraceHandle();
|
||
}
|
||
|
||
if (!IsValid(SourceObject))
|
||
{
|
||
GCS_CLOG(Warning, "Missing source object for trace definition:%s", *TraceDefinition.ToString())
|
||
return FGCS_TraceHandle();
|
||
}
|
||
|
||
if (!IsValid(SourceComponent))
|
||
{
|
||
GCS_CLOG(Warning, "Missing source component for trace definition:%s", *TraceDefinition.ToString())
|
||
return FGCS_TraceHandle();
|
||
}
|
||
|
||
FInstancedStruct Shape = TraceDefinition.CollisionShape;
|
||
bool bWasInitialized = Shape.GetMutable<FGCS_CollisionShape>().InitializeShape(SourceComponent);
|
||
|
||
if (!bWasInitialized)
|
||
{
|
||
return FGCS_TraceHandle();
|
||
}
|
||
|
||
UWorld* World = GetWorld();
|
||
if (!IsValid(World))
|
||
{
|
||
GCS_CLOG(Error, "Invalid world context!")
|
||
return FGCS_TraceHandle();
|
||
}
|
||
|
||
UGCS_TraceSubsystem* Subsystem = World->GetSubsystem<UGCS_TraceSubsystem>();
|
||
if (!IsValid(Subsystem))
|
||
{
|
||
GCS_CLOG(Error, "Failed to get TraceSubsystem!")
|
||
return FGCS_TraceHandle();
|
||
}
|
||
int32 StateIdx = Subsystem->AddTraceState();
|
||
|
||
FGCS_TraceHandle Handle = {TraceDefinition.TraceTag, FGuid::NewGuid(), SourceObject};
|
||
|
||
float TickInterval = 0;
|
||
float AngleThreshold = TraceDefinition.AngleTickThreshold;
|
||
if (TraceDefinition.TraceTickType == EGCS_TraceTickType::DistanceBased)
|
||
{
|
||
TickInterval = TraceDefinition.DistanceTickThreshold;
|
||
}
|
||
else if (TraceDefinition.TraceTickType == EGCS_TraceTickType::FixedFrameRate)
|
||
{
|
||
TickInterval = 1.0f / static_cast<float>(TraceDefinition.FixedTickFrameRate);
|
||
}
|
||
auto& TraceState = Subsystem->GetTraceStateAt(StateIdx);
|
||
TraceState.World = GetWorld();
|
||
TraceState.SourceComponent = SourceComponent;
|
||
TraceState.OwningSystem = this;
|
||
|
||
TraceState.SweepSetting = TraceDefinition.SweepSetting;
|
||
TraceState.Shape = Shape;
|
||
|
||
TraceState.Handle = Handle;
|
||
TraceState.ExecutionState = EGCS_TraceExecutionState::Stopped;
|
||
TraceState.TimeSinceLastTick = 0;
|
||
TraceState.TimeSinceActive = 0;
|
||
TraceState.TickPolicy = TraceDefinition.TraceTickType;
|
||
TraceState.TickInterval = TickInterval;
|
||
TraceState.AngleThreshold = AngleThreshold;
|
||
TraceState.bShouldTickThisFrame = false;
|
||
if (AActor* Owner = GetOwner())
|
||
{
|
||
TraceState.CollisionParams.AddIgnoredActor(Owner);
|
||
}
|
||
TraceState.CollisionParams.bTraceComplex = TraceDefinition.SweepSetting.bTraceComplex;
|
||
TraceState.CollisionParams.bReturnPhysicalMaterial = true;
|
||
for (const auto& ObjType : TraceDefinition.SweepSetting.ObjectTypes)
|
||
{
|
||
TraceState.ObjectQueryParams.AddObjectTypesToQuery(ObjType);
|
||
}
|
||
|
||
HandleToStateIdx.Add(Handle, StateIdx);
|
||
TagToHandles.Add(TraceDefinition.TraceTag, Handle);
|
||
GCS_CLOG(Verbose, "Added trace(%s) with source component:%s", *Handle.ToDebugString(), *GetNameSafe(SourceComponent))
|
||
return Handle;
|
||
}
|
||
|
||
FGCS_TraceHandle UGCS_TraceSystemComponent::AddTrace(const FDataTableRowHandle& TraceDefinitionHandle, UPrimitiveComponent* SourceComponent, UObject* SourceObject)
|
||
{
|
||
if (!TraceDefinitionHandle.IsNull())
|
||
{
|
||
if (FGCS_TraceDefinition* TraceDefinition = TraceDefinitionHandle.GetRow<FGCS_TraceDefinition>(TEXT("AddTrace")))
|
||
{
|
||
AddTrace(*TraceDefinition, SourceComponent, SourceObject);
|
||
}
|
||
else
|
||
{
|
||
GCS_CLOG(Warning, "definition handle doesn't point to valid TraceDefinition Table. %s", *TraceDefinitionHandle.ToDebugString())
|
||
}
|
||
}
|
||
GCS_CLOG(Warning, "Passed in invalid trace definition handle! %s", *TraceDefinitionHandle.ToDebugString())
|
||
return FGCS_TraceHandle();
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::RemoveTraces(const TArray<FGCS_TraceHandle>& TraceHandles)
|
||
{
|
||
for (const FGCS_TraceHandle& TraceHandle : TraceHandles)
|
||
{
|
||
RemoveTrace(TraceHandle);
|
||
}
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::RemoveTrace(const FGCS_TraceHandle& TraceHandle)
|
||
{
|
||
if (TraceHandle.IsValidHandle())
|
||
{
|
||
if (const int32* StateIdx = HandleToStateIdx.Find(TraceHandle))
|
||
{
|
||
UGCS_TraceSubsystem* Subsystem = GetWorld()->GetSubsystem<UGCS_TraceSubsystem>();
|
||
if (IsValid(Subsystem) && Subsystem->IsValidStateIdx(*StateIdx))
|
||
{
|
||
auto& State = Subsystem->GetTraceStateAt(*StateIdx);
|
||
State.ChangeExecutionState(false);
|
||
OnTraceStateChanged(TraceHandle, false);
|
||
Subsystem->RemoveTraceState(*StateIdx, TraceHandle.Guid);
|
||
GCS_CLOG(Verbose, "Removed trace(%s)", *TraceHandle.ToDebugString())
|
||
}
|
||
HandleToStateIdx.Remove(TraceHandle);
|
||
TagToHandles.RemoveSingle(TraceHandle.TraceTag, TraceHandle);
|
||
}
|
||
}
|
||
}
|
||
|
||
TArray<FGCS_TraceHandle> UGCS_TraceSystemComponent::GetTraceHandlesBySource(const UObject* SourceObject) const
|
||
{
|
||
TArray<FGCS_TraceHandle> Handles;
|
||
for (const auto& Pair : HandleToStateIdx)
|
||
{
|
||
if (Pair.Key.SourceObject == SourceObject)
|
||
{
|
||
Handles.Add(Pair.Key);
|
||
}
|
||
}
|
||
return Handles;
|
||
}
|
||
|
||
TArray<FGCS_TraceHandle> UGCS_TraceSystemComponent::GetTraceHandlesByTagsAndSource(const FGameplayTagContainer& TraceTags, const UObject* SourceObject) const
|
||
{
|
||
TArray<FGCS_TraceHandle> Handles;
|
||
for (const FGameplayTag& Tag : TraceTags)
|
||
{
|
||
TArray<FGCS_TraceHandle> TagHandles;
|
||
TagToHandles.MultiFind(Tag, TagHandles);
|
||
for (const FGCS_TraceHandle& Handle : TagHandles)
|
||
{
|
||
if (!IsValid(SourceObject) || Handle.SourceObject == SourceObject)
|
||
{
|
||
Handles.AddUnique(Handle);
|
||
}
|
||
}
|
||
}
|
||
return Handles;
|
||
}
|
||
|
||
TArray<FGCS_TraceHandle> UGCS_TraceSystemComponent::StartTraces(const FGameplayTagContainer& TraceTags, const UObject* SourceObject)
|
||
{
|
||
TArray<FGCS_TraceHandle> Handles = GetTraceHandlesByTagsAndSource(TraceTags, SourceObject);
|
||
for (const FGCS_TraceHandle& Handle : Handles)
|
||
{
|
||
StartTrace(Handle);
|
||
}
|
||
return Handles;
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::StartTraces(const TArray<FGCS_TraceHandle>& TraceHandles)
|
||
{
|
||
for (const FGCS_TraceHandle& Handle : TraceHandles)
|
||
{
|
||
StartTrace(Handle);
|
||
}
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::StartTrace(const FGCS_TraceHandle& TraceHandle)
|
||
{
|
||
if (TraceHandle.IsValidHandle())
|
||
{
|
||
if (const int32* StateIdx = HandleToStateIdx.Find(TraceHandle))
|
||
{
|
||
auto& State = GetWorld()->GetSubsystem<UGCS_TraceSubsystem>()->GetTraceStateAt(*StateIdx);
|
||
GCS_CLOG(Verbose, "Started trace(%s).", *TraceHandle.ToDebugString())
|
||
State.ChangeExecutionState(true);
|
||
OnTraceStateChanged(TraceHandle, true);
|
||
}
|
||
}
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::StopTraces(const TArray<FGCS_TraceHandle>& TraceHandles)
|
||
{
|
||
UGCS_TraceSubsystem* Subsystem = GetWorld()->GetSubsystem<UGCS_TraceSubsystem>();
|
||
for (const FGCS_TraceHandle& TraceHandle : TraceHandles)
|
||
{
|
||
if (TraceHandle.IsValidHandle())
|
||
{
|
||
if (const int32* StateIdx = HandleToStateIdx.Find(TraceHandle))
|
||
{
|
||
auto& State = Subsystem->GetTraceStateAt(*StateIdx);
|
||
GCS_CLOG(Verbose, "Stopped trace(%s).", *TraceHandle.ToDebugString())
|
||
State.ChangeExecutionState(false);
|
||
OnTraceStateChanged(TraceHandle, false);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::StopTrace(const FGCS_TraceHandle& TraceHandle)
|
||
{
|
||
if (TraceHandle.IsValidHandle())
|
||
{
|
||
if (const int32* StateIdx = HandleToStateIdx.Find(TraceHandle))
|
||
{
|
||
auto& State = GetWorld()->GetSubsystem<UGCS_TraceSubsystem>()->GetTraceStateAt(*StateIdx);
|
||
State.ChangeExecutionState(false);
|
||
OnTraceStateChanged(TraceHandle, false);
|
||
}
|
||
}
|
||
}
|
||
|
||
TArray<FGCS_TraceHandle> UGCS_TraceSystemComponent::AddTracesByDefinitions(const TArray<FGCS_TraceDefinition>& Definitions, UPrimitiveComponent* SourceComponent,
|
||
UObject* SourceObject)
|
||
{
|
||
return AddTraces(Definitions, SourceComponent, SourceObject);
|
||
}
|
||
|
||
TArray<FGCS_TraceHandle> UGCS_TraceSystemComponent::AddTracesByDefinitionHandles(const TArray<FDataTableRowHandle>& DefinitionHandles, UPrimitiveComponent* SourceComponent,
|
||
UObject* SourceObject)
|
||
{
|
||
return AddTraces(DefinitionHandles, SourceComponent, SourceObject);
|
||
}
|
||
|
||
FGCS_TraceHandle UGCS_TraceSystemComponent::AddTraceByDefinition(const FGCS_TraceDefinition& Definition, UPrimitiveComponent* SourceComponent, UObject* SourceObject)
|
||
{
|
||
return AddTrace(Definition, SourceComponent, SourceObject);
|
||
}
|
||
|
||
TArray<FGCS_TraceHandle> UGCS_TraceSystemComponent::StartTracesByTagsAndSource(const FGameplayTagContainer& TraceTags, const UObject* SourceObject)
|
||
{
|
||
return StartTraces(TraceTags, SourceObject);
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::StartTracesByHandles(const TArray<FGCS_TraceHandle>& TraceHandles)
|
||
{
|
||
return StartTraces(TraceHandles);
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::StartTraceByHandle(const FGCS_TraceHandle& TraceHandle)
|
||
{
|
||
return StartTrace(TraceHandle);
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::StopTracesByHandles(const TArray<FGCS_TraceHandle>& TraceHandles)
|
||
{
|
||
StopTraces(TraceHandles);
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::StopTraceByHandle(const FGCS_TraceHandle& TraceHandle)
|
||
{
|
||
StopTrace(TraceHandle);
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::RemoveTraceByHandle(const FGCS_TraceHandle& TraceHandle)
|
||
{
|
||
RemoveTrace(TraceHandle);
|
||
}
|
||
|
||
TArray<FGCS_TraceHandle> UGCS_TraceSystemComponent::GetTraceHandlesByTag(FGameplayTag TraceToFind) const
|
||
{
|
||
TArray<FGCS_TraceHandle> Handles;
|
||
TagToHandles.MultiFind(TraceToFind, Handles);
|
||
return Handles;
|
||
}
|
||
|
||
AActor* UGCS_TraceSystemComponent::GetTraceSourceActor(const FGCS_TraceHandle& TraceHandle) const
|
||
{
|
||
if (UPrimitiveComponent* SourceComponent = GetTraceSourceComponent(TraceHandle))
|
||
{
|
||
return SourceComponent->GetOwner();
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
UPrimitiveComponent* UGCS_TraceSystemComponent::GetTraceSourceComponent(const FGCS_TraceHandle& TraceHandle) const
|
||
{
|
||
if (TraceHandle.IsValidHandle())
|
||
{
|
||
if (const int32* StateIdx = HandleToStateIdx.Find(TraceHandle))
|
||
{
|
||
FGCS_TraceState& State = GetWorld()->GetSubsystem<UGCS_TraceSubsystem>()->GetTraceStateAt(*StateIdx);
|
||
return State.SourceComponent;
|
||
}
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::RemoveAllTraces()
|
||
{
|
||
UWorld* World = GetWorld();
|
||
if (!IsValid(World))
|
||
{
|
||
return;
|
||
}
|
||
UGCS_TraceSubsystem* Subsystem = World->GetSubsystem<UGCS_TraceSubsystem>();
|
||
if (!IsValid(Subsystem))
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 新增:收集所有要移除的Idx和Guid,避免边遍历边修改
|
||
TArray<TPair<int32, FGuid>> ToRemove;
|
||
for (const TPair<FGCS_TraceHandle, int32>& Pair : HandleToStateIdx)
|
||
{
|
||
if (Pair.Key.IsValidHandle())
|
||
{
|
||
if (Subsystem->IsValidStateIdx(Pair.Value)) // 额外验证
|
||
{
|
||
const FGCS_TraceState& State = Subsystem->GetTraceStateAt(Pair.Value);
|
||
ToRemove.Add(TPair<int32, FGuid>(Pair.Value, Pair.Key.Guid));
|
||
}
|
||
}
|
||
}
|
||
|
||
// 先停止所有状态
|
||
for (const auto& Pending : ToRemove)
|
||
{
|
||
if (Subsystem->IsValidStateIdx(Pending.Key))
|
||
{
|
||
auto& State = Subsystem->GetTraceStateAt(Pending.Key);
|
||
State.ChangeExecutionState(false);
|
||
}
|
||
}
|
||
|
||
// 然后批量移除
|
||
for (const auto& Pending : ToRemove)
|
||
{
|
||
Subsystem->RemoveTraceState(Pending.Key, Pending.Value);
|
||
}
|
||
|
||
// 清空映射
|
||
for (const TPair<FGCS_TraceHandle, int32>& Pair : HandleToStateIdx)
|
||
{
|
||
TagToHandles.RemoveSingle(Pair.Key.TraceTag, Pair.Key);
|
||
}
|
||
HandleToStateIdx.Empty();
|
||
}
|
||
|
||
bool UGCS_TraceSystemComponent::IsTraceActive(const FGCS_TraceHandle& TraceHandle) const
|
||
{
|
||
if (const int32* StateIdx = HandleToStateIdx.Find(TraceHandle))
|
||
{
|
||
const FGCS_TraceState& State = GetWorld()->GetSubsystem<UGCS_TraceSubsystem>()->GetTraceStateAt(*StateIdx);
|
||
return State.ExecutionState == EGCS_TraceExecutionState::InProgress;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::OnTraceHitDetected(const FGCS_TraceHandle& TraceHandle, const TArray<FHitResult>& HitResults, const float DeltaTime, const uint32 TickIdx)
|
||
{
|
||
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("UGCS_TraceSystemComponent::OnTraceHitDetected"), UGCS_TraceSystemComponent_OnTraceHitDetected, STATGROUP_GCS)
|
||
|
||
FScopeLock ScopedLock(&TraceDoneScopeLock);
|
||
|
||
for (const FHitResult& HitResult : HitResults)
|
||
{
|
||
// GCS_CLOG(Verbose, "Trace(%s) hit:%s at location(%s)", *TraceHandle.ToDebugString(), *GetNameSafe(HitResult.GetActor()), *HitResult.Location.ToString())
|
||
GCS_CLOG(Verbose, "Trace(%s) hit:%s", *TraceHandle.ToDebugString(), *HitResult.ToString())
|
||
OnTraceHitEvent.Broadcast(TraceHandle, HitResult);
|
||
}
|
||
}
|
||
|
||
void UGCS_TraceSystemComponent::OnTraceStateChanged(const FGCS_TraceHandle& TraceHandle, bool bNewState)
|
||
{
|
||
if (bNewState)
|
||
{
|
||
OnTraceStartedEvent.Broadcast(TraceHandle);
|
||
}
|
||
else
|
||
{
|
||
OnTraceStoppedEvent.Broadcast(TraceHandle);
|
||
}
|
||
OnTraceStateChangedEvent.Broadcast(TraceHandle, bNewState);
|
||
}
|