Simple Radar for Space Android game

In space-shooter-like game it is nice to have Radar on the Hud.

Tere is simple way to draw radar as a Rectangle shape with dots on it. To do it I will use special Scene to manage dots movement and Rectangle as a Radar to show position of the objects.

Becouse this is only how to to create radar I will use simple graphics – rectangle with colored dots.

1)
First thing we need to define our World size fields. I will handle differend size of world and camera view. World should be bigger than camera size lets set it to 2 times bigger.

In BaseGameActivity class “MrRadar”

    public static final int CAMERA_HEIGHT = 320;
    public static final int CAMERA_WIDTH = 480;

    public static final int SPACE_HEIGHT = CAMERA_HEIGHT * 2;
    public static final int SPACE_WIDTH = CAMERA_WIDTH * 2;

2)
To manage Entities on Radar I will define interface “IRadarObject”. I will extends IEntity becouse of simple get size and position of the object.

	public interface IRadarObject extends IEntity {
		public ObjectConfig getConfig();
	}

Also I define class to hold all graphic information about dot on the radar, like color, etc.

	public class ObjectConfig {
		public int red;
		public int green;
		public int blue;
		public ObjectConfig(int red, int green, int blue) {
			this.red = red;
			this.green = green;
			this.blue = blue;
		}
	}

3)
Now lets define Radar class.

It will be Rectangle – that is simplest way.
To handle all object I should create a Map – it will be managed from our Scene (defined in p.4).

Map radarObjectsMap = new HashMap();

Dots will be draw on rectangle but first I must find a way to transform space points to radar points.
To calculate x position of the object I get it is X position in the space and devide it by space width – this is my percentage of the space where object is paced, so this is also percentage of position on radar.
It is very simple and can be done like this:

	
private Pos convertPosition(IEntity ent) {
		Pos pos = new Pos();
		pos.x = (ent.getX() / MrChain.SPACE_WIDTH) * getWidth();
		pos.y = (ent.getY() / MrChain.SPACE_HEIGHT) * getHeight();
		pos.width = this.getWidth() * SCALER;
		pos.height = this.getHeight() * SCALER;
			
		return pos;
	}

Pos is special class that contains only fields: x,y, width and height.

I must add methods to handle adding, removing and moving objects on the radar.
I will just use Map. In adding I will define size of the object, first position and color. to define position I will use my method from above. Also I will check if Map already contains this object:

	
public void addObject(IRadarObject obj) {
		if (!radarObjectsMap.containsKey(obj)) {
			Pos pos = convertPosition(obj);
			Rectangle rec = new Rectangle(pos.x, pos.y, pos.width, pos.height);
			rec.setColor(obj.getConfig().red, obj.getConfig().green, obj.getConfig().blue);
			attachChild(rec);
			radarObjectsMap.put(obj, rec);
		}
	}

In similar way I remove objects and update its size, color and position.

Radar class looks like this:

public class Radar extends Rectangle {
	private static final float SCALER = 0.05f;
	
	Map radarObjectsMap = new HashMap();

	public Radar(float pX, float pY, float pWidth, float pHeight) {
		super(pX, pY, pWidth, pHeight);
		setAlpha(0.3f);
	}

	public void addObject(IRadarObject obj) {
		if (!radarObjectsMap.containsKey(obj)) {
			Pos pos = convertPosition(obj);
			Rectangle rec = new Rectangle(pos.x, pos.y, pos.width, pos.height);
			rec.setColor(obj.getConfig().red, obj.getConfig().green, obj.getConfig().blue);
			attachChild(rec);
			radarObjectsMap.put(obj, rec);
		}
	}
	
	public void removeObject(IRadarObject obj) {
		if (radarObjectsMap.containsKey(obj)) {
			Rectangle rec = radarObjectsMap.remove(obj);
			detachChild(rec);
		}
	}
	
	public void removeAllObjects() {
		radarObjectsMap.clear();
		this.detachChildren();
	}
	
	public void updateObject(IRadarObject obj) {
		Rectangle rec = radarObjectsMap.get(obj);
		if (rec != null) {
			Pos pos = convertPosition(obj);
			rec.setPosition(pos.x, pos.y);
			rec.setHeight(pos.height);
			rec.setWidth(pos.width);
		}
	}
	
	private Pos convertPosition(IEntity ent) {
		Pos pos = new Pos();
		pos.x = (ent.getX() / MrChain.SPACE_WIDTH) * getWidth();
		pos.y = (ent.getY() / MrChain.SPACE_HEIGHT) * getHeight();
		pos.width = this.getWidth() * SCALER;
		pos.height = this.getHeight() * SCALER;
			
		return pos;
	}
	private class Pos {
		float x;
		float y;
		float width;
		float height;
	}
}

4)
To define scene I will extends Scene from AndEngie API and in each attachChild and removeChild I will call Radar methods to add and remove objects. Finally I define UpdateHandler to manage object movement.

public class SceneWithRadar extends Scene {
	Radar radar;
	IUpdateHandler radarHandler = new IUpdateHandler() {
		@Override
		public void onUpdate(float pSecondsElapsed) {
			for (IEntity child : SceneWithRadar.this.mChildren) {
				if (child instanceof IRadarObject) {
					radar.updateObject((IRadarObject) child);
				}
			}
		}
		@Override
		public void reset() {
		}
	};
	public SceneWithRadar(float radarPosX, float radarPosY, float radarWidth, float radarHeight) {
		radar = new Radar(radarPosX, radarPosY, radarWidth, radarHeight);
		registerUpdateHandler(radarHandler);
	}
	@Override
	public void attachChild(IEntity pEntity) throws IllegalStateException {
		if (pEntity instanceof IRadarObject) {
			radar.addObject((IRadarObject) pEntity);
		}
		super.attachChild(pEntity);
	}
	
	@Override
	public boolean detachChild(IEntity pEntity) {
		if (pEntity instanceof IRadarObject) {
			radar.removeObject((IRadarObject) pEntity);
		}
		return super.detachChild(pEntity);
	}
	
	@Override
	public void detachChildren() {
		radar.removeAllObjects();
		super.detachChildren();
	}
}

How to handle states of sprites

Most of sprites in games has states that will change after fires some event.

Simple example:
We have one sprite “Dot”. This sprite has 3 states. Sprite can be clicked, when it will change state. Every state has different texture.

SHOW_EMPTY – empty Dot, sprite will not change it state
SHOW_SELECTED – Dot will be filled, sprite will not change it state
DRAW_EMPTY – Empty dot to change, sprite will change state to DRAW_SELECTED
DRAW_SELECTED- Filled Dot to change, sprite will change state to DRAW_EMPTY

BAD SOLUTION
We will use “if … else” statement and some methods and fields to handle states.

It will look like this.

boolean selected;
boolean clickable;

funtion void setSelected(boolean selected) {
  this.seleced = selected;
}
function boolean getSelected() {
   return selected;
}
function void setCkickable(boolean clickable) {
   this.clickable = clickable;
}
function boolean getClickable() {
   return clickable;
}

and for checking state and draw texture from touch listener:

function void manageTexture() {
if (clickable && selected) {
   // do something with texture
} else if (!clickable && selected) {
   // do something else with texture
}

… and so on.

For simple sprites it might look OK, but after some time new state comes, eg. HINT that will be same like SHOW_SELECTED but with minor changes. Then we must add new “if … else” statement.

After adding 3 or more sates code is hard to maintenance.

Pros:
Simple and fast to start implement
For 1-3 states small lines of code

Cons:
For more than 3 states huge number of lines that is hard to maintenance.

BETTER SOLUTION
I will use enums and interfaces
My enums will be:

enum DotState {
SHOW_EMPTY
SHOW_SELECTED
DRAW_SELECTED
DRAW_HINT
}

I will define our textures, lets simplified this to use TiledTexture from Andengine (integers define index of texture in image file.

enum DotState {
SHOW_EMPTY(0),
SHOW_SELECTED(1),
DRAW_SELECTED(2),
DRAW_HINT(3)
}

Then I will define what will happen after click. Click will be managed in touch listener that call our function but this function will be inside enum as a object that implements interface.

My interface will be:

private interface IManageMyState {
manage();
}

I will create objects for all states inside Sprite class like this:

private static final IManageMyState manageDrawSelected = new IManageMyState() {
public void manage(Dot dot) {
dot.state = DotState.DRAW_EMPTY;
}
};

Because enum is static my ImanageMyState objects must be static also. Dot object to handle will be passed as a parameter “manage(Dot dot)”.

And at the end I will pass my manager objects to enum:

enum DotState {
SHOW_EMPTY(0, manageShowEmpty),
SHOW_SELECTED(1, manageshowSelected),
DRAW_ENPTY(2, manageDrawEmpty),
DRAW_SELECTED(3, manageDrawSelected)
} 

We can manage textures simple to use our state.

@Override
function void onTouch(…) {
this.state.manager.manage(this);
}

Note that I passed “this” to manage method, this my dot to manage.

And that’s it!

WHY IT IS BETTER SOLUTION?

We can simply add new functions to IManageMyState, all states is simple to maintenance because all thins is in one place.

If you know better solution to handle states please write your comment 🙂

Pros:
Simple to maintenece for more than 3 states

Cons:
Lot of empty methods for states that do nothing