This is some code that uses a fractal algorithm to generate a 1D line for a 2D game. I thought it was OK until I tried changing the map size, when I realized that the terrain actually stretches with the map. A 2048x128 map looks like a flat plain with gently rolling hills; a 256x256 one with the same settings is extremely jagged and almost impossible to traverse.
Changing the factor in the bottom of LineSegment.java is a workaround (this determines how much the random number range decreases in each iteration), but I don’t want to do it that way–that number should reflect the roughness of the terrain, not the size of the map!
public class LineSegment {
public double x1;
public double y1;
public double x2;
public double y2;
public int level;
private double range;
public LineSegment(double x1, double y1, double x2, double y2, int level, double roughness)
{
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.level = level;
this.range = roughness;
}
public LineSegment[] subdivide(Random r)
{
double midpoint = (x1 + x2)/2;
LineSegment[] t = new LineSegment[2];
double rand;
if(level == 1)
{
double upperLimit = 0.20;
double lowerLimit = -0.20;
double randomRange = upperLimit - lowerLimit;
rand = lowerLimit + r.nextDouble() * randomRange;
}
else
{
rand = ((y1+y2)/2) + (r.nextDouble() * (range * 2) - range);
}
double l1x1 = x1;
double l1y1 = y1;
double l1x2 = midpoint;
double l1y2 = rand;
double l2x1 = midpoint;
double l2y1 = rand;
double l2x2 = x2;
double l2y2 = y2;
double factor = 0.620;
LineSegment l1 = new LineSegment(l1x1, l1y1, l1x2, l1y2, level + 1, range * factor);
LineSegment l2 = new LineSegment(l2x1, l2y1, l2x2, l2y2, level + 1, range * factor);
t[0] = l1;
t[1] = l2;
return t;
}
}
public void generate()
{
Random r = new Random();
int numLineSegments = width;
Vector<LineSegment> result = new Vector<LineSegment>(numLineSegments);
double upperLimit = 0.20;
double lowerLimit = -0.20;
double range = upperLimit - lowerLimit;
result.add(new LineSegment(0, lowerLimit + r.nextDouble() * range, width, lowerLimit + r.nextDouble() * range, 1, 1));
int currentLevel = 1;
while(result.size() < numLineSegments)
{
Iterator<LineSegment> itr = result.iterator();
Vector<LineSegment> tmp = new Vector<LineSegment>(numLineSegments);
while(itr.hasNext())
{
LineSegment l = itr.next();
if(l.level == currentLevel)
{
LineSegment[] parts = l.subdivide(r);
itr.remove();
tmp.add(parts[0]);
tmp.add(parts[1]);
}
}
result.clear();
Iterator<LineSegment> tmpItr = tmp.iterator();
while(tmpItr.hasNext())
{
result.add(tmpItr.next());
}
currentLevel++;
}
I’m 99% sure that rendering code isn’t the problem, but just in case:
for(Object o : result.toArray())
{
LineSegment l = (LineSegment) o;
int blockX = (int) l.x1;
int blockY = (int) (height * ((l.y1 + 1) / 2)); //It is originally a number from -1 to 1
if(blockX >= 0 && blockX < width && blockY >= 0 && blockY < width)
{
setBlock(blockX, blockY, 3); //Grass
int columnY = blockY - 1;
for(int y = 0; y < 5; y++) //Dirt
{
if(columnY > 0)
setBlock(blockX, columnY, 2);
columnY--;
}
while(columnY > 0) //Stone
{
setBlock(blockX, columnY, 1);
columnY--;
}
}
}