claude-code-arcane 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +54 -6
- package/agents/game/ue-blueprint-specialist.md +125 -0
- package/agents/game/ue-gas-specialist.md +133 -0
- package/agents/game/ue-umg-specialist.md +131 -0
- package/dist/cli.js +5 -0
- package/docs/SKILLS-CATALOG.md +42 -3
- package/docs/USER-GUIDE.md +2 -2
- package/hooks/detect-unreal.sh +90 -0
- package/package.json +1 -1
- package/profiles/unreal-dev.yaml +97 -0
- package/rules/gamedev/unreal-code.md +125 -0
- package/skills/install-mcp/SKILL.md +135 -46
- package/skills/install-mcp/references/unreal-operations.md +55 -0
- package/skills/ue-actor-component-architecture/SKILL.md +509 -0
- package/skills/ue-actor-component-architecture/references/actor-lifecycle.md +301 -0
- package/skills/ue-actor-component-architecture/references/component-types.md +592 -0
- package/skills/ue-ai-navigation/SKILL.md +526 -0
- package/skills/ue-ai-navigation/references/behavior-tree-patterns.md +392 -0
- package/skills/ue-ai-navigation/references/behavior-tree-workflow.md +107 -0
- package/skills/ue-ai-navigation/references/custom-bttask.md +104 -0
- package/skills/ue-ai-navigation/references/eqs-reference.md +423 -0
- package/skills/ue-animation-system/SKILL.md +521 -0
- package/skills/ue-animation-system/references/anim-notify-reference.md +486 -0
- package/skills/ue-animation-system/references/locomotion-setup.md +550 -0
- package/skills/ue-async-threading/SKILL.md +433 -0
- package/skills/ue-async-threading/references/thread-safety-guide.md +213 -0
- package/skills/ue-async-threading/references/threading-patterns.md +345 -0
- package/skills/ue-audio-system/SKILL.md +527 -0
- package/skills/ue-audio-system/references/audio-setup-patterns.md +700 -0
- package/skills/ue-blueprints/SKILL.md +134 -0
- package/skills/ue-blueprints/references/communication.md +82 -0
- package/skills/ue-character-movement/SKILL.md +475 -0
- package/skills/ue-character-movement/references/cmc-extension-patterns.md +335 -0
- package/skills/ue-character-movement/references/movement-pipeline.md +189 -0
- package/skills/ue-cpp-foundations/SKILL.md +533 -0
- package/skills/ue-cpp-foundations/references/components-and-gc.md +107 -0
- package/skills/ue-cpp-foundations/references/container-patterns.md +544 -0
- package/skills/ue-cpp-foundations/references/delegate-patterns.md +497 -0
- package/skills/ue-cpp-foundations/references/gameplay-classes-cpp.md +136 -0
- package/skills/ue-cpp-foundations/references/property-specifiers.md +306 -0
- package/skills/ue-data-assets-tables/SKILL.md +527 -0
- package/skills/ue-data-assets-tables/references/asset-loading-patterns.md +306 -0
- package/skills/ue-data-assets-tables/references/data-driven-design.md +378 -0
- package/skills/ue-editor-tools/SKILL.md +523 -0
- package/skills/ue-editor-tools/references/detail-customization-patterns.md +395 -0
- package/skills/ue-editor-tools/references/editor-module-setup.md +480 -0
- package/skills/ue-game-features/SKILL.md +477 -0
- package/skills/ue-game-features/references/experience-system.md +222 -0
- package/skills/ue-game-features/references/game-feature-patterns.md +308 -0
- package/skills/ue-gameplay-abilities/SKILL.md +518 -0
- package/skills/ue-gameplay-abilities/references/ability-task-reference.md +391 -0
- package/skills/ue-gameplay-abilities/references/gameplay-effect-reference.md +397 -0
- package/skills/ue-gameplay-abilities/references/gas-setup-patterns.md +369 -0
- package/skills/ue-gameplay-framework/SKILL.md +466 -0
- package/skills/ue-gameplay-framework/references/framework-class-map.md +243 -0
- package/skills/ue-input-system/SKILL.md +394 -0
- package/skills/ue-input-system/references/cpp-setup.md +131 -0
- package/skills/ue-input-system/references/enhanced-input-workflow.md +117 -0
- package/skills/ue-input-system/references/input-action-reference.md +731 -0
- package/skills/ue-mass-entity/SKILL.md +476 -0
- package/skills/ue-mass-entity/references/mass-entity-patterns.md +319 -0
- package/skills/ue-mass-entity/references/mass-fragment-reference.md +119 -0
- package/skills/ue-materials-rendering/SKILL.md +526 -0
- package/skills/ue-materials-rendering/references/material-parameter-reference.md +216 -0
- package/skills/ue-materials-rendering/references/post-process-settings.md +383 -0
- package/skills/ue-module-build-system/SKILL.md +487 -0
- package/skills/ue-module-build-system/references/build-cs-reference.md +418 -0
- package/skills/ue-module-build-system/references/common-build-errors.md +381 -0
- package/skills/ue-naming-conventions/SKILL.md +142 -0
- package/skills/ue-naming-conventions/references/asset-name-modifiers.md +233 -0
- package/skills/ue-naming-conventions/references/content-structure.md +83 -0
- package/skills/ue-networking-replication/SKILL.md +452 -0
- package/skills/ue-networking-replication/references/replication-patterns.md +526 -0
- package/skills/ue-networking-replication/references/rpc-decision-guide.md +393 -0
- package/skills/ue-niagara-effects/SKILL.md +531 -0
- package/skills/ue-niagara-effects/references/niagara-data-interfaces.md +491 -0
- package/skills/ue-niagara-effects/references/niagara-parameter-types.md +153 -0
- package/skills/ue-niagara-effects/references/niagara-workflow.md +115 -0
- package/skills/ue-packaging/SKILL.md +116 -0
- package/skills/ue-physics-collision/SKILL.md +423 -0
- package/skills/ue-physics-collision/references/collision-channel-setup.md +218 -0
- package/skills/ue-physics-collision/references/trace-patterns.md +499 -0
- package/skills/ue-procedural-generation/SKILL.md +505 -0
- package/skills/ue-procedural-generation/references/pcg-node-reference.md +381 -0
- package/skills/ue-procedural-generation/references/procedural-mesh-patterns.md +940 -0
- package/skills/ue-project-context/SKILL.md +305 -0
- package/skills/ue-sequencer-cinematics/SKILL.md +506 -0
- package/skills/ue-sequencer-cinematics/references/sequencer-patterns.md +629 -0
- package/skills/ue-serialization-savegames/SKILL.md +524 -0
- package/skills/ue-serialization-savegames/references/save-system-architecture.md +705 -0
- package/skills/ue-state-trees/SKILL.md +489 -0
- package/skills/ue-state-trees/references/state-tree-mass-integration.md +184 -0
- package/skills/ue-state-trees/references/state-tree-patterns.md +321 -0
- package/skills/ue-testing-debugging/SKILL.md +520 -0
- package/skills/ue-testing-debugging/references/automation-test-patterns.md +387 -0
- package/skills/ue-testing-debugging/references/profiling-commands.md +357 -0
- package/skills/ue-ui-umg-slate/SKILL.md +431 -0
- package/skills/ue-ui-umg-slate/references/common-ui-setup.md +484 -0
- package/skills/ue-ui-umg-slate/references/widget-types.md +487 -0
- package/skills/ue-world-level-streaming/SKILL.md +518 -0
- package/skills/ue-world-level-streaming/references/streaming-patterns.md +414 -0
- package/templates/gamedev/unreal-project-context.md +107 -0
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
# Trace Patterns Reference
|
|
2
|
+
|
|
3
|
+
Common trace query patterns for gameplay mechanics in Unreal Engine 5. All patterns use the C++ world query API (`UWorld::LineTraceSingleByChannel` etc.) with `FCollisionQueryParams`. For Blueprint-callable wrappers with built-in debug drawing, see `UKismetSystemLibrary`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Pattern 1: Hitscan Weapon
|
|
8
|
+
|
|
9
|
+
Single line trace from camera or muzzle, returning the first blocking hit. Reports impact point, surface normal, hit actor, and physical surface type for decal/sound selection.
|
|
10
|
+
|
|
11
|
+
```cpp
|
|
12
|
+
// MyWeaponComponent.h
|
|
13
|
+
UFUNCTION()
|
|
14
|
+
void FireHitscan(const FVector& MuzzleLocation, const FVector& AimDirection, float Range);
|
|
15
|
+
|
|
16
|
+
// MyWeaponComponent.cpp
|
|
17
|
+
void UMyWeaponComponent::FireHitscan(const FVector& MuzzleLocation,
|
|
18
|
+
const FVector& AimDirection,
|
|
19
|
+
float Range)
|
|
20
|
+
{
|
|
21
|
+
const FVector TraceStart = MuzzleLocation;
|
|
22
|
+
const FVector TraceEnd = MuzzleLocation + AimDirection * Range;
|
|
23
|
+
|
|
24
|
+
FCollisionQueryParams Params;
|
|
25
|
+
Params.TraceTag = TEXT("WeaponTrace");
|
|
26
|
+
Params.bTraceComplex = false; // simple hull — sufficient for shooting
|
|
27
|
+
Params.bReturnPhysicalMaterial = true; // needed for surface type
|
|
28
|
+
Params.AddIgnoredActor(GetOwner()); // don't hit self
|
|
29
|
+
|
|
30
|
+
FHitResult Hit;
|
|
31
|
+
const bool bHit = GetWorld()->LineTraceSingleByChannel(
|
|
32
|
+
Hit,
|
|
33
|
+
TraceStart,
|
|
34
|
+
TraceEnd,
|
|
35
|
+
ECC_GameTraceChannel1, // "Weapon" channel
|
|
36
|
+
Params
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
if (bHit)
|
|
40
|
+
{
|
|
41
|
+
// Impact data
|
|
42
|
+
const FVector ImpactPoint = Hit.ImpactPoint;
|
|
43
|
+
const FVector ImpactNormal = Hit.ImpactNormal;
|
|
44
|
+
AActor* HitActor = Hit.GetActor();
|
|
45
|
+
|
|
46
|
+
// Surface type for decal / sound selection
|
|
47
|
+
EPhysicalSurface Surface = SurfaceType_Default;
|
|
48
|
+
if (UPhysicalMaterial* PhysMat = Hit.PhysMaterial.Get())
|
|
49
|
+
{
|
|
50
|
+
Surface = UPhysicalMaterial::DetermineSurfaceType(PhysMat);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Apply damage
|
|
54
|
+
if (HitActor)
|
|
55
|
+
{
|
|
56
|
+
FPointDamageEvent DamageEvent(DamageAmount, Hit, AimDirection, DamageTypeClass);
|
|
57
|
+
HitActor->TakeDamage(DamageAmount, DamageEvent, GetOwner()->GetInstigatorController(), GetOwner());
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Debug draw (editor/development only)
|
|
62
|
+
#if ENABLE_DRAW_DEBUG
|
|
63
|
+
DrawDebugLine(GetWorld(), TraceStart, bHit ? Hit.ImpactPoint : TraceEnd,
|
|
64
|
+
FColor::Red, false, 1.f, 0, 1.f);
|
|
65
|
+
if (bHit) DrawDebugSphere(GetWorld(), Hit.ImpactPoint, 5.f, 8, FColor::Green, false, 1.f);
|
|
66
|
+
#endif
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Pattern 2: Melee Attack — Sphere Sweep
|
|
73
|
+
|
|
74
|
+
Sweeps a sphere along an arc for melee hit detection. Captures multiple targets in one call with `SweepMultiByChannel`. Avoids hitting the same actor twice.
|
|
75
|
+
|
|
76
|
+
```cpp
|
|
77
|
+
void UMeleeComponent::PerformMeleeSwing(const FVector& SwingStart,
|
|
78
|
+
const FVector& SwingEnd,
|
|
79
|
+
float AttackRadius)
|
|
80
|
+
{
|
|
81
|
+
FCollisionShape Sphere = FCollisionShape::MakeSphere(AttackRadius);
|
|
82
|
+
|
|
83
|
+
FCollisionQueryParams Params;
|
|
84
|
+
Params.bTraceComplex = false;
|
|
85
|
+
Params.AddIgnoredActor(GetOwner());
|
|
86
|
+
|
|
87
|
+
TArray<FHitResult> Hits;
|
|
88
|
+
const bool bAnyHit = GetWorld()->SweepMultiByChannel(
|
|
89
|
+
Hits,
|
|
90
|
+
SwingStart,
|
|
91
|
+
SwingEnd,
|
|
92
|
+
FQuat::Identity,
|
|
93
|
+
ECC_Pawn, // "Pawn" channel — hits characters and pawns
|
|
94
|
+
Sphere,
|
|
95
|
+
Params
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// Deduplicate actors (sweep may return multiple hits per actor)
|
|
99
|
+
TSet<AActor*> DamagedActors;
|
|
100
|
+
for (const FHitResult& Hit : Hits)
|
|
101
|
+
{
|
|
102
|
+
AActor* HitActor = Hit.GetActor();
|
|
103
|
+
if (HitActor && !DamagedActors.Contains(HitActor))
|
|
104
|
+
{
|
|
105
|
+
DamagedActors.Add(HitActor);
|
|
106
|
+
HitActor->TakeDamage(MeleeDamage,
|
|
107
|
+
FDamageEvent(),
|
|
108
|
+
GetOwner()->GetInstigatorController(),
|
|
109
|
+
GetOwner());
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
#if ENABLE_DRAW_DEBUG
|
|
114
|
+
DrawDebugSphere(GetWorld(), SwingEnd, AttackRadius, 12, FColor::Orange, false, 0.5f);
|
|
115
|
+
#endif
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Pattern 3: Interaction / Use — Line Trace from Camera
|
|
122
|
+
|
|
123
|
+
Player looks at an object and presses interact. Uses a trace channel dedicated to interaction so non-interactable geometry is filtered automatically by the Interactable object type's channel response.
|
|
124
|
+
|
|
125
|
+
```cpp
|
|
126
|
+
// In PlayerController or Character
|
|
127
|
+
|
|
128
|
+
void AMyCharacter::TryInteract()
|
|
129
|
+
{
|
|
130
|
+
APlayerController* PC = Cast<APlayerController>(GetController());
|
|
131
|
+
if (!PC) return;
|
|
132
|
+
|
|
133
|
+
FVector CameraLocation;
|
|
134
|
+
FRotator CameraRotation;
|
|
135
|
+
PC->GetPlayerViewPoint(CameraLocation, CameraRotation);
|
|
136
|
+
|
|
137
|
+
const FVector TraceStart = CameraLocation;
|
|
138
|
+
const FVector TraceEnd = CameraLocation + CameraRotation.Vector() * InteractRange;
|
|
139
|
+
|
|
140
|
+
FCollisionQueryParams Params;
|
|
141
|
+
Params.AddIgnoredActor(this);
|
|
142
|
+
|
|
143
|
+
FHitResult Hit;
|
|
144
|
+
// Use object type query to find only Interactable objects
|
|
145
|
+
FCollisionObjectQueryParams ObjectParams;
|
|
146
|
+
ObjectParams.AddObjectTypesToQuery(ECC_GameTraceChannel3); // "Interactable" type
|
|
147
|
+
|
|
148
|
+
const bool bHit = GetWorld()->LineTraceSingleByObjectType(
|
|
149
|
+
Hit, TraceStart, TraceEnd, ObjectParams, Params
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
if (bHit)
|
|
153
|
+
{
|
|
154
|
+
if (AActor* HitActor = Hit.GetActor())
|
|
155
|
+
{
|
|
156
|
+
if (HitActor->GetClass()->ImplementsInterface(UInteractableInterface::StaticClass()))
|
|
157
|
+
{
|
|
158
|
+
IInteractableInterface::Execute_Interact(HitActor, this);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Pattern 4: Ground Detection / IsGrounded
|
|
168
|
+
|
|
169
|
+
Character or physics object checks whether it is on the ground. Uses a short downward line trace against WorldStatic and WorldDynamic. Common for jump/fall logic without relying on `CharacterMovementComponent`.
|
|
170
|
+
|
|
171
|
+
```cpp
|
|
172
|
+
bool UMyMovementComponent::IsGrounded() const
|
|
173
|
+
{
|
|
174
|
+
const FVector Start = GetOwner()->GetActorLocation();
|
|
175
|
+
const FVector End = Start + FVector::DownVector * (GroundCheckDistance + CapsuleHalfHeight);
|
|
176
|
+
|
|
177
|
+
FCollisionQueryParams Params;
|
|
178
|
+
Params.bTraceComplex = false;
|
|
179
|
+
Params.AddIgnoredActor(GetOwner());
|
|
180
|
+
|
|
181
|
+
FHitResult Hit;
|
|
182
|
+
const bool bHit = GetWorld()->LineTraceSingleByChannel(
|
|
183
|
+
Hit,
|
|
184
|
+
Start,
|
|
185
|
+
End,
|
|
186
|
+
ECC_Visibility,
|
|
187
|
+
Params
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
return bHit && Hit.bBlockingHit;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Slope normal — for movement alignment or sliding
|
|
194
|
+
FVector UMyMovementComponent::GetGroundNormal() const
|
|
195
|
+
{
|
|
196
|
+
FHitResult Hit;
|
|
197
|
+
const FVector Start = GetOwner()->GetActorLocation();
|
|
198
|
+
const FVector End = Start - FVector::UpVector * (GroundCheckDistance + CapsuleHalfHeight);
|
|
199
|
+
|
|
200
|
+
FCollisionQueryParams Params;
|
|
201
|
+
Params.bTraceComplex = false;
|
|
202
|
+
Params.AddIgnoredActor(GetOwner());
|
|
203
|
+
|
|
204
|
+
if (GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Visibility, Params))
|
|
205
|
+
{
|
|
206
|
+
return Hit.ImpactNormal;
|
|
207
|
+
}
|
|
208
|
+
return FVector::UpVector;
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Pattern 5: Explosion Area-of-Effect — Sphere Overlap
|
|
215
|
+
|
|
216
|
+
Instant radial check at detonation point. Finds all Pawns and PhysicsBodies within radius. Applies damage with falloff.
|
|
217
|
+
|
|
218
|
+
```cpp
|
|
219
|
+
void AExplosiveActor::Detonate(const FVector& ExplosionCenter, float Radius, float MaxDamage)
|
|
220
|
+
{
|
|
221
|
+
FCollisionShape Sphere = FCollisionShape::MakeSphere(Radius);
|
|
222
|
+
|
|
223
|
+
FCollisionObjectQueryParams ObjectParams;
|
|
224
|
+
ObjectParams.AddObjectTypesToQuery(ECC_Pawn);
|
|
225
|
+
ObjectParams.AddObjectTypesToQuery(ECC_PhysicsBody);
|
|
226
|
+
ObjectParams.AddObjectTypesToQuery(ECC_WorldDynamic);
|
|
227
|
+
|
|
228
|
+
FCollisionQueryParams Params;
|
|
229
|
+
Params.AddIgnoredActor(this);
|
|
230
|
+
|
|
231
|
+
TArray<FOverlapResult> Overlaps;
|
|
232
|
+
GetWorld()->OverlapMultiByObjectType(
|
|
233
|
+
Overlaps,
|
|
234
|
+
ExplosionCenter,
|
|
235
|
+
FQuat::Identity,
|
|
236
|
+
ObjectParams,
|
|
237
|
+
Sphere,
|
|
238
|
+
Params
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
TSet<AActor*> DamagedActors;
|
|
242
|
+
for (const FOverlapResult& Result : Overlaps)
|
|
243
|
+
{
|
|
244
|
+
AActor* Actor = Result.GetActor();
|
|
245
|
+
if (!Actor || DamagedActors.Contains(Actor)) continue;
|
|
246
|
+
DamagedActors.Add(Actor);
|
|
247
|
+
|
|
248
|
+
// Line of sight check — don't damage through walls
|
|
249
|
+
FHitResult LOSHit;
|
|
250
|
+
FCollisionQueryParams LOSParams;
|
|
251
|
+
LOSParams.AddIgnoredActor(this);
|
|
252
|
+
LOSParams.AddIgnoredActor(Actor);
|
|
253
|
+
|
|
254
|
+
const FVector ActorCenter = Actor->GetActorLocation();
|
|
255
|
+
const bool bBlocked = GetWorld()->LineTraceSingleByChannel(
|
|
256
|
+
LOSHit, ExplosionCenter, ActorCenter, ECC_Visibility, LOSParams
|
|
257
|
+
);
|
|
258
|
+
if (bBlocked) continue; // wall between explosion and target
|
|
259
|
+
|
|
260
|
+
// Damage falloff (linear)
|
|
261
|
+
const float Distance = FVector::Dist(ExplosionCenter, ActorCenter);
|
|
262
|
+
const float Falloff = FMath::Clamp(1.f - (Distance / Radius), 0.f, 1.f);
|
|
263
|
+
const float Damage = MaxDamage * Falloff;
|
|
264
|
+
|
|
265
|
+
FRadialDamageEvent DmgEvent;
|
|
266
|
+
DmgEvent.Params = FRadialDamageParams(MaxDamage, 0.f, Radius, Radius, 1.f);
|
|
267
|
+
DmgEvent.Origin = ExplosionCenter;
|
|
268
|
+
// FOverlapResult has no ToHitResult() — build FHitResult manually
|
|
269
|
+
FHitResult ComponentHit(Result.GetActor(), Result.GetComponent(), ActorCenter, FVector::ZeroVector);
|
|
270
|
+
DmgEvent.ComponentHits.Add(ComponentHit);
|
|
271
|
+
|
|
272
|
+
Actor->TakeDamage(Damage, DmgEvent, GetInstigatorController(), this);
|
|
273
|
+
|
|
274
|
+
// Radial impulse on physics bodies
|
|
275
|
+
if (UPrimitiveComponent* PrimComp = Result.GetComponent())
|
|
276
|
+
{
|
|
277
|
+
if (PrimComp->IsSimulatingPhysics())
|
|
278
|
+
{
|
|
279
|
+
PrimComp->AddRadialImpulse(ExplosionCenter, Radius, ImpulseStrength,
|
|
280
|
+
RIF_Linear, false);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
#if ENABLE_DRAW_DEBUG
|
|
286
|
+
DrawDebugSphere(GetWorld(), ExplosionCenter, Radius, 16, FColor::Orange, false, 2.f);
|
|
287
|
+
#endif
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Pattern 6: Camera Line-of-Sight Check
|
|
294
|
+
|
|
295
|
+
Test whether a target actor is visible from the camera position, blocked by world geometry. Common for AI perception fallback or UI visibility indicators.
|
|
296
|
+
|
|
297
|
+
```cpp
|
|
298
|
+
bool AMyAIController::HasLineOfSightTo(AActor* Target) const
|
|
299
|
+
{
|
|
300
|
+
if (!Target) return false;
|
|
301
|
+
|
|
302
|
+
APawn* ControlledPawn = GetPawn();
|
|
303
|
+
if (!ControlledPawn) return false;
|
|
304
|
+
|
|
305
|
+
const FVector EyeLocation = ControlledPawn->GetPawnViewLocation();
|
|
306
|
+
const FVector TargetLocation = Target->GetActorLocation();
|
|
307
|
+
|
|
308
|
+
FCollisionQueryParams Params;
|
|
309
|
+
Params.bTraceComplex = false;
|
|
310
|
+
Params.AddIgnoredActor(ControlledPawn);
|
|
311
|
+
Params.AddIgnoredActor(Target);
|
|
312
|
+
|
|
313
|
+
FHitResult Hit;
|
|
314
|
+
const bool bBlocked = GetWorld()->LineTraceSingleByChannel(
|
|
315
|
+
Hit,
|
|
316
|
+
EyeLocation,
|
|
317
|
+
TargetLocation,
|
|
318
|
+
ECC_Visibility,
|
|
319
|
+
Params
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
return !bBlocked; // no blocking hit means clear line of sight
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## Pattern 7: Capsule Sweep for Character Movement Check
|
|
329
|
+
|
|
330
|
+
Before moving a character or vehicle to a new position, sweep a capsule to detect blocking geometry. Returns the first blocking hit to adjust the path.
|
|
331
|
+
|
|
332
|
+
```cpp
|
|
333
|
+
bool UMyMovementComponent::CanMoveToLocation(const FVector& TargetLocation) const
|
|
334
|
+
{
|
|
335
|
+
const FVector CurrentLocation = GetOwner()->GetActorLocation();
|
|
336
|
+
|
|
337
|
+
// Capsule matching the character's collision: radius 34, half-height 88 (standard UE character)
|
|
338
|
+
FCollisionShape Capsule = FCollisionShape::MakeCapsule(34.f, 88.f);
|
|
339
|
+
|
|
340
|
+
FCollisionQueryParams Params;
|
|
341
|
+
Params.bTraceComplex = false;
|
|
342
|
+
Params.AddIgnoredActor(GetOwner());
|
|
343
|
+
|
|
344
|
+
FHitResult Hit;
|
|
345
|
+
const bool bHit = GetWorld()->SweepSingleByChannel(
|
|
346
|
+
Hit,
|
|
347
|
+
CurrentLocation,
|
|
348
|
+
TargetLocation,
|
|
349
|
+
FQuat::Identity,
|
|
350
|
+
ECC_Pawn,
|
|
351
|
+
Capsule,
|
|
352
|
+
Params
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
if (bHit)
|
|
356
|
+
{
|
|
357
|
+
// Hit.Distance tells us how far we can actually move
|
|
358
|
+
// Hit.ImpactNormal tells us the blocking surface orientation
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Pattern 8: Async Trace for High-Frequency Sensors
|
|
369
|
+
|
|
370
|
+
For sensors that fire every frame on many actors (AI perception, grass interaction, liquid detection), synchronous traces stall the game thread. Use async traces queued this frame and read next frame.
|
|
371
|
+
|
|
372
|
+
```cpp
|
|
373
|
+
// .h
|
|
374
|
+
FTraceHandle PendingTraceHandle;
|
|
375
|
+
bool bHasPendingTrace = false;
|
|
376
|
+
|
|
377
|
+
// .cpp — in Tick or timer callback
|
|
378
|
+
|
|
379
|
+
void UMySensorComponent::TickComponent(float DeltaTime, ELevelTick TickType,
|
|
380
|
+
FActorComponentTickFunction* TickFunc)
|
|
381
|
+
{
|
|
382
|
+
Super::TickComponent(DeltaTime, TickType, TickFunc);
|
|
383
|
+
|
|
384
|
+
// Read result from last frame's trace
|
|
385
|
+
if (bHasPendingTrace)
|
|
386
|
+
{
|
|
387
|
+
FTraceDatum Datum;
|
|
388
|
+
if (GetWorld()->QueryTraceData(PendingTraceHandle, Datum))
|
|
389
|
+
{
|
|
390
|
+
bHasPendingTrace = false;
|
|
391
|
+
if (Datum.OutHits.Num() > 0 && Datum.OutHits[0].bBlockingHit)
|
|
392
|
+
{
|
|
393
|
+
OnSensorHit(Datum.OutHits[0]);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Queue this frame's trace (result readable next frame)
|
|
399
|
+
if (!bHasPendingTrace)
|
|
400
|
+
{
|
|
401
|
+
FCollisionQueryParams Params;
|
|
402
|
+
Params.AddIgnoredActor(GetOwner());
|
|
403
|
+
|
|
404
|
+
const FVector Start = GetComponentLocation();
|
|
405
|
+
const FVector End = Start + GetForwardVector() * SensorRange;
|
|
406
|
+
|
|
407
|
+
PendingTraceHandle = GetWorld()->AsyncLineTraceByChannel(
|
|
408
|
+
EAsyncTraceType::Single,
|
|
409
|
+
Start,
|
|
410
|
+
End,
|
|
411
|
+
ECC_Visibility,
|
|
412
|
+
Params
|
|
413
|
+
);
|
|
414
|
+
bHasPendingTrace = true;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## FCollisionQueryParams Quick Reference
|
|
422
|
+
|
|
423
|
+
```cpp
|
|
424
|
+
FCollisionQueryParams Params(
|
|
425
|
+
TEXT("TraceName"), // tag for profiling / debug drawing
|
|
426
|
+
false, // bTraceComplex — true for per-poly, false for simple hull
|
|
427
|
+
GetOwner() // actor to ignore (optional convenience constructor arg)
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
Params.bReturnPhysicalMaterial = true; // populate Hit.PhysMaterial (slight cost)
|
|
431
|
+
Params.bReturnFaceIndex = false; // populate Hit.FaceIndex (expensive, rarely needed)
|
|
432
|
+
Params.bFindInitialOverlaps = true; // report overlapping hits at Start position
|
|
433
|
+
Params.MobilityType = EQueryMobilityType::Any; // Any, Static, Dynamic
|
|
434
|
+
|
|
435
|
+
// Ignore specific actors or components
|
|
436
|
+
Params.AddIgnoredActor(SomeActor);
|
|
437
|
+
Params.AddIgnoredActors(ArrayOfActors);
|
|
438
|
+
Params.AddIgnoredComponent(SomeComponent);
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## FCollisionShape Quick Reference
|
|
444
|
+
|
|
445
|
+
All shapes from `CollisionShape.h`:
|
|
446
|
+
|
|
447
|
+
```cpp
|
|
448
|
+
// Line — default, no volume (same as calling LineTrace functions directly)
|
|
449
|
+
FCollisionShape Line; // default constructor
|
|
450
|
+
|
|
451
|
+
// Sphere
|
|
452
|
+
FCollisionShape Sphere = FCollisionShape::MakeSphere(50.f); // radius in cm
|
|
453
|
+
|
|
454
|
+
// Box (axis-aligned, rotated by the Rotation parameter in sweep calls)
|
|
455
|
+
FCollisionShape Box = FCollisionShape::MakeBox(FVector(50.f, 30.f, 80.f)); // half-extents
|
|
456
|
+
|
|
457
|
+
// Capsule — HalfHeight includes the sphere radius at each end
|
|
458
|
+
FCollisionShape Capsule = FCollisionShape::MakeCapsule(34.f, 88.f);
|
|
459
|
+
// Capsule shaft half-length = HalfHeight - Radius = 88 - 34 = 54 cm
|
|
460
|
+
|
|
461
|
+
// Shape introspection
|
|
462
|
+
bool bSphere = Sphere.IsSphere();
|
|
463
|
+
float Radius = Sphere.GetSphereRadius();
|
|
464
|
+
float CHH = Capsule.GetCapsuleHalfHeight(); // full half-height
|
|
465
|
+
float CAxisHL = Capsule.GetCapsuleAxisHalfLength(); // shaft only (HalfHeight - Radius)
|
|
466
|
+
FVector Ext = Box.GetBox(); // returns half-extents as FVector
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
## EDrawDebugTrace Reference (UKismetSystemLibrary)
|
|
472
|
+
|
|
473
|
+
```cpp
|
|
474
|
+
EDrawDebugTrace::None // No debug drawing
|
|
475
|
+
EDrawDebugTrace::ForOneFrame // Draw for one frame only
|
|
476
|
+
EDrawDebugTrace::ForDuration // Draw for specified duration (DrawTime parameter)
|
|
477
|
+
EDrawDebugTrace::Persistent // Draw until explicitly cleared
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## Trace Performance Guidelines
|
|
483
|
+
|
|
484
|
+
| Technique | CPU cost | Use case |
|
|
485
|
+
|-----------|----------|----------|
|
|
486
|
+
| `LineTraceSingleByChannel` | Low | Shooting, interaction, simple AOS |
|
|
487
|
+
| `LineTraceMultiByChannel` | Medium | Penetration, multi-target beams |
|
|
488
|
+
| `SweepSingleByChannel` | Medium | Movement, precise melee |
|
|
489
|
+
| `SweepMultiByChannel` | Medium–High | Melee arc, projectile sweeping |
|
|
490
|
+
| `OverlapMultiByObjectType` | Medium | AoE, proximity detection |
|
|
491
|
+
| Complex collision (`bTraceComplex=true`) | High (4–10x) | Only when triangle accuracy needed |
|
|
492
|
+
| Async trace | Low (deferred) | Sensors, AI, effects polling >5 actors per frame |
|
|
493
|
+
|
|
494
|
+
**General rules:**
|
|
495
|
+
- Never run complex collision traces every tick for more than a handful of actors.
|
|
496
|
+
- Prefer `SweepSingle` over `SweepMulti` when only the first hit matters.
|
|
497
|
+
- Use `OverlapMultiByObjectType` for AoE — it is cheaper than multi-sweep for static positions.
|
|
498
|
+
- Group traces using `AsyncLineTraceByChannel` for sensors and background detection.
|
|
499
|
+
- Throttle non-critical traces with timers (5–10 Hz is sufficient for most AI awareness checks).
|