XanderPaintingFramework

From Robowiki
Revision as of 02:40, 6 November 2011 by Skotty (talk | contribs) (Add initial details to an Introduction section)
Jump to navigation Jump to search

Introduction

The Xander Painting Framework is a framework to aid in painting information to the screen in Robocode. It's primary goals are to:

  1. Provide a convenient means for toggling painters on and off.
  2. Provide a construct for painting informational displays in windows that can be moved around the screen.
  3. Make it easy to remove most of the painting components from finished robots to reduce code size, and make it equally easy to add them back in for development and debugging.

Xander Painting Framework Screen shot (showing Painters of the Xander Robot Framework): Xander-painter-framework.png

If you have looked at or are aware of the Xander Robot Framework, note that the Xander Painting Framework stands on it's own. While the Xander Robot Framework makes use of the Xander Painting Framework, the Xander Painting Framework does not require the Xander Robot Framework.

Paintables and the Paintable Interface

The first step towards using the Xander Painting Framework is to make some information paintable. Doing this requires two steps:

  1. Implementing the Paintable interface on a class that will provide information to be painted.
  2. Adding the Paintable object to the Paintables class.

Any code this is necessary to make an object paintable will remain with the robot whether the framework is enabled or disabled, so it is best to keep such code to a minimum if possible.

The Paintable interface:

public interface Paintable {

	/**
	 * Returns a unique name for the Painter that is responsible for painting
	 * this Paintable.
	 * 
	 * @return   Painter name for this Paintable
	 */
	public String getPainterName();
	
}

There are two types of paintable objects: paintable objects with specific painters, and paintable objects without. In the former case, the getPainterName() method should return the name of the specific painter responsible for painting the specific paintable instance. In the latter case, the getPainterName() method should return null and any Painter can paint information from a single instance (and only a single instance) of the class.

The Painter Interface

Implementing the Painter interface is the minimum required to be painted by the framework. While you can create a class that implements Painter, it is better to extend one of the framework abstract classes. Otherwise, the Painter will still appear in the menu but you will not be able to toggle the painter on and off.

The AbstractPainter Class

The AbstractPainter class adds support for toggling the painter on and off. Anything that extends AbstractPainter or one of it's subclasses will have a toggle button for it in the menu.

Extending this class is best for painters that paint information dependent on robot or bullet positions, such as painters that paint bullet waves.

The WindowPainter Class

The WindowPainter class provides a interactive window that the painter can paint within. When using this class, position (0, 0) is the lower left hand corner of the window. Windows can be moved around the screen by dragging them by their title bars. Not all Graphics2D painting methods are supported when using a WindowPainter; only draw(Shape) and the various drawString(...) methods are supported.

The TextPainter Class

The TextPainter class is a subclass of WindowPainter that makes it a little easier to draw text to the screen. When using a TextPainter, you need only provide the lines of text to print, without having to worry about actually drawing them to the window.

Enabling and Disabling the Framework

The heart of the framework is the PaintManager class. To enable the framework, you need to call one of the enable(...) methods on the PaintManager instance. To disable the framework, comment out or remove the line that calls enable(...).

The arguments to the enable(...) methods activate painting and provide the PaintManager with two things it needs to do its job: the battle field height and the list of Painters.

Example:

	// in a one-time setup method somewhere in the robot class
	PaintManager.getInstance().enable(getBattleFieldHeight(), painters);

Given that Robocode does not provide built-in listeners, you also need to ensure the following methods get called on the PaintManager instance (if you do this manually, you will need to comment out these lines as well when disabling the framework, and uncomment them when enabling the framework):

  • public void onPaint(Graphics2D g)
  • public void mouseClicked(MouseEvent e)
  • public void mousePressed(MouseEvent e)
  • public void mouseReleased(MouseEvent e)
  • public void mouseDragged(MouseEvent e)
  • public void mouseMoved(MouseEvent e)

To make this potentially easier, PaintManager implements MouseListener and MouseMotionListener.

Extending PaintManager to interface with other frameworks

To avoid having to manually call the onPaint, MouseListener, and MouseMotionListener methods, you can optionally extend PaintManager and provide the appropriate code to interface with your own robot.

PaintManager is a Singleton, but you can extend it so long as you implement the static getInstance() method in a manner that initializes the instance in PaintManager, and ensure that you call the getInstance() method on your extended PaintManager class instead of PaintManager the first time you call getInstance() (which is usually when you call enable(...)). As a Singleton, you should also make the constructor private.

As an example, below is the XanderPaintManager class used by the Xander Robot Framework, that extends PaintManager and nicely integrates the Xander Painting Framework into the Xander Robot Framework:

/**
 * PaintManager to use with the Xander robot framework.  If using the Xander
 * robot framework, make sure you call one of the enable(...) methods on this class, 
 * and not on the regular PaintManager class. 
 * 
 * @author Scott Arnold
 */
public class XanderPaintManager extends PaintManager implements PaintListener {
	
	private XanderPaintManager() {
		// Resources is a Xander Robot Framework class that manages events.
		// It contains all the event listeners needed to ensure all the proper
		// PaintManager methods get called.
		RobotEvents robotEvents = Resources.getRobotEvents();
		robotEvents.addPainter(this);
		robotEvents.addMouseListener(this);
		robotEvents.addMouseMotionListener(this);
	}
	
	// Provide a getInstance() method that initializes the instance variable of the superclass
	public static synchronized PaintManager getInstance() {
		if (instance == null) {
			instance = new XanderPaintManager();
		}
		return instance;
	}

	/**
	 * Enable the PaintManager with only the painters that are
	 * part of the Xander robot framework.
	 * 
	 * @param battleFieldHeight
	 */
	public void enable(double battleFieldHeight) {
		enable(battleFieldHeight, (List<Painter<? extends Paintable>>)null);
	}
	
	@Override
	public void enable(double battleFieldHeight,
			List<Painter<? extends Paintable>> painters) {
		List<Painter<? extends Paintable>> fullPainterList = new ArrayList<Painter<? extends Paintable>>();
		// create all the framework painters first
		fullPainterList.addAll(XanderPainters.getPainters());
		// then add in all the custom painters
		if (painters != null) {
			fullPainterList.addAll(painters);
		}
		// then pass all painters to the superclass
		super.enable(battleFieldHeight, fullPainterList);
	}

This example from the Xander Robot Framework also overrides the primary enable(...) method, and adds another custom enable(...) method. This is not something that needs to be done; the Xander Robot Framework does this to ensure it's framework painters get added to the PaintManager in addition to any custom painters the robot adds.

Orphan Painters and Paintables

An orphan is any Painter that does not have a Paintable to paint, or any Paintable that does not have a Painter to paint it.

When an orphan Paintable exists, the Xander Painting Framework simply ignores it as if it didn't exist.

When an orphan Painter exists, the Xander Painting Framework assumes that you were expecting it to paint something. Despite having nothing to paint, it will still appear in the paint menu on the screen, but it will appear in the disabled menu color and will not have a toggle button.

Errors

If you have a Paintable that specifies a Painter name, and the corresponding Painter of that name specifies a Paintable class that is not compatible with the Paintable class, an IllegalArgumentException will be thrown.