-
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 ue4This 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 UStaticMeshComponentThis 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 UStaticMeshComponentLet'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 = sceneCompConstructor 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.durationSecsEnums 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