存储游戏范围变量的最佳方法


23

我有一个选项屏幕来处理诸如难度,分辨率,全屏等内容,但是我正在努力寻找在运行时存储/获取这些变量的“最佳”方法。

当前,我实现了一个Constants包含所有GameOption枚举的类,但是如何为所有这些选项选择默认值?另外,如何获取当前选择的枚举?

具体来说,关于分辨率,我决定存储这些值,但是我不确定如何获取默认值或当前存储的值。任何方向都很好;谢谢!:)

namespace V1.test.RPG
{
  public class GameOptions
  {
    public enum Difficulty { EASY, MEDIUM, HARD }
    public enum Sound { ON, QUIET, OFF }
    public enum Music { ON, QUIET, OFF }
    public enum ResolutionWidth
    {
        SMALL      = 1280,
        MEDIUM     = 1366,
        LARGE      = 1920,
        WIDESCREEN = 2560
    }
    public enum ResolutionHeight
    {
        SMALL      = 800,
        MEDIUM     = 768,
        LARGE      = 1080,
        WIDESCREEN = 1080
    }
    public Boolean fullScreen = false;
  }
}

NB:我在SO那里问过,他们指出我去了这个地方。那里有评论,但我想听听其他方式/最常用的方式。


1
你问对了地方;送你过来的人都错了。无论如何,我回答了这个问题以帮助您,但这不是游戏开发的特定问题,这是一个通用的编程问题。
2014年

我刚刚读了SO线程;我喜欢斯科特·张伯林的回答。
2014年

@jhocking我这样指出他,以防游戏开发存在某些可能与普通应用程序不同的方面。我还认为你们可能已经对此主题进行了规范的问答,因为它是如此普遍。
克里斯·海斯

与有关全局变量的实际问题相切,请不要假设那里有一组固定的解决方案。
Lars Viklund

Answers:


32

计划增长:
硬编码的常量适合于小型项目,但是最终,随着软件规模的增长,您希望可以更改这些设置而不必重新编译所有内容。很多时候,您会想在游戏运行时更改设置,而不能使用硬编码的常量来进行更改。

CVars:
项目扩展后,您可能需要看一下CVAR。可以说,CVAR是“智能变量”,您可以在运行时通过控制台,终端或UI对其进行修改。CVAR通常根据包装基础值的对象来实现。然后,对象可以跟踪值以及将其保存到文件或从文件中加载。您可以将CVAR对象存储到映射中,以使用名称或其他唯一标识符来访问它们。

为了进一步说明该概念,以下伪代码是包装int值的CVAR类型的简单示例:

// just some flags to exemplify:
enum CVarFlags {
    CVAR_PERSISTENT, // saved to file once app exits
    CVAR_VOLATILE    // never saved to file
};

class CVar {
public:
    // the constructor registers this variable with the global list of CVars
    CVar(string name, int value, int defaultValue, int flags);

    int getValue();
    void setValue(int v);
    void reset(); // reset to the default value

    // etcetera...

private:
    int flags; // flags like: save-to-file, etc.
    int value; // the actual value
    int defaultValue; // the default value to reset the variable to
};

// global list of all CVars:
map<string, CVar> g_cvars;

全局访问:
在上面的示例中,我假设的构造函数CVar始终将变量注册到全局cvars映射中。这非常有用,因为它允许您像这样声明一个变量:

CVar my_var = new CVar("my_var", 0, 42, CVAR_PERSISTENT);

该变量将自动在全局映射中提供,您可以通过使用变量名称索引映射来从其他任何地方访问它:

CVar v = g_cvars.find("my_var");

持久性:
关闭游戏时,请迭代地图并将所有标记为的变量保存CVAR_PERSISTENT到文件中。下次游戏开始时,请重新加载它们。

案例法:
有关健壮的CVAR系统的更具体示例,请查看Doom 3中的实现。


4

好了第一关枚举定义了什么价值可以,没有什么价值。因此,在声明枚举之后,仍然需要声明另一个变量。例如:

public enum Sound
{
    ON,
    QUIET,
    OFF
}

public Sound soundValue;

在此示例中,您现在可以设置soundValue为ON,QUIET或OFF。


然后,您仍然需要结构化代码,以便代码的其他部分可以访问此“设置”对象。我不知道您是否也需要该部分的帮助,但是解决此问题的常用模式包括单例(这些日子已经很皱眉)或服务定位器或依赖项注入。


1

glampert解决方案非常完整,但我会增加我的个人经验。

我遇到了同样的问题,我的解决方案是使用静态Variables类。

Variables类在内部保留一个字符串到另一个字符串的映射(到目前为止,我所有的变量都是字符串),并且可以通过getter和setter进行访问。

关键是访问全局变量可能会引入各种细微的错误,因为代码的完全不相关的部分会突然相互干扰。

为了避免这种情况,我施加了以下语义:set如果字典中已经存在具有该名称的变量,则使用该方法将引发异常,并get在返回变量之前将其从字典中删除。

另外两种方法可以提供您期望的结果,setAndOverwritegetAndKeep。其他方法的语义要点是,您可以轻松地发现“该方法应该初始化此变量,而其他方法却曾这样做”的错误。

为了初始化字典,初始变量存储在json文件中,然后在游戏开始时读取。

遗憾的是,我与游戏的距离还不算太远,因此我无法证明这种方法的鲁棒性。尽管如此,也许它可以在CVAR之上提供一些有趣的东西。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.