我正在用Java开发一个小游戏,我遇到了一个问题,决定如何从我正在使用的框架中抽象出我的游戏世界。
我现在拥有一个名为World
的班级,可以跟踪世界上所有不同的对象,并根据游戏规则对其进行更新。然后我有三个基本对象类Person
,Obstacle
和Item
。每个对象都有不同的“类型”,由成员变量表示。所有对象类都从类GameObject
扩展而来。
正如我现在所知,World
和对象类与呈现游戏等的框架是分开的。类Game
都告诉世界更新并呈现世界和对象世界。
问题是我使用三种方法drawPersons()
,drawObstacles()
和drawItems()
渲染对象,这很好,因为我已经编写了代码而我不会再添加对象类。真正的问题是呈现每个对象类的不同“类型”。正如我现在所知,draw方法通过检查变量object.type
来绘制项目,然后有一个switch语句,它选择要绘制的相应位图。
我想简化这个,所以每次我向Item
类添加一个新的“type”时我都不必添加到switch语句中。我能想到的唯一方法就是让对象存储它们在构造时应该呈现的位图,并设置其item.type变量。这样我就可以从drawItems()
方法中删除switch语句。另外我还可以给每个对象类一个draw()方法等等等等但是我不想这样做的原因是因为它破坏了我从渲染器/框架和想象中的游戏世界的分离。
我不知道该怎么做,一方面我知道使用像我现在这样的switch语句是一个坏主意,但另一方面我知道使用我的World
和对象类更好的设计独立于框架,所以如果我想,我可以把它打成一个新的框架或任何下线。
我有两种方法可以同时拥有吗?
答案 0 :(得分:2)
在每个可绘制对象中存储图像对象应该不是问题。存储图像不会破坏渲染循环的运行方式。如果不需要,有时过度抽象可能很麻烦。我认为在对象中存储图像没有错。一旦开始向游戏中添加更多可绘制对象,切换语句很快就会变得非常低效。想想有数百个可绘制对象的游戏。从长远来看,指针需要很小的空间并加快代码的速度。
答案 1 :(得分:2)
您可以调查Visitor pattern。您的游戏世界对象会让访问者通过他们执行某些操作:在这种情况下,操作正在绘制。根据游戏对象选择不同实现的逻辑保留在访问者一侧。
答案 2 :(得分:2)
您可以做的是使用合成,即您的实体包含几个具有不同目的的组件。例如,可能存在渲染组件,物理组件,ai组件等。
世界将管理一般的实体,然后子系统将查询实体以获取正确的组件。因此,如果渲染器想要渲染实体,则它查询渲染组件并且可能是位置组件并使用那些提供的数据来渲染实体。
在这种情况下,渲染组件可能包含纹理/位图,形状,材质等数据。
有关详细信息,您可能需要查看entity systems。
答案 3 :(得分:2)
我相信你可以使用Decorator Pattern包装你的课程。
装饰器包含GameObject
实例,并且还将通过调用原始实例上的方法来实现GameObject
函数。
当您以这种方式继承GameObject时,您还可以使装饰器使用Renderable
方法实现draw()
接口。
答案 4 :(得分:1)
我建议你使用位图图谱,即一个大图像中的所有图像,然后在GameObject类中创建一个抽象方法,返回游戏对象在图集中的区域。
像这样public abstract class GameObject{
...
...
..
public abstract TextureRegion getGameObjectTextureRegion();
...
..
}
并且在每个扩展GameObject的类中存储一个像这样的实例变量
public class Person extends GameObject{
....
....
...
public static final TextureRegion person_region = new TextureRegion(top,left,height,width);
...
...
public TextureRegion getGameObjectTextureRegion(){
return person_region;
}
然后将所有游戏对象存储在数组或ArrayList
中
像这样
ArrayList<GameObject> game_objects = new ArrayList<GameObject>();
//add items to it
game_objects.add(new Person());
gmae_objects.add(...);
并在你的draw方法中循环game_objects列表并调用getGameObjectTextureRegion
for(GameObject item : game_objects)
{
TextureRegion region = item.getGameObjectTextureRegion();
// then pass them with the game object position to your rendering method
...
}
注意:TextureRegion看起来像这样
public class TextureRegion{
float top;
float left;
float width;
float height;
}
答案 5 :(得分:0)
我喜欢托马斯的“实体系统”答案。但我自己想的是另一种方法。我将尝试尽可能抽象地呈现它,因为我对Java不是很熟悉。
您可以使用某种“资源”变量来存储object_type对数据:包含您打算在您的世界中插入的每种对象所需资源的数据。 'resources'变量可以是动画列表或任何其他公共资源。
更具体一点,让我举一个Python的例子(因为这是我最熟悉的)。在Java或几乎任何其他语言中应用相同的问题应该不是问题。在Python中,我将创建一个如下所示的字典变量:
{
Person1:["person1.bmp", "person1_alternative1.bmp", "person1_whatever.wmv"],
Person2:["person2.bmp", "person2_alternative1.bmp", "person2_whatever.wmv"],
Obstacle1:["Obstacle1.bmp", "Obstacle1_alternative1.bmp", "Obstacle1_whatever.xyz"],
Obstacle1:["Item1.bmp", "Item1_alternative1.bmp", "Item1_whatever.xyz"]
}
......等等
你可以在你认为合适时进一步扩展;添加其他类型的资源,可能是对象坐标,尺寸等等。例如
{
person1:{basic_animation:"person1.bmp", alt_animation:"person1_alternative1.bmp", sfx1:"person1_whatever.wmv"},
person2:{basic_animation:"person2.bmp", alt_animation:"person2_alternative1.bmp", sfx1:"person2_whatever.wmv"},
obstacle1:{basic_animation:"obstacle1.bmp", alt_animation:"obstacle1_alternative1.bmp", res1:"obstacle1_whatever.xyz"},
item1:{basic_animation:"item1.bmp", alt_animation:"item1_alternative1.bmp", sfx1:"item1_whatever.wmv"}
}
Java中的语法可能有所不同,但您可以得到一般的想法。我不确定Java如何处理密钥:值类型的变量,我认为它有一些类。如果没有,你可以随时自己制作。
这个大的“资源”变量可以成为游戏类的一部分,并在您实例化游戏类时初始化并填充相关数据。如果你喜欢将所有常量分组到一个单独的地方/模块/任何地方(我这样做),它甚至可以存储在其他地方。然后你可以将那些drawPersons(),drawObstacles()和drawItems()统一到一个draw()函数中,该函数检查每个'对象'的'object.type'并在'resources'中查找它以获得它的相关数据。 / p>
现在我不知道性能和其他因素,可能需要一些聪明的人进行一些优化!