Godot ライブラリのヘッダーファイルを公開して iOS アプリケーションのコードから定義を参照する

2023/11/05に公開

Godot iOS アプリケーションの main 関数のありかを探るの最後に書いたように、Godot のライブラリは外部から参照される必要がないため、Godot の内部で定義されているクラスの情報などを公開していません。より具体的に言うと、Godot からエクスポートされる xcframework はヘッダーファイルを公開していませんし、アプリケーションのコードからインポートすらできません。

今回は、Godot からエクスポートされた xcframework をアプリケーションのコードからインポートできるようにして、Godot 側で定義されているクラスの情報をアプリケーション側で見えるようにします。

1. Godot からエクスポートされた xcframework の構成を確認する

Godot から godot_ios という名前でプロジェクトをエクスポートした場合の xcframework の構成を確認します。

プロジェクト名  でエクスポートされた Godot の実装を含む xcframework
プロジェクト名 godot_ios でエクスポートされた Godot の実装を含む xcframework

xcframework のディレクトリの中には、Info.plist、実機向けのライブラリ、シミュレータ向けのライブラリが格納されています。

Info.plist の中身を確認します。

 のプレビュー
Info.plist のプレビュー

AvailableLibraries の中に実機向け、シミュレータ向けのライブラリの情報が書かれています。眺めてみると、Item 0 が実機向け、Item 1 がシミュレータ向けであることが分かります。

この構成に手を加えて、Godot のライブラリをインポートして内部の定義が見える状態にします。

2. 公開するヘッダー情報を配置する

xcframework は内部に含まれるものの種類によって構成が変わってきますので、以下の手順は静的ライブラリ(*.a)から構成される xcframework に対してのみ成り立ちます。

2-1. Info.plist にヘッダー情報の場所を記述する

Info.plist にヘッダー情報が格納されている場所についての情報を追記します。以下のように HeadersPath という項目を追加をして、値を Headers とします。「Headers にヘッダー情報があるよ」ということを伝えています。

 のプレビュー( 追記後)
Info.plist のプレビュー(HeadersPath 追記後)

2-2. HeadersPath に指定した名前のディレクトリを作成する

指定した名前 Headers のディレクトリを作成します。ディレクトリはライブラリ(*.a)と同じ階層に作成します。

ライブラリ()のある階層に指定した名前のディレクトリを作成する
ライブラリ(*.a)のある階層に指定した名前のディレクトリを作成する

2-3. Godot ライブラリのヘッダーファイルをコピーする

作成したディレクトリに、Godot の公開する情報に関するヘッダーファイルを配置します。godotengine/godotplatform/ios にあるヘッダーファイルを、作成したディレクトリにコピーします。

 にあるヘッダーファイルをコピーする
platform/ios にあるヘッダーファイルをコピーする

2-4. module.modulemap と umbrella header を作成する

指定したディレクトリにヘッダーファイルを配置しただけでは、情報は公開されません。module.modulemap の記述によってインポートができるようになり、公開するヘッダー情報を記述することによって、インポートした側で情報が見えるようになります。今回は複数のヘッダーファイルを公開したいので、umbrella header に情報をまとめました。

module.modulemap は以下のように記述しました。この後に作成する unbrella header ファイルの名前を umbrella.h と指定しています。

module.modulemap
module Godot {
  umbrella header "umbrella.h"

  export *
  module * { export * }
}

umbrella.h は以下のように記述しました。ここではアプリケーション側での動作の確認の為に最低限必要なヘッダー情報を記述しています。

umbrella.h
#import "app_delegate.h"
#import "display_layer.h"
#import "godot_app_delegate.h"
#import "godot_view.h"
#import "view_controller.h"

 と  の作成後
module.modulemapumbrella.h の作成後

ここまででアプリケーション側に情報を公開する準備はできましたが、アプリケーション側にはまだコードがないので、読み込みを確認するためのコードを用意します。

3. Swift ファイルを作成し、インポートできることを確認する

Xcode 上で Swift ファイルを作成し、ライブラリをインポートできることを確認します。main.swift を作成して、import に続いて予測変換で指定したモジュール名 Godot が出てくれば成功です。

アプリケーション側で Godot ライブラリがインポートできるようになったようす
アプリケーション側で Godot ライブラリがインポートできるようになったようす

Swift ファイルの作成後、初回のビルド時にはコンパイル時の Swift のバージョンが指定されていないというエラーが発生します。プロジェクトの Build Settings から該当の項目に値を設定しましょう。

Swift のバージョンが指定されていないエラーが発生したら設定する項目
Swift のバージョンが指定されていないエラーが発生したら設定する項目

また、Swift ファイルの作成時に Bridging Header を作成するか聞かれますが、アプリケーション側に Objective-C を追加する訳ではないので、不要です。

4. GodotApplicalitionDelegate クラスへの参照で発生するリンカエラーを確認する

万事がうまくいっている訳ではありません。

GodotApplicalitionDelegate は Godot ライブラリの中でアプリケーションのライフサイクルと密接な関係にあり、Godot ライブラリをアプリケーション側から制御する為には必ず参照する必要のあるクラスです。

上記までの作業で main.swift から、Godot の内部で定義されているクラスが見えるようになっているはずです。試しに GodotApplicalitionDelegate のインスタンスを生成するコードを書いてみます。

main.swift
import Foundation
import Godot

_ = Godot.GodotApplicalitionDelegate()

すると、少なくとも2023年10月21日時点の master ブランチからビルドしたライブラリでは、ビルドエラーが発生します。

 のインスタンス化のコードを追加すると発生するエラー
GodotApplicalitionDelegate のインスタンス化のコードを追加すると発生するエラー

このエラーは godot/platform/ios/app_delegate.mm に定義されている extern int gargc; に起因するエラーです。

Godot アプリケーションでは、main 関数の引数を一時的にグローバル変数 gargc, gargv に保存し、アプリケーションの起動直後のタイミングで、この引数の値を参照しています

つまり、このエラーは Godot 側のコードを修正しないと解消されないのですが、アプリケーションの起動直後に必要な処理と密接に関係しているため、単に extern についての記述を修正してリンカエラーを解消するだけでは十分ではなく、main 関数に渡される引数の扱い自体を見直す必要があります。アプリケーション側から Godot ライブラリを操作する構図を作る為には、アプリケーションの起動時の処理をアプリケーション側のコードで制御する必要があるからです。

この扱いについては、次回以降で見ていきます。

Discussion