From eab834563c3182a1caea5e33527e7d5d34de3cc7 Mon Sep 17 00:00:00 2001 From: Sam Deane Date: Fri, 31 Jan 2025 15:35:53 +0000 Subject: [PATCH] Cleaner Example Extensions (#658) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 4.3 project update... apparently opening the project did this ¯\_(ツ)_/¯ * Simplified SimpleExtension. Moved manual registration example to ManualExtension. --- Package.swift | 18 +++- .../ManualExtension.gdextension | 8 ++ .../ManualExtension.swift} | 92 +++---------------- Sources/ManualExtension/README.md | 14 +++ Sources/SimpleExtension/README.md | 6 +- ...dextension => SimpleExtension.gdextension} | 0 Sources/SimpleExtension/SimpleExtension.swift | 91 ++++++++++++++++++ 7 files changed, 144 insertions(+), 85 deletions(-) create mode 100644 Sources/ManualExtension/ManualExtension.gdextension rename Sources/{SimpleExtension/Demo.swift => ManualExtension/ManualExtension.swift} (64%) create mode 100644 Sources/ManualExtension/README.md rename Sources/SimpleExtension/{SwiftSprite.gdextension => SimpleExtension.gdextension} (100%) create mode 100644 Sources/SimpleExtension/SimpleExtension.swift diff --git a/Package.swift b/Package.swift index 6d4b7604a..30bf3d9d3 100644 --- a/Package.swift +++ b/Package.swift @@ -39,6 +39,12 @@ var products: [Product] = [ type: .dynamic, targets: ["SimpleExtension"] ), + + .library( + name: "ManualExtension", + type: .dynamic, + targets: ["ManualExtension"] + ), ] /// Targets are the basic building blocks of a package. A target can define a module, plugin, test suite, etc. @@ -127,7 +133,17 @@ var targets: [Target] = [ .target( name: "SimpleExtension", dependencies: ["SwiftGodot"], - exclude: ["SwiftSprite.gdextension", "README.md"], + exclude: ["SimpleExtension.gdextension", "README.md"], + swiftSettings: [.swiftLanguageMode(.v5)], + plugins: [.plugin(name: "EntryPointGeneratorPlugin")] + ), + + // This contains sample code showing how to use the SwiftGodot API + // with manual registration of methods and properties + .target( + name: "ManualExtension", + dependencies: ["SwiftGodot"], + exclude: ["ManualExtension.gdextension", "README.md"], swiftSettings: [.swiftLanguageMode(.v5)] ), diff --git a/Sources/ManualExtension/ManualExtension.gdextension b/Sources/ManualExtension/ManualExtension.gdextension new file mode 100644 index 000000000..cb182e9aa --- /dev/null +++ b/Sources/ManualExtension/ManualExtension.gdextension @@ -0,0 +1,8 @@ +[configuration] +entry_symbol = "swift_entry_point" +compatibility_minimum = 4.2 + + +[libraries] +macos.debug = "res://bin/ManualExtension" +macos.release = "res://bin/ManualExtension" diff --git a/Sources/SimpleExtension/Demo.swift b/Sources/ManualExtension/ManualExtension.swift similarity index 64% rename from Sources/SimpleExtension/Demo.swift rename to Sources/ManualExtension/ManualExtension.swift index ab52f036b..2f67453f1 100644 --- a/Sources/SimpleExtension/Demo.swift +++ b/Sources/ManualExtension/ManualExtension.swift @@ -9,6 +9,7 @@ import SwiftGodot var sequence = 0 + @Godot class Rigid: RigidBody2D { override func _integrateForces(state: PhysicsDirectBodyState2D?) { @@ -19,86 +20,15 @@ class Rigid: RigidBody2D { } } -@Godot -class SwiftSprite: Sprite2D { - var time_passed: Double = 0 - var count: Int = 0 - - #signal("picked_up_item", arguments: ["kind": String.self, "isGroovy": Bool.self, "count": Int.self]) - #signal("scored") - #signal("lives_changed", arguments: ["count": Int.self]) - - @Callable - public func computeGodot (x: String, y: Int) -> Double { - return 1.0 - } - - @Callable - public func wink () { - print ("Wink") - } - - @Callable - public func computerSimple (_ x: Int, _ y: Int) -> Double { - return Double (x + y) - } - - @Callable - func returnNullable () -> String? { - let x: Variant = Variant (1) - if let y: Resource = x.asObject () { - print ("Y is = \(y)") - } - return nil - } - - @Export var resource: Resource? - @Export(.dir) var directory: String? - @Export(.file, "*txt") var file: String? - @Export var demo: String = "demo" - @Export var food: String = "none" - - var x: Rigid? - - override func _process (delta: Double) { - time_passed += delta - - if x == nil { - self.x = Rigid() - } - guard let imageVariant = ProjectSettings.getSetting(name: "shader_globals/heightmap", defaultValue: Variant(-1)) else { - return - } - - GD.print("Found this value IMAGE: \(imageVariant.gtype) variant: \(imageVariant) desc: \(imageVariant.description)") - - let dict2: GDictionary? = GDictionary(imageVariant) - GD.print("dictionary2: \(String(describing: dict2)) \(dict2?["type"]?.description ?? "no type") \(dict2?["value"]?.description ?? "no value")") - - // part b - if let result = dict2?.get(key: Variant("type"), default: Variant(-1)) { - let value = String(result) ?? "No Result" - GD.print("2 Found this value \(value)") - } - - let lerp = Double(0.1).lerp(to: 10, weight: 1) - print ("Lerp result from 0.1 to 10 weight:1 => \(lerp)") - let newPos = Vector2(x: Float (10 + (10 * sin(time_passed * 2.0))), - y: Float (10.0 + (10.0 * cos(time_passed * 1.5)))) - - self.position = newPos - } -} - // This shows how to register methods and properties manually -class SwiftSprite2: Sprite2D { +class SwiftSprite: Sprite2D { var time_passed: Double var count: Int // This is a class initializer, must be invoked from all of your constructors to register the various // features of this class with the Godot engine. static var initClass: Void = { - let classInfo = ClassInfo (name: "SwiftSprite2") + let classInfo = ClassInfo (name: "SwiftSprite") classInfo.addPropertyGroup(name: "Miguel's Demo", prefix: "demo_") @@ -110,8 +40,8 @@ class SwiftSprite2: Sprite2D { hintStr: "Some kind of food", usage: .default) ] - classInfo.registerMethod(name: "demo_set_favorite_food", flags: .default, returnValue: nil, arguments: foodArgs, function: SwiftSprite2.demoSetFavoriteFood) - classInfo.registerMethod(name: "demo_get_favorite_food", flags: .default, returnValue: foodArgs [0], arguments: [], function: SwiftSprite2.demoGetFavoriteFood) + classInfo.registerMethod(name: "demo_set_favorite_food", flags: .default, returnValue: nil, arguments: foodArgs, function: SwiftSprite.demoSetFavoriteFood) + classInfo.registerMethod(name: "demo_get_favorite_food", flags: .default, returnValue: foodArgs [0], arguments: [], function: SwiftSprite.demoGetFavoriteFood) let foodProp = PropInfo (propertyType: .string, propertyName: "demo_favorite_food", @@ -123,14 +53,14 @@ class SwiftSprite2: Sprite2D { }() required init (nativeHandle: UnsafeRawPointer) { - _ = SwiftSprite2.initClass + _ = SwiftSprite.initClass time_passed = 0 count = sequence super.init (nativeHandle: nativeHandle) } required init () { - _ = SwiftSprite2.initClass + _ = SwiftSprite.initClass count = sequence sequence += 1 time_passed = 0 @@ -191,22 +121,22 @@ class SwiftSprite2: Sprite2D { } } +/// Setup func setupScene (level: GDExtension.InitializationLevel) { if level == .scene { - register(type: SwiftSprite.self) - register(type: SwiftSprite2.self) register(type: Rigid.self) + register(type: SwiftSprite.self) } } -// Set the swift.gdextension's entry_symbol to "swift_entry_point +/// Manually defined entry point for the extension. @_cdecl("swift_entry_point") public func swift_entry_point( godotGetProcAddr: OpaquePointer?, libraryPtr: OpaquePointer?, extensionPtr: OpaquePointer?) -> UInt8 { - print ("SwiftSprite: Starting up") + print ("ManualExtension: Starting up") guard let godotGetProcAddr, let libraryPtr, let extensionPtr else { return 0 } diff --git a/Sources/ManualExtension/README.md b/Sources/ManualExtension/README.md new file mode 100644 index 000000000..bafd0e79d --- /dev/null +++ b/Sources/ManualExtension/README.md @@ -0,0 +1,14 @@ +# ManualExtension + +This example extension shows how you can write custom registration code +for your extension, to do more complicated things. + +For a simpler example, see SimpleExtension. + +The code is contained in ManualExtension.swift, the package definition +is contained by ../Package.swift and you must copy the +resulting binary (ManualExtension) into the bin directory +of your Godot project and place the ManualExtension.gdextension file +in the Godot project root. + + diff --git a/Sources/SimpleExtension/README.md b/Sources/SimpleExtension/README.md index 38537ceac..7c3903bb5 100644 --- a/Sources/SimpleExtension/README.md +++ b/Sources/SimpleExtension/README.md @@ -3,10 +3,10 @@ This simple extension shows how you can create a class that is exposed to Godot and how you bootstrap it. -The code is contained in Demo.swift, the package definition +The code is contained in SimpleExtension.swift, the package definition is contained by ../Package.swift and you must copy the -resulting binary (SimpleExtnesion) into the bin directory -of your Godot project and place the SwiftSprite.gdextension file +resulting binary (SimpleExtension) into the bin directory +of your Godot project and place the SimpleExtension.gdextension file in the Godot project root. diff --git a/Sources/SimpleExtension/SwiftSprite.gdextension b/Sources/SimpleExtension/SimpleExtension.gdextension similarity index 100% rename from Sources/SimpleExtension/SwiftSprite.gdextension rename to Sources/SimpleExtension/SimpleExtension.gdextension diff --git a/Sources/SimpleExtension/SimpleExtension.swift b/Sources/SimpleExtension/SimpleExtension.swift new file mode 100644 index 000000000..2dc6eaefa --- /dev/null +++ b/Sources/SimpleExtension/SimpleExtension.swift @@ -0,0 +1,91 @@ +// +// Demo.swift +// +// Created by Miguel de Icaza on 4/4/23. +// + +import Foundation +import SwiftGodot + +var sequence = 0 + +@Godot +class Rigid: RigidBody2D { + override func _integrateForces(state: PhysicsDirectBodyState2D?) { + guard let xform = state?.transform else { + return + } + print (xform) + } +} + +@Godot +class SwiftSprite: Sprite2D { + var time_passed: Double = 0 + var count: Int = 0 + + @Signal var pickedUpItem: SignalWithArguments + @Signal var scored: SimpleSignal + @Signal var livesChanged: SignalWithArguments + + @Callable + public func computeGodot (x: String, y: Int) -> Double { + return 1.0 + } + + @Callable + public func wink () { + print ("Wink") + } + + @Callable + public func computerSimple (_ x: Int, _ y: Int) -> Double { + return Double (x + y) + } + + @Callable + func returnNullable () -> String? { + let x: Variant = Variant (1) + if let y: Resource = x.asObject () { + print ("Y is = \(y)") + } + return nil + } + + @Export var resource: Resource? + @Export(.dir) var directory: String? + @Export(.file, "*txt") var file: String? + @Export var demo: String = "demo" + @Export var food: String = "none" + + var x: Rigid? + + override func _process (delta: Double) { + time_passed += delta + + if x == nil { + self.x = Rigid() + } + guard let imageVariant = ProjectSettings.getSetting(name: "shader_globals/heightmap", defaultValue: Variant(-1)) else { + return + } + + GD.print("Found this value IMAGE: \(imageVariant.gtype) variant: \(imageVariant) desc: \(imageVariant.description)") + + let dict2: GDictionary? = GDictionary(imageVariant) + GD.print("dictionary2: \(String(describing: dict2)) \(dict2?["type"]?.description ?? "no type") \(dict2?["value"]?.description ?? "no value")") + + // part b + if let result = dict2?.get(key: Variant("type"), default: Variant(-1)) { + let value = String(result) ?? "No Result" + GD.print("2 Found this value \(value)") + } + + let lerp = Double(0.1).lerp(to: 10, weight: 1) + print ("Lerp result from 0.1 to 10 weight:1 => \(lerp)") + let newPos = Vector2(x: Float (10 + (10 * sin(time_passed * 2.0))), + y: Float (10.0 + (10.0 * cos(time_passed * 1.5)))) + + self.position = newPos + } +}