游戏架构

时间:2011-03-28 12:08:05

标签: c# architecture xna xna-4.0

我对我正在制作的XNA游戏有疑问,但它也是未来游戏的一般性问题。我正在制作Pong游戏而且我不确切知道要在哪里更新,所以我会更好地解释我的意思。我有一个类Game,Paddle和Ball,例如,我想验证球与屏幕限制或桨之间的碰撞,但我遇到了两种方法来做到这一点:

更高级别的方法 - 将paddle和ball属性公开,并在Game.Update上检查碰撞?

低层次方法 - 我将所需的每个信息(屏幕限制和拨片信息)提供给球类(通过参数,或在公共公共静态类中)和Ball.Update我检查了碰撞?

我想我的问题更通用的方式是:

对象是否需要知道如何更新和绘制自己,甚至是从某种程度上提供给它们的更高级别的依赖项?

最好在Game.Update或Game.Draw中使用更高级别处理它,还是使用Managers来简化代码?

我认为这是一个适用于每个游戏的游戏逻辑模型问题。我不知道我的问题是否清楚,如果没有,请随时问。

4 个答案:

答案 0 :(得分:46)

回答你的问题的困难部分是你要问两个:“我现在应该做什么,对于乒乓球”和“我应该在以后做什么,在一些通用游戏上”。


为了制作Pong你甚至不需要Ball和Paddle课程,因为他们基本上只是位置。在你的游戏课中坚持这样的东西:

Vector2 ballPosition, ballVelocity;
float leftPaddlePosition, rightPaddlePosition;

然后只需在游戏的UpdateDraw功能中以适合您的顺序更新并绘制它们。简单!


但是,假设您想创建多个球,并且球具有许多属性(位置,速度,旋转,颜色等):您可能想要创建一个Ball类或结构,您可以实例化(相同)去划桨)。您甚至可以将某些函数移动到它们自包含的类中(Draw函数就是一个很好的例子)。

但保持设计概念相同 - 所有对象 - 对象交互处理(即:游戏玩法)都发生在Game类中。

如果您有两个或三个不同的游戏元素(或类),这一切都很好。


然而,让我们假设一个更复杂的游戏。让我们采取基本的乒乓球比赛,添加一些弹球元素,如多球和球员控制的脚蹼。让我们从Snake中添加一些元素,比如我们有一个AI控制的“蛇”以及一些球或蛇可以击中的拾取物。并且为了更好的衡量,我们可以说桨也可以像太空侵略者那样射击激光器,激光螺栓可以根据它们的击打而做不同的事情。

Golly 这是一个巨大的互动混乱!我们如何应对呢?我们不能把它全部放在游戏中!

简单!我们创建一个接口(或抽象类或虚拟类),我们的游戏世界中的每个“东西”(或“演员”)将来自。这是一个例子:

interface IActor
{
    void LoadContent(ContentManager content);
    void UnloadContent();

    void Think(float seconds);
    void UpdatePhysics(float seconds);

    void Draw(SpriteBatch spriteBatch);

    void Touched(IActor by);

    Vector2 Position { get; }
    Rectangle BoundingBox { get; }
}

(这只是一个例子。没有“一个真正的演员界面”将适用于每个游戏,你需要设计自己的。这就是为什么我不喜欢{ {1}}。)

拥有一个通用界面允许Game只谈论Actors - 而不需要知道游戏中的每一种类型。只需要做各种类型的共同事情 - 碰撞检测,绘图,更新,加载,卸载等。

一旦你 in 演员,你就可以开始担心特定类型的演员了。例如,这可能是DrawableGameComponent中的方法:

Paddle

现在,我喜欢让球被桨反弹,但这真的是一个品味问题。你可以反过来做。

最后,您应该能够将所有演员都放在一个大型列表中,您只需在游戏中进行迭代。

实际上,出于性能或代码简单性的原因,最终可能会有多个不同类型的actor列表。这是可以的 - 但总的来说,试着坚持游戏的原则只知道通用演员。

演员也可能想要查询出于各种原因存在的其他演员。因此,给每个演员一个参考游戏,并在游戏中公开演员列表(当你编写游戏代码并且它是你自己的内部代码时,不需要对公共/私人超级严格。)


现在,您甚至可以更进一步,拥有多个接口。例如:一个用于渲染,一个用于脚本和AI,一个用于物理等。然后有多个实现可以组成对象。

this article详细介绍了这一点。我在this answer中有一个简单的例子。如果你开始发现你的单个actor接口开始变成更多抽象类的“树”,那么这是一个合适的下一步。

答案 1 :(得分:2)

您也可以选择开始考虑游戏的不同组件如何相互通信。

Ball and Paddle,两者都是游戏中的对象,在这种情况下,是可渲染的可移动物体。 Paddle具有以下标准

它只能上下移动

  1. 桨叶固定在屏幕的一侧或底部
  2. 桨可能由用户控制(1对计算机或1对1)
  3. 可以渲染球拍
  4. 只能将挡板移动到屏幕的底部或顶部,它无法通过它的边界
  5. 球具有以下标准

    它不能离开屏幕边界

    1. 可以渲染
    2. 根据它在桨上的击中位置,你可以间接控制它(一些简单的物理学)
    3. 如果它落在桨的后面,圆形完成
    4. 比赛开始时,球通常会附着在丢失的人的桨上。
    5. 确定可以提取界面的通用标准

      public interface IRenderableGameObject
      {
         Vector3 Position { get; set; }
         Color Color { get; set; }
         float Speed { get; set; }
         float Angle { get; set; }
      }
      

      你也有一些GamePhysics

      public interface IPhysics
      {
          bool HasHitBoundaries(Window window, Ball ball);
          bool HasHit(Paddle paddle, Ball ball);
          float CalculateNewAngle(Paddle paddleThatWasHit, Ball ball);
      }
      

      然后有一些游戏逻辑

      public interface IGameLogic
      {
         bool HasLostRound(...);
         bool HasLostGame(...);
      }
      

      这不是所有逻辑,但它应该让您了解要查找的内容,因为您正在构建一组库和函数,您可以使用它们来确定将要发生的事情以及可能发生的事情以及如何当这些事情发生时你需要采取行动。

      另外,看看这个,你可以改进和重构它,这样它就是一个更好的设计。

      了解您的域名并撰写您的想法。 计划失败正计划失败

答案 2 :(得分:1)

你可以看到你的球和桨作为游戏的一个组成部分,XNA为你提供了一个基类GameComponent,它有一个Update(GameTime gameTime)方法,你可以覆盖它来做逻辑。此外,还有DrawableGameComponent类,它有自己的Draw方法来覆盖。每个GameComponent类都有一个Game属性,用于保存创建它们的游戏对象。在那里,您可以添加一些服务,您的组件可以使用它来自行获取信息。

您想要制作哪种方法,要么拥有" master"处理每个交互,或者向组件提供信息并让它们自己做出反应的对象完全取决于您。后一种方法在大型项目中是首选。此外,这将是面向对象的处理事物的方式,为每个实体提供自己的Update和Draw方法。

答案 3 :(得分:0)

我同意安德鲁所说的话。我也在学习XNA和我的课程,例如你的球类。我至少有一个更新(游戏时间)方法和一个Draw()方法。通常是Initialize(),Load()。然后从主游戏课我将从他们各自的表兄弟那里调用这些方法。这是在我了解GameComponent之前。这是一篇关于你是否应该使用它的好文章。 http://www.nuclex.org/blog/gamedev/100-to-gamecomponent-or-not-to-gamecomponent