集成通用运动/库存系统

此文档知道你如何在通用战斗系统中集成通用运动系统。

Cover.GCS

作者

作者

创建时间: 2025年4月08日更新时间: 2026年4月14日

目录

准备工作

本文仅适用于同时购买了我的多个产品的用户,如GCS,GMS,或者GIS。

由于战斗系统本身最复杂,牵扯的游戏机制也更多。因此,建议的流程是将GIS或GMS整合到GCS中。

前置知识

在此之前,你应该至少了解各系统的基础用法,以及GCS的BC_DemoCombatEntity的实现方式。

背景

首先,GCS和GMS,以及GIS都属于GGF(通用游戏框架插件,我的All-In-One解决方案,但它还未发布)。

我希望我的产品能够保持相互之间的独立,模块化。因此GCS和GMS以及GIS都有自己对应的配套项目,它们都是从我当前的All-In-One中分离出来的子集。

想了解更多请看这里

合并项目

你可以手动将各插件对应版本的最新配套项目进行手动合并,或者将项目内容同时迁移到你自己的项目。

注意

配套项目均由我自己的项目拆分而成,因此你可以通过复制粘贴自由地再合并到一起。配套项目会共享一些内容,因此合并过程中的重复文件直接进行覆盖即可。

同时要注意配套项目的DefaultGame.ini和DefaultEngine.ini中的内容(若存在),也要进行合并。

安装所需插件

所有的配套项目都共享通用游戏系统(GGS),插件不需要合并,你只需从Fab安装好对应版本的插件即可。

整合GMS到GCS

GCS的配套项目中的Demo部分包含一个简单的运动系统,其演示了如何在GCS中引入运动系统,并包含:

  1. BC_Demo_MovementManager:位于/GenericDemo/GCS/Blueprints/MovementManager,是一个简单的运动管理组件。
  2. 若干动画蓝图:位于/GenericDemo/GCS/Animations/下,是几个轻量的动画蓝图,可以说是GMS的极端简化版本。

你可以将其替换为成别的实现,如:使用ALS/GASP或者我的GMS去替换GCS中的运动系统。

这里会介绍如何将GMS整合进GCS。

完成运动系统的基础设置

替换运动系统

在将GMS整合到GCS之前,你至少应该已经熟悉和了解GMS本身的使用。你可以参考运动系统的配置,在GCS中完成运动系统的基础组件设置。

关键点如下:

  1. 将GCS角色中的BC_Demo_MovementManager替换为GMS的运动系统组件,GMS的运动系统组件提供与BC_Demo_MovementManager相似的API/事件。
  2. 将GCS角色中的SkeletalMesh组件的动画蓝图从ABP_CombatCharacter替换为GMS提供的ABPT_GMS_Main。
  3. 参考GMS中的ABP_Mannequin_Ext在GMS中的用法,在GCS角色的Beginplay中,将其Link到SkeletalMesh组件的主动画实例中,ABP_Mannequin_Ext是设置角色Montage布局的动画层,它与GCS的Demo提供的ABP_CombatCharacter_Ext提供相同功能。

Loading Blueprint

Initializing BlueprintUE renderer...


更替运动API

GCS的战斗实体接口中,有几个运动相关的API与GMS的API兼容,因此你可以继承自BC_GCS_CombatEntity或者修改BC_GCS_DemoCombatEntity,并按照指示替换API调用。

GCS集成GMS.API案例

DemoCombatEntity中的案例代码。

GCS集成GMS.运动api

SetMovementSet/SetMovementState/SetRotationMode由GMS实现。


集成指南.GMSAPI.Getter

GetRotationMode/GetMovementState/GetMovementSet由GMS实现。

武器与运动定义整合

在GCS_Demo中,DemoWeapon是通过WeaponAnimLayerSelections 来配置了不同的动画层,并通过在合适的时候调用RefreshWeaponAnimLayerUnlinkWeaponAnimLayer来实现针对角色的状态,根据当前武器切换不同的动画层(不同的动画姿态)。

若采用GMS,你无需再手动调用武器上的动画层的刷新,而是在你的武器类中新增运动定义变量,然后在武器激活/取消激活的时候,为GMS运动系统组件设置运动定义。

当运动API调用时,GMS运动系统组件会自动刷新对应的控制/动画层逻辑。

GCS集成GMS.切换武器定义

取决于你的游戏设计,你可能需要更复杂的逻辑来决定每个武器在激活时,为运动系统设置什么样的运动定义(比如根据角色的性别,采用不同的运动定义)。

总结

理想情况下,当你在GCS中切换武器时,会切换GMS组件上的运动定义。当你通过CombatEntityInterface调用运动相关API时,会通过GMS组件来执行具体的运动控制/动画替换。

GCS集成GMS.004

整合GIS到GCS

jing'gao

此部分内容仍在编写中...

GCS的配套项目中的包含一个简单的武器管理组件,它演示了如何在GCS中引入装备管理系统。

你可以使用别的库存/装备系统去替换GCS中的武器管理功能等。

而这里会介绍如何使用GIS来管理GCS中的武器。

关键点

GIS是数据驱动的库存系统,若要用GIS来代替GCS中的装备管理功能,你需要从武器接口层去实现你的武器,而不是使用GCS_WeaponActor。

数据层

定义武器相关的道具Fragment,这部分是将GCS武器中的一些静态配置,以道具Fragment的形式制作。

cpp
1/**
2 * The primary weapon setting.
3 */
4UCLASS(Blueprintable, BlueprintType, Const, meta=(DisplayName="Weapon Settings"))
5class UGGF_ItemFragment_WeaponSettings : public UGIS_ItemFragment
6{
7 GENERATED_BODY()
8
9public:
10 /** Associated montages for abilities. */
11 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Weapon")
12 TSoftObjectPtr<UGCS_AbilityActionSetSettings> AbilityActionsLibrary;
13
14 /** Please add a variable description */
15 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Weapon")
16 TSoftObjectPtr<UGMS_MovementDefinition> MovementDefinition;
17
18 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Weapon")
19 TObjectPtr<UStaticMesh> StaticMesh;
20
21 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Weapon")
22 TObjectPtr<USkeletalMesh> SkeletalMesh;
23
24 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Weapon")
25 TSubclassOf<UAnimInstance> AnimInstanceClass;
26
27 /**
28 * Socket to attach to(will try one by one) while active.
29 * 当激活时,会附加到的插槽(一个个尝试)。
30 */
31 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Weapon")
32 TArray<FName> ActiveSockets{};
33
34 /**
35 * Socket to attach to(will try one by one) while not active.
36 * 当没激活时,会附加到的插槽(一个个尝试)。
37 */
38 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Weapon")
39 TArray<FName> InactiveSockets{};
40
41 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Weapon", meta=(RequiredAssetDataTags = "RowStructure=/Script/GenericCombatSystem.GCS_ComboDefinition"))
42 TObjectPtr<const UDataTable> ComboDefinitionTable;
43
44 /**
45 * Weapon trace definitions.
46 * 武器的检测定义
47 */
48 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Weapon", meta=(TitleProperty="TraceTag"))
49 TArray<FGCS_TraceDefinition> TraceDefinitions;
50
51#if WITH_EDITOR
52 virtual bool FragmentDataValidation_Implementation(FText& OutMessage) const override;
53#endif
54};
55
56
57/**
58 * The offhand weapon setting. The second equipment actor will be treated as OffhandWeapon.
59 */
60UCLASS(Blueprintable, BlueprintType, Const, meta=(DisplayName="Offhand Weapon Settings"))
61class UGGF_ItemFragment_OffHandWeaponSettings : public UGGF_ItemFragment_WeaponSettings
62{
63 GENERATED_BODY()
64};

逻辑层

装备实例

首先抽象出装备实例,负责所有装备的共通逻辑。

cpp
1// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
2
3#pragma once
4
5#include "CoreMinimal.h"
6#include "GIS_EquipmentInstance.h"
7#include "GGF_EquipmentInstance.generated.h"
8
9
10struct FGGA_AbilitySet_GrantedHandles;
11
12/** Base class for Equipment Instance, contains shared feature of ""Equipment */
13UCLASS(Blueprintable, BlueprintType)
14class GENERICGAMEFRAMEWORK_API UGGF_EquipmentInstance : public UGIS_EquipmentInstance
15{
16 GENERATED_BODY()
17
18protected:
19 virtual void OnEquipmentBeginPlay_Implementation() override;
20 virtual void OnEquipmentEndPlay_Implementation() override;
21 virtual void OnActiveStateChanged_Implementation(bool bNewActiveState) override;
22
23#pragma region AbilitySets
24 UFUNCTION(BlueprintCallable, Category="GGF|EquipmentInstance", meta=(BlueprintProtected))
25 virtual void GrantAbilitySets();
26
27 UFUNCTION(BlueprintCallable, Category="GGF|EquipmentInstance", meta=(BlueprintProtected))
28 virtual void GrantActiveAbilitySets();
29
30 UFUNCTION(BlueprintCallable, Category="GGF|EquipmentInstance", meta=(BlueprintProtected))
31 virtual void TakeAwayGrantedAbilitySets();
32
33 UFUNCTION(BlueprintCallable, Category="GGF|EquipmentInstance", meta=(BlueprintProtected))
34 virtual void TakeAwayGrantedActiveAbilitySets();
35
36 UFUNCTION(BlueprintCallable, Category="GGF|EquipmentInstance", meta=(BlueprintProtected))
37 virtual int32 GetEquipmentLevel() const;
38
39protected:
40 UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Transient, Category="EquipmentInstance")
41 TArray<FGGA_AbilitySet_GrantedHandles> GrantedAbilitySetHandles;
42
43 UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Transient, Category="EquipmentInstance")
44 TArray<FGGA_AbilitySet_GrantedHandles> GrantedActiveAbilitySetHandles;
45
46#pragma endregion
47};
48
cpp
1// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
2
3
4#include "Inventory/GGF_EquipmentInstance.h"
5
6#include "AbilitySystemBlueprintLibrary.h"
7#include "GIS_ItemInstance.h"
8#include "Inventory/GGF_ItemFragments.h"
9#include "Utilities/GGA_AbilitySystemFunctionLibrary.h"
10
11void UGGF_EquipmentInstance::OnEquipmentBeginPlay_Implementation()
12{
13 Super::OnEquipmentBeginPlay_Implementation();
14}
15
16void UGGF_EquipmentInstance::OnEquipmentEndPlay_Implementation()
17{
18 Super::OnEquipmentEndPlay_Implementation();
19}
20
21void UGGF_EquipmentInstance::OnActiveStateChanged_Implementation(bool bNewActiveState)
22{
23 Super::OnActiveStateChanged_Implementation(bNewActiveState);
24}
25
26void UGGF_EquipmentInstance::GrantAbilitySets()
27{
28 if (OwningPawn && OwningPawn->HasAuthority() && SourceItem)
29 {
30 if (auto Fragment = SourceItem->FindFragmentByClass<UGGF_ItemFragment_AbilitySets>())
31 {
32 if (auto ASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(OwningPawn))
33 {
34 GrantedAbilitySetHandles = UGGA_AbilitySet::GiveAbilitySetsToAbilitySystem(Fragment->AbilitySetsToGrant, ASC, this);
35 }
36 }
37 }
38}
39
40void UGGF_EquipmentInstance::GrantActiveAbilitySets()
41{
42 if (OwningPawn && OwningPawn->HasAuthority() && SourceItem)
43 {
44 if (auto Fragment = SourceItem->FindFragmentByClass<UGGF_ItemFragment_AbilitySets>())
45 {
46 if (auto ASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(OwningPawn))
47 {
48 GrantedActiveAbilitySetHandles = UGGA_AbilitySet::GiveAbilitySetsToAbilitySystem(Fragment->ActiveAbilitySetsToGrant, ASC, this, GetEquipmentLevel());
49 }
50 }
51 }
52}
53
54void UGGF_EquipmentInstance::TakeAwayGrantedAbilitySets()
55{
56 if (OwningPawn && OwningPawn->HasAuthority())
57 {
58 if (auto ASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(OwningPawn))
59 {
60 UGGA_AbilitySet::TakeAbilitySetsFromAbilitySystem(GrantedAbilitySetHandles, ASC);
61 GrantedAbilitySetHandles.Reset();
62 }
63 }
64}
65
66void UGGF_EquipmentInstance::TakeAwayGrantedActiveAbilitySets()
67{
68 if (OwningPawn && OwningPawn->HasAuthority())
69 {
70 if (auto ASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(OwningPawn))
71 {
72 for (auto& Handle : GrantedActiveAbilitySetHandles)
73 {
74 UGGA_AbilitySet::TakeAbilitySetFromAbilitySystem(Handle, ASC);
75 }
76 GrantedActiveAbilitySetHandles.Reset();
77 }
78 }
79}
80
81int32 UGGF_EquipmentInstance::GetEquipmentLevel() const
82{
83 return 1;
84}
85

武器实例

然后基于装备实例,实现武器实例,负责所有武器相关的逻辑。武器实例通过武器接口将GCS的武器功能桥接在一起。

cpp
1// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
2
3#pragma once
4
5#include "CoreMinimal.h"
6#include "GGF_EquipmentInstance.h"
7#include "Collision/GCS_TraceDelegates.h"
8#include "Weapon/GCS_WeaponInterface.h"
9#include "GGF_WeaponInstance.generated.h"
10
11class UGGF_ItemFragment_WeaponSettings;
12
13DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FGGF_OnWeaponTrailStateChangedSignature, bool, bEnable);
14
15/**
16 * An equipment instance bridge to GCS's weapon interface.
17 */
18UCLASS()
19class GENERICGAMEFRAMEWORK_API UGGF_WeaponInstance : public UGGF_EquipmentInstance, public IGCS_WeaponInterface
20{
21 GENERATED_BODY()
22
23public:
24 // Begin Weapon Interface
25 virtual APawn* GetWeaponOwner_Implementation() const override final;
26 virtual const FGameplayTagContainer GetWeaponTags_Implementation() const override;
27 virtual void SetWeaponActive_Implementation(bool bNewActive) override final;
28 virtual bool IsWeaponActive_Implementation() const override final;
29 virtual void ToggleTrail_Implementation(bool bEnable) override;
30 virtual UPrimitiveComponent* GetPrimitiveComponent_Implementation() const override;
31 // End WeaponInterface
32 // Begin Equipment Interface
33 virtual void OnEquipmentBeginPlay_Implementation() override;
34 virtual void OnEquipmentEndPlay_Implementation() override;
35 // End Equipment Interface
36
37 UPROPERTY(BlueprintAssignable, Category="Events")
38 FGCS_OnTraceHitSignature OnTraceHitEvent;
39
40 UPROPERTY(BlueprintAssignable, Category="Events")
41 FGCS_OnTraceStateChangedSignature OnTraceStateChangedEvent;
42
43 UPROPERTY(BlueprintAssignable, Category="Events")
44 FGGF_OnWeaponTrailStateChangedSignature OnTrailStateChangedEvent;
45
46protected:
47 virtual void OnActiveStateChanged_Implementation(bool bNewActiveState) override;
48
49#pragma region Settings
50
51public:
52 /**
53 * Gets the default native weapon setting.
54 * @return Setting fragment.
55 */
56 UFUNCTION(BlueprintCallable, BlueprintPure, Category="GGF|EquipmentInstance")
57 const UGGF_ItemFragment_WeaponSettings* GetWeaponSettings() const;
58
59 UFUNCTION(BlueprintCallable, BlueprintPure, Category="GGF|EquipmentInstance")
60 const UGGF_ItemFragment_WeaponSettings* GetOffhandWeaponSettings() const;
61
62#pragma endregion
63
64
65#pragma region Movements
66
67protected:
68 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GGF|EquipmentInstance", meta=(BlueprintProtected))
69 void RefreshMovementSet();
70 virtual void RefreshMovementSet_Implementation();
71
72#pragma endregion
73};
74
cpp
1// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
2
3
4#include "Inventory/GGF_WeaponInstance.h"
5#include "GIS_EquipmentActorInterface.h"
6#include "GIS_ItemInstance.h"
7#include "GIS_LogChannels.h"
8#include "GMS_MovementSystemComponent.h"
9#include "Inventory/GGF_ItemFragments.h"
10
11
12APawn* UGGF_WeaponInstance::GetWeaponOwner_Implementation() const
13{
14 return OwningPawn;
15}
16
17const FGameplayTagContainer UGGF_WeaponInstance::GetWeaponTags_Implementation() const
18{
19 return IsValid(SourceItem) ? SourceItem->GetItemTags() : FGameplayTagContainer::EmptyContainer;
20}
21
22void UGGF_WeaponInstance::SetWeaponActive_Implementation(bool bNewActive)
23{
24 bIsActive = true;
25}
26
27bool UGGF_WeaponInstance::IsWeaponActive_Implementation() const
28{
29 return bIsActive;
30}
31
32//UPrimitiveComponent* UGGF_WeaponInstance::GetPrimitiveComponent_Implementation() const
33//{
34// checkf(0, TEXT("%S %s Not implemented, Should implemented by sub class!"), __FUNCTION__, *GetClass()->GetName());
35// return nullptr;
36//}
37
38void UGGF_WeaponInstance::ToggleTrail_Implementation(bool bEnable)
39{
40 OnTrailStateChangedEvent.Broadcast(bEnable);
41}
42
43UPrimitiveComponent* UGGF_WeaponInstance::GetPrimitiveComponent_Implementation() const
44{
45 for (int32 i = 0; i < EquipmentActors.Num(); i++)
46 {
47 if (IsValid(EquipmentActors[i]))
48 {
49 if (UPrimitiveComponent* WeaponPrimitive = IGIS_EquipmentActorInterface::Execute_GetPrimitiveComponent(EquipmentActors[i]))
50 {
51 return WeaponPrimitive;
52 }
53 }
54 }
55 GIS_OWNED_CLOG(OwningPawn, Warning, "Weapon(%ss) failed to get primitive component on EquipmentActors!", *GetNameSafe(this));
56 return nullptr;
57}
58
59void UGGF_WeaponInstance::OnEquipmentBeginPlay_Implementation()
60{
61 Super::OnEquipmentBeginPlay_Implementation();
62}
63
64void UGGF_WeaponInstance::OnEquipmentEndPlay_Implementation()
65{
66 Super::OnEquipmentEndPlay_Implementation();
67}
68
69void UGGF_WeaponInstance::OnActiveStateChanged_Implementation(bool bNewActiveState)
70{
71 Super::OnActiveStateChanged_Implementation(bNewActiveState);
72}
73
74const UGGF_ItemFragment_WeaponSettings* UGGF_WeaponInstance::GetWeaponSettings() const
75{
76 return SourceItem->FindFragmentByClass<UGGF_ItemFragment_WeaponSettings>();
77}
78
79const UGGF_ItemFragment_WeaponSettings* UGGF_WeaponInstance::GetOffhandWeaponSettings() const
80{
81 return SourceItem->FindFragmentByClass<UGGF_ItemFragment_OffHandWeaponSettings>();
82}
83
84void UGGF_WeaponInstance::RefreshMovementSet_Implementation()
85{
86 if (OwningPawn && OwningPawn->HasAuthority())
87 {
88 if (bIsActive)
89 {
90 if (auto MSC = UGMS_MovementSystemComponent::GetMovementSystemComponent(OwningPawn))
91 {
92 MSC->SetMovementDefinition(GetWeaponSettings()->MovementDefinition);
93 }
94 }
95 }
96}
97

物理层

GIS中,一个装备可以有多个物理呈现,这里仅讨论武器Actor本身。

cpp
1// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
2
3#pragma once
4
5#include "CoreMinimal.h"
6#include "GIS_EquipmentActorInterface.h"
7#include "Collision/GCS_TraceStructLibrary.h"
8#include "GameFramework/Actor.h"
9#include "GGF_Weapon.generated.h"
10
11class UGGF_WeaponInstance;
12class UGIS_EquipmentInstance;
13class UGIS_EquipmentSystemComponent;
14class UGGF_ItemFragment_WeaponSettings;
15
16/**
17 * Physical representation for a weapon instance.
18 * 武器实例的物理呈现。
19 */
20UCLASS(BlueprintType, Blueprintable, Abstract, ClassGroup=(GGF))
21class GENERICGAMEFRAMEWORK_API AGGF_Weapon : public AActor, public IGIS_EquipmentActorInterface
22{
23 GENERATED_BODY()
24
25public:
26 /**
27 * Default constructor.
28 * 默认构造函数。
29 */
30 AGGF_Weapon();
31
32 // Begin EquipmentActorInterface
33 virtual UGIS_EquipmentInstance* GetSourceEquipment_Implementation() const override;
34 virtual UPrimitiveComponent* GetPrimitiveComponent_Implementation() const override;
35 // End EquipmentActorInterface
36
37protected:
38 // Begin EquipmentActorInterface
39 virtual void ReceiveSourceEquipment_Implementation(UGIS_EquipmentInstance* NewEquipmentInstance, int32 Idx) override;
40 virtual void ReceiveEquipmentBeginPlay_Implementation() override;
41 virtual void ReceiveEquipmentEndPlay_Implementation() override;
42 // End EquipmentActorInterface
43
44 //是否是副手武器。
45 UFUNCTION(BlueprintCallable, BlueprintPure, Category="GIS|EquipmentActor")
46 virtual bool IsOffhandWeapon() const;
47 /**
48 * Get correct weapon settings based on if offhand / primary weapon.
49 * @return The actual weapon settings.
50 */
51 UFUNCTION(BlueprintCallable, BlueprintPure, Category="GIS|EquipmentActor")
52 virtual const UGGF_ItemFragment_WeaponSettings* GetWeaponSettings() const;
53
54#pragma region Traces
55
56 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|WeaponTrace", meta=(BlueprintProtected))
57 void AddWeaponTraces();
58
59 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|WeaponTrace", meta=(BlueprintProtected))
60 void RemoveWeaponTraces();
61
62 /**
63 * Allow you to customize the source object used for weapon traces.
64 * 允许你自定义用于武器碰撞检测的源对象。
65 * @note The source object is the weapon itself by default. 默认是武器本身就是源对象.
66 * @return The object used as source object for weapon trace.
67 */
68 UFUNCTION(BlueprintNativeEvent, Category="GCS|WeaponTrace", meta=(BlueprintProtected))
69 UObject* GetSourceObjectForTrace();
70 virtual UObject* GetSourceObjectForTrace_Implementation();
71
72 /**
73 * Allow you to customize the source component used for weapon traces.
74 * 允许你自定义用于武器碰撞检测的源组件。
75 * @note The source component is the weapon primitive component by default. 默认是武器的PrimitiveComponent就是源组件.
76 * @return The component used as source component for weapon trace.
77 */
78 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|WeaponTrace", meta=(BlueprintProtected))
79 UPrimitiveComponent* GetSourceComponentForTrace(const FGameplayTag& TraceTag) const;
80 virtual UPrimitiveComponent* GetSourceComponentForTrace_Implementation(const FGameplayTag& TraceTag) const;
81
82 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|WeaponTrace")
83 void OnWeaponActiveStateChanged(bool bNewActiveState);
84
85 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|WeaponTrace")
86 void OnWeaponTrailStateChanged(bool bEnable);
87
88 UFUNCTION()
89 void OnAnyTraceHit(const FGCS_TraceHandle& TraceHandle, const FHitResult& HitResult);
90
91 /**
92 * Handles weapon trace hits.
93 * 处理武器碰撞命中。
94 * @param TraceHandle The collision trace instance. 碰撞检测实例。
95 * @param HitResult The hit result. 命中结果。
96 */
97 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|WeaponTrace")
98 void OnWeaponTraceHit(const FGCS_TraceHandle& TraceHandle, const FHitResult& HitResult);
99
100 UFUNCTION()
101 void OnAnyTraceStateChanged(const FGCS_TraceHandle& TraceHandle, bool NewState);
102
103 /**
104 * Handles trace state changes.
105 * 处理碰撞状态变化。
106 * @param TraceHandle The collision trace instance. 碰撞检测实例。
107 * @param NewState The new state. 新状态。
108 */
109 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|WeaponTrace")
110 void OnWeaponTraceStateChanged(const FGCS_TraceHandle& TraceHandle, bool NewState);
111
112 /**
113 * Traces associated with this weapon.
114 * 与该武器关联的碰撞检测.
115 */
116 UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Transient, Category="EquipmentInstance")
117 TArray<FGCS_TraceHandle> TraceHandles;
118#pragma endregion
119
120#pragma region Visuals
121
122protected:
123 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|WeaponVisual", meta=(BlueprintProtected))
124 void ApplyWeaponVisual();
125 virtual void ApplyWeaponVisual_Implementation();
126
127 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|WeaponVisual", meta=(BlueprintProtected))
128 void ResetWeaponVisual();
129 virtual void ResetWeaponVisual_Implementation();
130
131#pragma endregion
132
133#pragma region Movements
134
135 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|Weapon", meta=(BlueprintProtected))
136 void RefreshMovementSet();
137 virtual void RefreshMovementSet_Implementation();
138
139#pragma endregion
140
141protected:
142 //The equipment instance who created this equipment actor.
143 UPROPERTY(VisibleAnywhere, Transient, Category="WeaponState")
144 TObjectPtr<UGGF_WeaponInstance> SourceEquipment{nullptr};
145};
146
cpp
1// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
2
3
4#include "Inventory/GGF_Weapon.h"
5#include "AbilitySystemBlueprintLibrary.h"
6#include "Components/PrimitiveComponent.h"
7#include "GameFramework/Pawn.h"
8#include "GCS_LogChannels.h"
9#include "GIS_EquipmentInstance.h"
10#include "GIS_LogChannels.h"
11#include "GMS_MovementSystemComponent.h"
12#include "Collision/GCS_TraceSystemComponent.h"
13#include "Components/PoseableMeshComponent.h"
14#include "Inventory/GGF_ItemFragments.h"
15#include "Inventory/GGF_WeaponInstance.h"
16
17
18AGGF_Weapon::AGGF_Weapon()
19{
20 PrimaryActorTick.bCanEverTick = false;
21 bReplicates = true;
22 bOnlyRelevantToOwner = true;
23}
24
25UGIS_EquipmentInstance* AGGF_Weapon::GetSourceEquipment_Implementation() const
26{
27 return SourceEquipment;
28}
29
30UPrimitiveComponent* AGGF_Weapon::GetPrimitiveComponent_Implementation() const
31{
32 GCS_CLOG(Error, "Doesn't implement this function to get properly PrimitiveComponent For Weapon.")
33 return nullptr;
34}
35
36void AGGF_Weapon::ReceiveSourceEquipment_Implementation(UGIS_EquipmentInstance* NewEquipmentInstance, int32 Idx)
37{
38 if (!IsValid(NewEquipmentInstance))
39 {
40 GIS_CLOG(Error, "received invalid equipment")
41 return;
42 }
43
44 if (UGGF_WeaponInstance* WeaponInstance = Cast<UGGF_WeaponInstance>(NewEquipmentInstance))
45 {
46 SourceEquipment = WeaponInstance;
47 }
48 else
49 {
50 GIS_CLOG(Error, "failed to received source equipment, expecting type of %s, got %s", *UGGF_WeaponInstance::StaticClass()->GetName(), *GetNameSafe(NewEquipmentInstance->GetClass()))
51 }
52}
53
54void AGGF_Weapon::ReceiveEquipmentBeginPlay_Implementation()
55{
56 if (IsValid(SourceEquipment))
57 {
58 SourceEquipment->OnActiveStateChangedEvent.AddDynamic(this, &ThisClass::OnWeaponActiveStateChanged);;
59 SourceEquipment->OnTrailStateChangedEvent.AddDynamic(this, &ThisClass::OnWeaponTrailStateChanged);
60 }
61 else
62 {
63 GIS_CLOG(Error, "Missing source equipment!")
64 }
65}
66
67void AGGF_Weapon::ReceiveEquipmentEndPlay_Implementation()
68{
69 if (IsValid(SourceEquipment))
70 {
71 SourceEquipment->OnActiveStateChangedEvent.RemoveDynamic(this, &ThisClass::OnWeaponActiveStateChanged);
72 SourceEquipment->OnTrailStateChangedEvent.RemoveDynamic(this, &ThisClass::OnWeaponTrailStateChanged);
73 }
74}
75
76bool AGGF_Weapon::IsOffhandWeapon() const
77{
78 if (IsValid(SourceEquipment))
79 {
80 if (int32 Idx = SourceEquipment->GetIndexOfEquipmentActor(this))
81 {
82 // the second equipment actor are considered as Offhand Weapon.
83 return Idx == 1;
84 }
85 }
86 return false;
87}
88
89const UGGF_ItemFragment_WeaponSettings* AGGF_Weapon::GetWeaponSettings() const
90{
91 if (IsOffhandWeapon())
92 {
93 return SourceEquipment->GetOffhandWeaponSettings();
94 }
95 return SourceEquipment->GetWeaponSettings();
96}
97
98void AGGF_Weapon::AddWeaponTraces_Implementation()
99{
100 APawn* Pawn = Cast<APawn>(GetOwner());
101 if (!IsValid(Pawn))
102 {
103 GCS_OWNED_CLOG(this, Warning, "invalid weapon owner!")
104 return;
105 }
106 UGCS_TraceSystemComponent* TSC = UGCS_TraceSystemComponent::GetTraceSystemComponent(Pawn);
107 if (!IsValid(TSC))
108 {
109 GCS_OWNED_CLOG(Pawn, Warning, "missing trace system on weapon owner(%s)!", *GetNameSafe(Pawn))
110 return;
111 }
112
113 TraceHandles.Reset();
114 for (const FGCS_TraceDefinition& TraceDefinition : GetWeaponSettings()->TraceDefinitions)
115 {
116 UPrimitiveComponent* Primitive = GetSourceComponentForTrace(TraceDefinition.TraceTag);
117 if (Primitive == nullptr)
118 {
119 GCS_OWNED_CLOG(this, Error, "No SourceComponent provided for trace:%s,check your GetSourceComponentForTrace implementation.", *TraceDefinition.TraceTag.ToString());
120 continue;
121 }
122 FGCS_TraceHandle Handle = TSC->AddTrace(TraceDefinition, Primitive, GetSourceObjectForTrace());
123 if (Handle.IsValidHandle())
124 {
125 TraceHandles.Add(Handle);
126 }
127 }
128 TSC->OnTraceHitEvent.AddDynamic(this, &ThisClass::OnAnyTraceHit);
129 TSC->OnTraceStateChangedEvent.AddDynamic(this, &ThisClass::OnAnyTraceStateChanged);
130}
131
132void AGGF_Weapon::RemoveWeaponTraces_Implementation()
133{
134 APawn* Pawn = Cast<APawn>(GetOwner());
135 if (!IsValid(Pawn))
136 {
137 GCS_OWNED_CLOG(this, Warning, "invalid weapon owner!")
138 return;
139 }
140 if (UGCS_TraceSystemComponent* TSC = UGCS_TraceSystemComponent::GetTraceSystemComponent(Pawn))
141 {
142 TSC->OnTraceHitEvent.RemoveDynamic(this, &ThisClass::OnAnyTraceHit);
143 TSC->OnTraceStateChangedEvent.RemoveDynamic(this, &ThisClass::OnAnyTraceStateChanged);
144 TSC->RemoveTraces(TraceHandles);
145 }
146}
147
148UObject* AGGF_Weapon::GetSourceObjectForTrace_Implementation()
149{
150 return SourceEquipment;
151}
152
153UPrimitiveComponent* AGGF_Weapon::GetSourceComponentForTrace_Implementation(const FGameplayTag& TraceTag) const
154{
155 GCS_CLOG(Error, "Doesn't implement this function to get properly SourceComponent For Trace.")
156 return nullptr;
157}
158
159void AGGF_Weapon::OnAnyTraceHit(const FGCS_TraceHandle& TraceHandle, const FHitResult& HitResult)
160{
161 if (TraceHandles.Contains(TraceHandle))
162 {
163 OnWeaponTraceHit(TraceHandle, HitResult);
164 }
165}
166
167void AGGF_Weapon::OnAnyTraceStateChanged(const FGCS_TraceHandle& TraceHandle, bool NewState)
168{
169 if (TraceHandles.Contains(TraceHandle))
170 {
171 OnWeaponTraceStateChanged(TraceHandle, NewState);
172 }
173}
174
175void AGGF_Weapon::OnWeaponActiveStateChanged_Implementation(bool bNewActiveState)
176{
177 GCS_CLOG(VeryVerbose, "weapon active state changed:%s", bNewActiveState?TEXT("True"):TEXT("False"))
178}
179
180void AGGF_Weapon::OnWeaponTrailStateChanged_Implementation(bool bEnable)
181{
182 GCS_CLOG(VeryVerbose, "weapon trail state changed:%s", bEnable?TEXT("True"):TEXT("False"))
183}
184
185void AGGF_Weapon::OnWeaponTraceHit_Implementation(const FGCS_TraceHandle& TraceHandle, const FHitResult& HitResult)
186{
187 GCS_CLOG(VeryVerbose, "weapon hit %s", *HitResult.ToString())
188}
189
190void AGGF_Weapon::OnWeaponTraceStateChanged_Implementation(const FGCS_TraceHandle& TraceHandle, bool NewState)
191{
192}
193
194void AGGF_Weapon::ApplyWeaponVisual_Implementation()
195{
196 UPrimitiveComponent* Primitive = Execute_GetPrimitiveComponent(this);
197 if (!IsValid(Primitive))
198 {
199 GCS_CLOG(Error, "Missing primitive component!")
200 return;
201 }
202 if (!IsValid(SourceEquipment))
203 {
204 GCS_CLOG(Error, "Missing weapon settings!")
205 return;
206 }
207 if (Primitive->IsA<USkeletalMeshComponent>())
208 {
209 CastChecked<USkeletalMeshComponent>(Primitive)->SetSkeletalMeshAsset(GetWeaponSettings()->SkeletalMesh);
210 }
211 else if (Primitive->IsA<UStaticMeshComponent>())
212 {
213 CastChecked<UStaticMeshComponent>(Primitive)->SetStaticMesh(GetWeaponSettings()->StaticMesh);
214 }
215 else if (Primitive->IsA<UPoseableMeshComponent>())
216 {
217 CastChecked<UPoseableMeshComponent>(Primitive)->SetSkinnedAsset(GetWeaponSettings()->SkeletalMesh);
218 }
219}
220
221void AGGF_Weapon::ResetWeaponVisual_Implementation()
222{
223 UPrimitiveComponent* Primitive = Execute_GetPrimitiveComponent(this);
224 if (!IsValid(Primitive))
225 {
226 GCS_CLOG(Error, "Missing primitive component!")
227 return;
228 }
229 if (Primitive->IsA<USkeletalMeshComponent>())
230 {
231 CastChecked<USkeletalMeshComponent>(Primitive)->SetSkeletalMeshAsset(nullptr);
232 }
233 else if (Primitive->IsA<UStaticMeshComponent>())
234 {
235 CastChecked<UStaticMeshComponent>(Primitive)->SetStaticMesh(nullptr);
236 }
237 else if (Primitive->IsA<UPoseableMeshComponent>())
238 {
239 CastChecked<UPoseableMeshComponent>(Primitive)->SetSkinnedAsset(nullptr);
240 }
241}
242
243void AGGF_Weapon::RefreshMovementSet_Implementation()
244{
245 if (APawn* OwningPawn = IGIS_EquipmentInterface::Execute_GetOwningPawn(SourceEquipment))
246 {
247 if (OwningPawn->HasAuthority() && GetWeaponSettings())
248 {
249 if (IGIS_EquipmentInterface::Execute_IsEquipmentActive(SourceEquipment))
250 {
251 if (auto MSC = UGMS_MovementSystemComponent::GetMovementSystemComponent(OwningPawn))
252 {
253 MSC->SetMovementDefinition(GetWeaponSettings()->MovementDefinition);
254 }
255 }
256 }
257 }
258}
259

TODO...