首页 > 解决方案 > Unity - 游戏模式作为状态机?

问题描述

我正在 Unity 中制作问答游戏,但遇到了架构问题。

我希望游戏有一些游戏模式,比如标准的、更快的答案 - 更多的分数等。每种模式都有自己的特定方式,但有些事情会非常相似,比如回答问题、开始计时器等。

目前其结构基于。有一个QuizSystem引用QuestionDatabaseUIReferences(按钮,得分文本等)和GameSettings(每个游戏/每个模式等的问题)。

要开始游戏,您需要调用QuizSystem.Start()它,它会启动它的电流GameMode,它源自抽象StateMachine并且是一种单一行为(不知道是否有必要)。我还有抽象State类,不同的游戏状态将从中派生。它有一个带有(GameMode owner)as 参数的构造函数和 3 个函数:Start(), Tick(), End().

所以,这样我就可以拥有Standard游戏模式,它会启动让我们说StandardPreparationState,最终会调用StandardAnswerState哪个会启动计时器并等待用户输入并再次调用StandardPreparationState。循环将重复,直到达到每个模式数量的问题,然后将下一个操作委托给QuizSystem

这种方法的优点是每种模式都可以以自己的方式运行,例如在其间添加额外的步骤,但这有点限制了可重用性。我的意思是,如果有些OtherMode人具有相同的准备功能但之后的动作不同,它就不会起作用,因为 StandardPreparationState会过渡到StandardAnswerState.

我可以添加另一个参数,例如构造函数(GameMode owner, State transitionTo)State但这似乎是错误的,我不知道为什么 xD

我想知道的是你们如何为你们的游戏实现不同的游戏模式?您是否将每种模式都作为单独的场景?或者也许使用状态机模式并拥有Manager处理启动/交换模式的类?

我知道每场比赛都是不同的,但可能有一些共同的方法吗?

提前致谢!

标签: unity3darchitecturestate-machine

解决方案


这个问题是相当开放和基于意见的。然而,“通用”的方法很少,其中最重要的方法之一就是让游戏“数据驱动”。


什么?为什么?如何?

想象一下,您正在使用太空射击游戏,您的飞船在其中飞来飞去并捡起枪支。每次添加新枪时,您都必须对它的伤害、射弹的种类以及射出的数量、颜色、产生的模式、速度、大小等进行编码...

每次您想添加一把新枪时,您都需要输入代码并在此处进行更改、编译、... 大量工作。

相反,人们想,“我们为什么不创建一个包含所有参数的简单类?我们将使它在 Unity 中可编辑,在项目中实例化它,我们不需要编写那么多代码。”

这是 Unity 带来Scriptable object的时候。


可编写脚本的对象

AScriptableObject是一个数据容器,可用于保存大量数据,独立于类实例。的主要用例之一ScriptableObjects是通过避免值的副本来减少项目的内存使用量。

这个想法是为您的模式创建可编写脚本的对象并设置模式使用的多种修饰符。文件夹结构可能如下所示:

> ScriptableObjects
| |--> Modes
|    |-> NormalSO (instance)
|    |-> HardWithLotOfExpSO (instance)
|    |-> EasyWithLowerExpSO (instance)
> Script
  |--> ScriptableObjects
     |-> ModeSO

ScriptableObject是内部没有真正逻辑的类,只是创建用于保存数据的“结构”。此类的示例是:

public class ModeSO : ScriptableObject
{
    public string modeName;

    public float scoreMultiplier;
    public int numberOfEnemiesMaxAlive;
    public int numberOfEnemiesTotal;

    public Vector3[] spawnPoints;
}

然后在 Unity 本身中创建此类对象的实例。那么与其他班级的互动呢?好吧,他们会像这样工作:

  • 游戏管理器持有活动模式的单个实例
  • 将处理分数的类(例如player/ scoreboard)或Enemy会询问GameManager分数的当前乘数是多少
  • WorldSpawner会问GameManager他应该产生多少敌人,在哪里以及何时产生下一个
  • 在游戏开始时,您可以按名称选择难度

其中一类(记分牌)的示例:

public class ScoreBoard: MonoBehavior
{
    GameManager manager;

    private float totalScore;

    OnEnemyDestroyed(float scoreForEnemy)
    {
        totalScore += scoreForEnemy * (manager?.activeMode?.modifier ?? 1);
    }
}

最好的是,每当您更改某些数据时,您只需修改 Unity 中的现有实例。无需进入代码。无需重新编译整个游戏。


推荐阅读