jjzjj

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

coder 2023-11-24 原文

我正在为 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;
    }
  }
}

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

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

  • 面向特定方向的类似炮塔的敌人怎么样?我想我可以向 Entity 添加一个旋转字段,Behavior 和 Renderer 都可以修改/读取该字段。

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

    • 当他的枪充电时会发光的敌人怎么样?您确实希望将充电时间存储在 Behavior 对象中,但 Renderer 类看不到它。

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

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

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

最佳答案

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

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

几个问题:

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

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

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

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

使用由行为设置的“TankRotation”和“TurretRotation”。渲染器在将图像显示在该位置之前简单地旋转图像。

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

希望对你有帮助

问候
纪尧姆

关于java - 如何以模块化方式对游戏对象渲染和行为进行建模?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2247293/

有关java - 如何以模块化方式对游戏对象渲染和行为进行建模?的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  4. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  5. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  6. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  7. ruby-on-rails - 建模收藏夹 - 2

    我希望将Favorite模型添加到我的User和Link模型。业务逻辑用户可以有多个链接(即可以添加多个链接)用户可以收藏多个链接(他们自己的或其他用户的)一个链接可以被多个用户收藏,但只有一个所有者我对如何为这种关联建模以及在模型就位后如何创建用户收藏夹感到困惑?classUser 最佳答案 下面的数据模型怎么样:classUser:destroyhas_many:favorite_links,:through=>:favorites,:source=>:linkendclassLink:destroyhas_many:favor

  8. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  9. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  10. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

随机推荐