Godot-iOSキーボードの高さの変化を通知するネイティブプラグインを作る
はじめに
Godot-iOSキーボードの高さの変化を通知するネイティブプラグインを作る-失敗を経て。
このノートからさらに改良した場合はまた新しいノートを書く -> 書いたら貼る
背景
Godotにはキーボードの高さを取得する関数はあるが、キーボードの高さの変化を取得する方法がないため、GodotでiOSのキーボードに追従するツールバーを実装するためにはiOSプラグインの実装が必要だった。
環境
- Godot 4.4.stable
- Xcode 16.2
参考リンク
- Creating iOS plugins - Godot Docs
- 失敗編で使っていたやり方。そのうちこっちでも再チャレンジする。
- zt-pawer/SwiftGodotIosPlugins
- 激感謝
- migueldeicaza/SwiftGodot
目標
iOS では UIKeyboardWillChangeFrameNotificationでキーボードの高さの変化と、キーボード表示までのアニメーション時間を取得することができる。
今回は Godot 側にこの値を伝える iOS プラグインを作るぞい。
利用するもの
- GDExtension
- SwiftPackage
- SwiftGodot
実装手順
- SwiftGodotを含むSwiftPackageを作る。
- Godot側の
res://addons/
内にプラグイン名.gdextension
を作る。 - SwiftPackage内にiOS側の処理を書く。
- 作ったSwiftPackageから
.framework
と.dylib
を作る。 - 4で作った
.framework
と.dylib
をres://addons
に配置する。 - Godot側でSwiftのコードを使う
- iOS にエクスポートする
- エクスポートされた
.xcodeproj
をXcodeで開き、実機でビルドを行う
実装
SwiftPackageをつくる
GodotでUIKitを使いたい!の方に書いた。
.gdextensionの書き方と、.framework
や .dylib
の配置方法などもここでは割愛。
キーボードの値を通知する
SwiftGodotのドキュメントを参考に。
Godotから見える @Godot
をつけた Object
の GodotKeyboardObserver.swift
を作り、その中で、 Godotからは見えない UIKitを用いた KeyboardObserver.swift
を呼び出す形になる。
Mac でもビルドするためにひどいコードになっているので、あとで直す。
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
Godotでつかう
Godotから次のようにSwiftのコードを呼び出すことができる。
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)
_on_changed_keyboard_height(height: float, duration: float)
は、キーボードの高さが変わる時に呼ばれる。(名前がちょっと良くないかも)_will_change_keyboard_height
とかのがいいのか、シグナルは _on
に統一したいような...
座標変換
この height
は、iPhone 上での論理ピクセル値になる。(iPhone 13 mini では height は 336 を返す)
つまり、DisplayServer.screen_get_scale()
をこの height
にかけてあげると、物理ピクセルが取得できる。そのあとさらに Viewport 座標に直す必要がある。
- UIKit 上の座標からスクリーン座標に直す
- スクリーン座標をViewport座標に直す
座標系の話は Godotでモバイル向けの画面サイズ取得を参考にしてね。
func _on_changed_keyboard_height(height: float, duration: float) -> void:
# iOS端末の座標 -> スクリーン座標 -> viewport座標への変換が必要.
var target_position := position
var screen_size := DisplayServer.screen_get_size()
var screen_scale := DisplayServer.screen_get_scale()
var viewport_size := get_viewport().get_visible_rect().size
var scale_y := screen_size.y / viewport_size.y
# スクリーン座標に変換
var keyboard_height_in_screen := height * screen_scale
# スクリーン座標を Viewport座標に変換
var keyboard_height := keyboard_height_in_screen / scale_y
# キーボードの上端の座標を取得
var keyboard_top_y := viewport_size.y - keyboard_height
使い方
あとはアニメーションを実装するだけ。残りは次のノートを参照してね。
- GodotでiOSのキーボードに追従するツールバーを実装する
関連ノート
- Godotでモバイル端末のキーボードの高さの取得する
- GodotでiOSのキーボードに追従するツールバーを実装する
- GodotでiOS用プラグインのgodot-ios-pluginsを使いたい
