// Copyright 2025 https://yuewu.dev/en All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "GameplayTagContainer.h" #include "CollisionShape.h" #include "CollisionQueryParams.h" #include "Collision/GCS_TraceEnumLibrary.h" #include "Engine/DataTable.h" #include "StructUtils/InstancedStruct.h" #include "UObject/Object.h" #include "GCS_TraceStructLibrary.generated.h" class UTargetingPreset; class UGCS_TraceSystemComponent; USTRUCT(BlueprintType) struct FGCS_TraceSweepSetting { GENERATED_BODY() /** * The type of sweep to perform for collision detection. * 执行碰撞检测的扫描类型。 */ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "GCS") EGCS_TraceSweepType SweepType = EGCS_TraceSweepType::ByChannel; /** * The collision channel to use for channel-based sweeping. * 用于基于通道扫描的碰撞通道。 */ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "GCS", meta = (EditCondition = "SweepType == EGCS_TraceSweepType::ByChannel", EditConditionHides)) TEnumAsByte TraceChannel = ECC_Visibility; /** * The object types to use for object-based sweeping. * 用于基于对象扫描的对象类型。 */ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "GCS", meta = (EditCondition = "SweepType == EGCS_TraceSweepType::ByObject", EditConditionHides)) TArray> ObjectTypes; /** * The collision profile name to use for profile-based sweeping. * 用于基于配置扫描的碰撞配置名称。 */ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "GCS", meta = (EditCondition = "SweepType == EGCS_TraceSweepType::ByProfile", EditConditionHides)) FName ProfileName = NAME_None; /** * Whether to trace against complex collision. * 是否对复杂碰撞进行追踪。 */ UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "GCS") bool bTraceComplex = false; }; /** * Build collision shape by static parameters * 通过静态参数构建碰撞形状。 * 在SourceComponent空间内的静态形状。 */ USTRUCT(meta = (Hidden, DisplayName = "Collision Shape")) struct GENERICCOMBATSYSTEM_API FGCS_CollisionShape { GENERATED_BODY() virtual ~FGCS_CollisionShape() = default; virtual bool InitializeShape(const UPrimitiveComponent* SourceComponent); virtual FTransform GetTransform(const UPrimitiveComponent* SourceComponent, const float& Time) const; virtual FCollisionShape GetDynamicCollisionShape(const UPrimitiveComponent* SourceComponent, const float& Time) const; }; /** * Build collision shape by static parameters * 通过静态参数构建碰撞形状。 * 在SourceComponent空间内的静态形状。 */ USTRUCT(meta = (DisplayName = "Collision Shape (Static)")) struct GENERICCOMBATSYSTEM_API FGCS_CollisionShape_Static : public FGCS_CollisionShape { GENERATED_BODY() /** * The type of collision shape to use. * 要使用的碰撞形状类型。 */ UPROPERTY(EditAnywhere, Category = "GCS") EGCS_CollisionShapeType ShapeType = EGCS_CollisionShapeType::Sphere; /** * The orientation/rotation of the collision shape. * 碰撞形状的方向/旋转。 */ UPROPERTY(EditAnywhere, Category = "GCS", meta = (EditCondition = "ShapeType != EGCS_CollisionShapeType::Sphere", EditConditionHides)) FRotator Orientation = FRotator::ZeroRotator; /** * The position offset of the collision shape. * 碰撞形状的位置偏移。 */ UPROPERTY(EditAnywhere, Category = "GCS") FVector Offset = FVector::ZeroVector; /** * The radius of the sphere or capsule. * 球体或胶囊体的半径。 */ UPROPERTY(EditAnywhere, Category = "GCS", meta = (EditCondition = "ShapeType != EGCS_CollisionShapeType::Box", EditConditionHides)) float Radius = 10.f; /** * The half height of the capsule. * 胶囊体的半高。 */ UPROPERTY(EditAnywhere, Category = "GCS", meta = (EditCondition = "ShapeType == EGCS_CollisionShapeType::Capsule", EditConditionHides)) float HalfHeight = 10.f; /** * The half size of the box. * 盒子的半尺寸。 */ UPROPERTY(EditAnywhere, Category = "GCS", meta = (EditCondition = "ShapeType == EGCS_CollisionShapeType::Box", EditConditionHides)) FVector HalfSize = FVector(10.f, 10.f, 10.f); virtual bool InitializeShape(const UPrimitiveComponent* SourceComponent) override; virtual FTransform GetTransform(const UPrimitiveComponent* SourceComponent, const float& Time) const override; virtual FCollisionShape GetDynamicCollisionShape(const UPrimitiveComponent* SourceComponent, const float& Time) const override; }; /** * Build dynamic collision shape by binding to shape component(Box/Sphere/Capsule). * 通过绑定Shape组件(Box/Sphere/Capsule)构建动态碰撞形状。 * 在SourceComponent空间内的,与SourceComponent进行匹配的形状。 */ USTRUCT(meta = (DisplayName = "Collision Shape (Shape Based)")) struct GENERICCOMBATSYSTEM_API FGCS_CollisionShape_ShapeBased : public FGCS_CollisionShape { GENERATED_BODY() /** * The type of collision shape to use. * 要使用的碰撞形状类型。 */ UPROPERTY(EditAnywhere, Category = "GCS") EGCS_CollisionShapeType ShapeType = EGCS_CollisionShapeType::Sphere; /** * The orientation/rotation of the collision shape. * 碰撞形状的方向/旋转。 */ UPROPERTY(EditAnywhere, Category = "GCS", meta = (EditCondition = "ShapeType != EGCS_CollisionShapeType::Sphere", EditConditionHides)) FRotator Orientation = FRotator::ZeroRotator; UPROPERTY(EditAnywhere, Category = "GCS") FVector Offset = FVector::ZeroVector; UPROPERTY() float Radius = 10.f; UPROPERTY() float HalfHeight = 10.f; UPROPERTY() FVector HalfSize = FVector(10.f, 10.f, 10.f); virtual bool InitializeShape(const UPrimitiveComponent* SourceComponent) override; virtual FTransform GetTransform(const UPrimitiveComponent* SourceComponent, const float& Time) const override; virtual FCollisionShape GetDynamicCollisionShape(const UPrimitiveComponent* SourceComponent, const float& Time) const override; }; /** * 在SourceComponent空间内,跟随SourceComponent的某Socket/Bone运动的形状。 */ USTRUCT(meta = (DisplayName = "Collision Shape (Attached)")) struct GENERICCOMBATSYSTEM_API FGCS_CollisionShape_Attached : public FGCS_CollisionShape_Static { GENERATED_BODY() /** * Name of the socket or bone the shape will be attached to. * 形状将附加到的插槽或骨骼的名称。 */ UPROPERTY(EditAnywhere, Category="GCS") FName SocketOrBoneName; virtual bool InitializeShape(const UPrimitiveComponent* SourceComponent) override; virtual FTransform GetTransform(const UPrimitiveComponent* SourceComponent, const float& Time) const override; }; /** * Build dynamic capsule collision shape by mesh's sockets. * 通过网格的指定Sockets构建动态碰撞形状。 */ USTRUCT(meta = (DisplayName = "Collision Shape (Socket Based)")) struct GENERICCOMBATSYSTEM_API FGCS_CollisionShape_SocketBased : public FGCS_CollisionShape { GENERATED_BODY() /** * The name of the starting socket on the mesh. * 网格上起始插槽的名称。 */ UPROPERTY(EditAnywhere, Category = "GCS") FName MeshSocketStart = TEXT("TrailStart"); /** * The name of the ending socket on the mesh. * 网格上结束插槽的名称。 */ UPROPERTY(EditAnywhere, Category = "GCS") FName MeshSocketEnd = TEXT("TrailEnd"); /** * Additional length offset for the socket-based shape. * 基于插槽的形状的额外长度偏移。 */ UPROPERTY(EditAnywhere, Category = "GCS") float MeshSocketLengthOffset = 0; /** * The radius of the capsule. * 胶囊体的半径。 */ UPROPERTY(EditAnywhere, Category = "GCS") float Radius = 10.f; /** * The orientation/rotation of the collision shape. * 碰撞形状的方向/旋转。 */ UPROPERTY() FRotator Orientation = FRotator::ZeroRotator; /** * The position offset of the collision shape. * 碰撞形状的位置偏移。 */ UPROPERTY() FVector Offset = FVector::ZeroVector; /** * The half height of the capsule. * 胶囊体的半高。 */ UPROPERTY() float HalfHeight = 10.f; virtual FTransform GetTransform(const UPrimitiveComponent* SourceComponent, const float& Time) const override; virtual bool InitializeShape(const UPrimitiveComponent* SourceComponent) override; virtual FCollisionShape GetDynamicCollisionShape(const UPrimitiveComponent* SourceComponent, const float& Time) const override; }; /** * Structure for defining collision trace instances. * 定义碰撞检测实例的结构。 */ USTRUCT(BlueprintType) struct GENERICCOMBATSYSTEM_API FGCS_TraceDefinition : public FTableRowBase { GENERATED_BODY() FGCS_TraceDefinition(); /** * Tag used as Trace identifier. * 此Trace的Tag标识。 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trace Settings", meta=(Categories="GGF.Combat.Trace")) FGameplayTag TraceTag; /** * Defines the shape used for detecting hit results. * 定义用于获取命中结果的形状。 */ UPROPERTY(EditAnywhere, Category = "Trace Settings", meta=(ExcludeBaseStruct, BaseStruct = "/Script/GenericCombatSystem.GCS_CollisionShape")) FInstancedStruct CollisionShape{FInstancedStruct::Make(FGCS_CollisionShape_Static())}; /** * Settings for the sweep operation. * 扫描操作的设置。 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trace Settings") FGCS_TraceSweepSetting SweepSetting; /** * The type of tick policy to use for this trace. * 此Trace使用的tick策略类型。 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trace Settings") EGCS_TraceTickType TraceTickType{EGCS_TraceTickType::FixedFrameRate}; /** * The fixed frame rate for ticking when using FixedFrameRate policy. * 使用固定帧率策略时的固定帧率。 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trace Settings", meta = (EditCondition = "TraceTickType == EGCS_TraceTickType::FixedFrameRate", EditConditionHides)) int32 FixedTickFrameRate = 30; /** * The distance threshold for ticking when using DistanceBased policy. * 使用基于距离策略时的距离阈值。 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trace Settings", meta = (EditCondition = "TraceTickType == EGCS_TraceTickType::DistanceBased", EditConditionHides)) int32 DistanceTickThreshold = 30; /** * The angle threshold for ticking when using DistanceBased policy (in degrees). * 使用基于距离策略时的角度阈值(度)。 */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Trace Settings", meta = (EditCondition = "TraceTickType == EGCS_TraceTickType::DistanceBased", EditConditionHides)) float AngleTickThreshold = 15.0f; // Degrees bool IsValidDefinition() const; FString ToString() const; }; /** * Simple struct used for trace query. */ USTRUCT(BlueprintType) struct GENERICCOMBATSYSTEM_API FGCS_TraceHandle { GENERATED_BODY() /** * The gameplay tag identifying this trace. * 标识此Trace的游戏标签。 */ UPROPERTY(BlueprintReadOnly, Category = "GCS") FGameplayTag TraceTag; /** * The unique GUID for this trace instance. * 此Trace实例的唯一GUID。 */ UPROPERTY(BlueprintReadOnly, Category = "GCS") FGuid Guid; /** * The source object that created this trace. * 创建此Trace的源对象。 */ UPROPERTY(BlueprintReadOnly, Category = "GCS") TWeakObjectPtr SourceObject; bool IsValidHandle() const; // Equality operator friend bool operator==(const FGCS_TraceHandle& A, const FGCS_TraceHandle& B) { return A.TraceTag == B.TraceTag && A.Guid == B.Guid && A.SourceObject == B.SourceObject; } // Inequality operator friend bool operator!=(const FGCS_TraceHandle& A, const FGCS_TraceHandle& B) { return !(A == B); } // Hash function for TMap/TSet friend uint32 GetTypeHash(const FGCS_TraceHandle& Handle) { uint32 Hash = GetTypeHash(Handle.TraceTag); Hash = HashCombine(Hash, GetTypeHash(Handle.Guid)); Hash = HashCombine(Hash, GetTypeHash(Handle.SourceObject)); return Hash; } FString ToDebugString() const { FString TagName = TraceTag.ToString(); if (TraceTag.IsValid()) { TArray TagNames; TraceTag.ToString().ParseIntoArray(TagNames,TEXT(".")); if (TagNames.Num() > 0) { TagName = TagNames.Last(); } } return FString::Printf(TEXT("%s;%s"), *TagName, SourceObject.IsValid() ? *GetNameSafe(SourceObject.Get()) : TEXT("None")); } }; // 补帧数据. struct FGCS_TraceSubTick { FTransform StartTransform; FTransform EndTransform; FTransform AverageTransform; }; USTRUCT(BlueprintType) struct FGCS_TraceState { GENERATED_BODY() void ChangeExecutionState(bool bNewTraceState, bool bStopImmediate = true); void UpdatePreviousTransform(const FTransform& Transform); // Get the world space transform of this trace. FTransform GetCurrentTransform() const; // Begin Runtime references UPROPERTY() UWorld* World{nullptr}; UPROPERTY() TObjectPtr SourceComponent{nullptr}; UPROPERTY() TObjectPtr OwningSystem{nullptr}; // End Runtime references // Begin Static Data FGCS_TraceSweepSetting SweepSetting; // End Static Data // Begin runtime data FGCS_TraceHandle Handle; bool IsPendingRemoval = false; // The modified dynamic shape. FInstancedStruct Shape; TArray> TransformsOverTime; EGCS_TraceExecutionState ExecutionState = EGCS_TraceExecutionState::Stopped; FCollisionShape CollisionShapeOverTime; TArray SubTicks; EGCS_TraceTickType TickPolicy; float TickInterval = 1 / 30; float AngleThreshold = 15.0f; // Degrees bool bShouldTickThisFrame = false; float TimeSinceLastTick = 0; // how long this state active? // float TimeSinceActive = 0; int32 TotalTickNumDuringExecution = 0; TArray> HitActors; FCollisionQueryParams CollisionParams; FCollisionResponseParams ResponseParams; FCollisionObjectQueryParams ObjectQueryParams; // End runtime data };