Sign in

Quod Soler ☀️

@quodsoler.com
261 followers 40 following 405 posts

Lead Gameplay Programmer for Lords of the Fallen at Hexworks • I will teach you Unreal Engine • Gameplay Ability System expert, opinions are my own

PostsRepliesMedia
Quod Soler ☀️ @quodsoler.com · 6h
In Unreal GAS sometimes you need to change aan ability behavior based on character tags granted by other abilities. There is one issue though. In ActivateAbility those tags may no longer be there. The activation canceled a previous ablity that granted them. What do you do?
101
Quod Soler ☀️ @quodsoler.com · 28/09/2026
Everyday I'm considering going back to doing some small C projects. Not even C++. I still have to try Raylib as well, so maybe I can use this as an opportunity to do so.
110
Quod Soler ☀️ @quodsoler.com · 25/09/2026
The attack starts blending out. Should the next action already be allowed? I'd decide that before wiring PlayMontageAndWait. OnBlendOut marks the start of blend-out. OnCompleted marks an uninterrupted finish. They are different moments on the timeline.
000
Quod Soler ☀️ @quodsoler.com · 24/09/2026
A mage fires a projectile, then equips a stronger staff before it lands. Should that projectile deal more damage? I'd settle that rule before setting up GAS attribute captures. Snapshotting decides whether a captured stat stays fixed or is evaluated live.
011
Quod Soler ☀️ @quodsoler.com · 24/09/2026
Teams tend to discover it the same way: a playtest hit clips through a wall and the server just agrees. Not a bug. GAS target data is client authoritative by default, validation is left to you on purpose. Decide per ability how much you trust the client.
000
Quod Soler ☀️ @quodsoler.com · 23/09/2026
Half of GAS debugging is realizing the engine already knows the answer. An ability that refuses to activate is not a mystery: Log LogAbilitySystem VeryVerbose prints the exact check that failed, from blocked tags to net policy. One console line before any breakpoint.
010
Quod Soler ☀️ @quodsoler.com · 23/09/2026
Want an evolving world in your game? Start with Game Flags: killed_dragon = true met_merchant = true village_burned = true Every NPC, cutscene, and door checks the flags. That's it. Silent Hill ships this. Simplest system you'll ever build. Most power you'll ever get from one.
020
Quod Soler ☀️ @quodsoler.com · 22/09/2026
I want a spear's bonus damage to reward spacing. In my setup, the pole is rank 0 and the tip is rank 1, in the same hitbox group. Lower rank wins. Touch both: normal damage. Touch only the tip: bonus damage. The hitbox data defines the reward.
000
Quod Soler ☀️ @quodsoler.com · 22/09/2026
If a hitbox looks right in the editor but misses in-game, the preview stops being useful. My preview and runtime sweeps use the same transform code, weapon meshes and sockets. I want designers adjusting the attack, not checking which view to believe.
010
Quod Soler ☀️ @quodsoler.com · 22/09/2026
The whole input buffer runs on the owning client: the queue, the gate check, the drain. Only the resulting gameplay event touches the network, and it goes through the same activation and prediction path as a live press. Buffered and live input can never behave differently.
000
Quod Soler ☀️ @quodsoler.com · 21/09/2026
For knockback, I want designers to author the distance an enemy should move. My code derives the root motion strength from that distance, the duration and the curve's average. They can reshape the shove without manually retuning its strength each time.
000
Quod Soler ☀️ @quodsoler.com · 21/09/2026
Symptom: veteran save files have quietly inflated stats and nobody can say when it started. Cause: the save system persisted CurrentValue, so every buff active at save time was baked in. Fix: save BaseValue, reapply persistent effects on load. Then audit your old saves.
010
Quod Soler ☀️ @quodsoler.com · 20/09/2026
You queue a dodge during an attack, then change direction before it starts. My buffer reads the stick again when the dodge fires. Above half deflection, it uses the new direction. Otherwise, it keeps the original one. A light nudge should not redirect the roll.
000
Quod Soler ☀️ @quodsoler.com · 20/09/2026
The frames where an early press is remembered, and the frame where the follow-up may start, are animation decisions. So they live on the montage as notify states, next to damage and i-frame windows. If retiming a combo window needs a programmer, it is authored in the wrong place.
000
Quod Soler ☀️ @quodsoler.com · 19/09/2026
An animator retimes a dodge. Now the safe frames need to move too. In my framework, an anim notify state controls the invulnerability effect: apply it on begin, remove it on end. The animator can adjust that window alongside the animation.
010
Quod Soler ☀️ @quodsoler.com · 19/09/2026
Hardcoding numbers into Gameplay Effects doesn't scale. The fix takes 3 steps: 1. Set the modifier magnitude to SetByCaller 2. Tag it, e.g. Data.Damage 3. Pass the value into the spec before applying it Now one effect serves every weapon, and the numbers live in a data table.
000
Quod Soler ☀️ @quodsoler.com · 18/09/2026
Hardcoding numbers into Gameplay Effects doesn't scale. 10 weapons means 10 damage effects. 50 weapons means 50. SetByCaller lets the caller send the magnitude at runtime, from a data table or the weapon asset. One effect definition. As many uses as you have data rows.
000
Quod Soler ☀️ @quodsoler.com · 18/09/2026
Gameplay programmers: if your dodge roll has an IFrameDuration float, it's already a bug. The animator retimes the roll. Nobody updates the float. Players get hit mid-roll and blame netcode. My C++ has no such float. I-frames are the notify state in the animation.
000
Quod Soler ☀️ @quodsoler.com · 18/09/2026
If GAS warns about too many live AbilityTasks, I would check cleanup before raising the limit. Start with cancellation and early returns. Does each path reach EndAbility? A higher cap can hide a leak for longer.
000
Quod Soler ☀️ @quodsoler.com · 17/09/2026
A player dies while a buff still has 20 seconds left. Should respawning remove it? I'd answer that before choosing where the ASC lives. A PlayerState-owned ASC can keep state across a Pawn change. The design tells you which lifetime you need.
000
Quod Soler ☀️ @quodsoler.com · 17/09/2026
If your GAS ability works once and breaks on the second use, check what it remembers. InstancedPerActor reuses the same object. A cached target or bHasHit can survive the last activation. Reset per-run state when the next one starts.
000
Quod Soler ☀️ @quodsoler.com · 16/09/2026
You don't need to use GAS to benefit from Gameplay Tags. You can use Gameplay Tags on their own. They are incredibly useful to track state, communicate events or even as a simple save system. The only thing I would recommend taking from GAS is the Gameplay Tag Count Container.
120
Quod Soler ☀️ @quodsoler.com · 16/09/2026
Why don't GAS Abilities have a Tick function? It catches almost everyone learning GAS. The system is event driven by design, so Gameplay Events fire when you need them and you skip the per frame work. If you do need to tick, a small Ability Task covers it.
010
Quod Soler ☀️ @quodsoler.com · 15/09/2026
If you must make sure of one thing is this: Saves must NEVER break. A good lead once engraved this into my mind and it has lived there forever. So if you need to improve your save system, don't hesitate to do so.
110
Quod Soler ☀️ @quodsoler.com · 15/09/2026
Why does an unset SetByCaller become 0 instead of failing loudly? The number never lived on the UGameplayEffect. It belongs to the spec, supplied at creation. No entry, nothing to evaluate, fallback is 0. And if that 0 feeds a divide op, you built a division by zero.
000
Quod Soler ☀️ @quodsoler.com · 14/09/2026
GAS tags are reference-counted, not boolean. If two effects grant State.Stunned, removing one effect must not clear the state. Only once all sources of that tag have been removed the Stunned state ends.
010
Quod Soler ☀️ @quodsoler.com · 14/09/2026
Small input buffering detail that matters: timestamp entries in real time, not game time. Hitstop and slow motion stretch the game clock, and how long a press stays valid should follow the player's wall clock. That one does not slow down with the game.
000
Quod Soler ☀️ @quodsoler.com · 13/09/2026
Sent the Mass data model to the newsletter yesterday: entities as 8 byte handles, fragments and tags, and the archetype layout that makes it all fast. The full article version is on the blog, with the code and the which-element-type decision list. Link below.
101
Quod Soler ☀️ @quodsoler.com · 13/09/2026
I said it before but it bears repeating. NEVER put gameplay logic in Gameplay Cues. Cues are cosmetic. They can be lost over network. Executing logic is not a Gameplay Cue job. They are there for player feedback.
000
Quod Soler ☀️ @quodsoler.com · 12/09/2026
Symptom: a stacked "+50% damage" buff deals +100%, but the balance sheet assumed +125%. Cause: Multiply ( Additive ) was used. Fix: Switch to Multiply ( Compound ) when buffs should add up in this way.
010
Quod Soler ☀️ @quodsoler.com · 12/09/2026
Tip when working with equipment buffs. If your equipment grants passives, store the FGameplayAbilitySpecHandle for the abilities they grant. Later when the equipment is unequipped, use that handle to remove the ability. Removing abilities by class may not be a good idea.
000
Quod Soler ☀️ @quodsoler.com · 11/09/2026
Silent Hill controls all of its progression, cutscenes, and game flow with simple Game Flags. No complex state machines. No quest engine. Booleans. If you're building an RPG or any evolving world, this is the highest leverage-to-effort system you can steal.
041
Quod Soler ☀️ @quodsoler.com · 11/09/2026
Tomorrow in the newsletter: Unreal's Mass framework in plain words. What an entity actually is, the two element types that do most of the work, and why the chunk layout makes tens of thousands of entities cheap. 4 minutes, one email. www.quodsoler.com/combat-arch...
001
Quod Soler ☀️ @quodsoler.com · 11/09/2026
Choose an Execution Calculation on your effect if you need side effects. Choose a Modifier Magnitude Calculation if you need one of the modifiers of your effect to be derived from a complex formula. One is used for input, the other for output.
000
Quod Soler ☀️ @quodsoler.com · 10/09/2026
If every grunt in the level shares the same movement tuning, do not store MaxSpeed 600 a thousand times. A const shared fragment in Mass stores one instance and every entity references it. But never register per-entity-unique values: different shared values can not share a chunk.
010
Quod Soler ☀️ @quodsoler.com · 10/09/2026
A buffered dodge stores the stick direction at press time. The player keeps steering while it waits. Fire it with the stored direction and it feels like a bug; re-sample the stick at consume time and it feels like the game read their mind. It is one small function.
000
Quod Soler ☀️ @quodsoler.com · 09/09/2026
Keep Mass fragments small. Every byte multiplies across thousands of entities, and cold data mixed into a hot fragment wastes cache bandwidth for every query touching it. Split per-frame data and rarely-touched data into separate fragments. Mass handles tons of small ones well.
000
Quod Soler ☀️ @quodsoler.com · 09/09/2026
After a GAS execution runs, the engine multiplies every output modifier by the effect's stack count, unless you call MarkStackCountHandledManually. A stackable DoT at three overlapping stacks deals triple damage per hit, and single-stack testing never shows it.
000
Quod Soler ☀️ @quodsoler.com · 08/09/2026
Why is an ECS fast? In Mass, entities of the same composition pack into 128 KB chunks, one parallel array per fragment type. Reading transforms is a linear walk over packed data of one type, exactly what the prefetcher wants. Fragments you did not ask for are never touched.
010
Quod Soler ☀️ @quodsoler.com · 08/09/2026
I think we all can learn a lot from old games like Zelda Ocarina of Time. The way character movement colliding with objects is handled in that game is to me superior than default Unreal Engine. They use "soft" collisions for objects. The player never gets stuck when colliding.
121
Quod Soler ☀️ @quodsoler.com · 08/09/2026
Once it clicks that GAS cooldowns are just tags, good things open up: a chill debuff that extends cooldowns, gear that resets them on kill ( remove the GE, the tag goes with it ), UI reading remaining time from the effect itself. The timer you were looking for never existed.
001
Quod Soler ☀️ @quodsoler.com · 07/09/2026
Mass fragments must be trivially copyable: no FString, no TArray, nothing that heap-allocates, because entities move between chunks with raw memcpy. There is an escape hatch trait literally named AuthorAcceptsItsNotTriviallyCopyable. The name is the warning.
010
Quod Soler ☀️ @quodsoler.com · 07/09/2026
GAS executions run on the server only and never predict. The damage number exists after a full network round trip. If your hit feedback waits for it, melee feels like it happens over the phone. Drive VFX, sound and numbers from a client-predicted cue at the moment of contact.
010
Quod Soler ☀️ @quodsoler.com · 06/09/2026
Tags in Mass cost nothing to store and something to flip. Zero bytes per entity, just a bit in the archetype bookkeeping. But adding or removing one moves the entity to a different archetype. Occasional state only. Per-frame state lives inside fragments as values.
010
Quod Soler ☀️ @quodsoler.com · 06/09/2026
When a follow-up window opens with three presses queued, which fires? Oldest first. The player pressed them in an order, and a buffered light-heavy string should come out light then heavy. One press per window too: the rest belong to the windows after it.
100
Quod Soler ☀️ @quodsoler.com · 05/09/2026
In PIE the cue channel looks reliable: everything runs on one machine with no packet loss and no dedicated server. That is how gameplay logic ends up in cue notifies. The unreliability that makes it wrong stays invisible until real network conditions arrive.
000
Quod Soler ☀️ @quodsoler.com · 05/09/2026
A lot of people get confused when they notice GAS Abilities have no Tick function. It's on purpose. GAS is event-driven, so you react to Gameplay Events instead of running logic every frame. And when an ability really does need to tick? Write a small Tick Ability Task.
000
Quod Soler ☀️ @quodsoler.com · 04/09/2026
The most powerful system in RPG design is also the dumbest one: Game Flags. Plain booleans. Silent Hill runs its entire progression, game flow, and cutscenes on them. You don't need a fancy quest framework. You need a list of flags and the discipline to check them.
000
Quod Soler ☀️ @quodsoler.com · 04/09/2026
The first Mass trap: Handle.IsValid() does not mean the entity exists. It never talks to the storage, it only checks the handle was ever assigned. A handle to an entity destroyed five minutes ago returns true forever. EntityManager.IsEntityValid() is the real question.
000
Quod Soler ☀️ @quodsoler.com · 04/09/2026
Buffering input is only half the system. Knowing when NOT to buffer is the other half: an idle character should never queue presses, because an idle press either fires immediately or means nothing. My gate is a gameplay tag a notify state holds open during recovery frames.
010