我在 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 中。