Overview
This article briefly introduces the attack and defense process in the GCS combat system to help you better utilize GCS.
I dislike writing this part because it feels more like a design document, and clever programmers can easily reverse-engineer and create their own versions.
Initiating the Attack Process
Any object, including the environment/traps, can initiate an attack. The attack process for humanoid characters in the game generally follows these steps:

Process Steps
- Activate ability: The attacker (player/AI) activates different abilities, such as melee abilities or spell abilities.
- Initiate Attack Request: The ability plays different combat animations. You place
AttackTrace/BulletTrace
animation notification states in the animation and configure the relevant attack request parameters. - Ability Handles Attack Request: The start and end of the
AttackTrace
notification state will pass the attack request to the Ability through gameplay events (attack information). The Ability will also include some of its own information in the attack information, such as the gameplay effects to be applied. - Melee: If it is a melee request, the character or weapon's collision trace instance (HitBox) will be activated, and the attack information will be passed to the collision trace instance. Once valid targets are detected, the attack information will be applied to those targets.
- Bullet: If it is a bullet request, a bullet instance will be created through the bullet subsystem, and the attack information will be passed to the bullet instance. Each bullet instance has its own collision trace instance, and when a valid target is detected, the bullet will apply the attack information to the target.
In the game, other environments and traps can also initiate attacks; they simply need the required attack information.
Before the attack information is applied to the target, you can modify the gameplay effect instances (GameplayEffectSpec
/GameplayEffectContainerSpec
) generated during the process, such as:
- Passing numerical parameters from the attack definition to the gameplay effect.
- Adding different tags to the gameplay effect based on conditions for later use (e.g., using tags to indicate whether it is fire damage or magic damage).
- Diminishing damage based on the bullet's flight distance.
- Adding new targets to the gameplay effect during bullet flight.
The ultimate goal of the attack process is to generate and modify gameplay effect instances and then apply them to the target.
Damage Calculation
The ultimate purpose of initiating an attack is to apply gameplay effects to the target. During the execution of the gameplay effect, specific attributes of the target will be modified. Gameplay effects can modify target attributes in various ways, and the GCS reference content provides a wealth of different effects for your reference, among which:
- GCS_Execution_Damage offers a default reference implementation of damage calculation logic found in soul-like games.
About the Default Implementation
This implementation is highly general and achieves multi-type damage and damage mitigation (absorption) logic through a minimal number of gameplay attributes. It is not hard-coded, allowing you to add more damage types (like holy damage, holy damage mitigation, etc.). You can use it directly or as a reference to create your own version.

Thanks to Generic Gameplay Abilities, you can fully leverage the extensive features of GAS through Blueprints without writing C++ code.
Handling the Attack Process

Combat Flow
CombatFlow
is a UObject
, and each combat system component can specify a CombatFlow
. You should inherit the built-in GCS_BaseCombatFlow
and override the relevant functions to implement your custom logic, or reference existing CombatFlow
and create a new one.
Handling Attribute Changes
As mentioned in the user guide, the attribute system component forwards all attribute change callbacks to the CombatFlow
for processing. Therefore, you only need to override HandleGameplayEffectExecute
.

How to handle attributes and what kind of attack results to produce is entirely determined by your own game mechanics, while the reference implementation provides common implementations found in soul-like games.
Generating Attack Results
After processing attribute changes in HandleGameplayEffectExecute
, you should convert the information from that process into attack results based on your game logic and register them with the combat system component.

For example, after handling IncomingDamage
, you should construct the relevant information into an AttackResult
structure and register it with the combat system. TaggedValues
includes some information generated during this process, such as "actual changed health." You can also customize this here.
Customizing Combat Flow
Attackers can initiate different attack requests, but each target may have a different "handling" for each attack request. You can implement different attack handling processes by customizing GCS_CombatFlow
and configuring different CombatFlow
classes on the CombatSystemComponent
.
For example, if an arrow is shot at an enemy, it should typically trigger a hit reaction. However, if your enemy is very large and you do not want them to react, you can specify a different CombatFlow
without needing extensive if/else checks at the attacker level.
When to Use Completely Different Combat Flows?
- You don't want a dog to deflect your attack.
- You don't want a gigantic boss to fall over just because you scratched its foot.
- You don't want a building to dodge your attack.
Attack Request
If you want to initiate any form of attack, you need to use an attack request. GCS has different types of attack requests, such as melee or bullet. Attack requests are generally triggered through animation notification states.
This section explains how to initiate different attack requests. Additionally, all Gameplay Abilities that inherit from GA_GCS_Attack
will automatically handle attack requests.
Melee Attacks
When a melee attack ability plays a montage, you can add ANS_GCS_AttackTrace
(animation notification state) within the appropriate attack frame range to configure an attack request.

- TraceToControls: Defines which collision trace instances need to be activated during the activation of the animation notification state.
- AttackDefinitionHandle: Points to a specific attack definition in the attack definition table, containing all static data related to "this hit" (such as attack power, cost, etc.).
In the above action, a right-hand weapon is used for the attack, so the detection instance named "RightHandWeapon" (which is bound to the weapon's mesh) should be activated. If the action involves a punch or a kick, you should also activate the corresponding detection instances (like RightHand
, LeftFoot
, which are bound to the character's mesh).
Ranged Attacks
In montages where bullets need to be fired (e.g., archery), you define the attack request by adding ANS_GCS_BulletTrace
in the attack frame.

- BulletDefinitionHandle: Points to a specific bullet definition in the bullet definition table, containing all static data related to "this bullet" (such as how many projectiles are actually generated, its attack power, etc.).
- TargetingSourceType: Determines how the bullet's spawn transform is calculated.

Custom Attack Request Types
You can create a subclass of GCS_AttackRequest
in Blueprints/C++ to add more information and use it in subsequent processes. For example, when you select Custom
for TargetingSourceType
, you should inherit from GCS_AttackRequest_Bullet
to create a new version and implement GetTargetingTransform
.

Using Attack Requests in the Combat Flow
The attack request itself is passed through the GameplayEffectContext
in different processes, meaning you can access various information configured in the attack request at any time. For instance, you can retrieve the attack request object via the context and access its associated attack definition.

Good to Know
- The attack request is an instantiated
UObject
, meaning it is not created at runtime but is created in the editor and stored on disk, which is the secret to its efficient network transmission. - The attack request is of type
Const
, meaning it can only store static data for game logic use, and you should not modify it during gameplay. - If you define a variable of type
AttackRequest
in Blueprints, you will see that it allows you to inline create an instance and save it with the outer asset.


Attack Definition
Concept
An attack definition
is a structure. For every type of attack in the game, there should be a corresponding attack definition. It contains static information for "each hit" in the combat game. A melee ability may play an attack animation but can produce multiple different types of attacks.
Each type of attack/weapon swing/projectile has specific properties associated with it, such as damage type, numerical bonuses, swing direction, etc.
In games like Elden Ring, there are thousands of different attack definitions. Since GCS manages attack definitions using a DataTable and uses soft class/object references, it will not slow down your project's performance.
Creating Attack Definition Tables
As mentioned earlier, when initiating an attack request, you need to specify an attack definition. You can create a new DataTable of type FGCS_AttackDefinition
and define all the attack definitions you will use in that table.
Typically, the number of attack definitions depends on the complexity of your game mechanics and how many attack variants exist. For example, the ability "Waterfowl Dance" in Elden Ring has as many as 20 attack definitions, each with different damage types, resilience damage, and associated attributes.

There are numerous fields to configure, with the most important properties being as follows:
- AttackTags: You describe the characteristics of the attack by adding game tags. These tags will be added to the attack information, allowing you to use this information for further logic checks when processing attack information.
- Example: When processing the attack result, you can check if the attack from the attacker has fire attributes and then check if the target is weak to fire. If both conditions are met, you can apply additional effects to the target.
- SetByCallerMagnitudes: Essentially a key-value pair of
GameplayTag -> float
, where you can fill in any information that will be passed into theGameplayEffectSpec
, allowing for further operations in calculations. - Example: If you have a series of combo attacks, the final segment may have a significant damage boost, and you can specify how much here, as long as your calculation accounts for it.
Managing Attack Definition Tables
From my understanding, Elden Ring categorizes different attack definition tables based on weapon types. Therefore, you can organize your attack definition tables as follows: DT_Atk_Sword_A
, DT_Atk_Sword_B
, DT_Atk_GreatSword_A
, DT_Atk_GreatSword_B
, etc.
Attack Results
Whenever CombatFlow
processes an attack, it generates an attack result and records it in the combat system component, synchronizing it with the client over the network.
When a combat result is generated, GCS triggers different combat feedback based on its content, such as playing hit reactions or generating visual effects through gameplay cues.
Handling Attack Results
In CombatFlow
, you can configure different AttackResultProcessors
(attack result processors) in a data-driven manner to handle different attack results accordingly.


You can refer to the GCS_BaseCombatFlow
Blueprint to understand the specific usage.
Built-in Attack Result Processors
Send Gampelay Event
This one allows you to send different gameplay events to attacker/victims based on attack results.

You can also use TagQuery to route different logic.
For example: only trigger BlockHit when the attack results's source tags have Block
tag. Only trigger hit reaction when attack results's source tags have Hit
tag.
Custom Attack Result Processors
For example: You can write a "Dmage Statistics Processor" which keeps track of received damage to other log systems.