我正在为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); }
Run Code Online (Sandbox Code Playgroud)
要只绘制存储为实体图像的内容,您可以附加一个简单的渲染器,例如
class SimpleRenderer extends Renderer
{
void draw(Canvas c)
{
// just draw the image
}
}
Run Code Online (Sandbox Code Playgroud)
要使实体每帧随机飞行,只需附加如下行为:
class RandomlyMoveBehaviour extends Behaviour
{
void update(Entity e)
{
// Add random direction vector to e.position
}
}
Run Code Online (Sandbox Code Playgroud)
或者添加更复杂的行为,例如在归位前等待玩家关闭:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
到目前为止,我对这个设计非常满意.它很好而且灵活,你可以例如模块化后一类,这样你可以提供"睡眠"行为和"清醒"行为,所以你可以说新的WaitUntilCloseBehaviour(播放器,50/像素 /,新的MoveRandomlyBehaviour(),new HomingBehaviour()).这使得制造新敌人变得非常容易.
困扰我的唯一部分是行为和渲染器的沟通方式.目前,Entity包含一个Image对象,如果行为选择这样做,行为可以修改.例如,一种行为可以改变睡眠和清醒图像之间的对象,渲染器只会绘制图像.我不确定这会如何扩展,例如:
那个面向某个方向的炮塔式敌人怎么样?我想我可以向Entity添加一个旋转字段,行为和渲染器都可以修改/读取.
那个坦克的主体和坦克的枪有不同方向的坦克怎么样?现在,渲染器需要访问来自某处的两个旋转以及要使用的两个图像.如果只有一个坦克,你真的不想用这个膨胀Entity类.
当他的枪充电时,一个发光的敌人怎么样?您真的想将充电时间存储在Behavior对象中,但Renderer类无法看到它.
我无法考虑如何对上述模型进行建模,因此渲染器和行为可以保持一定程度的分离.我能想到的最好的方法是让行为对象包含额外的状态和渲染器对象,然后行为对象调用渲染器绘制方法,并在需要时传递额外的状态(例如旋转).
然后你可以有一个像坦克一样的行为物体,它需要一个像坦克一样的渲染器,后者要求两个图像和两个旋转绘制.如果你想让你的坦克只是一个普通的图像,你只需要编写一个忽略旋转的子类渲染器.
谁能想到任何替代品?我真的很想要简单.因为它是一个游戏,效率可能也是一个问题,例如,当我有50个敌人以60fps飞行时,绘制单个5x5敌人图像,涉及多层函数调用.
合成设计是有效的,因为它允许混合搭配行为和渲染。
在我们正在玩的游戏中,我们添加了一个“数据包”,其中包含基本信息(在您的情况下是位置和死/活状态)以及由行为和碰撞子系统设置/取消设置的变量数据。然后渲染器可以使用这些数据(如果不需要,则不使用)。这工作得很好,并且可以实现简洁的效果,例如为给定的图形效果设置“目标”。
有几个问题:
目前我们使用 HashMap 作为数据包,但这是在 PC 上,而不是 iPhone。我不知道性能是否足够,在这种情况下另一个结构可能会更好。
同样在我们的例子中,我们决定使用一组专门的渲染器。例如,如果实体拥有非空盾数据,则 ShieldRenderer 显示该表示形式...在您的情况下,坦克可以拥有链接到两个(初始化定义的)数据的两个渲染器:
Renderer renderer1 = new RotatedImage("Tank.png", "TankRotation");
Renderer enderer2 = new RotatedImage("Turret.png", "TurretRotation");
Run Code Online (Sandbox Code Playgroud)
通过行为设置“TankRotation”和“TurretRotation”。渲染器只需旋转图像,然后将其显示在该位置即可。
image.rotate (entity.databag.getData(variable));
Run Code Online (Sandbox Code Playgroud)
希望这有帮助
问候
纪尧姆