回答你的问题有难度,因为你同时在问:“我现在应该为乒乓球做什么”和“将来在一般游戏中应该做什么”。
要制作乒乓球游戏,甚至不需要Ball和Paddle这样的类,因为它们基本上只是位置。只需将以下代码放入您的Game类中:
Vector2 ballPosition, ballVelocity;
float leftPaddlePosition, rightPaddlePosition;
然后只需在游戏的 Update
和 Draw
函数中以任何适合您的顺序更新和绘制它们。简单!
但是,假设您想创建多个球,并且球具有许多属性(位置、速度、旋转、颜色等):您可能希望创建一个Ball
类或结构体,使其可以实例化(对于挡板也是一样)。甚至可以将某些函数移动到该类中,使它们成为自包含的函数(Draw
函数就是一个很好的例子)。
但要保持设计概念相同-所有对象之间的交互处理(即游戏玩法)都发生在Game
类中。
如果您只有两个或三个不同的游戏元素(或类),那么这很好。
然而,让我们假设有一个更复杂的游戏。我们拿基本的乒乓球游戏来举例,加入一些弹球的元素,例如多球和玩家控制的翻板。让我们从贪吃蛇中添加一些元素,例如一个由AI控制的“蛇”,以及一些可以被球或蛇撞击的捡起物品。为了增加趣味,让我们说挡板也可以像太空侵略者中那样发射激光,并且激光雷达根据撞击的物体不同有不同的效果。
天哪,这是一堆复杂的交互!我们该如何应对?我们无法将所有内容都放在Game
类中!
简单!我们创建一个接口(或抽象类或虚拟类),每个游戏世界中的“物体”(或“角色”)都将从中派生。以下是一个示例:
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; }
}
(这只是一个例子。并不存在“真正的演员接口”,适用于每个游戏,您需要设计自己的接口。这就是为什么我不喜欢 DrawableGameComponent
的原因。)
拥有一个共同的界面使得游戏只需要与角色交流 - 而不需要了解游戏中的每个类型。它只需处理每个类型都具有的常见事项 - 碰撞检测,绘制,更新,加载,卸载等。
一旦你进入角色,就可以开始关注特定类型的角色。例如,这可能是 Paddle
中的一个方法:
void Touched(IActor by)
{
if(by is Ball)
((Ball)by).BounceOff(this.BoundingBox);
if(by is Snake)
((Snake)by).Kill();
}
现在,我想让球通过挡板弹起来,但这纯粹是个人口味问题。你可以反过来做。
最终你应该能够将所有的演员都放在一个大列表中,在游戏中简单迭代它们。
实际上,出于性能或代码简洁性等原因,您可能会拥有多个不同类型的演员列表。这没问题-但总的来说,尽量坚持游戏只知道通用演员的原则。
演员还可能希望查询存在哪些其他演员,出于各种原因。因此,给每个演员一个对Game的引用,并使演员列表在Game上公开(当您编写游戏代码并且它是您自己的内部代码时,没有必要非常严格关注public/private)。
现在,您甚至可以采取更进一步的措施,并拥有多个接口。例如:一个用于渲染,一个用于脚本和AI,一个用于物理等。然后有多个实现,可以组合成对象。
这在这篇文章中有详细描述。我在这个答案中有一个简单的例子。如果您发现自己的单个演员接口开始变成更多的抽象类“树”,那么这是一个适当的下一步。