tiled map case study: rendering with jpanel © allan c. milne v14.12.26
TRANSCRIPT
tiled Map Case Study:Rendering with JPanel
© Allan C. Milne
v14.12.26
Agenda.
Accessing a JPanel.
Map & tile representation.
Drawing the map.
Using the renderer.
Where are we now?
• we use a strategy pattern to include rendering behaviour;• the MapRenderer interface;• composition of tiledMap with a concrete
MapRenderer object;• the render() method mechanism.
• We have a concrete rendering class;• ConsoleRenderer.
Where do we want to be?
• Add more concrete rendering implementations;– different ways of displaying on the console;– using different display technologies.
• In particular, rendering in a windows user interface;– for example, using a JPanel to draw some
representation of the map.
A problem with JPanel.
• There is only one console and any code can access it;– thus any code writing to the console to
render a map simply uses System.out
• … but a client application’s user interface may have many JPanel objects making it up;– which JPanel object will the map
renderering code draw on?
Our approach.
• Make the concrete rendering class – implement the MapRenderer interface; and– inherit from JPanel;
• This means that our class can both– act as a concrete rendering class; and– be used as a JPanel within a client’s user
interface.
class JPanelRenderer extends JPanel implements MapRenderer { @Override public void render (TiledMap aMap) { … … … } // end render method.
@Override protected void paintComponent (Graphics g) { … … … } // end paintcomponent method.
… … …
} // end JPanelRenderer class.
The concrete renderer.
paintComponent(Graphics g)
• Renders a map by drawing a representation on the JPanel through its supplied Graphics object.
• Called by the panel’s redraw() method when this is called by a client.
• We require to have– a map object to draw; and– a representation to use for the drawing.
class JPanelRenderer extends JPanel implements MapRenderer { private TiledMap mMap;
@Override public void render (TiledMap aMap) { mMap = aMap; redraw(); } // end render method.
@Override protected void paintComponent (Graphics g) { … … … } // end paintcomponent method.
… … …
} // end JPanelRenderer class.
So we have …
Constructor method(s).
• Since our rendering class is also a JPanel any constructor methods must reflect the requirements of a JPanel.
• In particular, we can supply the size of the panel in pixels.
• We should also initialize the mMap field.
• public JPanelRenderer (int aWidth, int aHeight) • { • //TODO validate the arguments are >0 and set to 0 if not.
• setPreferredSize (new Dimension (aWidth, aHeight));
• mMap = null; • } // end constructor method.
A constructor …
Map and tile representation.
• Consider the panel to be made up of a grid of squares:
– size of grid = size of map;
– Each rid square is drawn in a colour representing the terrain tile type of the corresponding map tile;
– each grid square will have a size (resolution) in pixels.
• Panel size (in pixels) and therefore shape will be determined by the client and Java layout manager when the JPanel object is created and added to the user interface.
• For a map to be rendered we must have– panel width >= map width; and– panel height >= map height.
• If dimension sizes are equal then the grid squares will be only 1 pixel in size.
• the shape of the grid is defined by the shape of the map and may not fill the panel if it has a different shape.
Map and grid size.
Map tile colours.
• java.awt.Color can represent colours.• The colour to draw a square on the grid
is determined by the terrain tile type of the corresponding tile on the map.
• Use a switch statement; or
• adopt a similar approach to that used for determining the character to display for a tile in the console renderer.
TerrainType enumeration.
• Add a private java.awt.Colour attribute.
• Amend the constructor to initialize this attribute with a supplied colour.
• Amend the value definitions to include a specific colour parameter.
• Expose the asColour() method to return the colour attribute for an value.
Drawing a map.
• paintComponent (Graphics g)– super.paintComponent(g);– for each map tile:
• use g.setColor(…) to set colour;• to draw the square use g.fillRect (
– x-coord of top left corner,– y-coord of top left corner,– x-size,– y-size )
We know …
• the size of the panel;– getWidth() and getHeight() panel methods.
• the map;– mMap field.
• the size of the map; – height() and width() methods of mMap.
• the colours to use for tiles;– asColour() method of TerrainType.
Outstanding problems.
• The resolution (pixels per grid square) must be determined.
• the coordinate system of a JPanel is different from that of the map.
Calculating resolution.• Is panel big enough to represent the map
even at 1 pixel per tile?– If not, then draw a message on the panel and
return.
• Resolution can only be an integer.• Is calculated as the smallest resolution of the
width and height dimensions;– Maintains the square aspect ratio of the grid
elements;– May result in unused areas of the panel if it is a
different shape than the map.
Coordinate systems.• In the map,
– (0,0) is at the bottom left;– X coordinates go from left to right;– Y coordinates go from bottom to top.
• On the JPanel,– (0,0) is at the top left;– X coordinates go from left to right;– Y coordinates go from top to bottom (but still positive).
• We must apply a transform from map to JPanel coordinates.
Coordinate transformation.
• Need to transform tile position (xm,ym) on the map to (xp,yp) on the JPanel.
• Requires us to consider– the resolution of the panel grid squares;– the different origin positions;– the different Y coordinate directions.
• xp = xm * resolution
• yp = (map-height * resolution) – ( (ym + 1) * resolution)
Client usage.
public static void main (…){ final TiledMap map = new TiledMap (…); … … … SwingUtilities.invokeLater(new Runnable() { @Override public void run() { createWindowAndRender (map); } }); … … …} // end main method.
… and the renderer …
private static final void createWindowAndRender (TiledMap aMap) { JPanelRenderer panelRenderer = new JPanelRenderer(…);
final JFrame frame = new JFrame(“ title "); … … … frame.add (panelRenderer, BorderLayout.CENTER); … … … frame.pack();
aMap.setRenderer (panelRenderer); aMap.render();
} // end createWindowAndRender method.