multijam/swiftgodot
SwiftGodot provides Swift language bindings for the Godot 4.2 game
Your Swift Code
Your Swift code will be compiled into a shared library that Godot will call. To get started, the simplest thing to do is to create a Swift Library Package that references the Swift Godot package, like this:
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "MyFirstGame",
products: [
.library(name: "MyFirstGame", type: .dynamic, targets: ["MyFirstGame"]),
],
dependencies: [
.package(url: "https://github.com/migueldeicaza/SwiftGodot", branch: "main")
],
targets: [
.target(
name: "MyFirstGame",
dependencies: ["SwiftGodot"])]
)The above will compile all of SwiftGodot for you - alternatively, if you do not need access to the source, you can use the .binaryTarget feature of SwiftPM and reference an .xcframework that I have conveniently published on GitHub at https://github.com/migueldeicaza/SwiftGodotBinary
The next step is to create your source file with the magic on it, here we declare a spinning cube:
import SwiftGodot
@Godot(.tool)
class SpinningCube: Node3D {
public override func _ready () {
let meshRender = MeshInstance3D()
meshRender.mesh = BoxMesh()
addChild(node: meshRender)
}
public override func _process(delta: Double) {
rotateY(angle: delta)
}
}Additionally, you need to write some glue code for your project to be loadable by Godot, you can do it like this:
/// We register our new type when we are told that the scene is being loaded
func setupScene (level: GDExtension.InitializationLevel) {
if level == .scene {
register(type: SpinningCube.self)
}
}
// Export our entry point to Godot:
@_cdecl("swift_entry_point")
public func swift_entry_point(
interfacePtr: OpaquePointer?,
libraryPtr: OpaquePointer?,
extensionPtr: OpaquePointer?) -> UInt8
{
print ("SwiftGodot Extension loaded")
guard let interfacePtr, let libraryPtr, let extensionPtr else {
print ("Error: some parameters were not provided")
return 0
}
initializeSwiftModule(interfacePtr, libraryPtr, extensionPtr, initHook: setupScene, deInitHook: { x in })
return 1
}Alternatively, you can use the #initSwiftExtension macro:
import SwiftGodot
#initSwiftExtension(cdecl: "swift_entry_point", types: [SpinningCube.self])Also, you can use EntryPointGeneratorPlugin that will scan the target source files and generate entry point called swift_entry_point with types array mentioning all classes with @Godot macro attached. All you need is to add plugins entry in your Package.swift as below:
let package = Package(
name: "MyFirstGame",
products: [
.library(name: "MyFirstGame", type: .dynamic, targets: ["MyFirstGame"]),
],
dependencies: [
.package(url: "https://github.com/migueldeicaza/SwiftGodot", branch: "main")
],
targets: [
.target(
name: "MyFirstGame",
dependencies: ["SwiftGodot"],
// this plugin will generate a source file visible to compiler with '#initSwiftExtension(cdecl: "swift_entry_point", types: [SpinningCube.self])'
plugins: [
.plugin(name: "EntryPointGeneratorPlugin", package: "SwiftGodot")
]
)
],
)Bundling Your Extension
To make your extension available to Godot, you will need to build the binaries for all of your target platforms, as well as creating a .gdextension file that lists this payload, along with the entry point you declared above.
You would create something like this in a file called MyFirstGame.gdextension:
[configuration]
entry_symbol = "swift_entry_point"
compatibility_minimum = 4.2
[libraries]
macos.debug = "res://bin/MyFirstGame"
macos.release = "res://bin/MyFirstGame"
windows.debug.x86_32 = "res://bin/MyFirstGame"
windows.release.x86_32 = "res://bin/MyFirstGame"
windows.debug.x86_64 = "res://bin/MyFirstGame"
windows.release.x86_64 = "res://bin/MyFirstGame"
linux.debug.x86_64 = "res://bin/MyFirstGame"
linux.release.x86_64 = "res://bin/MyFirstGame"
linux.debug.arm64 = "res://bin/MyFirstGame"
linux.release.arm64 = "res://bin/MyFirstGame"
linux.debug.rv64 = "res://bin/MyFirstGame"
linux.release.rv64 = "res://bin/MyFirstGame"
android.debug.x86_64 = "res://bin/MyFirstGame"
android.release.x86_64 = "res://bin/MyFirstGame"
android.debug.arm64 = "res://bin/MyFirstGame"
android.release.arm64 = "res://bin/MyFirstGame"In the example above, the extension always expects the platform specific payload to be called "MyFirstGame", regarless of the platform. If you want to distribute your extension to other users and have a single payload, you will need to manually set different names for those.
Installing your Extension
You need to copy both the new .gdextension file into an existing project, along with the resources it references.
Once it is there, Godot will load it for you.
Using your Extension
Once you create your extension and have loaded it into Godot, you can reference it from your code by using the "Add Child Node" command in Godot (Command-A on MacOS) and then finding it in the hierarchy.
In our example above, it would appear under Node3D, as it is a Node3D subclass.
Community
Join the community on Slack
Contributing
Have a bug fix or feature request you'd like to see added? Consider contributing! Join our community to get started.
Package Metadata
Repository: multijam/swiftgodot
Default branch: main
README: README.md