Skip to main content

15 / 16

Editor-only code and data

Guard editor-only code and data so a cooked build compiles without any of it.

Editor code and runtime code share the same files but not the same builds. The compiler only reports the difference when someone packages the project, which is usually the worst moment to find out.

Rules

  1. 01Wrap editor-only members in #if WITH_EDITORONLY_DATA. That keeps the field out of the cooked asset as well as out of the build.
  2. 02Wrap editor-only functions in #if WITH_EDITOR. Unreal Build Tool always defines it as 0 or 1, so #ifdef is the wrong test and will silently take the wrong branch.
  3. 03Override PostEditChangeProperty to validate a value the moment a designer changes it. Read PropertyChangedEvent.GetPropertyName() to find out which property moved.
  4. 04Use PostEditChangeChainProperty when the edited property sits inside a nested struct or array and you need the full path.
  5. 05Code inside an editor guard is never compiled in Shipping, so it is never type-checked there. Package the project on a schedule rather than discovering the break at release.
  6. 06An editor-only module belongs in the editor section of Build.cs. Referencing an editor module from runtime code links the editor into the game.
  7. 07Transient and editor-only are different problems. Transient keeps a field out of serialization while it still exists at runtime.

Guard editor-only data and overrides

The authoring notes and the validation hook exist in the editor and disappear from a cooked build.

WeaponDefinition.hcpp
UCLASS()
class ACTIONGAME_API UWeaponDefinition : public UPrimaryDataAsset
{
    GENERATED_BODY()

public:
    UPROPERTY(EditDefaultsOnly, Category = "Weapon", meta = (ClampMin = "0.0"))
    float DamagePerShot = 25.0f;

#if WITH_EDITORONLY_DATA
    UPROPERTY(EditDefaultsOnly, Category = "Authoring")
    FString BalanceNotes;
#endif

#if WITH_EDITOR
    virtual void PostEditChangeProperty(
        FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
};
Why
A cooked runtime has no editor module. Anything referencing editor types has to compile out, and WITH_EDITORONLY_DATA also keeps the field out of the packaged asset.
Costs
Code inside the guard is not compiled in a Shipping build, so it is never checked by that compile. A packaging break can therefore appear long after the change that caused it.
Instead
Use UPROPERTY(Transient) when a field should exist at runtime but never serialize. Use a separate editor module when the editor-only logic grows past a few functions.

Gotcha

Use #if WITH_EDITOR, never #ifdef WITH_EDITOR

Unreal Build Tool always defines the macro, as 0 in a cooked build and 1 in an editor build. #ifdef only asks whether the name exists, so it is true in both and the guarded code compiles into your shipped game.

All 16 rules