【问题标题】:Monogame game settings单人游戏设置
【发布时间】:2023-09-02 02:41:01
【问题描述】:

我正在开发一个使用 C# 和 monogame 的平台游戏作为一个有趣的项目来提高我的技能。我想添加对音频、视频、控件等设置的支持,但我不知道最好的方法。

我打算有一个存储所有设置的 XML 文件,如下所示:

<Settings>
  <Audio>
    <masterVolume>100</masterVolume>
    <musicVolume>40</musicVolume>
    <sfxVolume>90</sfxVolume>
  </Audio>
  <Video>
    <Resolution>
      <width>1920</width>
      <height>1080</height>
    </Resolution>
  </Video>
  <Controls>
    <forward>W</forward>
    <back>S</back>
    <left>A</left>
    <right>D</right>
  </Controls>
</Settings>

我的计划是使用 Settings 类,该类具有用于音频、视频和控件等的静态变量,这些变量分为单独的类,以便我可以通过 Settings.Audio.MasterVolume 访问它们>Settings.Controls.Forward。 我想使用 Linq 将设置从 XML 加载到这些类中,并且我想在用户更改设置时将设置保存到 XML 文件中。

我知道也可以使用单例,但就我所做的研究而言,它有太多缺点。这是真的吗?

这是执行此操作的好方法,还是有更好的方法可以让我在整个项目中访问设置而不让所有内容都保持静态?

提前致谢!

【问题讨论】:

  • 使用单例的主要问题是“全局静态”。您描述的解决方案正是“具有音频、视频和控件的静态变量的设置类”。需要明确的是,纯 static 方法很好。全局常量很好,游戏都是关于管理状态的,但是以这种方式将所有 3 结合起来可能会导致问题。

标签: c# xml linq linq-to-xml monogame


【解决方案1】:

你的方法有效。但是,您限制了类的重用。

如果你向别人描述MyClass,你会说读取配置参数的方法对于MyClass是必不可少的吗?换句话说:如果不是从配置文件中读取配置参数,而是从 CSV 文件、互联网或其他任何方法中读取配置参数,那么您的类的大多数代码是否毫无意义?

你可能会说不:如果要以不同的方式提供配置参数,我的类的大部分代码都可以重用。

那么为什么要将你的类限制为只有从配置文件中读取配置参数才能工作的类呢?

我猜你目前有类似的东西:

static class ConfigurationSettings
{
    public static int LeftControl => ...
    public static int RighControl => ...
}

class MyClass
{
    // MyClass uses configuration settings:
    public int LeftControl => ConfigurationSettings.LeftControl;
    public int RightControl => ConfigurationSettings.RightControl;

    ...
}

您不能实例化MyClass 的两个对象,每个对象都有自己的一组配置参数。您不能创建使用不同方法提供配置值的MyClass 对象。

我经常需要的一个用例是在单元测试时:我想要一种快速的方法来更改配置设置,而无需更改单元测试项目的配置文件。

让您的类的用户提供不同的设置方法的一种简单方法是使用设置创建一个接口并为该接口提供一个构造函数:

interface IMySettings
{
    int LeftControl {get;}
    int RightControl {get;}
    ...
}

class ConfigurationSettings : IMySettings
{
     // ConfigurationSettings reads settings from the configuration file
}

class MyClass
{
    // default constructor use the configuration file via ConfigurationSettings:
    public MyClass() : this (new ConfigurationSettings())
    {
    }

    // extra constructor for those who don't want to use configuration file
    public MyClass(IMySettings settings)
    {
        this.Settings = settings;
    }

    protected readonly IMySettings settings;

    // MyClass uses settings:
    public int LeftControl => this.settings.LeftControl;
    public int RightControl => this.settings.RightControl;
}

想要使用配置文件中的设置的用户可以使用默认构造函数;需要特定设置的用户使用替代构造函数。如果您想要两个不同的 MyClass 对象,也可以使用此替代构造函数,每个对象都有自己的设置。

乍一看,您似乎仍然可以使用配置文件进行最小的更改。但是,为了能够实现接口,读取配置文件(MyClass)的类不能是静态的。

但是,您是对的:只有一个配置文件,因此应该只有一个从配置文件中读取的类的实例:创建一个单例。

根据我的经验,代表配置的类是使用单例而不是静态类的常用原因之一

【讨论】:

    【解决方案2】:

    在我的游戏项目中,我总是使用一些静态类(和/或变量)来保存在整个游戏中都有效的信息。 如果您希望能够从游戏的不同点更改音量等,这可能会派上用场,尤其是对于音频设置。 这不一定只包括用户设置。考虑在某个角色说话时降低背景音乐。

    长话短说: 我会选择静态方法。但注意不要超过它。

    【讨论】: