Skip to content

Defining Unreal Types

Ruslan Mustakov edited this page May 19, 2016 · 6 revisions

Unreal Class

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

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

Unreal Enum

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
Clone this wiki locally