XanderPaintingFramework

From Robowiki
Revision as of 01:11, 11 November 2011 by Skotty (talk | contribs) (Add details on saving and restoring painting framework state between battles.)
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.

Saving and Restoring Painter Settings

Optionally, you can choose to save the state of the painting framework between battles. This allows all of your previously enabled painters, colors, windows sizes, and window locations to be as you left them the next time you use the painting framework.

To use this functionality, make sure the following methods gets called:

  • public void restoreState(AdvancedRobot robot) -- call this method after enabling the framework.
  • public void saveState(AdvancedRobot robot) -- call this method at the end of the battle.

Updating your robot version does not clear out the saved painting framework information. If your robot has changed significantly and you want to clear out previously saved window information, you can either not call restoreState(...), or at any time before calling saveState(...), you can call clearState().

Window information is keyed by the Painter names, so if you change a painter name, the state will not be restored.

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.

ToDo's

As of the version released with XanderCat 10.19, the following features remain to be implemented:

  1. Clip the window painters so that they cannot paint outside of the window bounds.
  2. Add resize support on the windows.
  3. Add close button on the window title bars.
  4. Provide better initial window positioning.
  5. Add code to save/load painter states between battles. (Done! But not available in 10.19)
  6. Add ability to change more of the colors and styles used in the framework.
  7. Improve default color palette.
  8. Change toggle button enabled images from squares to check marks to make their behavior more obvious.

Obtaining a Copy

An early release of the Xander Painting Framework is available in robot XanderCat 10.19. All Xander Painting Framework classes are in package "xander.paint". For some examples of integrating and using the Xander Painting Framework with the Xander Robot Framework, look in package "xander.core.paint".