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表示插件里的控制器对象。ControllerConnected和ControllerDisconnected处理游戏运行中手柄插入、断开的情况。
输入读取放在更新循环里更自然。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。
- 这条工作流适合先把空间音频接进场景,再逐步调整声音事件和混响参数。
核心启发
-
做一个 Game Center 安全启动流程
- 做什么:在 Unity 游戏标题画面完成 Game Center 认证,并根据玩家限制切换内容、多人和通信功能。
- 为什么值得做:session 说明认证只需做一次,限制状态可以直接从
GKLocalPlayer查询。 - 怎么开始:创建
GameManager组件,调用GKLocalPlayer.Authenticate(),再处理IsUnderage、IsMultiplayerGamingRestricted、IsPersonalizedCommunicationRestricted。
-
做一个可热插拔的手柄输入层
- 做什么:把所有手柄连接、断开、轮询和按钮映射集中到 Unity 的
InputManager。 - 为什么值得做:Game Controller 插件提供
GCControllerService、连接事件和GCController输入读取。 - 怎么开始:启动时调用
GCControllerService.Initialize(),保存GetConnectedControllers()返回的对象,在 update loop 里对每个控制器调用Poll()。
- 做什么:把所有手柄连接、断开、轮询和按钮映射集中到 Unity 的
-
给关键游戏时刻加入可调触觉资产
- 做什么:把碰撞、加速、命中、受伤等事件做成 AHAP 资产,由设计或音频同事在 Unity Inspector 中调整。
- 为什么值得做:Core Haptics 插件支持 AHAP 导入、导出和 Inspector 调参,代码只负责创建引擎和播放 player。
- 怎么开始:创建
AHAPAsset,在组件中保存[SerializeField]引用,使用CHHapticEngine和MakePlayer(_hapticAsset.GetPattern())播放。
-
把 3D 场景的音频遮挡做成组件配置
- 做什么:让建筑、墙体、载具等场景物体影响声音传播,而不只播放左右声道。
- 为什么值得做:PHASE 插件提供
PHASEListener、PHASEOccluder、PHASESource和SoundEvent,可以从 Unity 编辑器完成基础搭建。 - 怎么开始:把 Listener 放到摄像机,把 Occluder 放到遮挡物,把 Source 放到发声物体,再用 SoundEvent composer 连接 sampler 和 mixer。
-
为 Unity 菜单和 HUD 补齐辅助技术入口
- 做什么:让主菜单、设置页、卡牌、提示文本等 UI 能被 VoiceOver 读取,并响应 Dynamic Type。
- 为什么值得做:Accessibility 插件把 VoiceOver、Switch Control、Dynamic Type 和系统 UI accommodation 带到 Unity 游戏。
- 怎么开始:从玩家必须点击或理解的对象开始,给它们添加无障碍标记,再用真实设备开启辅助功能测试。
关联 Session
- Reach new players with Game Center dashboard — 继续了解 Game Center Dashboard、Activity Feed、Access Point,以及 Game Center 如何提升游戏发现和回访。
- Add accessibility to your Unity games — 深入学习本场略过的 Unity Accessibility 插件实战,包括 VoiceOver、Switch Control 和 Dynamic Type。
- Discover Metal 3 — 从平台图形能力角度理解 WWDC 2022 对游戏和高性能渲染的整体更新。
- Boost performance with MetalFX Upscaling — 学习游戏渲染性能优化路径,适合与 Unity 项目中的平台集成一起规划。
评论
GitHub Issues · utterances