Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initialize superclass variables (needed in constructor) in a subclass

I'm writing a simple Asteroids-clone game, using Swing to display the graphics. I'm kind of following Derek Banas' tutorials, but decided to expand on my own.

The original idea is that every graphic element in the game (i.e. the Asteroids, the Spaceship and the Bullets) extends the Polygon class. Their constructor would look something like this:

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);
    }
}

And it will be similar for the other graphic elements.

EDIT: The key element here is that the two arrays don't store the actual coordinates of the objects, but their position relative to their centre, whose coordinates are double-type class-variable. Thus, the arrays describe just the shape of the object, while the subclass move() method will affect the centre's coordinates. The class responsible for the actual drawing will call the move() method and then apply an affine transform to move and rotate the shape (according to a properly defined angle parameter). I'm doing this to avoid precision problems related to dealing with double arithmetic.

Now, since the elements share a lot of "equal" variables (their centre coordinates, which I need in order to translate them with an affine transform, their speed components etc...) and methods (getters and setters, move() methods, etc...) I thought about making them be the extension of an abstract class - say, GameShape - which holds all these common methods and variables. GameShape would now be the one extending Polygon directly:

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);
        }
}

Then, I'd want to assign the desired value to polyXArray and polyYArray when I define the different subclasses in order to draw the different shapes I need, but I haven't been able to find a way to do it.

I do want those variable to be static because they are specific properties of the single classes, and I wouldn't want to pass them as a parameter every time I instantiate a new object.

My situation is very similar to the one described in this question, but the proposed solution doesn't seem to work, since I need those very variables in the constructor. Is there a way to get over - or around - this problem? Regardless of the procedure, my main aim is to have a superclass common to all the graphic elements, in order to avoid tens of lines of copy-pasted code.

like image 478
Ka Mai Avatar asked Oct 07 '15 20:10

Ka Mai


3 Answers

You have pairs of arrays that describe the shapes of specific kinds of game objects. If different game objects can have different shapes, then they cannot all share a single pair of arrays, as would be the case if they were static properties of a common superclass of all the game object classes. Different objects of the same kind can share the same pair of arrays (supposing that those don't need to be modified on a per-object basis), which could correspond to those arrays being static fields of the concrete game object classes. In that case, however, if you want a superclass of those classes to be able to access the correct shape data for a given game object, then it has to be told what those shape data are.

There are two main ways you could do that:

  1. You could pass the appropriate shape arrays to the superclass's constructor. You say you don't want to do this, but I don't understand why.

  2. You could define accessor methods on the superclass that the subclasses are supposed to override to provide the correct shape data (this is called the Template Method pattern).

like image 116
John Bollinger Avatar answered Sep 23 '22 21:09

John Bollinger


The solution from this question will work if your classes will NOT extend shape, but provide shapes via accessor + private static field.

public abstract class GameObject {
    ...
    public abstract Polygon getShape();

This also helps to escape shapes duplication.

like image 28
ursa Avatar answered Sep 22 '22 21:09

ursa


If you really do want to initialize things in your constructor, just call the empty super(); and then loop against abstract getPolyXArray() and getPolyYArray() to feed 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}; }
}
like image 43
PaoloC Avatar answered Sep 21 '22 21:09

PaoloC