【问题标题】:SceneKit game architectureSceneKit 游戏架构
【发布时间】:2017-08-22 06:15:47
【问题描述】:

我正在尝试为 SceneKit iOS 游戏设计架构。我在下面展示了我当前首选想法的两个粗略 高级对象图/图表。

我目前只关注高级架构,即管理 Menu/GameLevel/Config/Paused 应用程序状态之间的转换,并使应用程序数据驱动,以便它可以处理多个级别等。一旦我得到这个工作,然后我将解决较低级别的架构,例如对于 GameLevel 状态,我将有一个 GameLevelModel 对象来表示实际的游戏关卡逻辑。

很高兴听到哪个看起来更有希望,有什么明显的陷阱或要避免的事情吗?

A 版)基于 MVC,带有自定义容器和嵌套 VC

我试图在这个版本中保持接近 MVC 范式。

应用程序的行为在其不同状态(菜单/游戏级别/配置/等)之间发生显着变化,因此我将使用永久容器视图控制器,以父子关系管理/转换多个嵌套 VC根据 Apple 的“iOS 视图控制器编程指南”。这些实际上是整个 MVC。

因为 Apple 的容器控制器(UINavigation-、UISplitScreen-、UITabBar-)都不适合(我需要整个屏幕),所以我将拥有一个自定义容器 VC (RootViewController),它始终有一个嵌套的子 VC。每个子 VC 的视图将始终完全覆盖容器的视图。子 VC 之间的转换将由 RootVC 管理并由其 StateMachine 驱动。

每个子 VC(实际上是整个 MVC)在其对应状态变为当前状态时创建,然后在被下一个状态替换且不再需要时释放。类似于 UINavigationController 如何处理它包含的 VC。

我对这种架构的唯一担心是它看起来有点沉重。 SceneKit 看起来像是设计用于单个 SCNView 并在我们需要更改 3D 内容时在多个 SCNScene 之间转换。这也提供了更多的过渡选项。

                         --------------------------
                         |   RootViewController   |
                         --------------------------
                           |  |   |        |    |
     ------------------    |  |   |        |    |      ------------------
     | GameLevelsData |-----  |   |        |    -------|     SCNView    |
     ------------------       |   |        |           ------------------
     ------------------       |   |        |
     |   PlayerData   |--------   |        |
     ------------------           |        |
                                  |        |
                       ----------------    |
                       | StateMachine |    |
                       ----------------    |
                         |                 |
                         |-(*MenuState)    |
                         |-(LevelState)    |
                         |-(ConfigState)   |
                                           |
                                           |
                         ------------------------
                         |  MenuViewController  |
                         ------------------------
      -----------------     |                |      ------------------
      |   MenuData    |------                -------|  MenuSCNView   |
      -----------------                             ------------------

   (child VCs for other States, Level-/Config-/etc are created as needed)
                                           |
                         ------------------------
                         |  LevelViewController |
                         ------------------------
     ------------------     |                |      ------------------
     | GameLevelModel |------                -------|  LevelSCNView  |
     ------------------                             ------------------

版本 B) 基于场景(一个 VC,多个场景)

我将只有一个视图控制器 (GameViewController) 及其视图 (SCNView),它们将持续整个应用程序生命周期。 (我将在这里有容器和嵌套的 VC。)

应用状态(Menu/GameLevel/Config/etc)之间的行为和内容的变化是通过场景和不同 TouchHandler(每个状态的 UIResponder 子类)之间的唯一视图转换来实现的,在相关时给予 First Responder 状态。

(Menu-、GameLevel- 等)TouchHandler 类只会覆盖touchesBegan/Moved/EndedcanBecomeFirstResponder,这样我就可以拦截触摸事件而不会使单个控制器和视图膨胀。我的其他对象都不是 UIResponder。我还没有完全测试过这部分。

对于每个应用程序状态,将创建该状态所需的对象,例如相应的 Scene、TouchHandler、(在 GameLevelState 中具有游戏关卡逻辑的 GameLevelModel)等,并在进入下一个状态时释放。

我目前的问题是,当使用 SKTransition 在单个 SCNView 上的多个 SCNScene 之间转换时,在 5-20 次转换后会出现内存泄漏。我尝试通过简化场景、在解除分配之前删除所有动作、节点来解决,但没有任何帮助。避免泄漏的唯一方法是避免 SKTransition 并直接将场景分配给 View 的场景属性scnView.scene = scnScene。所以这需要我自己制作过渡动画。

                         --------------------------
                         |   GameViewController   |
                         --------------------------
                           |  |   |             |
     ------------------    |  |   |             |      ------------------
     | GameLevelsData |-----  |   |             -------|     SCNView    |
     ------------------       |   |                    ------------------
     ------------------       |   |                            |
     |   PlayerData   |--------   |                            |
     ------------------           |                            |
                                  |                            |
                       ----------------                        |
                       | StateMachine |                        |
                       ----------------                        |
                         |                            ------------------
                         |-(*MenuState)---------      |  MenuSCNScene  |
                         |-(LevelState)     |  |      ------------------
                         |-(ConfigState)    |  |
                                            |  |
                     --------------------   |  |
                     | MenuTouchHandler |---|  |
                     --------------------      |
                      -----------------        |
                      |   MenuData    |---------
                      -----------------

              (When entering, each state creates objects of corresponding
              dedicated subclasses for its Model, TouchHandler and Scene)
                                            |  |              |
                    --------------------    |  |     -------------------
                    | LevelTouchHandler |---|  |     |  LevelSCNScene  |
                    --------------------       |     -------------------
                     ------------------        |
                     | GameLevelModel |---------
                     ------------------

提前致谢。

【问题讨论】:

  • 我会在gamedev.stackexchange.com问这个问题
  • 谢谢大卫,我也是这么想的,首先在那里搜索答案和解决方案,但发现很少,然后在这里查看,stackoverflow 上还有更多内容。例如,在 GameDev 搜索“SceneKit”会得到 27 次点击,而在这里我得到 3400 次 :)

标签: architecture game-engine scenekit


【解决方案1】:

我不太了解您的术语,尤其是“子 MVC”的概念。 iOS 中的首字母缩写词 MVC 通常指的是模型-视图-控制器模式,在这种情况下,我不知道子 MVC 会是什么。

多个 SCNView 没有任何问题,选项 B 中显示您将它们用于不同目的。请记住,您不必在 SCNView 中执行所有操作。你仍然可以使用 UIView/NSView,甚至是 SKView。对菜单和控件使用 SKView 是一种常见的方法。

实现您自己的触摸处理程序和过渡动画在 IMO 中是多余的。使用那里的东西。这就是 SceneKit 的用途。

我希望看到您将游戏逻辑从视图控制器逻辑中分离出来。考虑一下当您想将游戏转移到 tvOS 或 macOS 时会发生什么。如果游戏逻辑在 VC 中,您将有很多放松的事情要做。将它们分开也避免了 MVC 的其他含义之一,“Massive View Controller”,试图一次处理所有任务的臃肿混乱。

许多在线教程和代码示例将所有内容都放在一个游戏/视图控制器类中,虽然这对于简单的示例和玩具问题来说很好,但这并不是一种有助于清洁代码、良好维护和单元可测试性的做法。如果我的游戏模型逻辑在一个单独的类中(可能拥有状态机),我可以轻松地为该部分编写单元测试。

【讨论】:

  • 谢谢,哈尔。我编辑(有效地重写)了这个问题,使其更清楚。 “子 MVC”是指嵌套在容器 VC 中的 MVC,如父子关系或容器嵌套关系。 Apple 在他们的 iOS 视图控制器编程指南中经常使用“子 VC”,我显然记得它是“子 MVC”,因为我们有效地嵌套了控制器管理的整个 MVC。关于游戏逻辑,我将在 GameLevelModel 对象(从 LevelData 重命名)中得到它。我正在寻找使应用程序数据驱动的顶级架构,但仍未达到游戏级模型。
【解决方案2】:

我将根据 Apple 为 SceneKit 提供的示例代码的设计来解释我实际正在从事的真实游戏设计。

正如@HalMuelller 正确指出的那样,您需要将游戏逻辑提取到独立于平台的层中,即使您正在为一个平台编写游戏也是一个好主意,因为这将有助于您进行测试。

正如你在上面的类图中看到的:

右侧是整个游戏逻辑,以 Game 类开始,它实际上用作场景和物理代理。这只是一个普通的旧 ObjC 或 Swift 类。

Game 类的主要职责之一是从 SceneKit/Model IO 支持的文件或根据您的情况以编程方式构建场景 SCNScene

Game 类也指其他 Model 类,例如加载 3D 骨骼动画的 Animation 和包含玩家数据等统计数据的 Player 类,这里的 Player 指的是一些游戏中的棒球运动员。

您还可以看到有一个由BatterComponent 组成的PlayerEntity 类,这是基于GamePlayKit 的实体组件设计。现在BatterComponent 有一个StateMachine 来处理击球手在各种状态之间的转换。

另一个重要的组件是BatterControlComponent,只有在用户击球时才将其添加到实体中。这也是一个依赖于平台的组件,因此您可以在 macOS/iOS 目标中独立编写它。同样,连击控制组件由状态机支持,因为状态机也可用于处理控制转换。此控件实现为 SpriteKit 场景,覆盖在 SCNScene 之上。

如果您正在投球,那么您可以使用由另一个状态机支持的PitcherComponent 配置玩家实体,依此类推。


在类图的左侧,您可以看到有一个GameViewController,它设置了一个SCNView,还实例化了一个Game 对象,这个游戏对象的场景就是您的游戏视图控制器场景视图渲染的内容。您还确保将场景视图委托设置为游戏对象。


您必须认真参考 Apple 的 SceneKit 示例代码和 GameKit 的示例。在 Apple 开发人员中,示例代码部分搜索:SceneKitGameplayKit


总体而言,您必须根据您的游戏要求使用普通的旧 Swift/ObjC 类和来自GamePlayKit 的适当模式来设计一个独立于平台的游戏逻辑层。


关于生产使用的说明。请参阅 this memory leak issueanimation issues,所以我不确定 SceneKit 是否已准备好迎接黄金时段,但 Google 是您的朋友。话虽如此,如果您已经在Apple平台上工作,SceneKit真的很棒编程。我相信 SceneKit 每年都在改进,在接下来的 2 个版本(WWDC 17、18)中,它应该是一个非常适合 Apple 平台的游戏引擎。根据我的经验,这些是我的 2 美元,YMMV。

【讨论】:

  • 感谢您的回复。我编辑(有效地重写了)我的问题以使其更清晰。我正在查看的架构目前只是高级部分。希望以自上而下的方法将其设计为数据驱动的应用程序。一旦我完成这项工作,我将详细了解游戏关卡模型(在图表上将其重命名为 GameLevelModel),并在那里考虑您的提示。我研究了 Apple 的示例和实体组件方法,但我可能只对游戏对象使用简单的类层次结构,它只有 2-3 个类、玩家和敌人的太空船和收藏品。
  • 您打算使用 SKTransition 在 SCNScene 之间进行过渡吗?大概是由您的 GameViewController 驱动的。在 5-20 次场景转换后,我经常会出现内存泄漏。但是,我不得不提一下,我使用的是 iOS 9,以便我也可以在我的版本中涵盖它,这些问题现在可能在 iOS 10 中得到解决。
  • 我已经编辑了关于生产使用的注释,包括内存泄漏和动画问题。
  • 将此回复标记为已接受,因为 Dmsurti 尽其所知回复了该回复,但仍有一些问题仍未解决,即:1) 在单个 SCNView 上转换场景时出现内存泄漏,2) Game 对象可能变得有点太大了,尽管这可以通过组合来解决。
猜你喜欢
  • 2019-02-06
  • 2011-07-24
  • 2015-12-24
  • 2016-09-27
  • 2012-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-01
相关资源
最近更新 更多