关于 mobile-development
面向 Godot 4.x 开发者的移动端开发技能,涵盖将游戏发布到 Android 和 iOS 的平台特有事项。重点包括签名、生命周期、权限、设备功能及移动端性能配置。
主要功能
- 01Android 与 iOS 导出及签名
- 02应用暂停与恢复处理
- 03权限声明和运行时请求
- 04Java API 与设备功能调用
- 05安全区域及设备方向适配
- 06移动渲染器与性能建议
适用场景
- 01配置 Android 发布密钥和 AAB
- 02处理应用暂停时的游戏存档
- 03适配刘海屏和设备旋转
- 04为移动端选择渲染器及纹理压缩
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jame581/ godotprompter --skill mobile-development安装后在 Claude Code 中输入/mobile-development确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/crash-debugging.mdiap-and-ads.mdplugins.md详细介绍
移动开发
将 Godot 4.x 游戏发布到 Android 和 iOS。除了通用导出流程外,还涵盖平台特有的差异:签名、生命周期、权限、插件、IAP、设备功能,以及移动渲染器和性能预算。
相关技能: 通用导出流程和 CI/CD 请参阅 export-pipeline;安全区布局请参阅 responsive-ui;触控请参阅 input-handling;移动性能请参阅 godot-optimization;C# 移动端注意事项请参阅 csharp-godot。
1. 导出与签名
**Android:**安装 OpenJDK 17 和 Android SDK;在 Editor Settings 中设置 Java SDK Path 和 Android SDK Path(按用户设置,而非按项目设置)。生成发布密钥库:
keytool -v -genkey -keystore mygame.keystore -alias mygame -keyalg RSA -validity 10000
预设字段:Release / Release User / Release Password(目前密钥库密码和密钥密码必须相同);取消勾选 Export With Debug。**新上传到 Play 的应用必须使用 AAB。**CI 环境变量覆盖项:GODOT_ANDROID_KEYSTORE_RELEASE_{PATH,USER,PASSWORD}。
**iOS:**需要 macOS 和 Xcode。导出需要 App Store Team ID 和反向 DNS 格式的包 Identifier;Godot 会生成 .xcodeproj,供你在 Xcode 中构建。请使用 Compatibility 渲染器在 iOS 模拟器上测试——Forward+/Mobile 是否受支持取决于模拟器的 Metal 支持情况。
自定义 Gradle 构建(Project → Install the Gradle Build template)是 v2 插件和 IAP 的必需项(Godot 4.2+)。从 Godot 4.7 起,导出选项 Use Gradle Build 不再标记为实验性(GH-119172)——需要插件或 IAP 时,应将其视为标准流程。
⚠️ **Godot 4.7 中的变更:**已从 Android 导出中移除已弃用的 Google Play OBB 扩展文件支持(
apk_expansion/*导出选项)。仍依赖 APK 扩展文件的项目应改用 Android App Bundles 和 Play Asset Delivery,或安装现已包含旧版逻辑的独立 Godot OBB 插件。参见 GH-118283。
2. 应用生命周期
实际的 Node 通知常量:NOTIFICATION_APPLICATION_PAUSED(2015)、NOTIFICATION_APPLICATION_RESUMED(2014)、NOTIFICATION_APPLICATION_FOCUS_IN/_OUT(2016/2017)、NOTIFICATION_WM_GO_BACK_REQUEST(1007,Android 返回键)。**移动端没有 WM_CLOSE_REQUEST。**在 PAUSED 时自动保存;iOS 在暂停后约有 5 秒完成工作,之后系统会终止应用。
GDScript
func _notification(what: int) -> void:
match what:
NOTIFICATION_APPLICATION_PAUSED:
SaveManager.save_game() # App backgrounded — persist now.
NOTIFICATION_WM_GO_BACK_REQUEST:
_confirm_quit() # Android Back button.
C# 等效写法
文档只逐字展示了 NotificationWMCloseRequest;以下 PascalCase 名称遵循相同的命名惯例。
public override void _Notification(int what)
{
switch ((long)what)
{
case NotificationApplicationPaused:
SaveManager.SaveGame(); // App backgrounded — persist now.
break;
case NotificationWMGoBackRequest:
ConfirmQuit(); // Android Back button.
break;
}
}
画中画(Android,Godot 4.7+)
DisplayServer.pip_mode_enter(window_id = 0) 可进入画中画模式;is_in_pip_mode(window_id = 0) 可报告当前状态;pip_mode_set_aspect_ratio(numerator, denominator, window_id = 0) 可设置画中画窗口的宽高比;pip_mode_set_auto_enter_on_background(auto_enter_on_background, window_id = 0) 可在应用进入后台时自动进入画中画模式。模式切换会通过 Node 通知传达:NOTIFICATION_APPLICATION_PIP_MODE_ENTERED(2019)/ NOTIFICATION_APPLICATION_PIP_MODE_EXITED(2020)。仅限 Android。
func _ready() -> void:
DisplayServer.pip_mode_set_aspect_ratio(16, 9)
DisplayServer.pip_mode_set_auto_enter_on_background(true)
func _notification(what: int) -> void:
match what:
NOTIFICATION_APPLICATION_PIP_MODE_ENTERED:
_set_minimal_hud(true) # PiP window is tiny — hide non-essential UI.
NOTIFICATION_APPLICATION_PIP_MODE_EXITED:
_set_minimal_hud(false)
public override void _Ready()
{
DisplayServer.PipModeSetAspectRatio(16, 9);
DisplayServer.PipModeSetAutoEnterOnBackground(true);
}
public override void _Notification(int what)
{
switch ((long)what)
{
case NotificationApplicationPipModeEntered:
SetMinimalHud(true); // PiP window is tiny — hide non-essential UI.
break;
case NotificationApplicationPipModeExited:
SetMinimalHud(false);
break;
}
}
3. 权限
在导出预设中将每项权限声明为 permissions/<name>;使用 OS.request_permission(name) 在运行时请求权限。结果会通过 MainLoop 的 on_request_permissions_result(permission, granted) 返回。必须同时在预设中启用该权限,仅发起请求还不够。
GDScript
func _ready():
if "android.permission.POST_NOTIFICATIONS" not in OS.get_granted_permissions():
OS.request_permission("android.permission.POST_NOTIFICATIONS")
get_tree().on_request_permissions_result.connect(_on_perm_result)
func _on_perm_result(permission: String, granted: bool):
print("%s granted: %s" % [permission, granted])
C# 等效写法
// Contains() on string[] needs: using System.Linq;
public override void _Ready()
{
if (!OS.GetGrantedPermissions().Contains("android.permission.POST_NOTIFICATIONS"))
OS.RequestPermission("android.permission.POST_NOTIFICATIONS");
GetTree().OnRequestPermissionsResult += OnPermResult;
}
private void OnPermResult(string permission, bool granted)
=> GD.Print($"{permission} granted: {granted}");
4. 调用 Android API(JavaClassWrapper)— Godot 4.4+
Godot 4.4+。 JavaClassWrapper.wrap("<java.class>")(早期版本中已有,但从 4.4 起才实用)无需插件即可调用 Java/Kotlin 类;4.4 新增的 AndroidRuntime 单例(Engine.get_singleton("AndroidRuntime"))提供 getActivity()、getApplicationContext() 和 createRunnableFromGodotCallable(callable)。
更简单的跨平台替代方案无需 4.4:Input.vibrate_handheld(duration_ms, amplitude)(需要 VIBRATE 权限;iOS 需要 iOS 13+)。完整的 JavaClassWrapper/AndroidRuntime API、Toast/Intent 示例和内部类语法,请参阅 插件。
GDScript
# Godot 4.4+ — requires the VIBRATE permission in the export preset.
func vibrate_ms(duration_ms: int) -> void:
if Engine.has_singleton("AndroidRuntime"):
var runtime := Engine.get_singleton("AndroidRuntime")
var context := runtime.getApplicationContext()
var vibrator := context.getSystemService("vibrator")
if vibrator.hasVibrator():
var Effect = JavaClassWrapper.wrap("android.os.VibrationEffect")
var effect = Effect.createOneShot(duration_ms, Effect.DEFAULT_AMPLITUDE)
vibrator.vibrate(effect)
C# 等效实现
这里的文档仅适用于 GDScript;确切的 C# Variant 封送调用链(对每个 Java 返回值调用 .AsGodotObject())尚未在设备上测试,请在实际的 4.4+ Android 构建中验证。
// Godot 4.4+ — requires the VIBRATE permission in the export preset.
public void VibrateMs(int durationMs)
{
if (!Engine.HasSingleton("AndroidRuntime")) return;
var runtime = Engine.GetSingleton("AndroidRuntime");
var context = runtime.Call("getApplicationContext").AsGodotObject();
var vibrator = context.Call("getSystemService", "vibrator").AsGodotObject();
if (vibrator.Call("hasVibrator").AsBool())
{
var effect = JavaClassWrapper.Wrap("android.os.VibrationEffect");
var oneShot = effect.Call("createOneShot", durationMs, effect.Get("DEFAULT_AMPLITUDE"));
vibrator.Call("vibrate", oneShot);
}
}
从脚本实现 Java 接口(Godot 4.7+)
JavaClassWrapper.create_proxy(object: Object, interfaces: PackedStringArray) -> JavaObject 使用 Godot 对象实现指定的 Java 接口——该对象的方法签名必须与 Java 接口的方法签名匹配,Java 调用会转发到匹配的方法。create_sam_callback(sam_interface: String, callable: Callable) -> JavaObject 用于单抽象方法(SAM)接口,其 Callable 必须与 SAM 方法的参数和返回类型匹配。除 Android 外,这两个方法在所有平台上都返回 null。
class PrintProxy:
func println(content: String) -> void:
print(content)
func _demo() -> void:
var print_proxy := PrintProxy.new()
var printer := JavaClassWrapper.create_proxy(print_proxy, ["android.util.Printer"])
printer.println("Hello Godot World!")
var cb := func(content: String) -> void: print(content)
var callback := JavaClassWrapper.create_sam_callback("android.util.Printer", cb)
callback.println("Hello Godot World!")
这里的文档仅适用于 GDScript。CreateProxy 根据注册的方法名进行匹配,因此 C# 实现类的方法名必须与 Java 接口完全一致——在 C# 中优先使用 CreateSamCallback:
var cb = Callable.From((string content) => GD.Print(content));
var callback = JavaClassWrapper.CreateSamCallback("android.util.Printer", cb);
callback.Call("println", "Hello Godot World!");
5. 设备功能与安全区域
DisplayServer.get_display_safe_area() -> Rect2i(Android/iOS)返回刘海/缺口以内的可用区域;get_display_cutouts()(Android)列出缺口矩形。运动传感器位于 Input 上(get_accelerometer/gravity/gyroscope/magnetometer,返回 Vector3,仅适用于 Android/iOS)。
GDScript
func _ready():
var safe := DisplayServer.get_display_safe_area() # Rect2i
$UI.position = safe.position
$UI.size = safe.size
C# 等效实现
public override void _Ready()
{
Rect2I safe = DisplayServer.GetDisplaySafeArea();
var ui = GetNode<Control>("UI");
ui.Position = safe.Position;
ui.Size = safe.Size;
}
Android 支持原生文件对话框(DisplayServer.file_dialog_show());从 Godot 4.7 起,所有设备都可使用原生文件选择器——已移除用于限制 FEATURE_NATIVE_DIALOG_FILE 支持的 Android 版本检查(GH-115257)。
Godot 4.7+: 屏幕触控摇杆请使用内置的
VirtualJoystickControl 节点,而不是自行搭建TouchScreenButton组件——完整 API 请参阅 input-handling(references/touch.md,“VirtualJoystick (Godot 4.7+)”)。
设备方向信号(Godot 4.7+)
设备方向变化时会触发 DisplayServer.orientation_changed(orientation: int)(Android/iOS):1 表示纵向,2 表示横向,0 表示未定义。
func _ready() -> void:
DisplayServer.orientation_changed.connect(_on_orientation_changed)
func _on_orientation_changed(orientation: int) -> void:
_relayout_hud(orientation == 2) # 1 portrait, 2 landscape, 0 undefined.
public override void _Ready()
{
DisplayServer.Singleton.Connect(DisplayServer.SignalName.OrientationChanged,
Callable.From((long orientation) => RelayoutHud(orientation == 2)));
}
6. 移动端性能与渲染器
使用 Mobile(或 Compatibility)渲染器;在 iOS 模拟器上优先使用 Compatibility。启用 rendering/textures/vram_compression/import_etc2_astc = true(ETC2/ASTC)以进行 Android 纹理压缩。尽量减小单架构 APK 的体积;AAB 会自动按设备拆分。深入的绘制调用/批处理调优请交由 godot-optimization 处理。
7. 移动端 C#
C# Android/iOS 导出从 Godot 4.2+ 起支持,但仍属实验性功能。Android C# 导出需要 .NET 9+(搭配 Godot 4.5);iOS 导出仅支持从 macOS 进行(导出过程也会构建 arm64 + x86_64 模拟器程序集)。C# 无法导出到 Web。 尽早测试 C# 导出流程——这是 C# 移动端项目中风险最高的部分。
深入了解: 插件(Android v2 / iOS) · 应用内购买与广告 · 崩溃调试
实施检查清单
- 已配置发布密钥库(Android)/描述文件 + Team ID(iOS)
- Play 上传使用 AAB;使用 v2 插件或 IAP 时已安装 Gradle 构建工具
- 已将自动保存接入
NOTIFICATION_APPLICATION_PAUSED(遵守 iOS 约 5 s 的时间限制) - 已在预设中启用所需权限,并在运行时请求这些权限
- 已标明 JavaClassWrapper 用法要求 Godot 4.4+;如有需要,为 4.3 提供回退方案
- UI 锚定在
get_display_safe_area()内;已处理刘海/挖孔 - 已启用 Mobile/Compatibility 渲染器和 ETC2/ASTC 压缩
- C# 项目:已确认 Android 导出使用 .NET 9+
- 已将 Play OBB/APK 扩展文件迁移到 App Bundles 并使用 Play Asset Delivery,或已安装 Godot OBB 插件(Godot 4.7 已移除内置支持)
- PiP(Android):已配置宽高比和自动进入;HUD 会响应 PIP_MODE 通知(Godot 4.7+)