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.swift
STEP 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)
}
}
#endif
STEP4: 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/macos
swift 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_output
build_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 を扱うことができそうだ。
