如何以模块化方式对游戏对象渲染和行为进行建模?

Posted

技术标签:

【中文标题】如何以模块化方式对游戏对象渲染和行为进行建模?【英文标题】:How to model game object rendering and behavior in modular way? 【发布时间】:2011-01-15 21:06:42 【问题描述】:

我正在为 android 手机制作 Java 射击游戏。我在游戏中有 20 个奇怪的敌人,每个都有一些独特的行为,但某些行为被大多数人重复使用。我需要对子弹、爆炸、小行星等以及其他所有行为都有点像敌人的事物进行建模。我目前的设计偏向于组合而不是继承,并且表示游戏对象有点像这样:

// Generic game object
class Entity

  // Current position
  Vector2d position;

  // Regular frame updates behaviour
  Behaviour updateBehaviour;
  // Collision behaviour
  Behaviour collideBehaviour;

  // What the entity looks like
  Image image;
  // How to display the entity
  Renderer renderer;

  // If the entity is dead and should be deleted
  int dead;


abstract class Renderer  abstract void draw(Canvas c); 

abstract class Behaviour  abstract void update(Entity e); 

要绘制存储为实体图像的任何内容,您可以附加一个简单的渲染器,例如

class SimpleRenderer extends Renderer

  void draw(Canvas c)
  
    // just draw the image
  

要让实体在每一帧随机飞行,只需附加如下行为:

class RandomlyMoveBehaviour extends Behaviour

  void update(Entity e)
  
    // Add random direction vector to e.position
  

或者添加更复杂的行为,比如等到玩家靠近后再归位:

class SleepAndHomeBehaviour extends Behaviour

  Entity target;
  boolean homing;

  void init(Entity t)  target = t; 

  void update(Entity e)
  
    if (/* distance between t and e < 50 pixels */)
    
      homing = true;
      // move towards t...
    
    else
    
      homing = false;
    
  

到目前为止,我对这个设计非常满意。它很好而且很灵活,你可以例如模块化后一个类,这样你就可以提供“睡眠”行为和“清醒”行为,这样你就可以说 new WaitUntilCloseBehaviour(player, 50/pixels/, new MoveRandomlyBehaviour(), new HomingBehaviour( ))。这使得结交新敌人变得非常容易。

唯一困扰我的部分是行为和渲染器之间的通信方式。目前,Entity 包含一个 Image 对象,如果 Behavior 选择这样做,它可以修改该对象。例如,一种行为可以在睡眠和清醒图像之间改变对象,而渲染器只会绘制图像。我不确定这将如何扩展,例如:

面对某个方向的炮塔状敌人呢?我想我可以向 Entity 添加一个 Behavior 和 Renderer 都可以修改/读取的旋转字段。

如果坦克的车身和坦克的火炮有不同的方向呢?现在渲染器需要从某个地方访问两个旋转和两个要使用的图像。如果只有一个坦克,你真的不想用这个来膨胀实体类。

如果敌人在他的枪充电时会发光呢?您确实想将充电时间存储在 Behavior 对象中,但是 Renderer 类看不到它。

我在考虑如何建模上述内容时遇到了麻烦,因此渲染器和行为可以保持一定程度的分离。我能想到的最好方法是让行为对象包含额外的状态渲染器对象,然后行为对象调用渲染器的绘制方法并在需要时传递额外的状态(例如旋转)到。

然后你可以例如有一个类似坦克的 Behavior 对象,它需要一个类似坦克的渲染器,后者要求两个图像和两个旋转来绘制。如果您希望您的坦克只是一个普通图像,您只需编写一个忽略旋转的子类 Renderer。

谁能想到任何替代方案?我真的想要简单。由于这是一场游戏,因此效率也可能是一个问题,例如绘制一个 5x5 的敌人图像,当我有 50 个敌人以 60fps 的速度飞来飞去时,涉及到多层函数调用。

【问题讨论】:

【参考方案1】:

组合设计是有效的,因为它允许混合和匹配行为和渲染。

在我们正在玩的游戏中,我们添加了一个“数据包”,其中包含基本信息(在您的情况下是位置和死/活状态),以及由行为设置/取消设置的变量数据和碰撞子系统。然后渲染器可以使用这些数据(如果不需要,也可以不使用)。这很好用,并且可以产生简洁的效果,例如为给定的图形效果设置“目标”。

几个问题:

如果渲染器请求行为未设置的数据。在我们的例子中,事件被记录,并使用默认值(在渲染器中定义)。 事先检查所需信息有点困难(即渲染器 A 的数据包中应该有哪些数据?行为 B 设置了哪些数据?)。我们试图使文档保持最新,但我们正在考虑按类记录 set/get,并生成一个文档页面...

目前我们正在为数据包使用 HashMap,但这是在 PC 上,而不是在 iPhone 上。我不知道性能是否足够,在这种情况下,另一个结构可能会更好。

同样在我们的案例中,我们决定使用一组专门的渲染器。例如,如果实体拥有非 void 盾牌数据,则 ShieldRenderer 显示表示...在您的情况下,坦克可能拥有两个渲染器链接到两个(初始化定义的)数据:

Renderer renderer1 = new RotatedImage("Tank.png", "TankRotation");
Renderer enderer2 = new RotatedImage("Turret.png", "TurretRotation");

通过行为设置“TankRotation”和“TurretRotation”。并且渲染器只是在将图像显示在该位置之前旋转图像。

  image.rotate (entity.databag.getData(variable));

希望有帮助

问候 纪尧姆

【讨论】:

谢谢。我没有对其进行分析,但如果我想要很多物体飞来飞去(例如子弹),我可能希望避免使用哈希图来更新 android 上的每个行为。我注意到将变量存储保持在行为对象本地的一个优点是它使系统更加健壮并且更容易测试,因为您不必担心组合行为会与相同的变量混淆。 同意。正如我所说,我们在 PC 上,并且没有太多的对象。另外,这是为了好玩:)【参考方案2】:

您要使用的设计对我来说看起来不错。 components上的这一章可能会对你有所帮助。

【讨论】:

谢谢。我现在已经实现了上面的方法,它很棒!我从未见过人们建议的一件事是对行为进行排序(例如,按顺序调用两种行为的行为)和有条件的行为(例如,一种行为有条件地调用另一种行为)。感觉就像我现在有了一点脚本语言,而且重量很轻。例如,通过使用对象的组合,我可以说“如果(你离玩家 300 像素)(回家在玩家身上)否则(向玩家射击)”。我可以编写新代码,例如几分钟后游戏中的敌人。 这很酷。你基本上确实有一种脚本语言:那是一本教科书interpreter pattern。

以上是关于如何以模块化方式对游戏对象渲染和行为进行建模?的主要内容,如果未能解决你的问题,请参考以下文章

verilog的行为级描述和RTL级描述有啥区别

JAVA面向对象设计

第四次作业

游戏制作大致流程粗谈之二

10大主流3D建模技术

10大主流3D建模技术