WWDC Quick Look 💓 By SwiftGGTeam
Plug-in and play: Add Apple frameworks to your Unity game projects

Plug-in and play: Add Apple frameworks to your Unity game projects

观看原视频

Highlight

Apple 在 WWDC 2022 发布开源 Unity 插件集合,让 Unity 游戏可以通过 C# 接入 Game Center、Game Controller、Accessibility、Core Haptics、PHASE 和 Apple.Core 构建能力。

核心内容

Unity 游戏要接入 Apple 平台能力,常见难点不在概念本身,而在桥接。团队需要把 C# 游戏逻辑、Unity 编辑器工作流、Xcode 工程设置和原生框架连起来。Game Center、手柄、触觉、空间音频、无障碍,每一项都可能变成一套单独的 glue code。

这场 session 给出的答案是一组官方 Unity Package。每个插件对应一个底层 Apple 框架,C# 脚本尽量直接映射框架 API,原生库负责连接 C# 和系统框架。Unity 开发者不必先把整套游戏改写成原生 App,也能把平台能力放进现有项目。

这个设计还保留了 Unity 的使用习惯。插件通过 Package Manager 从 tarball 导入,编辑器里有 Project Settings、Inspector、Asset 创建菜单和自定义编辑窗口。开发者依然在 Unity 里组织场景、组件和资源;需要进入 Xcode 的地方,主要集中在能力配置和最终工程集成上。

详细内容

1. Apple.Core 先管理构建,再支撑其他插件(05:01

Apple.Core 是所有 Apple Unity 插件的基础依赖。它在 Unity 的 Project Settings 里加入 Apple Build Settings,把各插件的构建选项放到同一个设置面板。它还包含 asset processor,在导入时确保原生库配置到正确平台;构建时通过 post-process 脚本把原生库正确引用到中间 Xcode 工程。

导入顺序也很明确:先导入 Apple.Core,再导入 GameKit、Game Controller、Accessibility、Core Haptics 或 PHASE。session 的演示用 Unity Package Manager 选择“add package from tarball”,导入编译好的插件包。Apple.Core 准备好之后,最低系统版本、安全设置、post-process 开关都会出现在统一设置页里。

1. clone Apple Unity Plug-Ins repository
2. run build.py to build native libraries and package plug-ins
3. import Apple.Core tarball with Unity Package Manager
4. configure Apple Build Settings in Project Settings
5. import the feature plug-ins needed by the game

关键点:

  • build.py 位于仓库根目录,负责构建原生库、更新 Unity meta files、打包 tarball 和构建插件测试。
  • 完整构建需要 Xcode、Python 3、npm 和 Unity。
  • Apple.Core 的设置会传播到中间 Xcode 工程,适合集中管理最低系统版本和安全选项。
  • 其他插件都依赖 Apple.Core,所以它应该第一个进入项目。

2. 用 GameKit 插件做一次玩家认证(09:02

Game Center 的入口是玩家认证。session 强调,认证只需要在游戏生命周期里做一次。认证成功后,游戏可以查询本地玩家的限制状态,再决定是否隐藏成人内容、关闭多人玩法或关闭游戏内通信 UI。

using Apple.GameKit;

public class GameManager : MonoBehaviour
{
    private GKLocalPlayer _localPlayer;

    private async Task Start()
    {
        try
        {
            _localPlayer = await GKLocalPlayer.Authenticate();
        }
        catch (Exception exception)
        {
            // Handle exception...
        }
    }
}

关键点:

  • using Apple.GameKit; 引入 GameKit Unity 插件暴露的 C# API。
  • GameManager 是 Unity 组件,适合放在游戏启动阶段。
  • _localPlayer 缓存认证后的 GKLocalPlayer,后续 Game Center 操作都可以基于它。
  • GKLocalPlayer.Authenticate() 是异步静态方法,返回本地玩家实例。
  • catch 分支需要按游戏自身流程处理认证失败,例如继续离线模式或显示提示。

认证之后,限制检查就是几个布尔值判断。

try
{
    _localPlayer = await GKLocalPlayer.Authenticate();

    if (_localPlayer.IsUnderage)
    {
        // Hide explicit game content.
    }

    if (_localPlayer.IsMultiplayerGamingRestricted)
    {
        // Disable multiplayer game features.
    }

    if (_localPlayer.IsPersonalizedCommunicationRestricted)
    {
        // Disable in-game communication UI.
    }
}

关键点:

  • IsUnderage 为真时,游戏应限制成人或露骨内容。
  • IsMultiplayerGamingRestricted 为真时,游戏应关闭或调整多人体验。
  • IsPersonalizedCommunicationRestricted 为真时,游戏应关闭游戏内通信 UI。
  • session 还提醒两步收尾工作:在中间 Xcode 工程加入 Game Center capability,并在 App Store Connect 配置 Game Center 功能。

3. 用 Game Controller 插件统一处理连接和输入(13:11

Game Controller 插件面向 MFi 控制器和部分 Sony、Microsoft 第三方控制器。它提供手柄自定义、按钮 glyphs 和输入访问。session 建议用一个 InputManager 组件做三件事:保存当前连接的控制器,启动时初始化服务,更新循环里轮询输入。

using Apple.GameController;

public class InputManager : MonoBehaviour
{
    void Start()
    {
        // Initialize the Game Controller service
        GCControllerService.Initialize();

        // Check for connected controllers
        var controllers = GCControllerService.GetConnectedControllers();
        foreach (GCController controller in controllers)
        {
            // Handle controllers
        }

        // Set up callbacks to handle connected/disconnected controllers
        GCControllerService.ControllerConnected    += _onControllerConnected;
        GCControllerService.ControllerDisconnected += _onControllerDisconnected;
    }
}

关键点:

  • GCControllerService.Initialize() 是一次性初始化入口。
  • GetConnectedControllers() 返回当前已经连接的控制器集合。
  • GCController 表示插件里的控制器对象。
  • ControllerConnectedControllerDisconnected 处理游戏运行中手柄插入、断开的情况。

输入读取放在更新循环里更自然。session 中的示例先轮询,再读取具体按钮。

foreach (GCController controller in _myConnectedControllers)
{
    controller.Poll();

    // Check the 'South' button ('A' button on most controllers)
    if (controller.GetButton(GCControllerInputName.ButtonSouth))
    {
        //Handle button pressed
    }

    // Check other controller inputs…
}

关键点:

  • controller.Poll() 获取最新控制器状态。
  • GCControllerInputName.ButtonSouth 对应多数手柄上的 A 键位置。
  • 每次轮询后再读取按钮、摇杆等输入,能让游戏逻辑拿到当前帧的控制器状态。
  • 轮询频率由游戏决定,session 建议从 Unity 的常规 update loop 开始。

4. Accessibility 插件把辅助技术放进 Unity 场景(14:27

Accessibility 插件让 Unity 游戏可以接入 VoiceOver、Switch Control、Dynamic Type 和系统级 UI accommodation 设置。VoiceOver 读取程序化标记的内容,Switch Control 支持辅助输入设备,Dynamic Type 根据用户文字大小偏好调整游戏 UI。

这场 session 没展开代码,而是把它定位为插件集合里的关键模块,并指向专门的《Add accessibility to Unity games》。对正在改造现有 Unity 项目的团队,实践路径很清楚:先找出玩家必须理解或操作的对象,再用插件提供的无障碍组件和属性把这些对象暴露给系统辅助技术。

Unity scene object
  -> add accessibility component
  -> provide label, traits, value, or preferred text behavior
  -> test with VoiceOver, Switch Control, Dynamic Type, and system settings

关键点:

  • VoiceOver 需要可读的程序化内容,游戏对象不能只停留在视觉层。
  • Switch Control 让更多输入设备可以操作游戏。
  • Dynamic Type 适合游戏菜单、提示、设置页等文本密集界面。
  • 这部分能力与 Game Center 的玩家限制不同,它解决的是玩家能否理解和操作游戏。

5. Core Haptics 插件把 AHAP 资产接到 C# 组件(20:30

Core Haptics 是数据驱动 API。session 把它拆成四个对象:CHHapticEngine 连接设备上的 haptic server,CHHapticPatternPlayer 控制播放,CHHapticPattern 组织一个或多个触觉和音频事件,CHHapticEvent 是触觉体验的基础单元。

Unity 插件还提供 AHAP Asset。开发者可以在 Unity 的 Assets 菜单创建 AHAP,使用 Inspector 调整 transient 或 continuous event,导入、导出 AHAP 文件,再把资产引用到游戏对象上的 Haptics 组件。

using Apple.CoreHaptics;

public class Haptics : MonoBehaviour
{
    private CHHapticEngine _hapticEngine;
    private CHHapticPatternPlayer _hapticPlayer;
    [SerializeField] private AHAPAsset _hapticAsset;

    private void PrepareHaptics()
    {
        _hapticEngine = new CHHapticEngine();
        _hapticEngine.Start();
        _hapticPlayer = _hapticEngine.MakePlayer(_hapticAsset.GetPattern());
    }

    private void Play()
    {
        _hapticPlayer.Start();
    }
}

关键点:

  • CHHapticEngine 是播放触觉 pattern 的前提。
  • _hapticEngine.Start() 启动引擎。
  • [SerializeField] private AHAPAsset _hapticAsset; 让 Unity Inspector 可以指定 AHAP 资产。
  • _hapticAsset.GetPattern() 从 AHAP 资产取得触觉 pattern。
  • MakePlayer(...) 创建播放器,_hapticPlayer.Start() 开始播放。

6. PHASE 插件用组件组织空间音频(22:13

PHASE 处理几何感知空间音频。声音从场景里的 mesh 发出,遇到建筑物等几何体会产生遮挡、反射和混响。Unity 插件把常见角色做成组件:PHASEListener 是场景的耳朵,PHASEOccluder 附着在有几何数据的物体上并衰减被遮挡的声音,PHASESource 使用游戏对象 transform 在世界里定位声音。

session 的演示没有写脚本。它在一个包含飞机、建筑物、摄像机的项目里,把 Listener 加到摄像机,把 Occluder 加到建筑物,把 Source 加到飞机。然后创建 SoundEvent 资产,在 PHASE sound event composer 里添加 sampler node、引用飞机怠速音频、保持 loop,再把输出接到 mixer。最后把 SoundEvent 绑定到飞机上的 PHASESource。

Camera   -> PHASEListener
Building -> PHASEOccluder
Airplane -> PHASESource -> SoundEvent -> sampler node -> mixer

关键点:

  • Listener 根据位置、朝向和 reverb preset 处理场景音频。
  • Occluder 依赖场景几何,在声音源和 Listener 中间时衰减声音。
  • Source 使用游戏对象 transform,让声音跟随对象移动。
  • SoundEvent 描述具体播放事件,composer 用节点图创建 sampler 和 mixer。
  • 这条工作流适合先把空间音频接进场景,再逐步调整声音事件和混响参数。

核心启发

  1. 做一个 Game Center 安全启动流程

    • 做什么:在 Unity 游戏标题画面完成 Game Center 认证,并根据玩家限制切换内容、多人和通信功能。
    • 为什么值得做:session 说明认证只需做一次,限制状态可以直接从 GKLocalPlayer 查询。
    • 怎么开始:创建 GameManager 组件,调用 GKLocalPlayer.Authenticate(),再处理 IsUnderageIsMultiplayerGamingRestrictedIsPersonalizedCommunicationRestricted
  2. 做一个可热插拔的手柄输入层

    • 做什么:把所有手柄连接、断开、轮询和按钮映射集中到 Unity 的 InputManager
    • 为什么值得做:Game Controller 插件提供 GCControllerService、连接事件和 GCController 输入读取。
    • 怎么开始:启动时调用 GCControllerService.Initialize(),保存 GetConnectedControllers() 返回的对象,在 update loop 里对每个控制器调用 Poll()
  3. 给关键游戏时刻加入可调触觉资产

    • 做什么:把碰撞、加速、命中、受伤等事件做成 AHAP 资产,由设计或音频同事在 Unity Inspector 中调整。
    • 为什么值得做:Core Haptics 插件支持 AHAP 导入、导出和 Inspector 调参,代码只负责创建引擎和播放 player。
    • 怎么开始:创建 AHAPAsset,在组件中保存 [SerializeField] 引用,使用 CHHapticEngineMakePlayer(_hapticAsset.GetPattern()) 播放。
  4. 把 3D 场景的音频遮挡做成组件配置

    • 做什么:让建筑、墙体、载具等场景物体影响声音传播,而不只播放左右声道。
    • 为什么值得做:PHASE 插件提供 PHASEListenerPHASEOccluderPHASESourceSoundEvent,可以从 Unity 编辑器完成基础搭建。
    • 怎么开始:把 Listener 放到摄像机,把 Occluder 放到遮挡物,把 Source 放到发声物体,再用 SoundEvent composer 连接 sampler 和 mixer。
  5. 为 Unity 菜单和 HUD 补齐辅助技术入口

    • 做什么:让主菜单、设置页、卡牌、提示文本等 UI 能被 VoiceOver 读取,并响应 Dynamic Type。
    • 为什么值得做:Accessibility 插件把 VoiceOver、Switch Control、Dynamic Type 和系统 UI accommodation 带到 Unity 游戏。
    • 怎么开始:从玩家必须点击或理解的对象开始,给它们添加无障碍标记,再用真实设备开启辅助功能测试。

关联 Session

评论

GitHub Issues · utterances