SwiftGodot Documentation

repository·main·Indexed 23 days ago

https://github.com/migueldeicaza/swiftgodot

Swift bindings for the Godot engine, featuring a Generator tool that automates binding creation from Godot's extension-api.json. The library provides GDExtension support via XCFrameworks for macOS and iOS, and includes macros such as @Godot, @Node, @Export, @Callable, and @Signal for creating custom Godot types and referencing scene nodes in Swift.

Tokens
20.4K
Snippets
66
Records
103
Agent score
82%

What's inside SwiftGodot

  1. Overview of SwiftGodot

    main

    SwiftGodot is a framework that allows you to write Godot Game Extensions using the Swift programming language. It provides bindings to Godot's public extension API, mapping them to Swift idioms.

    Key features:

    • GD Extensions: Author code in Swift that runs as a Godot extension.
    • Side-by-side execution: Works alongside GDScript or C# code within the same project.
    • SwiftGodotKit: A companion library used to embed and host Godot directly within an existing Swift application.
    • Performance & Safety: Leverages Swift's high performance and type safety while accessing the Godot engine.
  2. Use the SwiftGodot Generator to produce Swift bindings

    main

    The Generator is a command-line tool that consumes the Godot extension-api.json file and produces the corresponding Swift bindings.

    If you are using the SwiftGodot package, this tool is automatically invoked using the Godot 4.0 API description to generate the necessary bindings and documentation.

  3. Expose optional GodotBuiltinConvertible types as Variants

    main

    If you conform a type to GodotBuiltinConvertible but use it as an Optional in an @Export property, Godot will treat the property as a Variant instead of the specific builtin type.

    For example, if Date is converted to a Double via GodotBuiltinConvertible, using @Export var variable0: Date? will result in Godot seeing a Variant type for that property.

    extension Date: GodotBuiltinConvertible {
        public func toGodotBuiltin() -> Double {
            timeIntervalSince1970
        }
    
        public static func fromGodotBuiltinOrThrow(_ value: Double) throws(VariantConversionError) -> Self {
            Date(timeIntervalSince1970: value)
        }
    }
    
    @Godot class CustomNode: Node {
        @Export var variable0: Date? = Date.now
        // Godot will see `Variant variable0` 
    }
  4. How Node-derived singletons work

    main

    If a singleton derives from Node, it will not receive lifecycle callbacks (like _ready(), _process(), or _enter_tree()) unless it is added to the Scene Tree.

    To make a Node behave like a Godot 'Autoload', you must add it as a child of the SceneTree.root. This ensures the node persists across scene changes and receives standard callbacks.

    Warning: You cannot add a node to the scene tree directly during GDExtension initialization without causing a crash. You must use callDeferred() to add the node to the tree safely.

    // Inside the singleton class
    @Callable
    func _addToSceneTree() {
        if let sceneTree = Engine.getMainLoop() as? SceneTree, 
           let root = sceneTree.root {
            root.addChild(node: self)
        }
    }
    
    // During registration
    singleton.callDeferred(method: StringName("_addToSceneTree"))
  5. How @Rpc configuration is applied

    main

    The @Rpc macro works by having the @Godot macro generate code in a special _before_ready() method. This method is called automatically before the node's _ready() method when the node enters the scene tree.

    The generated code calls Node/rpcConfig(method:config:) for each marked method, passing a configuration dictionary containing rpc_mode, call_local, transfer_mode, and channel.

  6. Understand how types are translated between Swift and Godot

    main

    SwiftGodot automatically translates types when data moves between the Swift runtime and the Godot engine. This translation occurs in several scenarios:

    • @Callable: When Godot calls a Swift function, arguments are translated from Godot to Swift, and return values are translated from Swift to Godot.
    • @Export: When Godot accesses a property, values are translated from Swift to Godot (get) or Godot to Swift (set).
    • Callable type: Creating a Callable from a Swift closure behaves identically to Godot calling an @Callable function.

    Toll-free bridging indicates that the translation has negligible performance cost. If a type is not explicitly marked as toll-free, it may involve allocations or parsing (e.g., String or Array).

  7. Understand SwiftGodot API differences to GDScript

    main

    SwiftGodot follows Swift conventions rather than GDScript's. Key differences include:

    • Naming Convention: Uses camelCase instead of snake_case.
    • Enumerations: Uses dot syntax with path-like names (e.g., Corner.topLeft) instead of constants (e.g., CORNER_TOP_LEFT).
    • Function Calls: Uses Swift's parameter name requirement (e.g., myNode.addChild(node: box) instead of myNode.add_child(box)).
    • Global Scope: Global functions and constants are moved to the GD class to avoid namespace pollution.
  8. Group exported properties in the Inspector

    main

    You can organize exported properties into visual groups or subgroups within the Godot Inspector using the #exportGroup and #exportSubgroup macros.

    • #exportGroup("Name") starts a new group. All subsequent properties belong to this group until a new group is started or you use #exportGroup("") to break out.
    • #exportGroup("Name", prefix: "prefix") groups only properties that start with the specified prefix.
    • #exportSubgroup("Name") creates a subgroup within the current group. Groups cannot be nested (you cannot put a group inside a group), but subgroups can exist within a group.
    #exportGroup("My Properties")
    @Export var number = 3
    
    #exportGroup("My Properties", prefix: "health")
    @Export var health_reload_speed = 3
    
    #exportSubgroup("Extra Properties")
    @Export var string = ""
    @Export var flag = false
  9. What are Variants in SwiftGodot

    main
    A Variant is Godot's fundamental data type used for passing data between Godot and Swift. It is similar to Swift's Any type but is restricted to holding Godot-compatible types. This includes native Godot types (like GString, Vector, Rect, Callable), Swift types with convenience conformances (like Bool, Int, String, Float, Double), and Godot Object subclasses (like Node).
  10. Understand the SwiftGodot binary package structure

    main

    SwiftGodot publishes prebuilt Apple-platform XCFrameworks. The binary package consists of four specific XCFrameworks:

    • SwiftGodot: Dynamically linked to SwiftGodotRuntime.
    • SwiftGodotRuntime: Contains the GDExtension support implementation.
    • GDExtension: Contains C headers and an empty static library.
    • SwiftGodotMacroPlugin: The prebuilt implementation of SwiftGodot macros.

    For the first three, the package includes slices for macOS (Apple silicon and Intel), iOS device, and iOS Simulator. Swift module interfaces are included in Release builds to ensure compatibility with newer Swift compilers.

  11. Handle missing nodes with optional or non-optional @Node properties

    main

    Because node lookup happens at runtime, the @Node macro behaves differently depending on whether the property is defined as an optional type. This is critical for handling incorrect paths, removed nodes, or type mismatches.

    Optional Properties

    If you define the property as an optional (e.g., Node2D?), the property will return nil if the node is not found or is of the wrong type. This approach is more resilient to scene changes but requires unwrapping the value during use.

    Non-optional Properties

    If you define the property as a non-optional type (e.g., Area2D), accessing the property will trigger a fatal runtime error if the node cannot be found or is the wrong type. Use this style when you want to assert that the node must exist and that its absence constitutes a coding error.

    @Godot
    class Main: Node {
        // Resilient: returns nil if missing
        @Node("CharacterBody2D") var player: PlayerController?
    
        // Assertive: crashes if missing
        @Node("Telepoint") var teleportArea: Area2D
    }