Renderer#
A game wouldn't really be much of a game, if you couldn't see anything on the screen.
In GameKit, the ability to draw anything to the screen is handled by the static Renderer class. Renderer works by
dispatching draw calls to the render queue. In the draw thread, these calls are evaluated and applied to the game
window.
Static Methods#
| Method | Description |
|---|---|
clear |
Clears the visible window area with a color |
drawLine |
Draws a line between two pairs of coordinates |
drawVerticalLine |
Draws a vertical line between two y-coordinates and an x-coordinate |
drawHorizontalLine |
Draws a horizontal line between two x-coordinates and an y-coordinate |
fillRect |
Draws a center-origin rectangle filled with a color |
drawRect |
Draws a center-origin rectangle outline |
fillRoundRect |
Draws a center-origin rounded rectangle filled with a color |
drawRoundRect |
Draws a center-origin rounded rectangle outline |
fillOval |
Draws a center-origin oval filled with a color |
drawOval |
Draws a center-origin oval outline |
fillCircle |
Draws a center-origin circle filled with a color |
drawCircle |
Draws a center-origin circle outline |
drawImage |
Draws a center-origin image |
fillPolygon |
Draws a polygon from a point array filled with a color |
drawPolygon |
Draws a polygon outline from a point array |
onLayer |
Creates a separate context to draw on a different layer |
In screen-space, drawing originates from the top-left of the shape by default. In GameKit, all drawing is offset to the center of the shape, hence the term center-origin.
Render Modifiers#
Each of the static methods in Renderer can further be enhanced with modifier methods which customize their
operation. These modifiers are defined in the table below:
| Method Call | Description |
|---|---|
withColor |
Sets the foreground color of the draw call |
withStroke |
Sets the border stroke (width and type) of the draw call |
withPaint |
Sets the paint of the draw call. Paint allows drawing of gradients |
withClip |
Sets the clip region of the draw call which prevents drawing outside the specified area |
withRotation |
Sets the clockwise rotation in radian of the draw call |
withOpacity |
Sets the opacity of the draw call |
withInterpolation |
Sets the image interpolation of the draw image call |
withMask |
Sets the alpha mask of the draw image call |
withTarget |
Sets the secondary render target of the draw image call |
Render Layers#
The Renderer provides 64 layers (0 to 63) on which drawing can take place. This allows you to control the order in
which draw calls are processed. Layers with higher indices are drawn in front of layers with lower indices and by
default rendering is done on layer 0.
To draw on a particular layer, use Renderer.onLayer() to create a new layer context as shown below:
Renderer.onLayer(2, () -> {
// Any Renderer command here will be drawn on the 3rd layer (index 2).
// Items drawn to layer 2 will appear in front of layer 0 and layer 1.
Renderer.fillRect(0, 0, 48, 16);
...
});
Image Masking#
The Renderer allows you to mask one image with another. Masking is where the alpha layer of an image determines
which portions of another image is visible.
When performing masking, at the portions of the mask where the alpha = 1.0, the pixels in the corresponding area of the underlying image are cleared and if the alpha is 0.0, the pixels in the overlapping area are unchanged.
In other words, the transparent parts of the mask are where the underlying image is drawn. Likewise, the opaque parts of the mask are where underlying image is hidden.
To set a mask on a draw image call, use the withMask() modifier method as shown below:
BufferedImage mask = ...;
Renderer.drawImage(...).withMask(target);
Custom Render Targets#
As stated initially, draw calls are objects dispatched by the Renderer which perform actual drawing onto the
screen, which is the primary render target.
The custom render targets feature allows you to set a BufferedImage as a secondary render target to a draw image
call. This means, after drawing the image to the screen, the draw call will also draw to the provided target.
This is useful if you need the rendered image for computation within your game.
To set a render target on a draw image call, use the withTarget() modifier method as shown below:
BufferedImage target = ...;
Renderer.drawImage(...).withTarget(target);
Rendering Sample#
The sample below is a scene which uses the renderer methods and modifiers shown above:
import dev.gamekit.core.Application;
import dev.gamekit.core.IO;
import dev.gamekit.core.Scene;
import dev.gamekit.settings.ImageInterpolation;
import java.awt.*;
import java.awt.image.BufferedImage;
public class RendererScene extends Scene {
private static final BufferedImage IMAGE = IO.getImage("test.jpg");
private static final int[] POLYGON_POINTS = new int[]{ 90, -190, 110, -110, 50, -99 };
private static final Stroke DEFAULT_STROKE = new BasicStroke(2, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
public RendererScene() {
super("Rendering Showcase");
}
public static void main(String[] args) {
Application game = new Application("Renderer Showcase") { };
game.loadScene(new RendererScene());
game.run();
}
@Override
protected void render() {
Renderer.clear(Color.DARK_GRAY);
Renderer.drawLine(0, 0, 10, 10)
.withColor(Color.GREEN); // Draw green line from (0, 0) to (10, 10) on layer 0
Renderer.drawVerticalLine(0, -20, -4)
.withStroke(DEFAULT_STROKE); // Draw vertical line from (0, -20) to (0, -4) using the DEFAULT_STROKE object on layer 0
Renderer.drawHorizontalLine(0, 20, 5)
.withColor(Color.GREEN)
.withStroke(DEFAULT_STROKE); // Draw a green horizontal line from (0, 5) to (20, 5) using the DEFAULT_STROKE object on layer 0
Renderer.onLayer(1, () -> {
Renderer.drawRect(0, 0, 50, 20); // A 50x20 rect centered at (0, 0) on layer 1
Renderer.fillRect(0, 0, 48, 16);
Renderer.drawRoundRect(50, 50, 75, 20, 4, 4); // A 75x20 rect centered at (50, 50) with horizontal arc radius 4 and vertical arc radius 4 on layer 1
Renderer.fillRoundRect(50, 50, 75, 20, 4, 4);
});
Renderer.drawOval(10, 20, 50, 30); // A 50x30 oval centered at (10, 20) on layer 0
Renderer.fillOval(10, 20, 48, 16);
Renderer.drawCircle(30, 30, 50); // A 50px radius circle centered at (30, 30) on layer 0
Renderer.fillCircle(30, 30, 48);
Renderer.drawImage(IMAGE, 100, 100, 300, 200) // A 300x200 image centered at (100, 100)...
.withRotation(100, 100, 0.5 * Math.PI) // rotated by 90 degrees about (100, 100)...
.withInterpolation(ImageInterpolation.NEAREST); // with nearest neighbour image interpolation on layer 0
Renderer.drawPolygon(POLYGON_POINTS)
.withClip(0, 0, 100, 100); // Polygon points cliped to the area defined by top-left (0, 0) and width and height of 100x100 on layer 0
Renderer.fillPolygon(POLYGON_POINTS);
}
}