将DuckHunt与ARKit结合使用? —第1课:欢迎来到组件世界

故事作者: nomtek的 iOS开发人员 Wojciech Trzasko

在WWDC 2017上,苹果迈出了将增强现实技术带入我们生活的第一步。 通过展示他自己的称为ARKit的框架,它大规模地引发了一个小现象。 互联网迅速被许多视频和演示所淹没,这些视频和演示提出了AR中疯狂的想法,从娱乐的想法(例如现实生活中的Street Fighter游戏)开始,以更实用的结尾,例如用于测量房间的简单解决方案。

得益于对流行工具的广泛支持,AR应用程序开发如此迅速的进展成为可能。 从最初的公告开始,苹果就发布了两个非常成熟的3D游戏引擎的插件:Unity和Unreal Engine。 两者都有庞大的社区,渴望测试新技术。

因此,如果您打算在增强现实中创建游戏或沉浸式体验,那么选择其中之一可能是您的最佳起点。 但是,当您要将应用程序制作成AR是默认用户体验的唯一不错的增强时,该怎么办?

您将步入一个需要将基于UIKit的经典应用程序与ARKit框架连接的地方。 在需要插入技术的地方,处理图像数据并基于结果将实时信息提供到UIKit表示的基于事件的基础结构中。 以正确的方式连接这两个不同的世界可能有些棘手,但并非不可能。

为了找出方便的方法,让我们从定义应用程序的体系结构开始。 最受欢迎的方法之一是使用VIPER ,它是适用于iOS世界的简洁架构的概念。 简而言之,它将单个模块分为五个部分:

  • 视图 -仅负责显示所告知的内容
  • 交互器-负责单个用例的业务逻辑
  • 演示者—Interactor获取结果,并准备将其显示在View上,并对从View传递来的用户输入做出反应
  • 实体交互器使用的模型层
  • 路由-处理两个屏幕之间的导航,其职责在Wireframe对象(创建新屏幕并将其放置在应用程序的窗口中)和Presenters (获取用户输入并选择将用户移动到哪个屏幕)之间分配

为了使所有连接更加清晰,请看下面的图:

现在假设我们要向VIPER编写的应用程序中添加简单但无缝的体验。 您可能已经发现它没有太多空间。 我们应该把负责场景管理的代码放在哪里? 如何处理互动? 我们应该在实时引擎中处理它吗? 还是返回主持人? 但是在这种情况下,如果我们的经验中没有明确的业务逻辑,我们应该在交互器中做什么?

这些只是您现在可能会想到的一些问题。

让我向您展示一个简单的示例,该示例将UIKit菜单添加到我们使用ARKit进行的演示体验中。 只看最终结果:

如您所见,我们的实体仅由组件构建:

  • SpriteComponent-负责渲染2D精灵
  • AgentComponent — GameplayKit代理对象的包装,实现AI行为
  • AnimationsComponent —根据上一次移动更改应用正确的动画
  • SoundComponent —播放声音效果
  • StateMachineComponent —允许实体更改其状态并将行为调整为当前状态(例如,当鸭子活着时使用不同的移动策略,而当鸭子死亡时使用不同的移动策略)

我们已经定义了实体,那么现在该如何处理它们呢? 答案比您想的要容易,只需在您的主要更新方法中遍历它们及其组件 ,然后对它们调用更新操作即可。

…好吧,也许并不是那么容易,但是首先让我们介绍ECS设计模式的最后一部分,即System😉。

为了了解Systems的工作原理,让我们介绍一个简单的实际示例。 假设我们目前正在开发具有丰富3D音效的交互式体验,这种体验会令用户大吃一惊,但是对于其中一些人来说,在拥挤的地方玩游戏可能会成为问题。 假设我们的用户不想只让整个手机静音就只是向咖啡馆里的朋友展示您的应用程序。

最简单的方法是允许用户在应用程序本身中关闭声音效果。 这是系统迎接挑战的地方。 System对象的主要职责是对相同方面的组件执行操作。 是的,听起来很复杂,但实际上,这是一个非常简单的模式。 只需回顾一下我们的经验即可。

我们已经知道Component是执行一项特定任务的对象,因此,如果要播放声音,则需要将该任务提取为一个名为SoundComponent的类型。 现在,让我们稍微更改一下迭代算法,并允许其按功能对组件进行分组。 例如,首先,它应该对计算物理的所有组件调用update,然后应该处理所有SoundComponents 。 分组正是System的目的。 单个系统是负责在一种类型的所有组件上调用更新方法的对象。 在这种方法中,主要的更新方法适用于Systems而不是Components

因此,现在,当我们需要关闭声音时,我们可以仅禁用SoundSystem,而不会使用大量if语句向我们的代码发送垃圾邮件。 但是还有更多好处。 一个简单的例子是当我们的应用开始丢失帧时,跳过播放声音或将其移至更新循环的下一个迭代。 我们可以将节省的时间用于物理引擎进行的复杂计算。 另一个示例可能是您不想为用户不可见的对象处理物理的情况。 您可以轻松地将用于剔除的简单逻辑放入PhysicsSystem对象。

但是不要害怕这还没有结束!

在下一章中,我们将研究VIPER架构,定义其新部件并找出连接ECS的方式。

有什么想法吗? 在评论中分享以下内容!

大家见!