Targeting EQS Tests
Candidate selection is authored as a standard Environment Query. You compose a generator (which produces candidates) with a set of tests (which score and filter them). This page lists the pieces the plugin adds, and the contexts that drive them.
The components
Before the EQS pieces, it helps to know the three runtime objects they read from and write to.
Player targeting component (BP_PulsePlayerTargeting). Placed on the player pawn, and the querier that owns and runs the query. It holds a map of tag to query config, dispatches each query on a timer, caches the sorted results, and exposes the lock, switching, commit, and event APIs. Its owning pawn is the EQS query owner, which is why the contexts below can read the pawn’s camera, movement, and forward vectors. Targetable component (BP_PulseTargetable). Placed on anything that can be targeted. It registers itself with the subsystem against one or more gameplay tags, and it holds the data the tests score: the component-wide priority and the aim positions (default plus socket-anchored extras). It also tracks who is currently targeting it. SubTargetable Actor (BP_PT_SubTargetable). It is a ready-made child-actor component which is targetable: add it to a composite enemy as a Child Actor Component, one per body part. Each instance carries its own targetable and automatically forwards its targeter events to the root enemy’s targetable. Targeting subsystem (UPT_TargetingSubsystem). A per-world registry that every targetable adds itself to on spawn and removes itself from on destroy. It buckets targetables by tag (with parent-tag expansion) and keeps an actor-to-targetable map, so candidates can be looked up by tag without sweeping the level. The generator pulls its candidate set straight from here. How they fit together: the player component runs a query, the generator asks the subsystem for targetables of the query’s tag, the tests score those candidates (using contexts that read the querier pawn, and data on each targetable), and the ranked result flows back into the player component’s cache. Everything below is what plugs into that query.
Generator: Pulse Targetable Actors
UPT_TargetableActorsGenerator produces the candidate set. It pulls every registered targetable whose Targeting Type matches the configured tag (parent tags included) from the world subsystem, excludes the querier, and optionally filters by Search Radius around the query context. Set its TargetingType and PT.SearchRadius param on the generator.
Because candidates come from the tag-bucketed registry rather than a world sweep, the generator scales with the number of matching targetables, not the number of actors in the level.
Contexts
Several tests need a reference point or direction. The plugin ships reusable EQS contexts so you can swap behavior without writing new tests:
These contexts work on any EQS test that takes a context, not just the ones below.
Pulse Directional Alignment
UPT_DirectionalAlignmentEnvQueryTest scores each item by how closely the direction from an origin to the item aligns with a reference direction (their dot product). Both come from contexts, so one test covers every directional behavior:
Options:
Use 2D Only - project both vectors onto the horizontal plane (ignore pitch), so only yaw matters. Discard Outside Max Angle + Max Angle - cull items outside a cone. Use Default Targetable Position - score against the targetable’s default aim position instead of the raw actor location. Pulse Targetable Priority
UPT_PriorityEnvQueryTest feeds each targetable’s component-wide Priority into the scoring pipeline (0 is the highest priority). Because lower is better, set the test’s scoring equation to an inverse equation so priority 0 scores highest, or use it as a filter. Missing Priority Value (default 255) is applied to items with no targetable component, so they sort last.
Pulse Camera Frustum Visibility
UPT_CameraFrustumEnvQueryTest is a boolean filter (filter-only by default) that passes items whose tested location projects inside the local player’s screen, and discards the rest. It is a frustum-membership test, not a line-of-sight trace, so it does not check for occluders. It needs a local player viewport; on a dedicated server it leaves items unfiltered.
Pulse Sticky Target Bonus
UPT_StickyTargetEnvQueryTest awards a score bonus to whichever candidate matches the querier’s previously cached top target. Adding it to a soft-lock query produces hysteresis, so the selection does not jitter between two near-equal candidates.
Adding the Pulse Sticky Target Bonus test gives the previous winner a per-frame bonus, so the selection stays stable when two candidates are close. Tune with the test’s Stickiness Score (bonus magnitude) and the EQS test weight (how strongly it counts). Low for snappy reactivity, high for sticky locks; typical starting values are a stickiness of 1.0 and a weight around 0.5 to 1.5.
A typical soft-lock query
A common starting point combines:
Pulse Targetable Actors generator (your soft-lock tag, a search radius). Pulse Camera Frustum Visibility (filter: only on-screen targets). Pulse Directional Alignment, camera-relative (score: prefer what the player is looking at). Distance (engine test, score: prefer closer). Pulse Sticky Target Bonus (score: avoid jitter). Order matters: cheap filters first, expensive tests last. EQS orders tests by their cost, so keep filters at Low cost and traces (if you add any) at High.
Advanced Features
Beyond the basic soft-lock loop, the plugin provides hard-lock and directional switching, target priority and aim positions, gameplay-tag gating, hysteresis, event hooks, and a server-authoritative commit channel.
Running the queries
The Pulse Player Targeting component can run the queries automatically on tick or on a fixed time step (preferred). You can then access the result of that query with GetMostRecentTargetable and GetMostRecentTargetables .
If you set the UpdateType to None, you can also run the query manually with the asynchronous nodes PulseRunFindTargetable and PulseRunFindTargetables . Caching the targets means they can later be accesed by the get functions above.
Accessing the targets
You can access the most recent found targetable (can be invalid) from a query with GetMostRecentTargetable which returns the best target, and GetMostRecentTargetables which returns all of the valid targets, ordered by descending score.
You can commit a target to the server manually by using CommitTarget or you can let the component handle it by enabling AutoReplicate on the query.
Send to server automatically
From the server, you can access the target by calling GetAuthoritativeTarget .
Priority and aim positions
Priority. Each targetable has a uint8 Priority where 0 is the highest. The priority EQS test and the directional-switch bias both prefer lower values, so you can rank a boss above trash without changing your query.
Aim positions. A targetable exposes a default aim position plus an array of extras (index 0 = default, 1…N = extras). Each has an optional tag, a socket name and offset (socket-anchored positions resolve against the configured skeletal mesh and fall back to the actor location), a score multiplier, and an enabled flag. Use them for body-part targeting on a single actor. Toggle them at runtime with Set Position Enabled By Index / Tag, and read them with Get Resolved Targetable Positions.
Hard-lock and directional switching
The soft-lock cache doubles as the hard-lock candidate pool. To lock a specific target, write the cache explicitly with Set Cached Targetable (Tag, Target, bResetTargetables); a null target clears it.
To cycle targets and body parts, call FindNextBestTargetableInDirection :
Screen Space Direction: (1,0) right, (-1,0) left, (0,1) up, (0,-1) down. Diagonals allowed. Current Lock / Out Target (FPT_TargetingState): carries the locked targetable, the position index, and the cached world position. The same type goes in and out. Settings (FPT_LockSwitchSettings): Max Angle Degrees (cone half-angle), Snap To Cardinal, Score Against All Positions (enable body-part cycling), Same Actor Advantage (bias to stay on the same actor), Consider Priority + Priority Bias (fold priority into the score). It returns true only when the lock actually changed. On a non-change it mirrors the input (with a refreshed world position), so HUD and camera code can read OutTarget unconditionally without branching.
The returned position index is always valid for the returned targetable, so switching from a many-position enemy to a fewer-position one never produces a mismatch.
Add a short input cooldown (around 200 ms) between switches in your input layer to prevent stick-tilt flythrough. See our demo map for more examples.
Gating queries by owner state
Two gameplay-tag fields on each query react to the owner’s gameplay tags:
Disable Update On Tags - if the owner has any of these tags, the query is skipped during Tick (no dispatch, no cache change). Use it to pause targeting while stunned, dead, mounted, or in a cutscene. Preserve Top Result On Tags - if the owner has any of these tags during a cache update, the previous top target stays pinned at index 0 if it is still present, while the rest of the cache keeps refreshing. This is the core of hard-lock: tag the owner while locked so newly spawned threats still enter the cache (so you can switch to them) without losing the lock. The two are composable and effectively free when unused.
Notifications for HUD, VFX, and AI
Best target changed. On Best Target Changed fires whenever the best target for a TargetingType changes (found, switched, or lost), carrying the new and previous targets. Bind it to drive a reticle without polling. Subclasses can override On Best Target Changed Native instead.
Targeter tracking. Each targetable knows who is currently targeting it (local, not replicated):
Is Targeted / Get Targeter Count for state. On Targeted / On Untargeted fire on the became / stopped transitions. On Targeter Added / On Targeter Removed fire on every add and remove, for exact forwarding. On Became Targeted / On Stopped Being Targeted are override hooks. The player component drives this automatically as the best target changes, adding the new target before removing the old one so an aggregating parent never flickers when you cycle parts of the same enemy.
Composite Enemies (child-actor components)
When an enemy is built from child-actor body parts that are each targetable, forward each part’s On Targeter Added / On Targeter Removed to the root’s targetable so the root aggregates an exact count across all parts. Use Get Target Root Actor on a part to reach the composite enemy; the root reacts to its own On Targeted / On Untargeted for a single enemy-level highlight.
This is already done in BP_PT_SubTargetable, so use it. See the demo map for more examples.
Multiplayer and the commit channel
The system is a client-side UX layer by default and a server-authoritative store on demand. Queries run only on the locally-controlled pawn.
Commit. Commit Target (Tag, Target) is the only path that writes authoritative state. Call it on player intent (fire, lock-on, ability). On the authority it validates inline; from a client it routes through a reliable server RPC. Client commits require the pawn to be replicated and owned by the calling connection. Validation. Validate Committed Target (overridable) decides whether a commit is accepted. The default checks tag membership plus radius plus slack; override it for line-of-sight, faction, or lag-compensated rules. Auto-replicate. A per-query opt-in pushes the cached selection to the authority every refresh (throttled), for hard-lock UX where the authoritative target should track the cache. Authoritative state is server-only by design. Get Authoritative Target reads the server’s accepted target; clients read their own cache for UX. If a client needs to mirror the authoritative pick (for a confirmed-lock HUD), add an owner-only client RPC on your subclass. Override points
UPT_PlayerTargetingComponent exposes BlueprintNativeEvents to override in C++ or Blueprint (call the parent for default behavior):
Get Owner Gameplay Tags - the tag source for the gating fields above. Validate Committed Target - server-side accept/reject policy. Can Run Queries - an extra gate ANDed into the locality check, to pause querying without disabling the component. On Best Target Changed Native - react to best-target changes in a subclass. UPT_TargetableComponent exposes Get Target Root Actor, On Became Targeted, and On Stopped Being Targeted.