最終確認日

GodotでUIKitを使いたい!

環境

実装手順

  1. Swift Packageを作る
  2. SwiftPackageSwiftGodotを導入する
  3. コードを書く
  4. Godotプロジェクトの res://addons/ 内に .gdextensionファイルを配置
  5. SwiftPackageGodotを紐づける
    • 現在は.framework.dylib を書き出して res://addons/ 内に配置する方法にしている。
  6. Godot上でエラーが出ないことを確認し、Swiftのコードを呼び出す
  7. iOS に Export して.xcodeproj を開き、実機で確認

STEP1: Swift Package を作る

既存のGodotプロジェクトに組み込み形で作ってみる。

  • SwiftPackage を配置するフォルダを作る
  • swift package initpackage.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

Pacakge.swift
// 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 をつけた ObjectGodotKeyboardObserver.swift
  • Godotからは見えない UIKitを用いた KeyboardObserver.swift
  • SwiftGodotドキュメントを参考に。
GodotKeyboardObserver.swift
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から、BuildAny 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 が得られるが、この .frameworkres://addons に配置してもうまく動作しなかった。

iOS 用に生成

GodotKeyboardObserver.frameworkSwiftGodot.frameworkXcodeから生成した。

Package.swiftXcodeで開き、 Any iOS Device (arm64) を選択して Product > Archive

GodotでUIKitの機能を使いたい!-1753793309925

Archive が完了したら Organizer が開かれるので Distribute Content を選択

GodotでUIKitの機能を使いたい!-1753793381903 Built Products を選択

GodotでUIKitの機能を使いたい!-1753793414497

適当な場所に保存。フォルダの中に Products > usr > local > libGodotKeyboardObserver.frameworkSwiftGodot.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

GodotでUIKitの機能を使いたい!-1753693843956

課題

  • SwiftPackageの中身を書き換えたら、この手順を全て踏まないといけないってこと?
  • Godotのコードを変えたら毎回 iOS に Export し直さないといけない。
  • UIKitの部分をXcodeで実機確認しながら作りたい。

おわりに

まだ課題はたくさんあるけれど、とりあえず無事に UIKit を扱うことができそうだ。

サイトアイコン
公開日
更新日