GodotでUIKitを使いたい!
環境
- Godot 4.4.stable
- Xcode 16.2
実装手順
- Swift Packageを作る
- SwiftPackageにSwiftGodotを導入する
- コードを書く
- Godotプロジェクトの res://addons/内に .gdextensionファイルを配置
- SwiftPackageと Godotを紐づける- 現在は.frameworkと.dylibを書き出してres://addons/内に配置する方法にしている。
 
- 現在は
- Godot上でエラーが出ないことを確認し、Swiftのコードを呼び出す
- iOS に Export して.xcodeprojを開き、実機で確認
STEP1: Swift Package を作る
既存のGodotプロジェクトに組み込み形で作ってみる。
- SwiftPackage を配置するフォルダを作る
- swift package initで- package.swiftを作る
// Godot プロジェクトに移動
$ cd ~/Godot/godot_project
// Godot プロジェクトとエクスポート後のiOSプロジェクトがある状態
$ ls
Godot         iOS
// SwiftPackages をおく場所をつくる
$ mkdir SwiftPackages
$ cd SwiftPackages
// 個々のパッケージ
$ mkdir GodotKeyboardObserver
$ cd GodotKeyboardObserver
// Swift Pacakge を作成する
$ swift package init
Creating library package: GodotKeyboardObserver
Creating Package.swift
Creating .gitignore
Creating Sources/
Creating Sources/GodotKeyboardObserver/GodotKeyboardObserver.swift
Creating Tests/
Creating Tests/GodotKeyboardObserverTests/
Creating Tests/GodotKeyboardObserverTests/GodotKeyboardObserverTests.swiftSTEP 2: Package.swift を用意する
Package.swift
- SwiftGodotを dependenciesに書こう。- これを使うために platformsにmacOSを追加する必要がある。
 
- これを使うために 
- zt-pawer/SwiftGodotIosPluginsを参考に書いた。
// swift-tools-version: 6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
var libraryType: Product.Library.LibraryType
#if os(Windows)
libraryType = .static
#else
libraryType = .dynamic
#endif
let package = Package(
    name: "GodotKeyboardObserver",
    platforms: [.iOS(.v17), .macOS(.v15)], // macOS も対象にしておく必要がある
    products: [
        .library(
            name: "GodotKeyboardObserver",
            type: libraryType,  // dynamic or static
            targets: ["GodotKeyboardObserver"]),
    ],
     dependencies: [
        .package(url: "https://github.com/migueldeicaza/SwiftGodot", revision: "088bf00ecfe91f5d76401256048e0f71ecc726fd")
    ],
    targets: [
        .target(
            name: "GodotKeyboardObserver",
            dependencies: [
                "SwiftGodot",
            ],
            swiftSettings: [.unsafeFlags(["-suppress-warnings"])]
        ),
        .testTarget(
            name: "GodotKeyboardObserverTests",
            dependencies: ["GodotKeyboardObserver"]
        ),
    ]
)STEP3: Swift で処理を書く
2つのファイルを用意した
- Godotから見える @GodotをつけたObjectのGodotKeyboardObserver.swift
- Godotからは見えない UIKitを用いた KeyboardObserver.swift
- SwiftGodotのドキュメントを参考に。
import SwiftGodot
#initSwiftExtension(
    cdecl: "keyboard_observer",
    types: [
        GodotKeyboardObserver.self,
    ]
)
@Godot
public final class GodotKeyboardObserver: Object {
    @Signal
    var changedKeyboardHeight: SignalWithArguments<Float, Double>
    #if os(iOS)
    // このプロパティは Godot から見えないが、Swift 側で保持する
    private var nativeObserver: KeyboardObserver?
    #endif
    @Callable
    func start() {
        #if os(iOS)
        nativeObserver = KeyboardObserver(target: self)
        #endif
    }
    @Callable
    func stop() {
        #if os(iOS)
        nativeObserver?.remove()
        nativeObserver = nil
        #endif
    }
    @Callable
    func notifyKeyboardHeight(_ height: Float, duration: Double) {
        changedKeyboardHeight.emit(max(height, 0), duration)
    }
}UIKitの処理は @Godot とは分ける必要がある
#if canImport(UIKit)
import UIKit
final class KeyboardObserver {
    weak var godotTarget: GodotKeyboardObserver?
    init(target: GodotKeyboardObserver) {
        godotTarget = target
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(keyboardWillChangeFrame(_:)),
            name: UIResponder.keyboardWillChangeFrameNotification,
            object: nil
        )
    }
    @MainActor
    @objc func keyboardWillChangeFrame(_ notification: Notification) {
        guard let userInfo = notification.userInfo,
              let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect,
            let duration: TimeInterval = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double else { return }
        let screenHeight = UIScreen.main.bounds.height
        let keyboardHeight = Float(screenHeight - keyboardFrame.origin.y)
        godotTarget?.notifyKeyboardHeight(keyboardHeight, duration: duration)
    }
    func remove() {
        NotificationCenter.default.removeObserver(self)
    }
}
#endifSTEP4: GDExtension ファイルを用意する
res://addons/ios/keyboard_observer/keyboard_observer.gdextension に配置。
- macos用に向けてリリースしなくても- .dylibを用意しておかないと、Macで開発中にエラーが出るようになる
[configuration]
entry_symbol = "keyboard_observer"
compatibility_minimum = 4.4
reloadable = true
[libraries]
ios.debug = "res://addons/swift/keyboard_observer/ios/GodotKeyboardObserver.framework"
ios.release = "res://addons/swift/keyboard_observer/ios/GodotKeyboardObserver.framework"
macos.debug = "res://addons/swift/keyboard_observer/macos/libGodotKeyboardObserver.dylib"
macos.release = "res://addons/swift/keyboard_observer/macos/libGodotKeyboardObserver.dylib"
[dependencies]
ios.debug = { "res://addons/swift/keyboard_observer/ios/SwiftGodot.framework" : "" }
ios.release = { "res://addons/swift/keyboard_observer/ios/SwiftGodot.framework" : "" }
macos.debug =  { "res://addons/swift/keyboard_observer/macos/libSwiftGodot.dylib" : "" }
macos.release =  { "res://addons/swift/keyboard_observer/macos/libSwiftGodot.dylib" : "" }STEP5: framework をビルド
- Mac用ビルドは swift build -c releaseで行う- ./build内に配置される
 
- iOS用の framework は Xcodeから、BuildをAny iOS Deviceに設定した状態でProduct>Archiveをして、Organizer から取得する。- もしくは xcodebuildで行う
 
- もしくは 
- 生成されたファイルを Godotプロジェクトに配置する
Mac 用に生成
swift build を利用
// package.swift があるところまで移動する
$ cd ~/Godot/godot_project/SwiftPackages/GodotKeyboardObserver/
// ビルド
$ swift build -c release
// Godotプロジェクトの `res://addons/` にコピーする
$ cp .build/arm64-apple-macosx/release/libGodotKeyboardObserver.dylib ../../Godot/addons/swift/keyboard_observer/macos
// SwiftGodotも必要
$ cp .build/arm64-apple-macosx/release/libSwiftGodot.dylib ../../Godot/addons/swift/keyboard_observer/macosswift build の代わりに xcodebuild を使うと .framework が得られるが、この .framework を res://addons に配置してもうまく動作しなかった。
iOS 用に生成
GodotKeyboardObserver.framework と SwiftGodot.framework は Xcodeから生成した。
Package.swift をXcodeで開き、 Any iOS Device (arm64) を選択して Product > Archive
Archive が完了したら Organizer が開かれるので Distribute Content を選択
適当な場所に保存。フォルダの中に Products > usr > local > lib に GodotKeyboardObserver.framework と SwiftGodot.framework が入っている。
これを Godot の .gdextension に書いたパスの場所に配置。
- res://addons/swift/keyboard_observer/ios/GodotKeyboardObserver.framework
- res://addons/swift/keyboard_observer/ios/SwiftGodot.framework
iOS向けにターミナルからビルド
コマンドで行う場合は次のように xcodebuildからできた。
$ xcodebuild build \
  -scheme GodotKeyboardObserver \
  -destination "generic/platform=iOS"次の場所に配置されている。
/Users/ユーザ名/Library/Developer/Xcode/DerivedData/GodotKeyboardObserver-hcvreboufjlqafctvhjwehfmzeiu/Build/Products/Debug-iphoneos/PackageFrameworks/GodotKeyboardObserver.framework-derivedDataPath で output 先を変更することもできる。
$ xcodebuild build \
  -scheme GodotKeyboardObserver \
  -destination "generic/platform=iOS" \
  -derivedDataPath ./build_outputbuild_output/Build/Products/Debug-iphoneos/PackageFrameworks にある。
STEP6: Godot 側で使う
ここまでで、iOSで定義した initSwiftExtensionで指定した types が使えるようになっている。
var _observer: GodotKeyboardObserver
func _ready() -> void:
    if _observer == null && ClassDB.class_exists("GodotKeyboardObserver"):
        _observer = ClassDB.instantiate("GodotKeyboardObserver")
        # Swift で作ったシグナルを使う
        _observer.changed_keyboard_height.connect(_on_changed_keyboard_height)
        # Swift で作った関数を使う
        _observer.start()
        print("GodotKeyboardObserver あるよ", _observer)
    else:
        print("GodotKeyboardObserver ないよ")
func _on_changed_keyboard_height(height: float, duration: float):
    print("キーボードの高さ変わったよ!", height, duration)STEP7: iOS に export
実機でビルドするために、Godotから iOS にExport する。
- Exportされた- .xcodeprojを開き- addonsの中に framework が入っていればok
- しかし、ここでGodotでモバイル向けの環境設定をするで行っているXcodeの設定をするとプラグインが無効になる。- 書き出されたそのままの状態でならうまくいく。
- 毎回Exportし直すのは大変なのでTODO
 
課題
- SwiftPackageの中身を書き換えたら、この手順を全て踏まないといけないってこと?
- Godotのコードを変えたら毎回 iOS に Export し直さないといけない。
- UIKitの部分をXcodeで実機確認しながら作りたい。
おわりに
まだ課題はたくさんあるけれど、とりあえず無事に UIKit を扱うことができそうだ。

 
   
   
  