Skip to content

Commit

Permalink
Cleaner Example Extensions (#658)
Browse files Browse the repository at this point in the history
* 4.3 project update... apparently opening the project did this ¯\_(ツ)_/¯

* Simplified SimpleExtension. Moved manual registration example to ManualExtension.
  • Loading branch information
samdeane authored Jan 31, 2025
1 parent 3e301d6 commit eab8345
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 85 deletions.
18 changes: 17 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)]
),

Expand Down
8 changes: 8 additions & 0 deletions Sources/ManualExtension/ManualExtension.gdextension
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import SwiftGodot

var sequence = 0


@Godot
class Rigid: RigidBody2D {
override func _integrateForces(state: PhysicsDirectBodyState2D?) {
Expand All @@ -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<SwiftSprite2> (name: "SwiftSprite2")
let classInfo = ClassInfo<SwiftSprite> (name: "SwiftSprite")

classInfo.addPropertyGroup(name: "Miguel's Demo", prefix: "demo_")

Expand All @@ -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",
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
14 changes: 14 additions & 0 deletions Sources/ManualExtension/README.md
Original file line number Diff line number Diff line change
@@ -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.


6 changes: 3 additions & 3 deletions Sources/SimpleExtension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.


91 changes: 91 additions & 0 deletions Sources/SimpleExtension/SimpleExtension.swift
Original file line number Diff line number Diff line change
@@ -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<String, Bool, Int>
@Signal var scored: SimpleSignal
@Signal var livesChanged: SignalWithArguments<Int>

@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
}
}

0 comments on commit eab8345

Please sign in to comment.