Editor camera never updates

Am I accessing the editor camera in the right way? It never seems to update.



auto views = GWorld->EditorViews;
for (int x = 0; x < 8; x++) {
	FLevelViewportInfo view = views[x];
	camloc = view.CamPosition;
	UE_LOG(LogTemp, Warning, TEXT("Camera%d] position: %f, %f, %f"), x, camloc.X, camloc.Y, camloc.Z);
}



This loops through the 8 possible views that are returned and the output just never changes, even with moving the camera around the level. What gives? I do it this way because I also need the camera rotation, which GWorld->ViewLocationsRenderedLastFrame doesn’t give me. Any ideas?

So I’ve solved this. I’m going to put the entire solution in one place for future generations. In this implementation, the camera position will be returned in the editor regardless of if you’re playing or not, which I find quite useful. It also doesn’t require an object to be placed into the level, which is super sweet. Correct me if I make any mistakes here.

First, you need a UObject header file. It needs to inherit a few things:


UCLASS()class OCTANERENDERPLUGIN_API UOctaneOutput : public UBlueprintFunctionLibrary, public FTickableGameObject
{
    GENERATED_BODY()

    UPROPERTY() uint32 bCanEverTick : 1;

    void Tick(float DeltaTime) override;
    bool IsTickable() const override;
    bool IsTickableInEditor() const override;

    static FVector OldCameraLocation;
    static FRotator OldCameraRotation;

}

The important things here are the FTickableGameObject parent class and the properties below GENERATED_BODY(). Now make a CPP file:


UOctaneOutput::UOctaneOutput(){
    bCanEverTick = true;
    this->AddToRoot();
}

bool UOctaneOutput::IsTickable() const { return true; }
bool UOctaneOutput::IsTickableInEditor() const { return true; }

TStatId UOctaneOutput::GetStatId() const
{
    TStatId stat = TStatId();
    return stat;
}

void UOctaneOutput::Tick(float DeltaTime)
{
    UOctaneOutput::UpdateCamera();
}

Go on and get you some methods in there. Now, this is important: that tick event will continue both in and outside of PIE. But these are two very different states.

This brings us to our UpdateCamera() method.


void UOctaneOutput::UpdateCamera(){
    FVector CameraLocation;
    FRotator CameraRotator;
    float FieldOfView = 90;
    float Multiplier = 1;
    bool CameraUpdated = false;
    
    for (FConstPlayerControllerIterator Iterator = GWorld->GetPlayerControllerIterator(); Iterator; ++Iterator)
    {
        APlayerController * PlayerController = Iterator->Get();
        APlayerCameraManager * Camera = PlayerController->PlayerCameraManager;
        CameraLocation = Camera->GetCameraLocation();
        CameraRotator = Camera->GetCameraRotation();
        CameraUpdated = true;
        break;
    }


    if (!CameraUpdated) {
        FViewport * Viewport = GEditor->GetActiveViewport();
        if (!Viewport) return;
        FEditorViewportClient * EditorViewportClient = (FEditorViewportClient *)Viewport->GetClient();
        if (!EditorViewportClient) return;
        CameraLocation = EditorViewportClient->GetViewLocation();
        CameraRotator = EditorViewportClient->GetViewRotation();
        CameraUpdated = true;
    }


    if (!CameraUpdated) return;


    if (CameraLocation != OldCameraLocation || CameraRotator != OldCameraRotation) {
        CameraLocation.X = FMath::RoundToFloat(CameraLocation.X); // The editor camera has some minuscule post-release drift in it which we just don't need
        CameraLocation.Y = FMath::RoundToFloat(CameraLocation.Y);
        CameraLocation.Z = FMath::RoundToFloat(CameraLocation.Z);


        UE_LOG(LogTemp, Log, TEXT("[Octane] Camera updated: %f, %f, %f, :: %f, %f, %f"), CameraLocation.X, CameraLocation.Y, CameraLocation.Z, CameraRotator.Yaw, CameraRotator.Pitch, CameraRotator.Roll);
        OldCameraLocation = CameraLocation;
        OldCameraRotation = CameraRotator;


        FVector CameraRotationVector = CameraRotator.Vector();
        FVector LookAt = CameraLocation + (CameraRotationVector * 1000);
    }
}

As you can see, there’s a few header declarations I didn’t put in the code above. This is left as an exercise for the reader. Also, the vast majority of these members are static. This is helpful later on, since you don’t want to need to instantiate this object, you just want it to work.

The key thing here is that IF there’s an editor camera to use, we’ll use that. If not, we’ll go grab the PIE game camera instead based on the first player controller we find.

Because this member is static, it should now just work. Go and open your output window and get spammed to high heaven every time you move the camera.