-
Notifications
You must be signed in to change notification settings - Fork 6
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.
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
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
.
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 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