Files
PHY/Plugins/GCS/Source/GenericCombatSystem/Private/Collision/GCS_TraceSystemComponent.cpp
2026-03-03 01:23:02 +08:00

490 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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);
}