内容来源:2018 年 9 月 15 日,Booking.com ios/安卓开发刘冠彬在“2018@swift 第三届 Swift 开发者大会”进行《做一个有趣的多人AR游戏》演讲分享。IT 大咖说作为独家视频合作方,经主办方和讲者审阅授权发布。
阅读字数:2404 | 7分钟阅读
获取嘉宾完整演讲视频及PPT,请复制:http://t.cn/EwqIy5w,粘贴至浏览器即可。
SceneKit
我本次选用的是SceneKit Cheatsheet,可能大多数人都没有接触过,不太清楚如何使用,下面通过两张图来给大家做下简单的入门介绍。
SceneKit有root Node,并通过parentNode和Children Node维护数群结构,或者说是显示层级。
创建一个Node仅使用以上几行代码即可实现。这里创建的是Plane Node,其中包含形状、材质等参数信息,也可以为它添加物理引擎。
要想从文件中读取预先已有的Scene,可以使用上面这两行代码。这可以将预制的Scene直接展示出来。
ARKit
ARKit历经1.0、1.5,再到iOS 12的2.0,慢慢从仅有几个Feature发展到拥有众多Feature(如上图所示),包含平面几何识别、光影、面部表情追踪、图片追踪、3D扫描等等。
平面识别
ARKit最基础的应用是平面识别,很多AR相关的游戏和App中,用户首次使用的时候都要扫描一个平面。
ARKit通过Session来管理整个运算,它会对数据进行整合,开发者不用关心太多细节,好比手机的移动、视频采集等。
以上为AR Session的配置代码,因为从1.5开始Session支持了垂直平面的扫描,所以代码中有版本判断。通过run运行的时候可以读取出当前配置中的内容,包含camera、物理配置信息。
因为本次使用的是SceneKit,所以会有ARSCNViewDelegate。AR Session识别到一个平面后,它会调用上图中的方法,方法中要传入对应的Node。
在移动的过程中会扫描到新的平面,其中有些平面会进行整合,此时会触发更新方法。
平面整合的时候,还会剔除掉重复多余的那个平面,这时会触发Remove方法。
以上几个函数中的Anchor表示的是3D空间内的位置和方向,而PlaneAnchor是特殊性质的Anchor,它还保存有尺寸信息,也就是长宽。Anchor中的Geometry属性包含了平面的形状、大小等各种信息。
这是显示平面的完整代码。生成的平面会添加到node中,该node是和anchor相关联的。
这是生成node的代码。
更新平面的时候,首先会更新平面的长宽,然后再对中心位置进行update。
不规则平面识别
ARKit还支持不规则平面的识别,用到了前面提到的geometry。这段代码中的ARSCNPlaneGeomtry是一个特殊的Geometry,它会根据Anchor中的geometry进行更新,显示出对应的形状, 且不局限于规则形状。
平面的更新也是类似的,差别在于这次调用的是Update方法。
Demo
此处有DEMO,详情请观看视频:http://t.cn/EwqIy5w
虚拟物体交互操作
ARKit的交互和UIKit很类似,UIKit通过Hit Test判断用户点击的控件,ARKit也有类似的概念,不同的是它监测的是3D空间。
ARKit所使用的技术是Ray Cast,可以理解为手机所处的位置发射出一条射线,这条线最终所抵达的位置就是与真实世界的交互点。
选中平面需要调用hitTest方法,它会返回接触到的Anchor,通过Anchor我们可以找到对应的node,然后就可以根据node进行一些处理,好比添加object。
由于这里的type是existingPlaneUsingExtent,所以使用的是已经识别出的平面,并且在此平面之外的点被判断为失效。
图片跟踪
图片跟踪在ARKit中实现起来非常简单,只用3步。首先在Assets.xcassets中创建一个AR Resource Group,然后将要识别的图片拖拽进去,最后指定在真实世界的物体大小。
这里同样要配置AR Session,可以用ARWorldTrackingConfiguration和ARImageTrackingConfiguration。
和Plane一样ARKit Session也为图片跟踪自动添加了一个特殊的Image Anchor,开发者可以据此进行一些特殊处理,好比在目标图片上添加一段文字信息。
多人AR场景
所谓的多人场景就是分享者和被分享者可以看到同样的虚拟空间或物体。ARKit 2.0为此提供了一个新的功能AR World Map,它包含了当前物理空间以及所有的anchors。
World Map在传递之前需要检查下状态,主要检查的是frame.worldMappingStatus,以包管数据完整性。
接下来使用getCurrentWorldMap返回一个WorldMap object,然后将它转换成archivedData从网络中发送出去。
接收到World Mpa后会进行解析,解析后的数据将被复制到AR Session的Configuration。最后run的时候需要提供两个options,resetTracking和removeExistiongAnchors,因为接收方在接收数据之前可能已经扫描了一些场景,需要将原先的这些数据清空,方便新场景的加载。
Multipeer Connectivity
Multipeer Connectivity是一个的去中心化的peer to peer的连接,可以使用wifi、蓝牙等进行附近设备间的通信。它可以进行平安的数据传输,数据类型包含message、streams以及file resource。
Multipeer Connectivity的使用形式主要是以广播服务为主,如上代码使用的就是MCNearbyServiceAdvertiser进行的广播。
同时也可以主动搜寻广播服务。
建立连接之后,就可以进行发送和收信操作。
以上为自定义加入第一个发现的session的流程图。首先本设备调用方法找到可以加入的Service,然后向目标设备发出邀请,目标设备收到邀请后调用ReceiveInvitation方法,最后接受邀请。
我们也可以使用主客形式进行连接,Host用MCNearbyServiceAdvertiser,guests用MCNearbyServiceBrowser寻找并加入。或者是以Peers形式加入,然后协商出一个作为master。
Logging
苹果提供了一个非常好的调试工具os.log,使用的时候需要传入subsystem和category。subsystem可以让我们对想要的功能进行group,方便更好的定位代码。
不同的log其实是有不同level的,好比上面代码中的Info。图中这些level仅有Info和Debug会存储在内存中,其他都是存储在data store内。
性能问题诊断可以用os.signpost来做,它需要有一个os.log object指定subsystem和category,然后使用begin和end方法log中间的问题代码。
Experience
其实想要写出好的代码架构,主要还是在于拆分。在我的实践中,所有AR相关的部分被放到一个层中,网络层和游戏逻辑也单独封装一层,中间通过dispatcher进行消息分发。
同时我们还要包管View Controller整洁。View Controller属于View layer,不应包含业务逻辑,而应该是UI layout布局、UI内容显示、用户交互。
数据流动
这是整个数据流转图。用户的所有交互都会抵达dispatcher,同时游戏中的事件来源不管是用户操作、AR事件、网络事件,对整个gameplay来说传过来的都是message,然后依此做出响应。
参考资料
· Inside SwiftShot: Creating an AR Game
· ARKit Sampler | Shuichi Tsutsumi
· App Architecture | objc.io
· Unified Logging and Activity Tracing | NSScreencast
· Activity.swift | Zach Waldowski
编者:IT大咖说,转载请标明版权和出处