我想摆脱我的“制作一切静态和全局”设计模式,但是如何做呢?


11

我正在太空中制作一个小地牢爬行者,我想听听一些有关如何使引擎后端更好的建议。基本上,当前所有内容都是基于管理人员的:

  • BackgroundManager:具有AddBackground(image, parallax)制作炫酷背景效果的方法。
  • ConfigManager:读取/制作配置文件,还保存从该配置文件读取的数据。
  • DrawManager:有Draw(sprite)方法来绘制的东西到屏幕上,事情就是这样SetView(view)GetView()等等。
  • EntityManager:包含所有活动实体,并具有添加,删除和查找实体的方法。
  • DungeonManager(实际上称为GridManager,但这是为了简单):有方法,如GenerateDungeon()PlaceEnemies()PlaceFinish()等。

现在我什至没有列出所有我的经理,所以我认为这必须改变。我的游戏也基于屏幕,这使它更令人讨厌,因为我不需要经理管理的一半内容,例如主菜单(我的主菜单中不需要物理/实体/地下城) !)

现在,我考虑使管理器不是静态的,并为我的ScreenMainGame提供所有特定于游戏的管理器的实例。但这使打电话给/获取与管理人员有关的任何东西变得一团糟...例如,如果我想从不在的任何地方绘制地牢ScreenMainGame.Draw(),我就必须这样做...

((ScreenMainGame)ScreenManager.CurrentScreen).GridManager.Draw()

真丑。

那么,游戏开发者们,你们有谁知道如何解决这个混乱局面吗?我将不胜感激!


我不确定究竟如何为您提供帮助,但是我建议您读一本关于设计模式的好书。我读过《 Head First设计模式》,这很容易理解。这些示例是用Java编写的,但是我认为这足够接近C#来为您提供帮助。抱歉,如果我的建议太新手了。
Amplify91 2011年

您正在使用什么来与图形卡接口?XNA?SlimDX?
BlueRaja-Danny Pflughoeft

@BlueRaja:我正在使用SFML.NET。
Dlaor 2011年

在这种情况下,您应该只使用普通的方法来处理C#中的这些问题:请参阅下面的最后一个链接,以及什么是依赖注入?
BlueRaja-Danny Pflughoeft

Answers:


10

怎么样一个基于组件的引擎

您将有一个名为的主类Engine,该主类将保留一个列表GameScreens,而它们本身将包含一个列表Components

该发动机具有UpdateDraw方法都调用GameScreenUpdateDraw方法,其本身经历的每个组件和呼叫UpdateDraw

如此呈现,我同意这听起来像是一个糟糕而重复的设计。但是请相信我,与所有旧的管理器类相比,通过使用基于组件的方法,我的代码变得更加整洁。

维护这样的代码也要简单得多,因为您只需要遍历一个大的类层次结构,而不必搜索BackgroundManager所有特定的不同背景。你只需要一个ScrollingBackgroundParallaxBackgroundStaticBackground等所有从一个派生Background类。

最终,您将构建一个非常可靠的引擎,可以使用很多常用的组件和辅助方法(例如,FrameRateDisplayer作为调试实用程序,Sprite作为基本Sprite 的类以及带有纹理和矢量扩展方法的类)在所有项目中重用和随机数生成)。

您将不再有一个BackgroundManager类,而是一个Background可以自行管理的类。

游戏开始时,基本上您要做的就是:

// when declaring variables:
Engine engine;

// when initializing:
engine = new Engine();
engine.Initialize();
engine.LoadContent();
engine.AddGameScreen(new MainMenuScreen());

// when updating:
engine.Update();

// when drawing:
engine.Draw();

这就是您的游戏开始代码。

然后,对于主菜单屏幕:

class MainMenuScreen : MenuScreen // where MenuScreen derives from the GameScreen class
{
    const int ENEMY_COUNT = 10;

    StaticBackground background;
    Player player;
    List<Enemy> enemies;

    public override void Initialize()
    {
        background = new StaticBackground();
        player = new Player();
        enemies = new List<Enemy>();

        base.AddComponent(background); // defined within the GameScreen class
        base.AddComponent(player);
        for (int i = 0; i < ENEMY_COUNT; ++i)
        {
            Enemy newEnemy = new Enemy();
            enemies.Add(newEnemy);
            base.AddComponent(newEnemy);
        }
    }
}

您有大致的想法。

您还将Engine在所有GameScreen类中保留的引用,以便即使在一个GameScreen类中也可以添加新屏幕(例如,当用户在内单击StartGame按钮时MainMenuScreen,可以切换到GameplayScreen)。

这同样适用于在Component类:它应该持有其母公司的参考GameScreen,同时具有接入Engine类和其父GameScreen添加新的组件(例如,你可以创建一个名为HUD相关类DrawableButton持有一个 DrawableText组件和StaticBackground组件)。

之后,您甚至可以应用其他设计模式,例如“服务设计模式”(不确定确切的名称),您可以在Engine类中保留不同的有用服务(您只需保留IServices 的列表,并让其他类自己添加服务) )。例如,我会Camera2D在整个项目中保留一个组件作为服务,以便在绘制其他组件时应用其转换。这样避免了将其作为参数传递到任何地方。

总之,肯定会有其他更好的引擎设计,但是我发现通过此链接提出的引擎非常优雅,极易维护和可重复使用。我个人建议至少尝试一下。


1
XNA通过GameComponent和服务支持所有这些现成的。无需单独的Engine课程。
BlueRaja-Danny Pflughoeft

为此答案+1。另外,将绘图与游戏更新线程放在单独的线程中是否有意义?
f20k 2011年

1
@BlueRaja-DannyPflughoeft:的确如此,但是我认为未使用XNA,所以我对操作方法进行了更多说明。
杰西·埃蒙德

@ f20k:是的,与XNA几乎相同。我不知道它是如何实现的。= //互联网上一定有一篇文章介绍如何做。:)
Jesse Emond

是的,我没有使用XNA,但这似乎是我正在做的一个不错的选择。我目前正在阅读《 Head First设计模式》这本书,以了解是否还有其他选择。干杯!
Dlaor 2011年


-3

仅因为它们是静态的或全局的,并不意味着它们必须始终被加载/初始化。使用Singleton模式,并允许管理器可释放并根据需要再次加载。


4
单例只是OP试图在此处解决的基本问题的基础。
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.