-
Notifications
You must be signed in to change notification settings - Fork 6
Defining Unreal Types
Let's start with an example of defining an Unreal Actor:
# SomeActorWithMesh.nim
import ue4
uclass(ASomeActorWithMesh of AActor, Blueprintable):
var mesh {.editorReadOnly, bpReadOnly, category: "Mesh".}: ptr UStaticMeshComponent
proc init() {.constructor.} =
let sceneComp = createDefaultSubobject[USceneComponent](this, "SceneComp".toFName())
this.rootComponent = sceneComp
let meshObj = ctorLoadObject(UStaticMesh, "/Game/Meshes/DummyMesh")
mesh = createDefaultSubobject[UStaticMeshComponent](this, "DummyMesh".toFName())
mesh.staticMesh = meshObj
mesh.relativeScale3D = vec(0.25, 1.0, 0.25)
mesh.attachParent = sceneComp
proc getMeshLocation*(): FVector {.bpCallable, category: "Mesh".} =
## Returns locations of the actor's mesh
result = this.mesh.getComponentLocation()
method tick(deltaSeconds: float32) {.override, callSuper.} =
ueLog("Ticking ASomeActorWithMesh")
Let's breakdown this example step-by-step:
import ue4
This line imports the UE4 integration library. You need that in every file, unless the file declares only auxiliary (non-Unreal) types and procedures.
uclass(ASomeActorWithMesh of AActor, Blueprintable):
This line starts a block that defines uclass (Unreal Class) called ASomeActorWithMesh
that extends class AActor
. The class has Blueprintable
specifier which means it can be extended in Blueprints. See Unreal Documentation for the full list of supported specifiers. The declaration of a class without any specifiers would look like this: uclass ASomeActorWithMesh of AActor
var mesh {.editorReadOnly, bpReadOnly, category: "Mesh".}: ptr UStaticMeshComponent
This declares a field mesh
which is a pointer to UStaticMeshComponent
. Field declaration for Unreal types uses the same syntax as local variable declaration, including specification of default values. editorReadOnly
, bpReadOnly
, category
are Nim pragmas. They mean, in order: "this field is visible in the editor windows, but cannot be edited there", "this field is accessible from blueprints, but cannot be edited there", "this field belongs to Mesh category". Such pragmas are shortcuts for Unreal Property Specifiers. The full list of property pragmas can be found on Pragmas page. There is a different syntax for using the specifiers as they are documented in Unreal. This declaration is equivalent:
UProperty(VisibleAnywhere, BlueprintReadOnly, Category = "Mesh"):
var mesh: ptr UStaticMeshComponent
Let's move on to the constructor definition:
proc init() {.constructor.} =
let sceneComp = createDefaultSubobject[USceneComponent](this, "SceneComp".toFName())
this.rootComponent = sceneComp
let meshObj = ctorLoadObject(UStaticMesh, "/Game/Meshes/DummyMesh")
mesh = createDefaultSubobject[UStaticMeshComponent](this, "DummyMesh".toFName())
mesh.staticMesh = meshObj
mesh.relativeScale3D = initFVector(0.25, 1.0, 0.25)
mesh.attachParent = sceneComp
Constructor is a procedure marked with constructor
pragma. Constructor is where you initialize the object's fields. Notice that objects in UE are constructed early, so you should only rely on game assets and not on other objects (such as UWorld
) in constructor. Logic that should be performed when the game starts belongs to beginPlay
method of AActor
(see below about methods).
proc getMeshLocation*(): FVector {.bpCallable, category: "Mesh".} =
## Returns locations of the actor's mesh
result = this.mesh.getComponentLocation()
The method (and constructor) definition syntax is the same as for any ordinary Nim procedure. Notice that there is an implicitly added this
argument that is a pointer to the object the method belongs to. That is similar to C++, C#, and some other object-oriented languages. bpCallable
pragma specifies that this method can be called from Blueprints. The full list of function pragmas can be found on Pragmas page. There is also a syntax that supports all the Unreal's Function Specifiers:
UFunction(BlueprintCallable):
proc getMeshLocation*(): FVector =
result = this.mesh.getLocation()
The next method overrides the Actor's tick function:
method tick(deltaSeconds: float32) {.override, callSuper.} =
ueLog("Ticking ASomeActorWithMesh")
The method
keyword specifies that we are declaring a virtual function. Use method
keyword for any behaviour that can be overridden by subclasses of a class. In this case, we are overriding an existing tick
method which is defined in AActor
. That is specified by override
pragma. callSuper
pragma means "invoke the parent class's method before invoking this method's body". ueLog
call outputs the specified string to the log. See more details on the Logging page.
See Instantiating Objects page for details on how to create instances of Unreal classes.
Unreal Struct declaration is similar to class declaration, except that you have to use ustruct
macro. Usually, you use ustruct
when defining data objects with no behaviour. Unlike Unreal classes, structs may reside on the stack as ordinary arguments or local variables. Example:
# DownloadInfo.nim
import ue4
ustruct FDownloadInfo:
var durationSecs*: float32
var bytesDownloaded*: int32
proc getAvgBandwidth(download: FDownloadInfo): float32 {.blueprint, category: "Download", noSideEffect.} =
## Returns avg bandwidth for the download in bytes per second
## or 0.0 if the download hasn't started yet
if download.durationSecs != 0.0:
result = float32(download.bytesDownloaded) / download.durationSecs
Enums are defined with uenum
block followed by the set of enum values, each on a new line:
# CrosshairDirection.nim
import ue4
uenum ECrosshairDirection:
Left
Right
Top
Bottom
Center