Event when actor selected in editor

I would like to know how to react to actors being selected in the editor.

I have tried:

UCLASS()
class TEST_API UTESTEditorEngine : public UEditorEngine
{
	GENERATED_BODY()

public:

	void SelectActor(AActor* Actor, bool InSelected, bool bNotify, bool bSelectEvenIfHidden /*= false*/, bool bForceRefresh /*= false*/) override;
	
};

but this method does not seem to run in the editor.

Just to clarify, this is before play is pressed, I want the event to run only when editing.

1 Like

Any way i can make this question clearer to help you guys answer it?

In the Actorā€™s constructor, bind the SelectObjectEvent like this:

AMyActor::AMyActor(const class FObjectInitializer& ObjectInitializer)
   	: Super(ObjectInitializer)
{
	...other stuff...

	#if WITH_EDITORONLY_DATA
		USelection::SelectObjectEvent.AddUObject(this, &AMyActor::OnObjectSelected);
	#endif
}

Then declare/implement the OnObjectSelected function:

In MyActor.h:

#if WITH_EDITORONLY_DATA
	void OnObjectSelected(UObject* Object);
	bool SelectedInEditor = false;
#endif

In MyActor.cpp:

#if WITH_EDITORONLY_DATA
void AMyActor::OnObjectSelected(UObject* Object)
{
	UE_LOG(LogTemp, Log, TEXT("%s::OnObjectSelected(): %s was %s."), *GetName(), *Object->GetName(), *FString(Object->IsSelected() ? "selected" : "de-selected"));

	if (Object == this)
	{
		SelectedInEditor = true;

		...do stuff when your actor is selected...
	}
	else if (SelectedInEditor && !IsSelected())
	{
		SelectedInEditor = false;

		...do stuff when your actor is un-selected...
	}
}
#endif

Beware: OnObjectSelected will be called on every actor you have in the scene. This is why you have to filter out only what is relevant to your object (hence the if (Object == this) ).

Itā€™s a bit harder to detect that an object that is un-selected. You have to keep a flag (bool SelectedInEditor) on your actor that is set when it is selected. Then, when you receive the object selected event for another object and IsSelected() on yourself returns false, you are officially un-selected.

4 Likes

I came across this thread and while the above solution works great for selecting actors in the viewport, it does not work when selecting actors in the World Outliner (see Unreal Engine Issue (UE-46676)).

In case anyone comes by this thread and is wondering, like me, how to get this working with the World Outliner, I wanted to share a similar solution that works any time an actor is selected in the editor, whether itā€™s through the viewport or World Outliner:

(This was tested in Unreal Engine 4.27)

In the Actorā€™s constructor, bind the selection event like this:

#include "Engine/Selection.h"

AExampleActor::AExampleActor()
{
    #if WITH_EDITOR
    // Broadcast whenever the editor selection changes (viewport
    // or world outliner)
    USelection::SelectionChangedEvent.AddUObject(this, 
                                         &AExampleActor::OnSelectionChanged);
    #endif
}

In ExampleActor.h

#if WITH_EDITOR
    void OnSelectionChanged(UObject* NewSelection);
    bool bSelectedInEditor;
#endif

In ExampleActor.cpp

void AExampleActor::OnSelectionChanged(UObject* NewSelection)
{
	TArray<AExampleActor*> SelectedExampleActors;
	
	// Get ExampleActors from the selection
	USelection* Selection = Cast<USelection>(NewSelection);
	if (Selection != nullptr)
	{
		Selection->GetSelectedObjects<AExampleActor>(
						SelectedExampleActors);
	}
	
	// Search the selection for this actor
	for (AExampleActor* SelectedExampleActor : SelectedExampleActors)
	{
		// If our actor is in the selection and was not previously
		// selected, then this selection change marks the actor
		// being selected
		if (SelectedExampleActor == this && !bSelectedInEditor)
		{
			// Respond to this actor being selected
			bSelectedInEditor = true;
		}
	}

	// If our record shows our actor is selected, but IsSelected() is false,
	// this selection change marks the actor being deselected
	if (bSelectedInEditor && !IsSelected())
	{
		// Respond to this actor being deselected
		bSelectedInEditor = false;
	}
}
#endif

I found this solution in Unreal Engineā€™s AIModuleā€™s EQSTestingPawn.cpp, which implements a lot of editor-only debugging tools like this if you want to see more examples / robust editor debugging tools.

2 Likes

The level editor module has a ā€œOnActorSelectionChangedā€ delegate you can use.

FLevelEditorModule& levelEditor = FModuleManager::GetModuleChecked(TEXT(ā€œLevelEditorā€));
levelEditor.OnActorSelectionChanged().AddUObject(this, &UElgEditorContext_LevelEditor::HandleOnActorSelectionChanged);

I use it in my EditorScripting plugin to expose that event to Editor Utility Widgets.

2 Likes

This worked well for me even in the PIE world! I came here looking for answers after trying EditorActorSubsystem->GetSelectedLevelActors() only to realize this only works in the editor itself and not the running game in PIE. Above solution works in both!

I tried to use this but it kept giving me unresolved symbol error eventhogh include was there and engine is already in dependencies, i do not know what iā€™m doing wrong or anyone else has that problem, but any help would be appreciated

in ue5ļ¼ˆjust test 5.0ļ¼‰ in Editor utility widget ļ¼Œ you can use get leveleditorsubsystem getselectionset event to on selection change bind customevent

1 Like

Worked well for me based on ElgSoft answer, with slight modifications in v 5.2 Iā€™ve managed to register an event on selecting actor in editor

void FEditorStandaloneWindowModule::HandleOnActorSelectionChanged(const TArray<UObject*>& NewSelection, bool bForceRefresh)
{
	UE_LOG(LogTemp, Warning, TEXT("Function has been called!"));
	for (UObject* object : NewSelection) {
		bool found = false;
	}
}

#if WITH_EDITOR
		FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>("LevelEditor");
		LevelEditorModule.OnActorSelectionChanged().AddRaw(this, &FEditorStandaloneWindowModule::HandleOnActorSelectionChanged);
	#endif

I have been unable to get the C++ working at all, because I canā€™t get it to compile.

Creating library F:\GrimmTales\Repositories\TrainGame\Binaries\Win64\UnrealEditor-TrainGame.patch_2.lib and object F:\GrimmTales\Repositories\TrainGame\Binaries\Win64\UnrealEditor-TrainGame.patch_2.exp
SelectableActor.cpp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: static class USelection::FOnSelectionChanged USelection::SelectObjectEvent" (__imp_?SelectObjectEvent@USelection@@2VFOnSelectionChanged@1@A) referenced in function "public: __cdecl ASelectableActor::ASelectableActor(void)" (??0ASelectableActor@@QEAA@XZ)
F:\GrimmTales\Repositories\TrainGame\Binaries\Win64\UnrealEditor-TrainGame.patch_2.exe : fatal error LNK1120: 1 unresolved externals

Iā€™ve spent all day trying to figure out whatā€™s wrong, and I just donā€™t get it. Iā€™m coming from Unity so I understand the separation between runtime/editor code, and I have the include and editor func call wrapped in the editor compiler flags. But I just canā€™t get the thing to compile. I have no idea what Iā€™m doing wrong. I canā€™t find any information about this

1 Like

The event I bind to OnActorSelectionChanged is executed twice, is it the same ?

Hi,

I had a similar issue and needed to update the PublicDependencyModuleNames in the Project.build.cs file where Project is your project name. For instance like this:

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UnrealEd" });

To complement Freedomistā€™s answer, this now can be done entirely in BP. Just create an editor utility object that runs at editor startups then bind the said delegate:


Performance wise I wouldnā€™t recommend binding this on a per actor basis if you were in C++ as each actor would have to sift through the same list. That could potentially freeze the editor if there were too many actors all selected at once.