In some of the games I wrote for Java 4K, I needed to create randomly shaped boulders and trees. I used this technique both in Raiden 4K and in Mini Miner 4K:
![]() |
![]() |
After checking many algorithms for generating convex polygons, I decided to go the other way round : after all, a rock or a tree top is more or less a regular polygon that has been somewhat deformed.
So we can start with a N-vertex regular polygon and move each point a bit along the axes that go through that point and the center of the polygon:
GeneralPath rockPath = new GeneralPath();
// Starting color of the rock. Subsequent layers
// become darker and darker
float rockHue = 0;
float rockSaturation = 14/255f;
float rockBrightness = 100/255f;
// Number of vertices
int numPoints = 20;
// Width of the rock in pixels
int rockSize = 200;
// Intensity of the deformation. The closer to 1 - the more jagged is the
// resulting rock
double intensity = 0.3;
BufferedImage rockImage =
new BufferedImage(rockSize,rockSize,BufferedImage.TYPE_INT_ARGB);
// Create tha basic polygon
Graphics2D gRock = rockImage.createGraphics();
for (int point = 0; point < numPoints; point++) {
// Calcuate a random perturbation along the axis of the nth point
double pertx = Math.random()*(rockSize/2.5*intensity)-rockSize/2.5*(intensity/2);
double perty = Math.random()*(rockSize/2.5*intensity)-rockSize/2.5*(intensity/2);
double angle = Math.PI/10*point;
int x = rockSize/2+(int)(rockSize/2.5*Math.cos(angle) + pertx*Math.cos(angle));
int y = rockSize/2-(int)(rockSize/2.5*Math.sin(angle) + perty*Math.sin(angle));
if (point == 0)
rockPath.moveTo((int)x,y);
else
rockPath.lineTo((int)x,y);
}
rockPath.closePath();
gRock.setColor(Color.getHSBColor(rockHue,rockSaturation,rockBrightness));
gRock.fill(rockPath);
In order to create a "shadow-like" effect or "3D-like effect" we proceed to clone the resulting polygon and scale it a bit, translate it a bit downwards and posslby shear it, depending on how different we want the "shadow" to be. The number of times we do this is a matter of preferences. Usually, 2-4 times is a good number. Each of these transformed polygons are clipped to remain within the boundaries of the bigger shape:
gRock.setClip(rockPath);
// Number of layers
int numLayers = 3;
Random rnd = new Random();
for (int layer = 1; layer < numLayers; layer++) {
int tx = (int)(rockSize/2.5/numLayers*layer);
int ty = (int)(rockSize/2.5/numLayers*layer);
double sx = 0.05/(rnd.nextDouble()+0.1);
double sy = 0.05/(rnd.nextDouble()+0.1);
gRock.translate(tx,ty);
gRock.shear(sx,sy);
gRock.setColor(
Color.getHSBColor( rockHue,
rockSaturation,
rockBrightness*(1-layer/(float)(numLayers+10)))
);
gRock.fill(rockPath);
gRock.setColor(Color.black);
gRock.draw(rockPath);
}
gRock = rockImage.createGraphics();
gRock.setStroke(new BasicStroke(2));
gRock.setColor(Color.black);
gRock.draw(rockPath);
Graphically, the process is as follows:

Here are some results that you can obtain with the above snippets, by varying parameters such as the number of points, the intensity of the deformation and the starting color:
|
|
![]() numPoints = 20 intensity = 0.5 HSB Color = (0.166,1,1) |
The RockTutorial.java application included contains the above code and is a fully-working application.
|
RockTutorial.java ( 3 Kb ) |