Following [this wiki on component visualizers][1], I am currently implementing a component visualizer for a Spawner component which spawns flocks of pawns at the specified locations.
The spawn points are represented with a sprite and an arrow which shows the initial orientation of the item to spawn. The red spawn point is selected and the white spawn points are unselected roughly following the implementation defined in the wiki article.
The spawn points successfully receive key input which allows me to delete, cycle through, and move them with keyboard input, so I know the visualizer can successfuly communicate with the proper component and edit it. However, the GetWidgetLocation
method does not set the widget location for handling the widget input delta:
bool FFlockSpawnPointVisualizer::GetWidgetLocation(
const FEditorViewportClient *ViewportClient,
FVector &OutLocation) const {
if (GetEditedSpawner() && CurrentlySelectedTarget != INDEX_NONE) {
auto Comp = GetEditedSpawner();
auto Pos = Comp->SpawnLocations[CurrentlySelectedTarget].Position;
OutLocation = Pos;
UE_LOG(
LogTemp, Display, TEXT("Draw Widget for %i"), CurrentlySelectedTarget);
return true;
}
return false;
}
However, the widget never appears with the selected spawner!
The log shoes the correct code path is reached, but the widget is never drawn over the spawn point.
Here is the full .cpp source code for the visualizer:
#include "FlockSpawnerEditor.h"
IMPLEMENT_HIT_PROXY(HSpawnVisProxy, HComponentVisProxy)
IMPLEMENT_HIT_PROXY(HFlockSpawnProxy, HSpawnVisProxy)
FFlockSpawnPointVisualizer::FFlockSpawnPointVisualizer() {}
FFlockSpawnPointVisualizer::~FFlockSpawnPointVisualizer() {}
void FFlockSpawnPointVisualizer::OnRegister() {}
void FFlockSpawnPointVisualizer::DrawVisualization(
const UActorComponent *Component,
const FSceneView *View,
FPrimitiveDrawInterface *PDI) {
auto *Spawner = Cast<const UFlockSpawnerComponent>(Component);
if (Spawner) {
for (int32 i = 0; i < Spawner->SpawnLocations.Num(); ++i) {
auto Spawn = Spawner->SpawnLocations[i];
FLinearColor Color = (i == CurrentlySelectedTarget)
? Spawner->SelectedColor
: Spawner->SpawnPointColor;
PDI->SetHitProxy(new HFlockSpawnProxy(Component, i));
auto ArrowLength = 25.0;
FTransform ArrowTransform(
Spawn.Orientation,
Spawn.Position + Spawn.Orientation.Vector() * ArrowLength,
FVector(1.0));
DrawConnectedArrow(
PDI,
ArrowTransform.ToMatrixNoScale(),
Color,
ArrowLength,
1.5,
SDPG_Foreground,
1.0,
8);
auto Sprite = Spawner->SpawnPointSprite;
if (Sprite) {
PDI->DrawSprite(
Spawn.Position + FVector(0.0, 0.0, 15.0),
20.0,
20.0,
Sprite->Resource,
Color,
SDPG_World,
0.0,
Sprite->Resource->GetSizeX(),
0.0,
Sprite->Resource->GetSizeY(),
BLEND_Masked);
}
PDI->SetHitProxy(NULL);
}
}
}
void FFlockSpawnPointVisualizer::DrawVisualizationHUD(
const UActorComponent *Component,
const FViewport *Viewport,
const FSceneView *View,
FCanvas *Canvas) {
// Canvas->DrawItem(FCanvasTextItem(FVector2D(10.0, 10.0), ));
}
bool FFlockSpawnPointVisualizer::VisProxyHandleClick(
FEditorViewportClient *InViewportClient,
HComponentVisProxy *VisProxy,
const FViewportClick &Click) {
bool bEditing = false;
if (VisProxy && VisProxy->Component.IsValid()) {
bEditing = true;
const UFlockSpawnerComponent *Component
= CastChecked<const UFlockSpawnerComponent>(VisProxy->Component);
CompPropName = GetComponentPropertyName(Component);
AActor *OldOwner = OwningActor.Get();
OwningActor = Component->GetOwner();
if (OwningActor != OldOwner) {
CurrentlySelectedTarget = INDEX_NONE;
}
if (VisProxy->IsA(HFlockSpawnProxy::StaticGetType())) {
HFlockSpawnProxy *Proxy = (HFlockSpawnProxy *)VisProxy;
CurrentlySelectedTarget = Proxy->TargetIndex;
}
} else {
CurrentlySelectedTarget = INDEX_NONE;
}
return bEditing;
}
void FFlockSpawnPointVisualizer::EndEditing() {
CurrentlySelectedTarget = INDEX_NONE;
}
bool FFlockSpawnPointVisualizer::GetWidgetLocation(
const FEditorViewportClient *ViewportClient,
FVector &OutLocation) const {
if (GetEditedSpawner() && CurrentlySelectedTarget != INDEX_NONE) {
auto Comp = GetEditedSpawner();
auto Pos = Comp->SpawnLocations[CurrentlySelectedTarget].Position;
OutLocation = Pos;
UE_LOG(
LogTemp, Display, TEXT("Draw Widget for %i"), CurrentlySelectedTarget);
return true;
}
return false;
}
bool FFlockSpawnPointVisualizer::HandleInputDelta(
FEditorViewportClient *ViewportClient,
FViewport *Viewport,
FVector &DeltaTranslate,
FRotator &DeltaRotate,
FVector &DeltaScale) {
bool bHandled = false;
if (GetEditedSpawner() && CurrentlySelectedTarget != INDEX_NONE) {
auto SpawnPoint
= GetEditedSpawner()->SpawnLocations[CurrentlySelectedTarget];
SpawnPoint.Position += DeltaTranslate;
SpawnPoint.Orientation += DeltaRotate;
SpawnPoint.InitialVelocity += DeltaScale.Size();
bHandled = true;
}
return bHandled;
}
bool FFlockSpawnPointVisualizer::HandleInputKey(
FEditorViewportClient *ViewportClient,
FViewport *Viewport,
FKey Key,
EInputEvent Event) {
if (!GetEditedSpawner() || CurrentlySelectedTarget == INDEX_NONE)
return false;
auto &Spawns = GetEditedSpawner()->SpawnLocations;
auto &Selected = Spawns[CurrentlySelectedTarget];
if (Key == EKeys::Delete) {
Spawns.RemoveAt(CurrentlySelectedTarget);
return true;
} else if (Key == EKeys::Up) {
Selected.Position
+= Selected.Orientation.RotateVector(FVector::ForwardVector);
return true;
} else if (Key == EKeys::Down) {
Selected.Position
-= Selected.Orientation.RotateVector(FVector::ForwardVector);
return true;
} else if (Key == EKeys::Left) {
Selected.Position
-= Selected.Orientation.RotateVector(FVector::RightVector);
return true;
} else if (Key == EKeys::Right) {
Selected.Position
+= Selected.Orientation.RotateVector(FVector::RightVector);
return true;
} else if (Key == EKeys::PageUp) {
Selected.Position += Selected.Orientation.RotateVector(FVector::UpVector);
return true;
} else if (Key == EKeys::PageDown) {
Selected.Position -= Selected.Orientation.RotateVector(FVector::UpVector);
return true;
}
return false;
}
TSharedPtr<SWidget> FFlockSpawnPointVisualizer::GenerateContextMenu() const {
return TSharedPtr<SWidget>();
}
UFlockSpawnerComponent *FFlockSpawnPointVisualizer::GetEditedSpawner() const {
if (!OwningActor.IsValid())
return nullptr;
return Cast<UFlockSpawnerComponent>(
GetComponentFromPropertyName(OwningActor.Get(), CompPropName));
}