我正在编写一个简单的小行星克隆游戏,使用Swing
来显示图形。我有点跟随Derek Banas' tutorials,但决定自己扩展。
最初的想法是游戏中的每个图形元素(即小行星,宇宙飞船和子弹)都扩展了Polygon
类。他们的构造函数看起来像这样:
public class SpaceShip extends Polygon {
//x and y coordinates
public static int[] polyXArray = {...};
public static int[] polyYArray = {...};
//other class variables
{...}
public SpaceShip() {
super(polyXArray, polyYArray, polyXArray.length);
}
}
它与其他图形元素类似。
编辑:这里的关键要素是两个数组不存储对象的实际坐标,但是它们相对于中心的位置,其坐标为double
- 类型为类变量。因此,数组仅描述对象的形状,而子类move()
方法将影响中心的坐标。负责实际绘图的类将调用move()
方法,然后应用仿射变换来移动和旋转形状(根据正确定义的角度参数)。我这样做是为了避免与处理double
算术相关的精度问题。
现在,由于这些元素共享了很多"等于"变量(它们的中心坐标,我需要它们用仿射变换转换它们,它们的速度组件等......)和方法(getter和setter,move()
方法等...)我考虑过制作它们是抽象类的扩展 - 比如GameShape
- 它包含所有这些常用的方法和变量。 GameShape
现在将是直接延长Polygon
的那个:
public abstract class GameShape extends Polygon {
//x and y coordinates, still unassigned
public static int[] polyXArray, polyYArray;
//other class variables
{...}
public GameShape() {
super(polyXArray, polyYArray, polyXArray.length);
}
}
然后,当我定义不同的子类以便绘制我需要的不同形状时,我想要将所需的值分配给polyXArray
和polyYArray
,但我还没有'找到了办法。
我确实希望这些变量是静态的,因为它们是单个类的特定属性,每次我实例化一个新对象时,我都不想将它们作为参数传递。
我的情况与this question中描述的情况非常相似,但建议的解决方案似乎不起作用,因为我需要构造函数中的那些变量。有没有办法克服 - 或周围 - 这个问题?无论程序如何,我的主要目标是为所有图形元素提供一个共同的超类,以避免数十行复制粘贴代码。
答案 0 :(得分:2)
您有一对数组,用于描述特定种类游戏对象的形状。如果不同的游戏对象可以具有不同的形状,则它们不能共享一对数组,如果它们是所有游戏对象类的公共超类的静态属性的话。相同类型的不同对象可以共享同一对数组(假设那些不需要基于每个对象修改),这可能对应于那些数组是静态字段的具体的游戏对象类。但是,在这种情况下,如果您希望这些类的超类能够访问给定游戏对象的正确形状数据,则必须告诉它们这些形状数据是什么。
有两种主要方法可以做到这一点:
您可以将适当的形状数组传递给超类的构造函数。你说你不想这样做,但我不明白为什么。
您可以在超类上定义子类应该覆盖的访问器方法,以提供正确的形状数据(这称为模板方法模式)。
答案 1 :(得分:1)
数组字段不能是static
,因为不同的形状具有不同的坐标。此外,您不需要在特定子类中使用这些数组,因为它们已经在Polygon
或GameShape
中。
这里或多或少我会写GameShape
(尽管我同意@Michael你不需要将polyXArray
和polyXArray.length
都传递给构造函数)
public abstract class GameShape extends Polygon {
// I got rid of the array fields as I think they are in Polygon anyway.
//other class variables
{...}
// I added arguments to your constructor.
public GameShape(int[] polyXArray, int[] polyYArray) {
super(polyXArray, polyYArray, polyXArray.length);
}
}
问题是super
必须是构造函数的第一行,但你可以使用私有方法来构建数组:
public final class BoringRectangle extends GameShape {
public BoringRectangle(int left, int right, int top, int bottom) {
super(xArray(left, right), yArray(top, bottom));
}
private static int[] xArray(int left, int right) {
return new int[] {left, right, right, left};
}
private static int[] yArray(int top, int bottom) {
return new int[] {bottom, bottom, top, top};
}
}
答案 2 :(得分:1)
this question的解决方案将适用于您的类不会扩展形状,但通过访问者+私有静态字段提供形状。
public abstract class GameObject {
...
public abstract Polygon getShape();
这也有助于避免形状重复。
答案 3 :(得分:1)
如果您确实想要在构造函数中初始化事物,只需调用空super();
,然后循环抽象getPolyXArray()
和getPolyYArray()
以提供addPoint
。
public abstract class GameShape extends Polygon {
public GameShape() {
super();
final int length = getPolyXArray().length;
for (int i = 0; i < length; i++) {
addPoint(getPolyXArray()[i], getPolyYArray()[i]);
}
}
public abstract int[] getPolyXArray();
public abstract int[] getPolyYArray();
//common stuff...
}
public class Asteroids extends Polygon {
public int[] getPolyXArray() { return new int[]{1, 2, 3}; }
public int[] getPolyYArray() { return new int[]{1, 2, 3}; }
}
答案 4 :(得分:0)
我认为polyXArray
和polyYArray
位于Polygon
类;这就是他们所属的地方。因此,重复字段不是一个好主意。另外,摆脱ned来调用super
构造函数。我会像这样设计类结构:
public class SquareShape extends Polygon {
private int size;
public SquareShape(int x, int y, int size) {
this.size = size;
int[] xpoints = new int[4]{
x - size / 2,
x - size / 2,
x + size / 2,
x + size / 2
};
int[] ypoints = new int[4]{
y - size / 2,
y + size / 2,
y + size / 2,
y - size / 2
};
setXArray(xpoints);
setYArray(ypoints);
}
}
这样,您可以确保所有SquareShape
个对象确实具有方形形状,但您可以自定义应该能够自定义的内容。像位置和大小一样,它不应该是静态共享字段。 setXArray
和setYArray
应该是位于protected
的{{1}}种方法。你不希望外面的世界弄乱各个点。不过,您可以添加公共getter。
您可能需要考虑使用复杂Polygon
类型的单个数组,而不是两个紧密耦合且依赖的数组。我觉得这会大大简化你项目中的很多任务。
答案 5 :(得分:-1)
编辑:
正如VGR在评论中所述,这将无法编译。所以,我们将不得不稍微改变实现,即我们将使用HAVE关系而不是IS关系: - )
首先,不要将多边形数组字段设为静态。如果这样做,它们对于所有子类也是一样的,那么重点是什么呢?
其次,在此处使用模板方法设计模式。你的课将看起来像这样:
public abstract class GameShape {
//x and y coordinates, still unassigned
public int[] polyXArray, polyYArray;
private Polygon polygon;
//other class variables
{...}
public GameShape() {
instantiatePolyArrays();
this.polygon = new Polygon(polyXArray, polyYArray, polyXArray.length);
}
protected abstract void instantiatePolyArrays();
public final Polygon getPolygon(){
return this.polygon;
}
}
每个扩展类都必须重写此方法,并且可以为每个方法覆盖中的每个类实例化数组。
此外,关于IS-HAVE关系问题的一个词 - 您在示例中呈现的是IS关系,其中GameShape
对象是Polygon
,因此需要调用超级构造函数和问题。在我的解决方案中,它由HAVE关系替换,其中GameShape
对象内部有一个Polygon
对象,使用getPolygon()方法访问。这使您可以获得更多灵活性: - )