The C++ vocabulary the course uses
When your teacher types something fast and moves on, look it up here. Each entry answers "what is it" and "why here".
Property specifiers
| Specifier | Editable where | Used for |
|---|---|---|
EditDefaultsOnly | Blueprint class defaults only | Tuning shared by every instance: InputConfigDataAsset, RelaxThreshold, activation policy |
EditAnywhere | Defaults and each placed instance | Per-instance values (a door's key ID) |
VisibleAnywhere | Shown, not assignable | Component pointers: you can edit the component's settings, but not swap it |
VisibleDefaultsOnly | Shown in defaults | Runtime anim values (GroundSpeed) |
BlueprintReadOnly / ReadWrite | (graph access) | Can Blueprint graphs get / set it |
meta = (AllowPrivateAccess = "true") | Lets a private C++ member still be BlueprintReadOnly. C++ stays encapsulated, Blueprint can still read it | |
Category = "Camera" | Groups it in the Details panel; "A|B" makes sub-groups |
No UPROPERTY at all (like IdleElapsedTime) is fine for plain numbers. For a
UObject pointer it's a crash waiting to happen: see GC.
Creating and finding things
| Code | Meaning |
|---|---|
CreateDefaultSubobject<T>(TEXT("Name")) | Make a component in the constructor. The name must be unique per actor and shouldn't change later, or saved Blueprints lose their overrides. |
NewObject<T>(Outer) | Make a UObject at runtime. |
GetWorld()->SpawnActor<T>(...) | Make an actor at runtime (the weapon, once you spawn it from C++). |
Cast<T>(Obj) | Returns null if Obj isn't a T. Use it when failure is a normal case you'll handle. |
CastChecked<T>(Obj) | Asserts (crashes in dev builds) if it isn't. Use it when failure means the project is set up wrong. |
TEXT("...") | A wide-character string literal, the type FString and logging expect on every platform. |
Asserts: check, checkf, ensure
| Macro | On failure | In Shipping builds |
|---|---|---|
check(x) | Crash with file/line | Removed (not evaluated) |
checkf(x, TEXT("msg")) | Crash with your message | Removed |
ensure(x) / ensureMsgf | Logs a callstack once, carries on | Still evaluated, doesn't break |
The course uses checkf(InputConfigDataAsset, ...) because a missing data asset is a setup error:
crash loudly now in the editor, with a message that says what to fix. Never put code with side effects inside
check(). It disappears in Shipping.
Header tricks
- Forward declaration:
class USpringArmComponent;at the top of a header says "this type exists". It's enough for a pointer, so the heavy#includemoves to the .cpp. That makes compiles faster and avoids include chains (includes page). class UInputComponent* PlayerInputComponentin a parameter is an inline forward declaration: the same thing in one go.FORCEINLINE: "really inline this" for tiny getters, so there's no function-call overhead.#pragma region Inputs…#pragma endregion: only folds code in the IDE. The compiler ignores it.//~ Begin APawn Interface: an Epic comment convention marking a group of overrides from one base class.GENERATED_BODY(): where Unreal's header tool pastes the reflection code. It also gives youSuper(the parent class) andThisClass.#include "X.generated.h"must be the last include in a reflected header.
The Debug helper, closely
namespace Debug {
static void Print(const FString& Msg, const FColor& Color = FColor::MakeRandomColor(), int32 InKey = -1) {
if (GEngine) {
GEngine->AddOnScreenDebugMessage(InKey, 7.f, Color, Msg);
UE_LOG(LogTemp, Warning, TEXT("%s"), *Msg);
}
}
}
const FString&: pass by const reference, so no copy of the string (refs and const).- Default arguments:
Debug::Print("hi")picks a random colour and key −1. - Key −1 = always add a new line. The same positive key replaces the previous message, which is great for values that change every frame.
*Msg:FString'soperator*gives the rawTCHAR*that%sneeds. Forgetting the*is a classic crash/garbage bug.staticin a header: each .cpp that includes it gets its own private copy. It works;inlinewould share one copy. Harmless for a debug helper.if (GEngine): GEngine can be null very early in startup and in some commandlets.
Quiz yourself
EditDefaultsOnly vs EditAnywhere?
Defaults-only: editable in the Blueprint class, not per placed instance. Anywhere: both.
Why are component pointers VisibleAnywhere rather than EditAnywhere?
You want to edit the component's properties in the Details panel, not reassign the pointer to a different object.
What does AllowPrivateAccess allow?
A private C++ member to still be exposed as BlueprintReadOnly/ReadWrite.
check vs ensure: which one crashes, and which survives in Shipping?
check crashes but is compiled out of Shipping. ensure logs once and carries on, and is still evaluated in Shipping.
Why is it dangerous to write check(DoSetup())?
In Shipping the whole expression disappears, so DoSetup() never runs.
When is a forward declaration enough, and when do you need the include?
Enough for pointers and references to the type. You need the full include to call its functions, access members, inherit from it, or hold it by value.
Why *Msg in UE_LOG(..., TEXT("%s"), *Msg)?
%s needs a TCHAR*. * on an FString returns its raw character pointer.
What does a positive key do in AddOnScreenDebugMessage?
A new message with the same key replaces the old one, instead of stacking a new line.