Unreal Engine 4 (UE4) can allow you to create an entire Virtual Reality (VR) experience without ever coding. UE4 has a visual scripting system known as Blueprint that allows you to connect logic blocks together to do what you want. However, if you are a software developer or software engineer, then you probably want to write your own functions in some cases. Maybe, you want to optimize the efficiency of computation intensive tasks or you want to access some low-level API that the Blueprint system does not expose.
The goal of this post is to help you get a basic foundation with using C++ in UE4 with emphasis in VR development.
UE4 Variable Types
C++ in UE4 is not the same as the C++ you are used to. They do share similarities in variable types, but the usage and or declaration are not.
Here are a few examples of what is similar:
C++ | UE4 C++ |
---|---|
int | int |
float | float |
bool | bool |
Here are a few examples of what is different:
C++ | UE4 C++ |
---|---|
string | Fstring |
enum | namespace somenamespace { enum Type { enum1 }; }; } |
array[] | Tarray<variable_type> |
This is how to declare an enum that is both usable in Blueprint and C++. By default, the enum will only be visible in C++.
UENUM(BlueprintType) namespace ColorSelectionEnum { enum Type { RED UMETA(DisplayName = "Red"), GREEN UMETA(DisplayName = "Green"), BLUE UMETA(DisplayName = "Blue"), YELLOW UMETA(DisplayName = "Yellow") }; } TEnumAsByte<ColorSelectionEnum::Type> item_color;
UE4 Exclusive Variable Types
Everything is in three-dimensional space with UE4, so coordinates are of variable type FVector, FRotator, and FTransform. FVector is for a coordinate in 3D space and FRotator is the rotation at that coordinate from the origin. FTransform is the combination of FVector, FRotator, and scale of x, y, and z.
Variable Visibility
If you want your variables to be accessible outside of code for example like Blueprint you must add the UPROPERTY macro before the variable declaration. You can set the attributes to that variable such as a category and what can read and write to the variable.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "NPC Static Mesh") UStaticMeshComponent* npc_static_mesh_component;
UE4 Functions
Every custom function that you write should have the UFUNCTION() function specifier before the declaration of your function. Without any attributes pass into the macro, only the class itself can see the function. With attributes, you can make it so the function is Blueprint implementable, Blueprint Callable only, and etc.
// visible only in code UFUNCTION() float getVelocity(); // blueprint child of class can call this function UFUNCTION(BlueprintCallable, Category = "VR Char Reference") void setVRCharacterReference(AVRCharacter* our_vr_character);
You can find out more about the function specifier from the Unreal Engine documentation.
UE4 Classes
You are most likely going to be working with the Actor or Character class of Unreal Engine for most of what you are developing. Because of this, I am basing this section on the AActor and ACharacter class of Unreal Engine. You can easily transfer the information to another class part of the Unreal Engine.
A UE4 class requires the UCLASS() and GENERATED_BODY() macros. By convention, an “A” starts at the beginning of a class name. There are two requirements that you must provide to any actor class and those are the constructor and BeginPlay function.
Like regular C++, when it comes to inheritance, private, protected, and public UE4 classes work the same way.
Here is an example of a UE4 class:
#pragma once #include "GameFramework/Character.h" #include "VRCharacter.generated.h" UCLASS() class VRFIRSTPERSON_API AVRCharacter : public ACharacter { GENERATED_BODY() private: int health; protected: UPROPERTY(VisibleAnywhere, Category = "Components") UCameraComponent* CameraComp; /* Component to specify origin for the HMD */ UPROPERTY(VisibleAnywhere, Category = "Components") USceneComponent* VROriginComp; UPROPERTY(EditDefaultsOnly, Category = "VR") bool bPositionalHeadTracking; /* Motion Controllers */ UPROPERTY(EditDefaultsOnly, Category = "Components") class UMotionControllerComponent* LeftHandComponent; UPROPERTY(EditDefaultsOnly, Category = "Components") class UMotionControllerComponent* RightHandComponent; public: // Sets default values for this character's properties AVRCharacter(); // Called when the game starts or when spawned virtual void BeginPlay() override; // Called to bind functionality to input virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override; void SetupVROptions(); /* Resets HMD Origin position and orientation */ void ResetHMDOrigin(); };
Forward Declaration
When you try to create an instance of your custom class in another class, you will have to forward declare it to compile your code successfully. You need to do this to avoid problems with circular dependency. Rama, a well-respected developer in the UE4 community, has more about forward declaration.
UE4 VR Components
When using C++ to implement your VR character, there are some components you need to be aware of. They are the UCameraComponent and UMotionControllerComponent. The UCameraComponent ties to your head-mounted display (HMD) movements and rotation and influence your line-of-sight. The UMotionControllerComponent represents your physical motion controller in 3D space. It works with Vive and Oculus motion controllers.
By default, the VR functions in UE4 is not enabled. To use the VR functions that UE4 has to offer in C++, you must include the “HeadMountedDisplay” Module in your ProjectName.Build.cs file.
VRFirstPerson.Build.cs
// Fill out your copyright notice in the Description page of Project Settings. using UnrealBuildTool; public class VRFirstPerson : ModuleRules { public VRFirstPerson(TargetInfo Target) { PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); /* VR Required Modules */ PrivateDependencyModuleNames.AddRange(new string[] { "HeadMountedDisplay" }); // Uncomment if you are using Slate UI // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); // Uncomment if you are using online features // PrivateDependencyModuleNames.Add("OnlineSubsystem"); // if ((Target.Platform == UnrealTargetPlatform.Win32) || (Target.Platform == UnrealTargetPlatform.Win64)) // { // if (UEBuildConfiguration.bCompileSteamOSS == true) // { // DynamicallyLoadedModuleNames.Add("OnlineSubsystemSteam"); // } // } } }
To use the HMD and or motion controller components in your VR character class make sure to include the following header files:
- #include “HeadMountedDisplay.h”
- #include “MotionControllerComponent.h”
Helper Functions
Here are two functions that can help make development easier for you.
/** * function: Print * This method print out a string for debugging purpose. We can specify whether to * print to a log or the screen. */ static FORCEINLINE void Print(FString text_to_print, bool print_to_log = false, FColor color = FColor::Green, float TimeToDisplay = 1.5f) { if (GEngine) { GEngine->AddOnScreenDebugMessage(-1, TimeToDisplay, color, text_to_print); } if (print_to_log) { UE_LOG(LogTemp, Warning, TEXT("%s"), *text_to_print); } }
Get HMD Type
If you are developing with UE4.13 or below, there is no way to obtain the HMD attached at the Blueprint level. To get the HMD attached you need to use C++ and access the low-level API of Unreal Engine.
If you are developing with Unreal Engine above version 4.13, then there is already a Blueprint node that you can use to find out which HMD is attached. It is still nice to know how to implement it 🙂
/** * function: getHMDType * This method communicates with the Unreal Engine to determine what type of HMD * is attached to host device. * * @return FString name of the HMD device; "None" if there is no HMD */ FString AVRCharacter::getHMDType() { IHeadMountedDisplay* hmd = (IHeadMountedDisplay*)(GEngine->HMDDevice.Get()); if (hmd) { EHMDDeviceType::Type current_hmd = hmd->GetHMDDeviceType(); switch (current_hmd) { case EHMDDeviceType::DT_ES2GenericStereoMesh: return FString("Generic"); break; case EHMDDeviceType::DT_GearVR: return FString("GearVR"); break; case EHMDDeviceType::DT_Morpheus: return FString("PSVR"); break; case EHMDDeviceType::DT_OculusRift: return FString("OculusRift"); break; case EHMDDeviceType::DT_SteamVR: return FString("SteamVR"); break; default: return FString("None"); break; } } return FString("None"); }
I hope this post helps you with using C++ in UE4. If you found this post helpful, share it with others.
If you have any questions or would like to share your experience with C++ in UE4, leave a comment, send me a tweet, or send me an email at steven@brightdevelopers.com. I will be glad to hear about your experiences with C++ in UE4.
To stay in touch, you can follow me on twitter.