准备工作
本文仅适用于同时购买了我的多个产品的用户,如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中引入运动系统,并包含:
- BC_Demo_MovementManager:位于/GenericDemo/GCS/Blueprints/MovementManager,是一个简单的运动管理组件。
- 若干动画蓝图:位于/GenericDemo/GCS/Animations/下,是几个轻量的动画蓝图,可以说是GMS的极端简化版本。
你可以将其替换为成别的实现,如:使用ALS/GASP或者我的GMS去替换GCS中的运动系统。
这里会介绍如何将GMS整合进GCS。
完成运动系统的基础设置
替换运动系统
在将GMS整合到GCS之前,你至少应该已经熟悉和了解GMS本身的使用。你可以参考运动系统的配置,在GCS中完成运动系统的基础组件设置。
关键点如下:
- 将GCS角色中的BC_Demo_MovementManager替换为GMS的运动系统组件,GMS的运动系统组件提供与BC_Demo_MovementManager相似的API/事件。
- 将GCS角色中的SkeletalMesh组件的动画蓝图从ABP_CombatCharacter替换为GMS提供的ABPT_GMS_Main。
- 参考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调用。

DemoCombatEntity中的案例代码。

SetMovementSet/SetMovementState/SetRotationMode由GMS实现。

GetRotationMode/GetMovementState/GetMovementSet由GMS实现。
武器与运动定义整合
在GCS_Demo中,DemoWeapon是通过WeaponAnimLayerSelections 来配置了不同的动画层,并通过在合适的时候调用RefreshWeaponAnimLayer和UnlinkWeaponAnimLayer来实现针对角色的状态,根据当前武器切换不同的动画层(不同的动画姿态)。
若采用GMS,你无需再手动调用武器上的动画层的刷新,而是在你的武器类中新增运动定义变量,然后在武器激活/取消激活的时候,为GMS运动系统组件设置运动定义。
当运动API调用时,GMS运动系统组件会自动刷新对应的控制/动画层逻辑。

取决于你的游戏设计,你可能需要更复杂的逻辑来决定每个武器在激活时,为运动系统设置什么样的运动定义(比如根据角色的性别,采用不同的运动定义)。
总结
理想情况下,当你在GCS中切换武器时,会切换GMS组件上的运动定义。当你通过CombatEntityInterface调用运动相关API时,会通过GMS组件来执行具体的运动控制/动画替换。

整合GIS到GCS
jing'gao
此部分内容仍在编写中...
GCS的配套项目中的包含一个简单的武器管理组件,它演示了如何在GCS中引入装备管理系统。
你可以使用别的库存/装备系统去替换GCS中的武器管理功能等。
而这里会介绍如何使用GIS来管理GCS中的武器。
关键点
GIS是数据驱动的库存系统,若要用GIS来代替GCS中的装备管理功能,你需要从武器接口层去实现你的武器,而不是使用GCS_WeaponActor。
数据层
定义武器相关的道具Fragment,这部分是将GCS武器中的一些静态配置,以道具Fragment的形式制作。
1/**2 * The primary weapon setting.3 */4UCLASS(Blueprintable, BlueprintType, Const, meta=(DisplayName="Weapon Settings"))5class UGGF_ItemFragment_WeaponSettings : public UGIS_ItemFragment6{7 GENERATED_BODY()89public:10 /** Associated montages for abilities. */11 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Weapon")12 TSoftObjectPtr<UGCS_AbilityActionSetSettings> AbilityActionsLibrary;1314 /** Please add a variable description */15 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Weapon")16 TSoftObjectPtr<UGMS_MovementDefinition> MovementDefinition;1718 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Weapon")19 TObjectPtr<UStaticMesh> StaticMesh;2021 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Weapon")22 TObjectPtr<USkeletalMesh> SkeletalMesh;2324 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Weapon")25 TSubclassOf<UAnimInstance> AnimInstanceClass;2627 /**28 * Socket to attach to(will try one by one) while active.29 * 当激活时,会附加到的插槽(一个个尝试)。30 */31 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Weapon")32 TArray<FName> ActiveSockets{};3334 /**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{};4041 UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Weapon", meta=(RequiredAssetDataTags = "RowStructure=/Script/GenericCombatSystem.GCS_ComboDefinition"))42 TObjectPtr<const UDataTable> ComboDefinitionTable;4344 /**45 * Weapon trace definitions.46 * 武器的检测定义47 */48 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Weapon", meta=(TitleProperty="TraceTag"))49 TArray<FGCS_TraceDefinition> TraceDefinitions;5051#if WITH_EDITOR52 virtual bool FragmentDataValidation_Implementation(FText& OutMessage) const override;53#endif54};555657/**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_WeaponSettings62{63 GENERATED_BODY()64};
逻辑层
装备实例
首先抽象出装备实例,负责所有装备的共通逻辑。
1// Copyright 2025 https://yuewu.dev/en All Rights Reserved.23#pragma once45#include "CoreMinimal.h"6#include "GIS_EquipmentInstance.h"7#include "GGF_EquipmentInstance.generated.h"8910struct FGGA_AbilitySet_GrantedHandles;1112/** Base class for Equipment Instance, contains shared feature of ""Equipment */13UCLASS(Blueprintable, BlueprintType)14class GENERICGAMEFRAMEWORK_API UGGF_EquipmentInstance : public UGIS_EquipmentInstance15{16 GENERATED_BODY()1718protected:19 virtual void OnEquipmentBeginPlay_Implementation() override;20 virtual void OnEquipmentEndPlay_Implementation() override;21 virtual void OnActiveStateChanged_Implementation(bool bNewActiveState) override;2223#pragma region AbilitySets24 UFUNCTION(BlueprintCallable, Category="GGF|EquipmentInstance", meta=(BlueprintProtected))25 virtual void GrantAbilitySets();2627 UFUNCTION(BlueprintCallable, Category="GGF|EquipmentInstance", meta=(BlueprintProtected))28 virtual void GrantActiveAbilitySets();2930 UFUNCTION(BlueprintCallable, Category="GGF|EquipmentInstance", meta=(BlueprintProtected))31 virtual void TakeAwayGrantedAbilitySets();3233 UFUNCTION(BlueprintCallable, Category="GGF|EquipmentInstance", meta=(BlueprintProtected))34 virtual void TakeAwayGrantedActiveAbilitySets();3536 UFUNCTION(BlueprintCallable, Category="GGF|EquipmentInstance", meta=(BlueprintProtected))37 virtual int32 GetEquipmentLevel() const;3839protected:40 UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Transient, Category="EquipmentInstance")41 TArray<FGGA_AbilitySet_GrantedHandles> GrantedAbilitySetHandles;4243 UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Transient, Category="EquipmentInstance")44 TArray<FGGA_AbilitySet_GrantedHandles> GrantedActiveAbilitySetHandles;4546#pragma endregion47};48
1// Copyright 2025 https://yuewu.dev/en All Rights Reserved.234#include "Inventory/GGF_EquipmentInstance.h"56#include "AbilitySystemBlueprintLibrary.h"7#include "GIS_ItemInstance.h"8#include "Inventory/GGF_ItemFragments.h"9#include "Utilities/GGA_AbilitySystemFunctionLibrary.h"1011void UGGF_EquipmentInstance::OnEquipmentBeginPlay_Implementation()12{13 Super::OnEquipmentBeginPlay_Implementation();14}1516void UGGF_EquipmentInstance::OnEquipmentEndPlay_Implementation()17{18 Super::OnEquipmentEndPlay_Implementation();19}2021void UGGF_EquipmentInstance::OnActiveStateChanged_Implementation(bool bNewActiveState)22{23 Super::OnActiveStateChanged_Implementation(bNewActiveState);24}2526void 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}3940void 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}5354void 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}6566void 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}8081int32 UGGF_EquipmentInstance::GetEquipmentLevel() const82{83 return 1;84}85
武器实例
然后基于装备实例,实现武器实例,负责所有武器相关的逻辑。武器实例通过武器接口将GCS的武器功能桥接在一起。
1// Copyright 2025 https://yuewu.dev/en All Rights Reserved.23#pragma once45#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"1011class UGGF_ItemFragment_WeaponSettings;1213DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FGGF_OnWeaponTrailStateChangedSignature, bool, bEnable);1415/**16 * An equipment instance bridge to GCS's weapon interface.17 */18UCLASS()19class GENERICGAMEFRAMEWORK_API UGGF_WeaponInstance : public UGGF_EquipmentInstance, public IGCS_WeaponInterface20{21 GENERATED_BODY()2223public:24 // Begin Weapon Interface25 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 WeaponInterface32 // Begin Equipment Interface33 virtual void OnEquipmentBeginPlay_Implementation() override;34 virtual void OnEquipmentEndPlay_Implementation() override;35 // End Equipment Interface3637 UPROPERTY(BlueprintAssignable, Category="Events")38 FGCS_OnTraceHitSignature OnTraceHitEvent;3940 UPROPERTY(BlueprintAssignable, Category="Events")41 FGCS_OnTraceStateChangedSignature OnTraceStateChangedEvent;4243 UPROPERTY(BlueprintAssignable, Category="Events")44 FGGF_OnWeaponTrailStateChangedSignature OnTrailStateChangedEvent;4546protected:47 virtual void OnActiveStateChanged_Implementation(bool bNewActiveState) override;4849#pragma region Settings5051public: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;5859 UFUNCTION(BlueprintCallable, BlueprintPure, Category="GGF|EquipmentInstance")60 const UGGF_ItemFragment_WeaponSettings* GetOffhandWeaponSettings() const;6162#pragma endregion636465#pragma region Movements6667protected:68 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GGF|EquipmentInstance", meta=(BlueprintProtected))69 void RefreshMovementSet();70 virtual void RefreshMovementSet_Implementation();7172#pragma endregion73};74
1// Copyright 2025 https://yuewu.dev/en All Rights Reserved.234#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"101112APawn* UGGF_WeaponInstance::GetWeaponOwner_Implementation() const13{14 return OwningPawn;15}1617const FGameplayTagContainer UGGF_WeaponInstance::GetWeaponTags_Implementation() const18{19 return IsValid(SourceItem) ? SourceItem->GetItemTags() : FGameplayTagContainer::EmptyContainer;20}2122void UGGF_WeaponInstance::SetWeaponActive_Implementation(bool bNewActive)23{24 bIsActive = true;25}2627bool UGGF_WeaponInstance::IsWeaponActive_Implementation() const28{29 return bIsActive;30}3132//UPrimitiveComponent* UGGF_WeaponInstance::GetPrimitiveComponent_Implementation() const33//{34// checkf(0, TEXT("%S %s Not implemented, Should implemented by sub class!"), __FUNCTION__, *GetClass()->GetName());35// return nullptr;36//}3738void UGGF_WeaponInstance::ToggleTrail_Implementation(bool bEnable)39{40 OnTrailStateChangedEvent.Broadcast(bEnable);41}4243UPrimitiveComponent* UGGF_WeaponInstance::GetPrimitiveComponent_Implementation() const44{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}5859void UGGF_WeaponInstance::OnEquipmentBeginPlay_Implementation()60{61 Super::OnEquipmentBeginPlay_Implementation();62}6364void UGGF_WeaponInstance::OnEquipmentEndPlay_Implementation()65{66 Super::OnEquipmentEndPlay_Implementation();67}6869void UGGF_WeaponInstance::OnActiveStateChanged_Implementation(bool bNewActiveState)70{71 Super::OnActiveStateChanged_Implementation(bNewActiveState);72}7374const UGGF_ItemFragment_WeaponSettings* UGGF_WeaponInstance::GetWeaponSettings() const75{76 return SourceItem->FindFragmentByClass<UGGF_ItemFragment_WeaponSettings>();77}7879const UGGF_ItemFragment_WeaponSettings* UGGF_WeaponInstance::GetOffhandWeaponSettings() const80{81 return SourceItem->FindFragmentByClass<UGGF_ItemFragment_OffHandWeaponSettings>();82}8384void 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本身。
1// Copyright 2025 https://yuewu.dev/en All Rights Reserved.23#pragma once45#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"1011class UGGF_WeaponInstance;12class UGIS_EquipmentInstance;13class UGIS_EquipmentSystemComponent;14class UGGF_ItemFragment_WeaponSettings;1516/**17 * Physical representation for a weapon instance.18 * 武器实例的物理呈现。19 */20UCLASS(BlueprintType, Blueprintable, Abstract, ClassGroup=(GGF))21class GENERICGAMEFRAMEWORK_API AGGF_Weapon : public AActor, public IGIS_EquipmentActorInterface22{23 GENERATED_BODY()2425public:26 /**27 * Default constructor.28 * 默认构造函数。29 */30 AGGF_Weapon();3132 // Begin EquipmentActorInterface33 virtual UGIS_EquipmentInstance* GetSourceEquipment_Implementation() const override;34 virtual UPrimitiveComponent* GetPrimitiveComponent_Implementation() const override;35 // End EquipmentActorInterface3637protected:38 // Begin EquipmentActorInterface39 virtual void ReceiveSourceEquipment_Implementation(UGIS_EquipmentInstance* NewEquipmentInstance, int32 Idx) override;40 virtual void ReceiveEquipmentBeginPlay_Implementation() override;41 virtual void ReceiveEquipmentEndPlay_Implementation() override;42 // End EquipmentActorInterface4344 //是否是副手武器。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;5354#pragma region Traces5556 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|WeaponTrace", meta=(BlueprintProtected))57 void AddWeaponTraces();5859 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|WeaponTrace", meta=(BlueprintProtected))60 void RemoveWeaponTraces();6162 /**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();7172 /**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;8182 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|WeaponTrace")83 void OnWeaponActiveStateChanged(bool bNewActiveState);8485 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|WeaponTrace")86 void OnWeaponTrailStateChanged(bool bEnable);8788 UFUNCTION()89 void OnAnyTraceHit(const FGCS_TraceHandle& TraceHandle, const FHitResult& HitResult);9091 /**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);99100 UFUNCTION()101 void OnAnyTraceStateChanged(const FGCS_TraceHandle& TraceHandle, bool NewState);102103 /**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);111112 /**113 * Traces associated with this weapon.114 * 与该武器关联的碰撞检测.115 */116 UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Transient, Category="EquipmentInstance")117 TArray<FGCS_TraceHandle> TraceHandles;118#pragma endregion119120#pragma region Visuals121122protected:123 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|WeaponVisual", meta=(BlueprintProtected))124 void ApplyWeaponVisual();125 virtual void ApplyWeaponVisual_Implementation();126127 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|WeaponVisual", meta=(BlueprintProtected))128 void ResetWeaponVisual();129 virtual void ResetWeaponVisual_Implementation();130131#pragma endregion132133#pragma region Movements134135 UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GCS|Weapon", meta=(BlueprintProtected))136 void RefreshMovementSet();137 virtual void RefreshMovementSet_Implementation();138139#pragma endregion140141protected:142 //The equipment instance who created this equipment actor.143 UPROPERTY(VisibleAnywhere, Transient, Category="WeaponState")144 TObjectPtr<UGGF_WeaponInstance> SourceEquipment{nullptr};145};146
1// Copyright 2025 https://yuewu.dev/en All Rights Reserved.234#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"161718AGGF_Weapon::AGGF_Weapon()19{20 PrimaryActorTick.bCanEverTick = false;21 bReplicates = true;22 bOnlyRelevantToOwner = true;23}2425UGIS_EquipmentInstance* AGGF_Weapon::GetSourceEquipment_Implementation() const26{27 return SourceEquipment;28}2930UPrimitiveComponent* AGGF_Weapon::GetPrimitiveComponent_Implementation() const31{32 GCS_CLOG(Error, "Doesn't implement this function to get properly PrimitiveComponent For Weapon.")33 return nullptr;34}3536void 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 }4344 if (UGGF_WeaponInstance* WeaponInstance = Cast<UGGF_WeaponInstance>(NewEquipmentInstance))45 {46 SourceEquipment = WeaponInstance;47 }48 else49 {50 GIS_CLOG(Error, "failed to received source equipment, expecting type of %s, got %s", *UGGF_WeaponInstance::StaticClass()->GetName(), *GetNameSafe(NewEquipmentInstance->GetClass()))51 }52}5354void 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 else62 {63 GIS_CLOG(Error, "Missing source equipment!")64 }65}6667void 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}7576bool AGGF_Weapon::IsOffhandWeapon() const77{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}8889const UGGF_ItemFragment_WeaponSettings* AGGF_Weapon::GetWeaponSettings() const90{91 if (IsOffhandWeapon())92 {93 return SourceEquipment->GetOffhandWeaponSettings();94 }95 return SourceEquipment->GetWeaponSettings();96}9798void 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 }112113 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}131132void 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}147148UObject* AGGF_Weapon::GetSourceObjectForTrace_Implementation()149{150 return SourceEquipment;151}152153UPrimitiveComponent* AGGF_Weapon::GetSourceComponentForTrace_Implementation(const FGameplayTag& TraceTag) const154{155 GCS_CLOG(Error, "Doesn't implement this function to get properly SourceComponent For Trace.")156 return nullptr;157}158159void AGGF_Weapon::OnAnyTraceHit(const FGCS_TraceHandle& TraceHandle, const FHitResult& HitResult)160{161 if (TraceHandles.Contains(TraceHandle))162 {163 OnWeaponTraceHit(TraceHandle, HitResult);164 }165}166167void AGGF_Weapon::OnAnyTraceStateChanged(const FGCS_TraceHandle& TraceHandle, bool NewState)168{169 if (TraceHandles.Contains(TraceHandle))170 {171 OnWeaponTraceStateChanged(TraceHandle, NewState);172 }173}174175void AGGF_Weapon::OnWeaponActiveStateChanged_Implementation(bool bNewActiveState)176{177 GCS_CLOG(VeryVerbose, "weapon active state changed:%s", bNewActiveState?TEXT("True"):TEXT("False"))178}179180void AGGF_Weapon::OnWeaponTrailStateChanged_Implementation(bool bEnable)181{182 GCS_CLOG(VeryVerbose, "weapon trail state changed:%s", bEnable?TEXT("True"):TEXT("False"))183}184185void AGGF_Weapon::OnWeaponTraceHit_Implementation(const FGCS_TraceHandle& TraceHandle, const FHitResult& HitResult)186{187 GCS_CLOG(VeryVerbose, "weapon hit %s", *HitResult.ToString())188}189190void AGGF_Weapon::OnWeaponTraceStateChanged_Implementation(const FGCS_TraceHandle& TraceHandle, bool NewState)191{192}193194void 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}220221void 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}242243void 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...
