SetActorLocation Fails, Returns False

I’m attempting to spawn a physical Waypoint actor from a WaypointManager object I have set up. The logic goes as follows:

  1. Controller gets mouse click info from player input
  2. Controller passes that info to the WPManager class via WPManager->SetNewWPLocation(OutHit.ImpactPoint);
  3. The WPManager object updates its list of WP locations stored in TArray WPList;
  4. WPManager calls UpdateWaypoints(), which is supposed to delete all existing Waypoint actors and respawn new Waypoint actors based on the updated WPList

The new Waypoints are spawned with no issues, but I can’t set their location. I’ve tried setting their locations from the WPManager class (which is ideal), and also from the Waypoint actor class itself for debugging purposes, but the Waypoints are stuck at the world origin.

WaypointManager.h

UCLASS()
class UWaypointManager : public UObject
{
	GENERATED_UCLASS_BODY()

	/** Stores entire waypoint array */
	UPROPERTY()
	TArray<FVector> WPList;
	
	/** Stores pointers to actual waypoint actors */
	UPROPERTY()
	TArray<AWaypoint*> WPActorList;

	/** Initial call for creating a new waypoint */
	UFUNCTION(BlueprintCallable, Category = "Waypoints")
	void SetNewWPLocation(FVector inLoc);

	/** Updates ALL waypoints (not 100% efficient) */
	void UpdateWaypoints();

	/** Stores pointer to world for context, set from Controller */
	UWorld* World;
};

WaypointManager.cpp

UWaypointManager::UWaypointManager(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{

}

void UWaypointManager::SetNewWPLocation(FVector inLoc)
{
	WPList.Add(inLoc);
	UpdateWaypoints();
}

void UWaypointManager::UpdateWaypoints()
{
	// Delete existing waypoints
	for (int32 i = 0; i < WPActorList.Num(); i++)
	{
		if (WPActorList[i])
			WPActorList[i]->Destroy();
	}

	WPActorList.Empty();

	// Respawn waypoints

	if (World)
	{
		for (int32 i = 0; i < WPList.Num(); i++)
		{
			FActorSpawnParameters SpawnInfo;

			SpawnInfo.bNoCollisionFail = true;

			WPActorList.Add(World->SpawnActor<AWaypoint>(WPList[i], FRotator(0, 0, 0), SpawnInfo));

			if (WPActorList[i])
				if (WPActorList[i]->SetActorLocation(WPList[i]))
					GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Green, FString("Successfully set location"));
		}
	}
}

Waypoint.h

UCLASS()
class AWaypoint : public AActor
{
	GENERATED_UCLASS_BODY()
	
	virtual void BeginPlay() OVERRIDE;
	
	virtual void Tick(float DeltaTime) OVERRIDE;
};

Waypoint.cpp

AWaypoint::AWaypoint(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{
	PrimaryActorTick.bCanEverTick = true;
}

void AWaypoint::BeginPlay()
{
	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, FString("WPActor spawned!"));

	// Temporary SetLocation call for debugging (doesn't work)
	SetActorLocation(FVector(0, 0, 50));
}

void AWaypoint::Tick(float DeltaTime)
{
	DrawDebugSphere((), GetActorLocation(), 16.f, 8, FColor::Red);
	GEngine->AddOnScreenDebugMessage(-1, DeltaTime, FColor::Green, GetActorLocation().ToString());
}

The DebugSpheres are being drawn, so I know the Waypoints are being spawned, but they’re all at the world origin, and GEngine->AddOnScreenDebugMessage(-1, DeltaTime, FColor::Green, GetActorLocation().ToString()); in AWaypoint::Tick prints (0,0,0).

I’m stumped, any and all help is welcome :slight_smile:

Actor requires atleast one component in order to be movable, because in reality SetActorLocation moves location of your root component as source (which is alsoin API reference) suggest

bool AActor::SetActorLocation(const FVector& NewLocation, bool bSweep)
{
    if (RootComponent)
    {
        const FVector Delta = NewLocation - GetActorLocation();
        return RootComponent->MoveComponent( Delta, GetActorRotation(), bSweep );
    }

    return false;
}

I recomad you to use billboard component, it a component of those icon things you see on the level in editor for example on lights, by default they only visible in editor so you don’t need to worry it will mess the look, not to mention it might be good to create debug that shows the waypoint actor by making bilbord component visible also in game :slight_smile: Here code to include billboard component to actor:

in actor class in .h include:

TSubobjectPtr<UBillboardComponent> Bill;

in contractor of a actor class put:

Bill = PCIP.CreateDefaultSubobject<UBillboardComponent>(this, TEXT("Billboard"));
Bill->Mobility = EComponentMobility::Type::Movable;
RootComponent = Bill;

and there you go ^^ here API refrence for billboard component

Thank you very much, that did the trick! Very informative :slight_smile: