Skip to content

Debugging Unreal C++ With Rider

This chapter is about debugging the current Andromeda C++ gameplay flow in Rider.

The important mental model is that Unreal Editor is the running application. The game code is loaded into the editor process. When you press Play in Editor, you are not launching a separate standalone game executable by default. You are running gameplay inside UnrealEditor.exe.

That affects how breakpoints work.

Normal Debugging Workflow

The usual workflow is:

Open Andromeda.uproject in Rider.
Choose an editor target, usually DebugGame Editor or Development Editor.
Place breakpoints in C++.
Launch Unreal Editor from Rider's debug configuration, or attach Rider to UnrealEditor.exe.
Press Play in Editor.
Trigger the gameplay path.

For active gameplay debugging, prefer:

DebugGame Editor

DebugGame Editor gives better symbols and stepping behavior for your game module than Development Editor, while avoiding the full cost of debugging all engine code. Development Editor is still useful for normal compile/test loops, but if a breakpoint behaves strangely, switch to DebugGame Editor.

Launch vs Attach

There are two common ways to debug.

Launching from Rider means Rider starts Unreal Editor under the debugger. This is the cleanest setup when you know you are about to debug C++.

Attaching means Unreal Editor is already open, and Rider attaches to the running UnrealEditor.exe process. This is useful if the editor is already in the exact state you want to test.

The tradeoff is:

Launch from Rider  Cleaner symbols and startup path, but slower.
Attach to editor   Faster when editor is already open, but easier to hit stale-code confusion.

If you are unsure, launch from Rider.

Live Coding Caveat

Live Coding is useful for quick .cpp iteration, but it can make debugging confusing when code or symbols become stale.

If a breakpoint should hit but does not, use the boring reliable path:

Close Unreal Editor.
Build from Rider.
Launch Unreal Editor under Rider's debugger.
Press Play.
Trigger the code path again.

This is slower, but it removes ambiguity. When learning a new codebase, reliable debugging is usually worth more than the fastest possible iteration loop.

Good Breakpoint Locations

These are good first breakpoints for the current codebase.

Startup and input:

AAndromedaPlayerController::BeginPlay
AAndromedaPlayerController::AddDefaultMappingContexts
AAndromedaCharacter::SetupPlayerInputComponent
AAndromedaCharacter::MoveInput
AAndromedaCharacter::MouseLookInput
AAndromedaCharacter::InteractInput

Focus and prompt:

AAndromedaCharacter::Tick
AAndromedaCharacter::UpdateFocusedInteractable
AAndromedaCharacter::ResolveInteractableFromHit
AAndromedaCharacter::NotifyFocusedInteractableChanged
UAndromedaInteractPromptWidget::SetPrompt
UAndromedaInteractPromptWidget::ClearPrompt

Carry flow:

AAndromedaSalvageItem::Interact_Implementation
UAndromedaCarryComponent::PickUpItem
UAndromedaCarryComponent::PlaceHeldItem
UAndromedaCarryComponent::DropHeldItem
UAndromedaCarryComponent::ReleaseHeldItemAtTransform

Station flow:

AAndromedaItemStation::CanInteract_Implementation
AAndromedaItemStation::Interact_Implementation
AAndromedaItemStation::PlaceItemFromCharacter
AAndromedaItemStation::HandleItemPlaced
AAndromedaItemStation::UsePlacedItem

Scanner, tagging, and warehouse:

AAndromedaScanner::ScanCurrentItem
AAndromedaScanner::ShowScanResultWidget
AAndromedaTaggingBench::CanAcceptItem
AAndromedaTaggingBench::UsePlacedItem
AAndromedaWarehouseZone::CanAcceptItem
AAndromedaWarehouseZone::HandleItemPlaced

Salvage data and processing:

AAndromedaSalvageItem::BuildScanResult
AAndromedaSalvageItem::ScanItem
AAndromedaSalvageItem::AssignRequiredPackage
AAndromedaSalvageItem::FindProcessingRule
AAndromedaSalvageItem::ProcessSalvage

UI:

UAndromedaScanResultWidget::NativeOnInitialized
UAndromedaScanResultWidget::SetScanResult
UAndromedaScanResultWidget::HandleCloseClicked
UAndromedaShiftResultWidget::NativeOnInitialized
UAndromedaShiftResultWidget::SetProcessingResult
UAndromedaShiftResultWidget::HandleCloseClicked

Debugging One Complete Item Flow

If you want to trace the whole current vertical slice, use this sequence.

First, put a breakpoint in:

AAndromedaSalvageSpawnPoint::BeginPlay

This confirms the item is spawned at runtime and that the spawn point has a configured salvage item class.

Then put a breakpoint in:

AAndromedaCharacter::UpdateFocusedInteractable

Look at the spawned item in Play mode. The trace should hit the item. If it does not, check the item's collision and placement.

Next, put a breakpoint in:

AAndromedaCharacter::InteractInput

Press E. This shows whether the character thinks it has a focused interactable, whether that object still implements the interface, and whether the fallback drop path is being used.

For pickup, step into:

IAndromedaInteractableInterface::Execute_Interact

then into:

AAndromedaSalvageItem::Interact_Implementation
UAndromedaCarryComponent::PickUpItem

This confirms that interface dispatch reached the salvage item and that the carry component accepted it.

For scanner placement, look at:

AAndromedaItemStation::Interact_Implementation
AAndromedaItemStation::PlaceItemFromCharacter
UAndromedaCarryComponent::PlaceHeldItem

This tells you whether the station is accepting the held item and whether the carry component is releasing it onto the placement point.

For scanning, interact with the scanner again and step through:

AAndromedaScanner::ScanCurrentItem
AAndromedaSalvageItem::ScanItem
AAndromedaSalvageItem::BuildScanResult
AAndromedaScanner::ShowScanResultWidget
UAndromedaScanResultWidget::SetScanResult

This confirms that the item has data, the scan result is being built, and the widget receives the result.

For tagging and final processing, use:

AAndromedaTaggingBench::UsePlacedItem
AAndromedaSalvageItem::AssignRequiredPackage
AAndromedaWarehouseZone::HandleItemPlaced
AAndromedaSalvageItem::ProcessSalvage
UAndromedaShiftResultWidget::SetProcessingResult

This follows the item from "scanned evidence" to "packaged" to "finalized outcome."

Reading Variables In The Debugger

When stopped at a breakpoint, inspect Unreal object references the same way you would inspect pointers in C++.

Useful variables to watch:

FocusedInteractableObject
CarryComponent
HeldItem
PlacedItem
CurrentStation
ItemData
ScanResultWidgetClass
ShiftResultWidgetClass
ProcessingAction

For TObjectPtr, Rider may show the wrapper. Expand it to find the underlying object. Conceptually, you are asking:

Is the reference set?
What concrete object does it point to?
Does that object have the expected class?
Do its reflected properties have the expected values?

If ItemData is null, the salvage item Blueprint or Data Asset assignment is wrong. If ScanResultWidgetClass is null, the scanner Blueprint is missing its widget class. If PlacedItem is null after placement should have happened, the station did not store the item or the carry release path failed.

Debugging Editor-Configured Values

A common Unreal debugging issue is that C++ looks correct but a Blueprint Class Default is missing.

Examples:

DefaultMappingContexts empty       BP_PlayerController is missing IMC_Player.
InteractAction null                BP_PlayerCharacter is missing IA_Interact.
InteractPromptWidgetClass null     BP_PlayerCharacter is missing WBP_InteractPrompt.
ScanResultWidgetClass null         BP_Scanner is missing WBP_ScanResult.
ItemData null                      Salvage item Blueprint is missing its Data Asset.

When this happens, do not immediately search for a missing C++ assignment. First ask:

Is this value supposed to be configured in Blueprint Class Defaults?

If yes, inspect the Blueprint asset.

Logs vs Breakpoints

Use breakpoints when you need to understand control flow or object state.

Use logs when you want persistent evidence over time. For example, if an interaction works once and then stops working, logging can show the sequence of state changes across multiple inputs.

Andromeda currently uses some on-screen debug messages for early feedback. Use breakpoints or logs when the exact state matters.

When To Close The Editor Before Building

You do not need to close the editor for every C++ change. Live Coding can handle many .cpp implementation edits.

Close the editor and do a normal build when:

You changed reflected headers: UCLASS, USTRUCT, UENUM, UPROPERTY, or UFUNCTION.
You added or removed reflected properties/functions.
Live Coding reports a failure.
Breakpoints or symbols seem stale.
Blueprints do not see a newly added C++ property.
The editor behaves as if old code is still running.

This is not superstition. Header reflection changes involve UnrealHeaderTool and generated code. A clean editor restart is often the simplest way to get the engine, generated code, Blueprints, and debugger all looking at the same version of the program.