Skip to content

Coding Quick Guide

xix xeaon edited this page Oct 20, 2024 · 24 revisions

Coding Quick Guide

You've got gdext for nim installed, and you've copied the quick_template demo. Now you need to understand the differences between how Godot/GDScript works normally, and how it works in nim using gdext.

Your Node Class

You can't attach a nim-script to a node - you have to create and register classes (like with class_name) using a nim type. You'll then be able to add it to your scene like the builtin nodes. Add your class-level variables to the type. Use the pragma gdexport to make them accessible in the inspector.

type MyClass* = ptr object of Node3D
    score :int = 0
    mob_scene* {.gdexport.} :GdRef[PackedScene]
    player* {.gdexport.} :Node3D

You can reference the datatypes of all Godot classes as long as you've imported gdext, but in order to use their methods and properties you need to import the actual class, which are located in gdext/classes/ with the names beginning with gd.

import gdext/classes / [gdNode3D]

For your class you'll mostly want to create routines which take as their first argument the type of your class. The gdsync pragma allows hotreloading. If you're overloading a routine then it needs to be a method and not start with _.

method ready(self :MyClass) {.gdsync.} = discard
proc do_thing(self :MyClass) {.gdsync.} = discard

Your code will run in the editor as well (like when using the @tool annotation), use Engine.isEditorHint to decide what code should run in the editor, and what in the game.

method process(self :MyClass, delta :float) {.gdsync.} =
    if Engine.isEditorHint: return
    # the following code will not run in the editor

Using nodes and Godot types and routines

Builtin global functions have the same names. Types like Vector3 etc do not need to be imported and work like in GDScript, but the constructor is lower case and names are technically Capitalized instead of UPPERCASE - nim of course allows uppercase anyway.

self.position = vector3(0, 0, 0) + Vector3.Down + Vector3.DOWN
print self.position.length

You can fetch nodes from the scene-tree (like with dollar-sign $) using special syntax, or using getNode. Note that you need to specify its type.

let node = self/"Path"/"to"/"Node" as Sprite2D
let other_node = self.getNode("Path/to/ThisNode") as MeshInstance3D

Create new instances of nodes using instantiate, with an optional name for the node.

let node :Node3D = Node3D.instantiate "some name"
node.position = vector3(0, 0, 3)
self.add_child(node)

Builtin enums are slightly different, for instance Mesh_PrimitiveType.primitiveTriangles instead of Mesh.PRIMITIVE_TRIANGLES. Look for them in gdext/gen/globalenums.nim and gdext/gen/localenums.nim.

Object or RefCounted

Nodes, and some other things, ultimately inherit from Object and their types are simple. Others inherit from RefCounted which means they're encapsulated with GdRef. If you need access to the plain datatype without GdRef they can be dereferenced with [].

let mi :MeshInstance = instantiate MeshInstance # Object type
let st :GdRef[SurfaceTool] = instantiate SurfaceTool # RefCounted type
st[].begin(primitiveTriangles) # st needs dereferencing
# [...]
let m :GdRef[ArrayMesh] = st[].commit()
mi.mesh = m as GdRef[Mesh] # Casting to super type

Signals

Signals can be identified using string names and so can the functions that connect to them. You'll then need to use the name pragma to give the called function its string name.

method ready(self :MyClass) {.gdsync.} =
    discard self.connect("visibility_changed", self.callable("_on_visibility_changed"))

proc on_visibility_changed(self :MyClass) {.gdsync, name: "_on_visibility_changed".} =
    print $self, " visibility: ", self.visible
Clone this wiki locally