Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overuse of static in my Java Game project?

Tags:

java

static

I am currently developing a little platform in Java and I wrote my own game engine for it called Bonsai. Now I'm asking myself the question "Did I overuse statics?".

On the one hand it's very convenient since I don't have to keep a reference to the game instance in each class like the map or the player. On the other hand... I have already had to strip out applet support since it was very buggy with all that static stuff in there.

So my question is, since you may be much more experienced Java programmers than I, should I get rid of all the statics? And if yes, what would be an effective approach to get to something like this:

public void draw(Graphics2D) {
  if (this.game.time() > this.timer) {
    this.game.image.draw(this.tiles[this.game.animation.get("tileAnim")], x, y, null)
  }
}

instead of:

public void draw(Graphics2D) {
  if (Game.time() > this.timer) {
    Image.draw(this.tiles[Animation.get("tileAnim")], x, y, null)
  }
}

or even worse in the map editor:

   public void control() {
     if(this.map.game.input.keyPressed(...)) {
       this.map.game.sound.play(...);
     }
   }

EDIT
Based on the answers I decided to have an GameObject class which provides wrapper methods for each component. Map, player etc. then subclass from it, this way I all the this.game calls are hidden behind the scens an it still looks nice on the frontside:

public class GameObject {
    private Game game;

    public GameObject(Game g) {
        game = g;
    }

    public Game Game() {
        return game;
    }

    public GameAnimation Animation() {
        return game.animation;
    }

    public GameInput Font() {
        return game.input;
    }

    // ...

    public long Time() {
        return game.time();
    }
}

Now the code looks like this:

public class Player() {
    public Player(Game g, int xpos, int ypos) {
      super(g);
      // do other stuff
    }

    public void jump() {
      // jump code
      Sound().play("jump");
    }
}

Or is this even worse Java?

EDIT2
Ok I'd already run into problems using method calls the compiler is giving me errors since it can't find methods of my subclassed Game in the original one, i think I'm jsut gonna use plain fields here.

EDIT3
Ok my GameObject class now looks like this, everything works fine again and I can reimplement that applet support :)

public class GameObject {
    protected Game game;
    protected GameAnimation animation;
    protected GameFont font;
    protected GameInput input;
    protected GameImage image;
    protected GameSound sound;

    public GameObject(Game g) {
        game = g;
        animation = game.animation;
        font = game.font;
        input = game.input;
        image = game.image;
        sound = game.sound;
    }
    }
like image 395
Ivo Wetzel Avatar asked Dec 08 '22 05:12

Ivo Wetzel


1 Answers

First of all.

You don't have to get rid of all your static code just because that would make it better on "paper".

You really have to understand what the difference is between instance code (non-static) and class code (static)

static code (class methods/attributes) is used when the methods/attributes do no need an instance of the class to work. A very good example is the image draw method: Image.draw()

instance methods/attributes are useful to keep state of a given object which must be kept separate from data in other objects.

For instance, if you have Player class in your game and you have two instances player1 and player2 it makes sense each of them to have their own score:

 public class Player {
     private int score;
     private String name;
     etc.....
 }

 Player one = new Player("Player 1");
 display( one.score );

 Player two = new Player("Player 2");
 display( two.score );

Instead of having to create artifacts to keep each player score (like putting them in arrays where each index is an attribute and make that array static etc. etc.)

Secondly

You may reduce the constructs you mentioned object1.atr2.other.next.etc by assigning to objects appropriate attributes and performing encapsulation in the right way.

If a object b needs to access the N'th element of another a it is likely that said attribute belongs to the object b instead of a or probably that object a should provide a method to avoid exposing it's internals.

It even makes the code easier to read:

ie.

public void draw(Graphics2D) {
  if( this.game.needsDrawing() ) {
       this.game.draw();
  }
}

instead of:

public void draw(Graphics2D) {
    if (this.game.time() > this.timer) {
        this.game.image.draw(this.tiles[this.game.animation.get("tileAnim")], x, y, null)
    }
}

Again, it depends on the situation, there might be scenarios where you don't need an instance (again, like the draw() utility method of Image)

Finally.

Instance methods allow you to use polymorphism while class methods do not (at least, in Java and other statically typed languages).

So, you may benefit from using runtime delegation and polymorphism if your code is instance code. Take for instance the State pattern you can't use it if all your code is static, but you can with instance code:

class Game {
     GameState state = GameState.getInitialState( this );

     void run() {
         while( state.alive ) {
              do X Y Z
              state.updateState();
          }
     }
  }


class GameState {
    Game context;
    static GameState getInitialState( Game g ) {
        return new StartGameState(g);
    }
    void updateState();
}

class StartGameState {
     void updateState() {
         if( this.context.someCondition() ) {
             this.context.state = new MidGameState();
         }
     }
 }


class MidGameState {
     void updateState() {
         if( this.context.someOtherCondition() ) {
             this.context.state = new EndGameState();
         }
     }
 }

class EndGameState {
     void updateState() {
        Game over... 
     }
 }

And once again, only if this make sense in terms of object orientation, like does the object have attributes whose data is required? If not it may be good to keep that section of code static.

All these concepts (encapsulation, polymorphism, abstraction, inheritance, etc) are the very nature of OO technology and covered in OOA/D while they may seem like syntactic sugar (and most of the times they are) your experience will tell you when you should have something as class code and when as instance code.

like image 89
OscarRyz Avatar answered Dec 30 '22 22:12

OscarRyz