【问题标题】:Collection with fast removal/iteration/insertion that recycles objects in Android/Java programs?具有快速删除/迭代/插入的集合,可回收 Android/Java 程序中的对象?
【发布时间】:2010-01-28 05:59:50
【问题描述】:

我正在为 Android 游戏编写程序。例如,游戏可能涉及子弹、敌人、宝石等,需要:

  • 在游戏过程中在游戏世界中创建和销毁,例如子弹会着火,然后在撞到墙上时消失。

  • 按顺序访问了很多,例如全部按顺序更新,然后全部按顺序绘制。

根据我目前在 Android 中的工作所知道的,为了保持我的帧速率,我需要考虑以下几点:

  • 在不必要时不要分配对象,因为垃圾收集器会启动并破坏您的帧速率。

  • 喜欢例如访问对象字段和调用函数的局部变量访问。

对于上面提到的 PC 游戏中的游戏对象,我通常会使用 Vector 或 LinkedList 之类的东西。但是,这些不会回收对象,使用 Iterator 会创建一个新对象,并且在迭代时会涉及多个函数调用。

什么是适合使用的集合?

我发现目前效果很好的是创建例如100 个子弹的标准数组,其中所有 100 个子弹都是预先创建的。然后,当所有活动的子弹出现在数组的开头时,我会计算有多少子弹是活动的。每当我迭代一系列子弹并且需要销毁一个子弹时,我只需将当前子弹索引与最后一个活动子弹索引交换,然后减少活动子弹编号。这会改变项目符号的顺序,但没关系。

这很好用:

优点:回收对象,很少/没有函数调用 缺点:当不作为集合类实现时容易出错(尤其是删除)

谁能推荐一个更好的选择?我见过很多管理对象池的类,但我不确定哪些适合我。

谢谢。

【问题讨论】:

  • 正如我在回答中所说的那样:拥有一个像您正在做的数组并手动将最后一个与消失的数组交换并计算您的子弹数是业界在帧速率为关注。它可能容易出错,但这就是单元测试的用途;)

标签: java android collections


【解决方案1】:

我在 80 年代后期开始编写游戏,最近使用 Java 编写移动设备。我可以告诉您,如果您使用 LinkedList 或 Vector 来存储与子弹等琐碎事物相对应的 Java 对象,那么您将会降低帧速率。手机游戏的编程效率并非如此。通过考虑“每一点都很重要”来编写高效的手机游戏。这是优化为王的领域之一。

过度简化,但想象一下,您有一个游戏,当前有四个“活动”子弹(它并不是真正“在屏幕上”,您的“活动世界”可以而且通常应该比您的屏幕大一点,它会产生很多处理更容易)。

(20,30,3) (10, 50, 2) (30, 40, -3) (50, 50, 5)

所以第一个项目符号位于坐标空间中的像素 (20,30) 处,并以 3 的速度(无论 3 的速度意味着什么,这只是一个示例)向右移动(过于简化,只是为了解释) ,子弹二在 (10,50) 以 2 的速度向右移动,子弹 3 在 (30,40),以 3 的速度向左移动(减号表示向左),最后一个子弹在(50,50,5) 以 5 的速度向右移动。

这在当前手机游戏的内存中是如何表示的?

像这样,在 int[] 中:

int[] = { 4, 20, 30, 3, 10, 50, 2, 30, 40, -3, 50, 50, 5, ..., ..., ... };

前 4 个告诉我们这个“数据结构由 4 个元素组成。您知道每个元素由 3 个值组成(在我们过于简化的示例中)。

现在想象子弹 2 撞到墙上然后消失了,会发生什么?

这个:

int[] = { 3, 20, 30, 3, 50, 50, 5, 30, 40, -3, 50, 50, 5, ..., ..., ... };

我们将第一个 int 的减量简化为 3,表示到目前为止我们的游戏世界中只剩下 3 个子弹,我们只是将 (50, 50, 5) 移动到位置“2”,替换为 (10, 50,2) 乘 (50, 50, 5)。那是因为我们的项目符号的顺序并不重要(它们都具有相同的效果)并且“将所有 int[] 元素向左移动”将非常低效。

请注意,我们甚至没有费心去“清除”“第 4 个子弹”:旧的 (50,50,5) 在最后仍然存在,但我们知道我们只剩下 3 个元素,所以我们不没关系。

在内存中看起来像这样:

int[] = { 3, 20, 30, 3, 50, 50, 5, 30, 40, -3, 50, 50, 5, ..., ..., ... };

你只关心这个:

int[] = { 3, 20, 30, 3, 50, 50, 5, 30, 40, -3, ..., ..., ..., ..., ..., . ..};

是目前大多数手机游戏中的做法:在主循环中为“子弹处理”创建零对象。您可以使用基元数组自己管理这些简单的数据结构。

并且 int[] 在您的关卡游戏开始时被初始化为您的游戏/关卡中可能发生的最大子弹数。

你的“可重复使用的子弹池”出现了。

如果您开始考虑将 Java 对象用于像子弹这样微不足道的事情并使用您将在每一帧修改的 LinkedList 或 Vector,您将永远无法获得可接受的性能:您将每秒 50 次生成无数不必要的对象,并且过于频繁地触发 GC。

现在我当然不是说 OO 编程在手机游戏中没有它的位置:我只是说如果你从对象的角度来思考像子弹这样微不足道的事情,你永远不会被接受性能。

我的“子弹移除”技术在这里涉及一个减量(子弹的数量)和 3 个 int 副本。你不能打败那个;)

它适用于很多事情:子弹、粒子效果、敌人、物品、奖金等等 :)

在游戏循环中可能经常被删除/重新创建的琐碎事物可能不应该使用对象建模,当然也不应该放在 Vector 或 LinkedList 中。

【讨论】:

    【解决方案2】:

    我认为您正在寻找/谈论Object Pool

    来自维基百科:

    对象池是一种软件设计模式。对象池是一组已初始化的对象,它们随时可以使用,而不是按需分配和销毁。在初始化类实例的成本很高、类的实例化率很高以及任何时候使用的实例数都很少

    您似乎在开始设计对象池时已经摸索过,所以它可能是一个不错的选择。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-29
      • 2012-02-04
      • 1970-01-01
      • 1970-01-01
      • 2018-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多