GAS vs Character Movement Component? - Where and how implement movement abilities?

Hi everyone

The Context:
I’m studying the Gameplay Ability System (GAS) more closely these days along with replicated movement abilities. I never did a multiplayer project in Unreal Engine and after investigation, I’ve found that there are several ways to implement the same feature and I want to know what’s best. I will be using the Gameplay Ability System and the Enhanced Input.

The Question:
Where would the implementation of movement gameplay abilities, such as sprinting, wall running, climbing, sliding, etc be best between the Character Movement Component and the Gameplay Abilities? Would it be both?

My reasoning:
I know that blueprints aren’t ideal for replicated movement mechanics. As such, I always saw that sprint functionality (for example) was always better to be implemented directly into the character movement component. In doing so, you can change the movement speed and acceleration by using different values instead of editing them at run time. However, the Lyra game sample confused me.

In Lyra, there is no sprinting ability by default, but the aim down sight ability does alter the Character Movement Component. I don’t think it does it in an ideal way either. When initiating the ADS ability, the Gameplay Ability itself (not a Gameplay Effect) casts to the Character Movement Component to locally save the character’s walking speed at the moment of triggering the ability, sets a new value, then when the ability ends, it sets back the value to the recorded value. I see here that it is possible to have a Gameplay Ability to have the character sprint, but I see a red flag here:

  • Aiming Down Sight is the only thing in the Lyra game sample that I could find that alters the character’s movement. As such, Epic didn’t appear to have implemented the functionality the best way it could have. I see a potential for bugs here. An example of a bug that jumped to my eyes is what happens if you have another speed-altering ability trigger while the character is Aiming Down Sight. These two abilities would take the character movement speed modified by the other abilities as if it was a default value, causing the wrong speed to be applied when these abilities end.

Attribute Sets could be used to track base movement values such as default walking speed and acceleration. When doing so, we would need to modify the character movement component to draw the attributes’ value from the attribute set instead of taking directly from within his own variable. Doing so sounds like a good idea, especially when considering that Gameplay Effects can be stacked together and used to calculate the movement speed with all modifiers associated with it without ever modifying the base value. However, I don’t like this because, from my understanding, it requires the Character Movement Component to continuously draw information from the Attribute Set (and the Gameplay Effects that modifies it) to know how to behave properly rather than simply using the value that it already included by default. For this, I’ve found two methods, but I don’t know which one would be the best.

  • The first method is to update the Character Movement Component to include his own array of mathematical modifiers and have the Gameplay Abilities add or remove the modifiers whenever starting and ending an ability.
  • The second method would be to check when creating the Character Movement Component if his component owner (the character) also possesses a specified Attribute Set that would include the attribute that would replace the default built-in variable. If found, the Character Movement Component saves a reference which is then used to draw information from the Attribute Set instead of casting to it each time.

If you apply multiple speed buffs, for example, that cumulate together, where is the best place to set the maximum cumulated speed? For example, a base speed of 600u + 2 bonus of 300, but you want a maximum of 1000u in speed.

Beyond the manipulation of value, I also ask myself about permissions, players’ inputs, and settings parameters. For permission, Gameplay Tags are a good source of information that can be used to know, for example, if the character is allowed to sprint in the first place. Using these Gameplay Tags would require the Character Movement Component to also cast to the Gameplay Ability Component to get the relevant information, but I don’t think we want a periodic check like with the speed and acceleration attributes. As such, I’ve come up with two ideas to potentially work around this.

  • The first idea is to have the Gameplay Ability Component run a check whenever a related Gameplay Tag changes in quantity. For example, uncrouch a character and set the “Can Crouch” variable to false of his Character Movement Component whenever it gains 1 or more “no crouch” Gameplay Tags, and revert that change whenever it loose the last of those tags. I don’t know if that’s a feasible thing, however.
  • The second idea would be to substitute the use of Gameplay Tags for simple flags in the Character Movement Component, one for each movement ability. We could use these flags like the Character Movement Component already use them when wanting to uncrouch. Simply counting the quantity of “this character cannot sprint” to prevent sprinting as a gameplay tag would.
    • The problem with this is that if you want to also prevent any abilities that involve sprinting, like a charge ability or something, would still need to use Gameplay Tags to prevent that ability from being used while the character cannot completely execute the ability. Because of this, I think mixing both ideas would be good. Having the Gameplay Ability Component still pushes a set quantity of flags whenever (and only when) the associated Gameplay Tag changes, which is better I think than having the Character Movement Component continuously check for the tag itself.

When thinking of input, a common thing to pay attention to is what direction the player wants to sprint toward versus what direction the character is allowed to sprint toward. If you’re using a Gameplay Ability to make the character sprint by updating the Character movement Component, you’ll also need that ability to monitor the inputs to see if the player wants to sprint in a direction that is not desired. This would make the Gameplay Ability monitor inputs already recorded by the Character Movement Component and change the speed values each time a change of eligibility occurs. When considering this, using Gameplay Ability may be less than optimal. I’m also aware that Gameplay Ability can listen from input cues triggered by the Enhanced Input to trigger an ability, but I’m not sure if that’s really relevant here since we still want to avoid using multiple time the same inputs.

We can also take into account the player’s setting. In some games, a setting can cause the character to sprint by default, making your character sprint whenever allowed to as long as you’re not trying to do a conflicting action and your input is important enough (like pushing a joystick past 90% forward).

I remained focused on the sprint ability so far. That’s because it’s somewhat simple to grasp, but I also wonder the same for other movement abilities like climbing walls and over ledges, wall-running, physic-based sliding, etc. In those contexts, the use of the character capsule component is crucial. For example, climbing a wall requires the character to collide with the wall surface, which can be detected with a simple hit detection like the Character Movement Component already does to get the floor walkable angles. As such, I think this kind of movement feature would be best inside the Character Movement Component to minimize.

Despite all of this, even if I believe that implementing the core movement ability is best made in the Character Movement Component, I still find somewhere that I struggle to find an answer. Let’s suppose the following context. In a game, the player can customize his character with a movement option. As such, the player may have the ability to wall run (horizontal movement along a wall in a given direction) or crawl on walls like a spider can walk on a wall in any direction it desires. Although those are two abilities that are chosen when creating a character, what kind of implementation would be best?

  • The first option I see is to create multiple Character Movement Components with each a different mechanic and give the correct one to the player’s character. This would prevent a character from toggling between the two abilities without being able to do a hot swap of movement components at run time, which I don’t think it’s advisable given the physic simulation, replication, etc.
  • A second option is to have both movement mechanics within the same Character movement Component and have another set of checks to see which movement method should be used. That would make the toggle between the two more easy on the system, but require an extra check and sets of conditions.
  • A third option would be to implement everything into Gameplay Abilities, but as mentioned before, we would need a lot of outside information to be passed over those abilities despite already being recorded elsewhere.

In the end, I do believe that implementing these movement abilities is made in a mixture of both components. The codes that run, trigger, and allow replication should be in the movement Component and the permission in both the Movement Component (to know if a character does something in the first place) and the Gameplay Tags (to know if anything prevents the character from doing something). Despite that, I feel like I’m missing something, probably due to never having done anything that complex with both the Gameplay Ability System and modifying the Character Movement Component. What do you think?

4 Likes

The role of the Character Movement Component (and any Movement Component) is to do the physical simulation, and providing enough information about what’s going on that you can drive an animation blueprint appropriately.

The role of the Gameplay Ability System is to keep track of what the character can do, and perhaps how gameplay changes when doing so.

These systems can (and should) work together. If you want to be able to enable/disable whether the character can wall run, or change their max speed based on injury, or similar gameplay affects and ability gates, then you implement those parts in the Gameplay Ability System.

If you want the character simulation to not “fall down” when it has a sideways contact in “wall run” mode, and instead stick to the wall to the side, then you implement this in the movement component, as a special movement mode.

The gameplay ability system might trigger the ability to wall run, which will in turn put the simulation of your movement component into wall running mode.

Also, if you want the character to animate differently when in wall running mode, you will typically detect this mode in the animation blueprint, and play another set of montage/blend-space/state machines when in that mode.

These systems all go together, where each of them focuses on what it does best. If you don’t have particularly fancy rules for when the player can and cannot wall run, cast spells, take damage, and so on, then you might not need the GAS.

7 Likes

Thanks for the clarification about the role of those components. Just one last question that I couldn’t solve with your response:

In this example, wouldn’t the wall running ability be best triggered within the Character Movement Component given that all the physic simulations (such as checking if the direction and angle of the wall surface) are done here already? I feel like having the character recording a collision hit even to trigger a Gameplay Ability which at his turn activates the new movement mode is a bit of a detour.

2 Likes

There are at least four separate things involved in “triggering” a wall run ability:

  1. The user gives some command to wall run. This may be the regular run command, or some special button, or an overload on Jump, or whatnot. The GAS can help with this, if you don’t want to use the PlayerController standard input events.
  2. The user may or may not have permission to wall run – there may be a de-buff, or the ability is not unlocked, or whatever. This is typically handled by GAS if you have any kind of sophisticated game rules.
  3. The physics simulation for wall run may decide that there’s no wall nearby so you can’t wall run, or it may detect which wall it is, and start running/simulating on it.
  4. The animation needs to switch to something that clamps feet/hands towards the wall and leans the body appropriately.

So, 1) is typically handled by PlayerController or GAS. 2) is typically handled by GAS but if your logic rules are simple and you don’t have fancy stats/unlocks, you can build it into something else. 3) is typically handled by the Movement Component, and 4) is typically handled by the Animation Blueprint. Plus the Character Pawn may be involved in things like “knowing which state it’s in, so the other components can check it.”

6 Likes

Thank you for answering my questions jwatte. It’s very appreciated.

2 Likes

Thank you for this topic, I was wondering about the same things. However, I feel the response a bit lacking. Let me extend / re-ask the question:

Let’s say we have a MovementSpeed attribute attached to the character. This is done to easily handle multiple sources of movement speed changes (e.g. stun can reduce it to 0, sprint can multiply it by 3, etc.). This attribute is typically changed by one or more gameplay effects.

Let’s also say that the client has a 500ms latency. When the client activates the sprint ability which adds a gameplay effect that triples the the movement speed, locally, it can immediately change the character movement component’s max walk speed. However, the serves receives this ability activation 500ms later, and during this period, the movement component’s data is out of sync between the client and the server. This will result in a server correction.

Typically, any movement alternating thing should be / must be added to both the saved moves and the packets sent to the server for processing. For a simple “I want to sprint” functionality, this is easy, we can even use the built-in compressed flags for this. (For anyone interested, here is an explanation).

But the real question is: what should we save during the movement component’s logic? We cannot just save the max walk speed, because the client could easily cheat with it.