SceneKit游戏架构

时间:2017-03-29 00:02:23

标签: architecture game-engine scenekit

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

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

很高兴听到哪一个看起来更有前途,任何明显的陷阱或要避免的事情?

版本A)基于MVC,具有Custom Container&嵌套VC

我试图与这个版本保持接近的MVC范式。

应用程序的行为在其不同状态(Menu / GameLevel / Config / etc)之间发生显着变化,因此我将使用永久容器视图控制器,它将在父子关系中管理/转换几个嵌套的VC根据Apple的“适用于iOS的View Controller编程指南”。这些将是有效的整个MVC。

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

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

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

                         --------------------------
                         |   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,多个场景)

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

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

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

对于每个app状态,将创建该状态所需的对象,如相应的Scene,TouchHandler,(GameLevelState中具有游戏级别逻辑的GameLevelModel)等,并在输入下一个状态时解除分配。

我目前的问题是,当使用SKTransition在单个SCNView上的多个SCNScenes之间进行转换时,在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 |---------
                     ------------------

提前致谢。

2 个答案:

答案 0 :(得分:5)

我将解释一个真正的游戏设计,我实际上是基于Apple为SceneKit提供的示例代码的设计。

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

enter image description here

如上图所示:

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

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

Game类还引用其他Model类,例如加载3D骨架动画的Animation和包含播放器数据的Player类,例如他的统计信息, Player在这里指的是某些玩家,比如游戏中的棒球运动员。

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

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

如果您正在投球,那么您可以使用其他状态机支持的PitcherComponent配置播放器实体,依此类推。

在类图的左侧,您可以看到坐着GameViewController设置SCNView并实例化Game对象和此游戏对象的场景是你的游戏视图控制器场景视图渲染。您还可以确保将场景视图委托设置为游戏对象。

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

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

关于生产用途的说明。请参阅this memory leak issueanimation issues,因此我不确定SceneKit是否已准备好迎接黄金时段,但Google是您的朋友。话虽如此,如果您已经在Apple平台上工作,SceneKit真的很棒,所以如果您没有使用任何游戏引擎并且将游戏作为业余爱好或学习游戏,那么学习新游戏引擎的转变时间会更短节目。我相信SceneKit每年都在不断改进,在接下来的两个版本(WWDC 17,18)中,它应该是苹果平台真正优秀的游戏引擎。根据我的经验YMMV,这些是我的2美元。

答案 1 :(得分:0)

我不太关注你的术语,特别是“儿童MVC”的概念。 iOS中的首字母缩略词MVC通常是指模型 - 视图 - 控制器模式,在这种情况下,我不知道子MVC是什么。

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

实施自己的触摸处理程序和过渡动画是IMO矫枉过正。用那里的东西。这就是SceneKit的用途。

我希望你能从视图控制器逻辑中打破你的游戏逻辑。考虑当您想要将游戏移动到tvOS或macOS时会发生什么。如果游戏逻辑在VC中,那么你将有很多需要放松的事情。保持它们分离也避免了MVC的另一个含义,即“大规模视图控制器”,一个臃肿的混乱,试图一次处理所有任务。

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