最終確認日

Godot-iOSキーボードの高さの変化を通知するネイティブプラグインを作る-失敗

前置き

このノートは失敗編。

成功したらちゃんとしたノートを書く

背景

Godotにはキーボードの高さを取得する関数はあるが、キーボードの高さの変化を取得する方法がないため、GodotでiOSのキーボードに追従するツールバーを実装するためにはiOSプラグインの実装が必要だった。

環境

公式ドキュメント

目標

iOS では UIKeyboardWillChangeFrameNotificationでキーボードの高さの変化を監視することができる。 なのでGodot上でこの値を監視できるようなiOSプラグインを作る。

ちなみにiOSネイティブアプリではUIKeyboardLayoutGuideという便利なものがiOS15より使えるようになっているよ。

実装

iOS 側

.xcframework を作る。

  • 新規 Xcode プロジェクト → Framework(iOS)
  • Product Name: GodotKeyboardPlugin
  • 言語: Swift

Godot-iOSキーボードの高さの変化を通知するネイティブプラグインを作る-1753444772094

Scripts フォルダを作成してその中にスクリプトを置くようにした。DocC はドキュメント用なのでその中のResources には置かないようにする。

  • KeyboardObserver.swift
  • GodotBridge.h
  • GodotBridge.mm

KeyboardObserver.swift

KeyboardObserver.swift
import UIKit

@objcMembers
public class KeyboardObserver: NSObject {
    
    public class var shared: KeyboardObserver {
        struct Static {
            static let instance: KeyboardObserver = KeyboardObserver()
        }
        return Static.instance
    }

    public var currentKeyboardHeight: Float = 0

    override init() {
        super.init()
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(keyboardWillChangeFrame(_:)),
            name: UIResponder.keyboardWillChangeFrameNotification,
            object: nil
        )
    }

    func keyboardWillChangeFrame(_ notification: Notification) {
        guard let userInfo = notification.userInfo,
              let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else {
            return
        }
        let screenHeight = UIScreen.main.bounds.height
        let keyboardHeight = Float(screenHeight - keyboardFrame.origin.y)
        currentKeyboardHeight = max(keyboardHeight, 0)
    }
}

GodotBridge.h

GodotBridge.h
#import <Foundation/Foundation.h>

@interface GodotBridge : NSObject
+ (void)initializePlugin;
+ (float)getKeyboardHeight;
@end

GodotBridge.mm

GodotBridge.mm
#import "GodotBridge.h"
#import "GodotKeyboardPlugin-Swift.h"

@implementation GodotBridge

+ (void)initializePlugin {
//    [KeyboardObserver shared]; // 初期化して監視開始
}

+ (float)getKeyboardHeight {
    return [KeyboardObserver shared].currentKeyboardHeight;
}

@end

// Godot が呼び出すプラグイン初期化関数
// この関数名を `.gdip` に書く必要がある
extern "C" void godot_ios_plugin_init() {
    [GodotBridge initializePlugin];
}

XCFramework をビルドする

generate_xcframework.sh を用意する

#!/bin/bash

set -e

### === 設定 ===

SCHEME="GodotKeyboardPlugin"
OUTPUT_NAME="GodotKeyboardPlugin"
ARCHIVE_DIR="./build"
OUTPUT_DIR="./output"

# 出力ファイル名
RELEASE_XC="$OUTPUT_DIR/${OUTPUT_NAME}.release.xcframework"
DEBUG_XC="$OUTPUT_DIR/${OUTPUT_NAME}.debug.xcframework"

# .framework 内の実体バイナリの仮名
FRAMEWORK_BINARY_NAME="$OUTPUT_NAME"  # 通常 .framework の中身と一致

### ============

if [[ -z "$ARCHIVE_DIR" || "$ARCHIVE_DIR" == "/" || "$ARCHIVE_DIR" == "." || "$ARCHIVE_DIR" == ".." ]]; then
    echo "❌ ARCHIVE_DIR が不正です → '$ARCHIVE_DIR'"
    exit 1
fi

if [[ -d "$ARCHIVE_DIR" ]]; then
    echo "アーカイブディレクトリ '$ARCHIVE_DIR' を削除します..."
    rm -rf "$ARCHIVE_DIR"
fi

if [[ -d "$OUTPUT_DIR" ]]; then
    echo "出力ディレクトリ '$OUTPUT_DIR' を削除します..."
    rm -rf "$OUTPUT_DIR"
fi

mkdir -p "$ARCHIVE_DIR" "$OUTPUT_DIR"

echo "📦 Archiving RELEASE (device/simulator)..."

# Archive: Release - iOS
xcodebuild archive \
  -scheme "$SCHEME" \
  -configuration Release \
  -destination "generic/platform=iOS" \
  -archivePath "$ARCHIVE_DIR/${SCHEME}-iOS-Release.xcarchive" \
  SKIP_INSTALL=NO \
  BUILD_LIBRARY_FOR_DISTRIBUTION=YES

# Archive: Release - Simulator
xcodebuild archive \
  -scheme "$SCHEME" \
  -configuration Release \
  -destination "generic/platform=iOS Simulator" \
  -archivePath "$ARCHIVE_DIR/${SCHEME}-Sim-Release.xcarchive" \
  SKIP_INSTALL=NO \
  BUILD_LIBRARY_FOR_DISTRIBUTION=YES

# 抽出関数
extract_a_from_framework() {
    archive_path=$1
    platform_name=$2
    out_dir=$3

    framework_path="$archive_path/Products/Library/Frameworks/${SCHEME}.framework"
    binary_path="$framework_path/$FRAMEWORK_BINARY_NAME"
    headers_path="$framework_path/Headers"

    cp "$binary_path" "$out_dir/lib${SCHEME}.a"
    cp -R "$headers_path" "$out_dir/include"
}

# 抽出先
RELEASE_IOS_DIR="$ARCHIVE_DIR/extracted-ios-release"
RELEASE_SIM_DIR="$ARCHIVE_DIR/extracted-sim-release"
mkdir -p "$RELEASE_IOS_DIR" "$RELEASE_SIM_DIR"

extract_a_from_framework "$ARCHIVE_DIR/${SCHEME}-iOS-Release.xcarchive" "ios" "$RELEASE_IOS_DIR"
extract_a_from_framework "$ARCHIVE_DIR/${SCHEME}-Sim-Release.xcarchive" "sim" "$RELEASE_SIM_DIR"

# Create XCFramework (RELEASE)
xcodebuild -create-xcframework \
  -library "$RELEASE_IOS_DIR/lib${SCHEME}.a" \
  -headers "$RELEASE_IOS_DIR/include" \
  -library "$RELEASE_SIM_DIR/lib${SCHEME}.a" \
  -headers "$RELEASE_SIM_DIR/include" \
  -output "$RELEASE_XC"

echo "✅ RELEASE xcframework created: $RELEASE_XC"

### === DEBUG ===

echo "📦 Archiving DEBUG (device/simulator)..."

# Archive: Debug - iOS
xcodebuild archive \
  -scheme "$SCHEME" \
  -configuration Debug \
  -destination "generic/platform=iOS" \
  -archivePath "$ARCHIVE_DIR/${SCHEME}-iOS-Debug.xcarchive" \
  SKIP_INSTALL=NO \
  BUILD_LIBRARY_FOR_DISTRIBUTION=YES

# Archive: Debug - Simulator
xcodebuild archive \
  -scheme "$SCHEME" \
  -configuration Debug \
  -destination "generic/platform=iOS Simulator" \
  -archivePath "$ARCHIVE_DIR/${SCHEME}-Sim-Debug.xcarchive" \
  SKIP_INSTALL=NO \
  BUILD_LIBRARY_FOR_DISTRIBUTION=YES

# 抽出先
DEBUG_IOS_DIR="$ARCHIVE_DIR/extracted-ios-debug"
DEBUG_SIM_DIR="$ARCHIVE_DIR/extracted-sim-debug"
mkdir -p "$DEBUG_IOS_DIR" "$DEBUG_SIM_DIR"

extract_a_from_framework "$ARCHIVE_DIR/${SCHEME}-iOS-Debug.xcarchive" "ios" "$DEBUG_IOS_DIR"
extract_a_from_framework "$ARCHIVE_DIR/${SCHEME}-Sim-Debug.xcarchive" "sim" "$DEBUG_SIM_DIR"

# Create XCFramework (DEBUG)
xcodebuild -create-xcframework \
  -library "$DEBUG_IOS_DIR/lib${SCHEME}.a" \
  -headers "$DEBUG_IOS_DIR/include" \
  -library "$DEBUG_SIM_DIR/lib${SCHEME}.a" \
  -headers "$DEBUG_SIM_DIR/include" \
  -output "$DEBUG_XC"

echo "✅ DEBUG xcframework created: $DEBUG_XC"

ターミナルから実行する

sh generate_xcframework.sh

GodotKeyboardPlugin.xcframework が作られればおk。

このとき GodotKeyboardPlugin.xcframework の内部に .framework になっている場合はGodot上で読み込みができないので注意。

# 失敗!

├── GodotKeyboardPlugin.debug.xcframework
│   ├── Info.plist
│   ├── ios-arm64
│   │   └── GodotKeyboardPlugin.framework  # これは認識できない
│   └── ios-arm64_x86_64-simulator
│       └── GodotKeyboardPlugin.framework  # これは認識できない

.a ファイルが入っていれば成功。

# ok!

├── GodotKeyboardPlugin.debug.xcframework
│   ├── Info.plist
│   ├── ios-arm64
│   │   ├── Headers
│   │   └── libGodotKeyboardPlugin.a
│   └── ios-arm64_x86_64-simulator
│       ├── Headers
│       └── libGodotKeyboardPlugin.a
└── GodotKeyboardPlugin.release.xcframework
    ├── Info.plist
    ├── ios-arm64
    │   ├── Headers
    │   └── libGodotKeyboardPlugin.a
    └── ios-arm64_x86_64-simulator
        ├── Headers
        └── libGodotKeyboardPlugin.a

フォルダの中身

GodotKeyboardPlugin のフォルダの中は次の通りになった。

.
├── build
│   ├── GodotKeyboardPlugin-iOS.xcarchive
│   └── GodotKeyboardPlugin-Simulator.xcarchive
├── generate_xcframework.sh
├── GodotKeyboardPlugin
│   ├── GodotKeyboardPlugin.docc
│   ├── GodotKeyboardPlugin.h
│   └── Scripts
├── GodotKeyboardPlugin.xcodeproj
│   ├── project.pbxproj
│   ├── project.xcworkspace
│   └── xcuserdata
└── xcframework
    └── GodotKeyboardPlugin.xcframework

.gdip ファイルを作る

Godotと紐づけるために godot_keyboard_plugin.gdip ファイルを作る。

このファイルは Godot プラグインを認識・読み込ませるための設定ファイルとなる。

[config]
name = "GodotKeyboardPlugin"
binary = "GodotKeyboardPlugin.xcframework"
initialization = "godot_ios_plugin_init"

[dependencies]
linked = []
embedded = []
system = ["UIKit.framework", "Foundation.framework"]

[linker_flags]
linker_flags = ["-ObjC"]
セクション キー 内容
[config] name プラグインの表示名
binary .xcframework のファイル名
initialization Godot が呼び出す C 関数名(Objective-C 側で extern "C" された godot_ios_plugin_init()
[dependencies] system 使用している Apple のフレームワーク。Swift/Obj-C で UIKit / Foundation を使用しているため記載
[linker_flags] -ObjC Objective-C カテゴリやメソッドを強制リンクするため(Swift → Obj-C ブリッジのために必要)

Godot 側

GodotKeyboardPlugin.xcframeworkGodotKeyboardPlugin.gdip を配置する。

res://ios/plugins/GodotKeyboardPlugin/
├── GodotKeyboardPlugin.gdip
├── GodotKeyboardPlugin.xcframework

だめ!!!!!!でした

ERROR: Invalid plugin config file godot_keyboard_plugin/godot_keyboard_plugin.gdip

【Godot】ATTダイアログを表示するプラグインを作る | コドモとアプリ

考える

必要なこと

  • Xcodeプロジェクトを作成する
    • Static Library
    • iOS DevelopmentTarget の設定
    • TARGET > Build Settings > Header Search Paths
      • Godot 本体の godot/platform/ios を設定する
  • .gdip を作る
  • SConsを使ってGodotエンジン本体のiOSヘッダーを抽出する
    • これは Godot.app からは取得できないので、godotengine/godot - GithHubから取得
    • bin フォルダを作る
    • SContruct ファイルから .a を作る

https://github.com/godotengine/godot/tree/master/platform/ios この中身が必要ってことよね これが SwiftPackage 的な感じになってればいいのになー

  • Compiling for iOS
  • Godot エンジンのヘッダファイルをライブラリの依存関係として追加
    • かいてあるじゃん!
  • naithar/godot_ios_plugin
    • これ用の .xcconfig はよさそう
      • .xcconfig は static library だとうまく動かなさそう
        • framework にする?
          • でもやっぱり framework からは無理そう

Xcode.app のエイリアスを貼っておく。(実際にはXcode16.2.appが使われる)

sudo ln -s /Applications/Xcode16.2.app /Applications/Xcode.app

シンボル一覧の確認

nm -gU build/extracted_static_libs/GodotKeyboardPlugin_ios-arm64.a 
0000000000004ee8 T _$s19GodotKeyboardPlugin0B8ObserverC07currentB6HeightSfvMTj

0000000000005fc4 S _$s19GodotKeyboardPlugin0B8ObserverC07currentB6HeightSfvMTq

0000000000004e90 T _$s19GodotKeyboardPlugin0B8ObserverC07currentB6HeightSfvgTj

0000000000005fb4 S _$s19GodotKeyboardPlugin0B8ObserverC07currentB6HeightSfvgTq

0000000000008168 S _$s19GodotKeyboardPlugin0B8ObserverC07currentB6HeightSfvpMV

0000000000004ebc T _$s19GodotKeyboardPlugin0B8ObserverC07currentB6HeightSfvsTj

0000000000005fbc S _$s19GodotKeyboardPlugin0B8ObserverC07currentB6HeightSfvsTq

0000000000004f14 T _$s19GodotKeyboardPlugin0B8ObserverC23keyboardWillChangeFrameyy10Foundation12NotificationVFTj

0000000000005fcc S _$s19GodotKeyboardPlugin0B8ObserverC23keyboardWillChangeFrameyy10Foundation12NotificationVFTq

0000000000004e78 T _$s19GodotKeyboardPlugin0B8ObserverC6sharedACvgZTj

0000000000005fac S _$s19GodotKeyboardPlugin0B8ObserverC6sharedACvgZTq

0000000000004440 T _$s19GodotKeyboardPlugin0B8ObserverCACycfC

0000000000004464 T _$s19GodotKeyboardPlugin0B8ObserverCACycfc

0000000000004be8 T _$s19GodotKeyboardPlugin0B8ObserverCMa

0000000000005f78 S _$s19GodotKeyboardPlugin0B8ObserverCMn

0000000000005cf8 S _$s19GodotKeyboardPlugin0B8ObserverCMo

0000000000004e5c T _$s19GodotKeyboardPlugin0B8ObserverCMu

000000000000c240 S _$s19GodotKeyboardPlugin0B8ObserverCN

0000000000004b70 T _$s19GodotKeyboardPlugin0B8ObserverCfD

0000000000005cb0 S _GodotKeyboardPluginVersionNumber

0000000000005c70 S _GodotKeyboardPluginVersionString

000000000000c1b8 S _OBJC_CLASS_$_GodotBridge

000000000000c240 S _OBJC_CLASS_$__TtC19GodotKeyboardPlugin16KeyboardObserver

000000000000c190 S _OBJC_METACLASS_$_GodotBridge

000000000000c2c8 D _OBJC_METACLASS_$__TtC19GodotKeyboardPlugin16KeyboardObserver

00000000000040b0 T _godot_ios_plugin_init
  • 調査した感じ libgodot.ios.a を使ってGodot 側のライブラリをiOSのプラグイン側に導入しないといけない。

これをGodot と iOS で紐づけるために libgodot.ios.a が必要になる。

extern "C" void godot_ios_plugin_init() {
    [GodotBridge initializePlugin];
}

そのために Godot本体から SConsを使ってそのライブラリを導入する必要がある。

# 重いからなるべく軽量にクローンする
git clone --depth=1 --branch 4.4-stable --single-branch https://github.com/godotengine/godot.git


scons platform=ios target=template_debug arch=arm64

もしかして .xcframework がここで入手できるなら、その .xcframework をXcodeにぶちこんでどうにかならんかね?

  • misc/dist/ios_xcode の中を見る
    • おお これ使ってできるんでない?
    • godot_ios.xcodeproj は壊れている
      • これは関係ないみたい
  • bin の中に libgodot.ios.template_debug.arm64.a があった!
    • Link Binary with Library に追加

    • Godot-iOSキーボードの高さの変化を通知するネイティブプラグインを作る-失敗-1753601698696

    • Godot C++ API のヘッダ も必要らしい

  • やっぱり GitHub サブモジュールとするのがいいのかね

サブモジュールでやる

ちゃんと公式に合わせた形でやるしかない。

ヘッダーファイルの抽出は大変なのでサブモジュールとしておいて、そこから HEADER_SEARCH_PATH に設定するほうが確実。

直接 git submodule add を使うと重たいので shallow clone を使う。

cd GodotPlugin/GodotKeyboardPlugin

// サブモジュール化したいリポジトリを取得
git clone --depth=1 --branch 4.4.1-stable https://github.com/godotengine/godot.git godot

// .gitmodules を追加
touch .gitmodules

// 書いた後に submodule init をしてサブモジュールとして認識させる (.git/configファイルに追加される)
git submodule init

// サブモジュールのアップデートが必要なとき
git submodule update --init --recursive --recommend-shallow --depth 1 

.gitmodules

[submodule "godot"]
    path = godot
    url = https://github.com/godotengine/godot.git
    shallow = true
    branch = 4.4.1-stable

Base.xcconfig に追加

HEADER_SEARCH_PATHS = \
  $(SRCROOT)/godot \
  $(SRCROOT)/godot/core \
  $(SRCROOT)/godot/core/os \
  $(SRCROOT)/godot/scene \
  $(SRCROOT)/godot/platform \
  $(SRCROOT)/godot/platform/iphone \
  $(SRCROOT)/godot/main \
  $(SRCROOT)/godot/servers \
  $(SRCROOT)/godot/modules \
  $(SRCROOT)/godot/drivers \
  $(SRCROOT)/godot/thirdparty
# godot フォルダに移動
cd GodotPlugin/GodotKeyboardPlugin/godot

# scons を実行する
# これで libgodot.ios.template_debug.arm64.a を得ることができる
scons platform=ios target=template_debug arch=arm64

# リリース用も作っておく
scons platform=ios target=template_release arch=arm64

Debug.xcconfig にファイルの指定

// リンクする静的ライブラリを明示(拡張子不要)
// ここに書かない場合には Link Binary With Libraries に追加する必要がある
OTHER_LDFLAGS = \
  $(inherited) \
  -lgodot.ios.template_debug.arm64

Release.xcconfig にファイルの指定

// リンクする静的ライブラリを明示(拡張子不要)
OTHER_LDFLAGS = \
  $(inherited) \
  -lgodot.ios.template_release.arm64

もし OTHER_LDFLAGS をかかない場合には lgodot.ios.template_debug.arm64.a ファイルを LInk Binary With Libraries に登録しておく必要がある。(書いた場合は不要)

xcodebuild -create-xcframework \
  -library ./build/Release-iphoneos/GodotKeyboardPlugin.a \
  -headers ./include \
  -library ./build/Release-iphonesimulator/GodotKeyboardPlugin.a \
  -headers ./include \
  -output GodotKeyboardPlugin.xcframework

&quot;Could not build module Foundation&quot; error when compiling for iOS with SDK 15.2 #56092

あかん。

とりあえずまずは objcでかいて、それがGodotで動くことを確認してからSwiftにしよう。 -> これももううまくいかぬ...どうして

  • zt-pawer/SwiftGodotIosPlugins
    • これためす
    • SwiftGodot を使っているね
      • Swift 側にそれを使うんだ
      • Package.swift もある
      • build.sh 実行してみた
        • 失敗したのでGPTに書き直してもらった
      • エラー出て GameCenter.framework ができていない
        • SwiftPackage を Xcode で開いてみるとエラーが出ている
        • Trust & Enable を実行しないとダメかも
        • したけどだめ
      • iOS をターゲットにして Archive したら GameCenter.framework をゲットした
      • Mac もターゲットにしてやってみる
  • res://addons/iosplugins/gamecenter.gdextension
    • res://addons/iosplugins/ios の中に GameCenter.frameworkSwiftGodot.framework をいれる
    • mac 版も入れないとだめ
      • これは framework だとダメ
      • swift build --configuration release --product GameCenter --build-path .build-macos を実行すると .build-macos/arm64-apple-macosx/release/ に書き出された!

gamecenter.gdextension の中身

[configuration]
entry_symbol = "gamecenter"
compatibility_minimum = 4.2
relodable = true

[libraries]
macos.debug = "res://addons/iosplugins/macos/libGameCenter.dylib"
macos.release = "res://addons/iosplugins/macos/libGameCenter.dylib"
ios.debug = "res://addons/iosplugins/ios/GameCenter.framework"
ios.release = "res://addons/iosplugins/ios/GameCenter.framework"

[dependencies]
macos.debug = {"res://addons/iosplugins/macos/libSwiftGodot.dylib" : ""}
macos.release = {"res://addons/iosplugins/macos/libSwiftGodot.dylib" : ""}
ios.debug = {"res://addons/iosplugins/ios/SwiftGodot.framework" : ""}
ios.release = {"res://addons/iosplugins/ios/SwiftGodot.framework" : ""}

それでこれ

.
├── addons
│   └── iosplugins
│       ├── gamecenter.gdextension
│       ├── gamecenter.gdextension.uid
│       ├── ios
│       │   ├── GameCenter.framework
│       │   └── SwiftGodot.framework
│       └── macos
│           ├── libGameCenter.dylib
│           └── libSwiftGodot.dylib

これで無事にGodot上では動いていた!!!!!!

正攻法かわからんが、とりあえずSwiftGodot + SwiftPackage + GDExtensionでできた。 ビルドがイマイチうまくいっていないので、うまくいったらまとめる

-> Godot-iOSキーボードの高さの変化を通知するネイティブプラグインを作る

SwiftGodotを使ってやってみるぞ!!

  • .gdextension
  • Swift Package Manager でやるぞい
  • プラグイン.framework
  • SwiftGodot.framework

関連ノート

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