I am trying to generate terrain for a simple game I'm making using midpoint displacement, and it's working well so far, but I keep getting weird artifacts that I don't see in most of the examples that I see online and I don't really know what is causing them. They look like this:
http://i.imgur.com/uuhfHPg.png
the mostly smooth generation seems to be littered with random and incredibly sudden changes in height. Is this normal or am I doing something wrong?
The class that I'm using to generate the heightmap looks like this:
package util;
import java.util.Random;
public class HeightmapGen {
private HeightmapGen(){}
//The size of the map will be 2^iterations + 1
public static int[][] genMap(int iterations, double roughness){
Random rand = new Random();
return genMap(iterations, roughness, rand.nextLong());
}
public static int[][] genMap(int iterations, double roughness, long seed){
Random r = new Random(seed);
double rConstant = Math.pow(2,-roughness);
double currRoughVal = 1.0;
int size = (int)Math.pow(2,iterations); //size needs to be a power of 2 + 1 - this size includes 0
int subSize = size; //the "working" size of the squares
int stride = subSize/2;
boolean oddline = false; //not entirely sure what this does either...
int[][] map = new int[size + 1][size + 1];
//initalize corners
//int init = r.nextInt();
//int init = 0;
//map[0][0] = map[0][size] = map[size][0] = map[size][size] = init;
map[0][0] = r.nextInt();
map[0][size] = r.nextInt();
map[size][0] = r.nextInt();
map[size][size] = r.nextInt();
while(stride != 0){
//the square part
for(int x = stride; x < subSize; x += stride){
for(int y = stride; y < subSize; y += stride){
map[x][y] = (int)(r.nextInt() * currRoughVal) + avgSquareVals(x, y, stride, map);
y += stride;
}
x += stride;
}
//the diamond part
//not entirely sure what oddline is for, but it seems nessicary
oddline = false;
for (int x = 0; x < subSize; x += stride){
oddline = (oddline == false);
for (int y = 0; y < subSize; y += stride){
if (oddline && y == 0){y += stride;}
map[x][y] = (int)(r.nextInt() * currRoughVal) + avgDiamondVals(x, y, stride, size, subSize, map);
//this wraps the map - i'm ignoring this for now
//if(x == 0){}
//if(y == 0){}
y += stride;
}
//not sure x doesn't need to be incremented, but it doesn't?
}
//reduce range and halve stride
currRoughVal *= rConstant;
stride /= 2;
}
return map;
}
private static int avgSquareVals(int x, int y, int stride, int[][] map){
return (map[x + stride][y + stride] +
map[x + stride][y - stride] +
map[x - stride][y + stride] +
map[x - stride][y - stride])/4;
}
private static int avgDiamondVals(int x, int y, int stride, int size, int subSize, int[][] map){
if(x == 0){
return (map[x + stride][y] +
map[x][y + stride] +
map[x][y - stride])/3;
}
else if(x == size){
return (map[x - stride][y] +
map[x][y + stride] +
map[x][y - stride])/3;
}
else if(y == 0){
return (map[x + stride][y] +
map[x - stride][y] +
map[x][y + stride])/3;
}
else if( y == size){
return (map[x + stride][y] +
map[x - stride][y] +
map[x][y - stride])/3;
}
else{
return (map[x + stride][y] +
map[x - stride][y] +
map[x][y + stride] +
map[x][y - stride])/4;
}
}
}
It's not perfect and parts of it I don't understand - it's based off of the code I found here: http://www.gameprogrammer.com/fractal.html
Related
I'm trying to get my code to recreate a random x and y value if there is already another object occupying the chosen value. But for some reason, the loop just keeps giving me the same float value causing it to loop forever instead of creating new value and then checking that one against previous x and y variables. Here's the code. Let me know if you need anymore.
static int numberOfDots = 5;
static int windowSize = 400;
float[] pos = new float[2];
float[] vel = new float[2];
float[] acc = new float[2];
float dotSize = 0;
float dotMass = 0;
float dotWidth = 0;
float dotRadius = 0;
float[] centrePos = new float[2];
float[][] dots = new float[numberOfDots][10];
//for making dots
boolean first = true;
boolean creationCollision = false;
boolean dotCollision = false;
// pos means x and y positions
// vel means x and y velocity
// centrePos means centre of the dot x and y
// dotMass isnt implemented yet
public boolean collisionOnCreate() {
//compare all dots
for (int i = 0; i < dots.length; i++) {
//against any dots later in the array
// since they will already have compared themselves to dots previous in the array
// - dont need to loop over them again
for (int a = i + 1; a < dots.length; a++) {
if (dots[a][0] != 0) {
creationCollision = dots[i][1] + dots[i][8] > dots[a][1] && dots[i][1] + dots[i][8] < dots[a][1] + dots[a][8] || dots[i][1] > dots[a][1] && dots[i][1] < dots[a][1] + dots[a][8] || dots[i][0] > dots[a][0] && dots[i][0] < dots[a][0] + dots[a][8] || dots[i][0] + dots[i][8] > dots[a][0] && dots[i][0] + dots[i][8] < dots[a][0] + dots[a][8];
System.out.println(dots[a][0] + " " + dots[a][1]);
}
//check if the left-most part of the dot is anywhere between the leftmost and right most part of another dot
//check if the rightmost part of the dot is anywhere between the left most and right most part of another dot
//check if the top part of the dot is anywhere between the top and bottom part of another dot
//check if the bottom of the dot is anywhere between the top and bottom part of another dot
// I know its long but its easier then running a bunch of if statements to do the same task
}
}
return creationCollision;
}
public void createDots(int i) {
pos[0] = r.nextFloat() * 300;
pos[1] = r.nextFloat() * 300;
dotWidth = r.nextFloat() * 30;
if (dotWidth < 5) {
dotWidth *= 10;
}
dotRadius = dotWidth / 2;
dotMass = r.nextFloat() / 10;
centrePos[0] = centrePos[0] + dotRadius;
centrePos[1] = pos[1] + dotRadius;
vel[0] = r.nextFloat() / 10;
vel[1] = r.nextFloat() / 10;
check(i);
}
public void check(int i) {
collisionOnCreate();
if (creationCollision) {
createDots(i);
System.out.println("collision on creation");
} else {
setValues(i);
System.out.println("dot number " + i + " is created");
}
}
public void setValues(int i) {
dots[i][0] = pos[0];
dots[i][1] = pos[1];
dots[i][2] = vel[0];
dots[i][3] = vel[1];
dots[i][4] = dotRadius;
dots[i][5] = centrePos[0];
dots[i][6] = centrePos[1];
dots[i][7] = dotMass;
dots[i][8] = dotWidth;
}
//create an array of dots and assign them values for x, y, radius, etc.
public float[][] Dots() {
if (first == true) {
for (int i = 0; i < numberOfDots; i++) {
createDots(i);
}
first = false;
} else {
for (int i = 0; i < numberOfDots; i++) {
// update values
dots[i][0] = dots[i][0] + dots[i][2];
dots[i][1] = dots[i][1] + dots[i][3];
centrePos[0] = dots[i][0] + dots[i][4];
centrePos[1] = dots[i][1] + dots[i][4];
dots[i][5] = centrePos[0];
dots[i][6] = centrePos[1];
collisionOnWall(i);
}
}
repaint();
return dots;
}
I don't think the problem is with random number generation. Instead there is something wrong with the logic of creation / checking.
It seems that first, inside of createDots(), you assign values to some variables that are meant to represent a new dot (why don't make a dedicated class for that?). Then you don't add that new dot to dots array but instead you go straight to checking if there are any collisions.
However, you only check the values that are already present in the dots array, without taking into account the newly created one.
Another thing, that creationCollision condition is really unreadable to me. You should use some parentheses just for the clarity. Plus make sure it is actually doing what you want it to.
You don't appear to ever set dots[i] to your new values here:
public void createDots(int i) {
pos[0] = r.nextFloat() * 300;
pos[1] = r.nextFloat() * 300;
dotWidth = r.nextFloat() * 30;
if (dotWidth < 5) {
dotWidth *= 10;
}
dotRadius = dotWidth / 2;
dotMass = r.nextFloat() / 10;
centrePos[0] = centrePos[0] + dotRadius;
centrePos[1] = pos[1] + dotRadius;
vel[0] = r.nextFloat() / 10;
vel[1] = r.nextFloat() / 10;
check(i);
}
Looks like you want to call setValues(i) at the end, just before check(i).
I have this array
Ball[] balls = new Ball[7]; // 7 just being an example
In my Ball class, I have getters and setters for x and y values.
I'm trying to compare the x and y values to make sure that they don't intersect.
My first thought was to make a loop looking like
for(Ball b1 : balls) {
for(Ball b2 : balls) {
if(b1.intersects(b1, b2)) {. . .} // I made intersects, not my issue
}
}
But this is no good, as it compares:
balls 0 to balls 0
balls 1 to balls 1
etc.
for(int i = 0; i < balls.length; i++) {
System.out.println(f.getContentPane().getWidth() + "\n" + f.getContentPane().getHeight());
int radius = 10 + rand.nextInt(20);
balls[i] = new Ball(360, radius,
rand.nextInt(f.getContentPane().getWidth() - 4 * radius - 5) + radius + 5,
rand.nextInt(f.getContentPane().getHeight() - 4 * radius - 5) + radius + 5
);
}
for(Ball b1 : balls) {
for (Ball b2 : balls) {
while (b1.intersects(b1, b2)) {
System.out.println("Ball started out inside of another, replacing now.");
b1.setX(rand.nextInt(f.getContentPane().getWidth() - 2 * b1.getRadius() - 5) + b1.getRadius() + 5);
b1.setY(rand.nextInt(f.getContentPane().getHeight() - 2 * b1.getRadius() - 5) + b1.getRadius() + 5);
}
}
}
////////////// class change //////////////////
class Ball {
private int direction;
private int radius;
private int x,y;
Ball(int direction, int radius, int x, int y) {
this.direction = direction;
this.radius = radius;
this.x = x;
this.y = y;
}
// Getters + Setters here
boolean intersects(Ball b1, Ball b2) {
double x = Math.pow((b2.getX() - b1.getX()), 2); // Distance formula
double y = Math.pow((b2.getY() - b1.getY()), 2); // Distance formula
double r = b1.getRadius() + b2.getRadius();
//System.out.println(x + " + " + y + " <= " + r );
return x + y <= r;
}
}
(Ignore that I didn't put my first hunk of code in a method and class, I've done that in my actual code.)
I, for whatever reason, can't think of a way to do this without a whole lot of if statements
(So I'm asking for the best way to do this)
One way to compare every distinct (i.e., no ball with itself) pair of Balls, without comparing any pair more than once would be:
for (int i = 0; i < balls.length; ++i) {
Ball b1 = balls[i];
for (int j = i+1; j < balls.length; ++j) {
Ball b2 = balls[j];
if (b1.intersects(b1, b2)) {
// ...
}
}
}
Detecting new collisions introduced in the process of resolving previous ones just means making multiple passes over balls until you no longer have any collisions. A simple, perhaps naive, way of doing this would be something like this:
boolean foundCollision;
int numTries = 0;
int maxTries = 1000000;
do {
foundCollision = false;
for (int i = 0; i < balls.length; ++i) {
Ball b1 = balls[i];
for (int j = i+1; j < balls.length; ++j) {
Ball b2 = balls[j];
if (b1.intersects(b1, b2)) {
foundCollision = true;
// resolve collision...
}
}
++numTries;
} while (foundCollision && numTries < maxTries);
if (numTries >= maxTries)
System.err.println("Couldn't sort out balls after " + maxTries + "tries: what now?");
I am attempting to draw a tile based floor for a game.
Basically each tile has a height value, and I am trying to get each cornet to connect to the adjacent tiles in a smooth way. So like, if the tile was at a height of 2, and the tile beside it was at a height of 3, then they would smoothly connect on a slope.
Here is my code for getting this as of now:
--see below for updated code --
The height of the tile is obtained by Tile.getCenter();
I am not sure where to go from here, I am using libGDX.
px and py are just the center of the loading point, and floor is the LibGDX model batch for the floor, hopefully containing each floor time in the end.
Thanks in advance!
-Bc
--Edit--
Just a little more info, The vertex class basically just stores two ints.
The tl, tr, bl, br, are for the tile to know Top left height, top right height (etc). -- I renamed everything to be a little more clear
This class is just to find the angles that I need, basically where each corner is rendered, once I have that I can find out how to render it on my own.
On the note of tl, tr, etc. should I be finding the corners? or the center of the sides? I have never done 3d gfx before.
--Edit 3--
I worked a bit more out on my own, hopefully this is closer:
private Map<Vertex, Tile> tiles = new HashMap<Vertex, Tile>();
public float getMode(float[] data)
{
int total = 0;
for (int i = 1; i < data.length; i++){
total += data[i];
}
return total / data.length;
}
protected void loadTiles(int px, int py){
//load tile from file
int FIXTHISTOSIZE = 10;
for (int x = px - (FIXTHISTOSIZE / 2); x <= FIXTHISTOSIZE; x++){
for (int y = (py - FIXTHISTOSIZE / 2); y <= FIXTHISTOSIZE; y++){
int aY = py - y;
int aX = px - x;
Tile tile = new Tile(aX, aY);
tiles.put(new Vertex(aX, aY) , tile);
}
}
for (int x = px - (FIXTHISTOSIZE / 2); x <= FIXTHISTOSIZE; x++){
for (int y = py - (FIXTHISTOSIZE / 2); y <= FIXTHISTOSIZE; y++){
int aY = py + y;
int aX = px + x;
Vertex pos = new Vertex(aX, aY);
float TOP_LEFT = -65536, TOP_RIGHT = -65536, BOTTOM_LEFT = -65536, BOTTOM_RIGHT = -65536; //TODO these should be 0, they are this number to demonstrate unloaded tiles for now
Tile TILE_CENTER = tiles.get(pos);
Tile TILE_TOP_RIGHT = tiles.get(new Vertex(aX + 1, aY + 1));
Tile TILE_TOP = tiles.get(new Vertex(aX, aY + 1));
Tile TILE_RIGHT = tiles.get(new Vertex(aX + 1, aY));
Tile TILE_BOTTOM_RIGHT = tiles.get(new Vertex(aX + 1, aY - 1));
Tile TILE_BOTTOM = tiles.get(new Vertex(aX, aY - 1));
Tile TILE_BOTTOM_LEFT = tiles.get(new Vertex(aX - 1, aY - 1));
Tile TILE_LEFT = tiles.get(new Vertex(aX - 1, aY));
Tile TILE_TOP_LEFT = tiles.get(new Vertex(aX - 1, aY + 1));
//TOP_RIGHT
if (TILE_TOP != null && TILE_TOP_RIGHT != null && TILE_RIGHT != null){
float[] dub = {TILE_TOP.getCenter(), TILE_TOP_RIGHT.getCenter(),TILE_RIGHT.getCenter(),TILE_CENTER.getCenter()};
TOP_RIGHT = getMode(dub);
}
//TOP_LEFT
if (TILE_TOP != null && TILE_TOP_LEFT != null && TILE_LEFT != null){
float[] dub = {TILE_TOP.getCenter(), TILE_TOP_LEFT.getCenter(),TILE_LEFT.getCenter(),TILE_CENTER.getCenter()};
TOP_LEFT = getMode(dub);
}
//TODO BOTTOM_RIGHT
//TODO BOTTOM_LEFT
TILE_CENTER.setConditions(TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT);
}
}
I FINALLY figured it out!
initialize:
int FIXTHISTOSIZE = 10;
for (int x = px - (FIXTHISTOSIZE / 2); x <= FIXTHISTOSIZE; x++){
for (int y = py -(FIXTHISTOSIZE / 2); y <= FIXTHISTOSIZE; y++){
int aY = py + y;
int aX = px + x;
Tile tile = new Tile(aX, aY);
tiles.put("" + aX + "." + aY, tile);
}
}
this.loadTiles(px, py);
main loop (loadTiles):
int FIXTHISTOSIZE = 10;
for (int x = px - (FIXTHISTOSIZE / 2); x <= FIXTHISTOSIZE; x++){
for (int y = py - (FIXTHISTOSIZE / 2); y <= FIXTHISTOSIZE; y++){
int aY = py + y;
int aX = px + x;
float TOP_LEFT = 0, TOP_RIGHT = 0, BOTTOM_LEFT = 0, BOTTOM_RIGHT = 0;
Tile TILE_CENTER = tiles.get("" + aX + "." + aY);
if (TILE_CENTER.isLoaded()) {
continue;
}
Tile TILE_TOP_RIGHT = tiles.get("" + (aX + 1) + "." + (aY + 1));
Tile TILE_TOP = tiles.get("" + aX + "." + (aY + 1));
Tile TILE_RIGHT = tiles.get("" + (aX + 1) + "." + aY);
Tile TILE_BOTTOM_RIGHT = tiles.get("" + (aX + 1) + "." + (aY - 1));
Tile TILE_BOTTOM = tiles.get("" + aX + "." + (aY - 1));
Tile TILE_BOTTOM_LEFT = tiles.get("" + (aX - 1) + "." + (aY - 1));
Tile TILE_LEFT = tiles.get("" + (aX - 1) + "." + aY);
Tile TILE_TOP_LEFT = tiles.get("" + (aX - 1) + "." + (aY + 1));
//TOP_RIGHT
if (TILE_TOP != null && TILE_TOP_RIGHT != null && TILE_RIGHT != null){
float[] dub = {TILE_TOP.getCenter(), TILE_TOP_RIGHT.getCenter(),TILE_RIGHT.getCenter(),TILE_CENTER.getCenter()};
TOP_RIGHT = getMedian(dub);
}
//TOP_LEFT
if (TILE_TOP != null && TILE_TOP_LEFT != null && TILE_LEFT != null){
float[] dub = {TILE_TOP.getCenter(), TILE_TOP_LEFT.getCenter(),TILE_LEFT.getCenter(),TILE_CENTER.getCenter()};
TOP_LEFT = getMedian(dub);
}
//BOTTOM_LEFT
if (TILE_BOTTOM != null && TILE_BOTTOM_LEFT != null && TILE_LEFT != null){
float[] dub = {TILE_BOTTOM.getCenter(), TILE_BOTTOM_LEFT.getCenter(),TILE_LEFT.getCenter(),TILE_CENTER.getCenter()};
BOTTOM_LEFT = getMedian(dub);
}
//BOTTOM_RIGHT
if (TILE_BOTTOM != null && TILE_BOTTOM_RIGHT != null && TILE_RIGHT != null){
float[] dub = {TILE_BOTTOM.getCenter(), TILE_BOTTOM_RIGHT.getCenter(),TILE_RIGHT.getCenter(),TILE_CENTER.getCenter()};
BOTTOM_RIGHT = getMedian(dub);
}
TILE_CENTER.setConditions(TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT);
median:
public static float getMedian(float[] data) {
float[] copy = Arrays.copyOf(data, data.length);
Arrays.sort(copy);
return (copy.length % 2 != 0) ? copy[copy.length / 2] : (copy[copy.length / 2] + copy[(copy.length / 2) - 1]) / 2;
}
Hopefully this will help somebody in the future
Result:
http://i.imgur.com/X66vDxT.png
Im trying to implement Heightmaps to my game, so now i have made several methods, but they are so slow and also, the height returns numbers like 1.463563298e14!
How can i get a value between 0&1 from the pixel on the heightmap?
These are my methods:
public void setupHeights() {
int pixWidth = heightfile.getWidth();
int pixHeight = heightfile.getHeight();
int w = pixWidth;
int h = pixHeight;
this.heights = new float[w][h];
boolean countWidth = true;
for (int z = 0; z < pixHeight; z++) {
depth++;
for (int x = 0; x < pixWidth; x++) {
if (countWidth)
width++;
int height;
if (whiteHigh) {
height = 256 - (-1 * heightfile.getRGB(x, z));
} else {
height = -1 * heightfile.getRGB(x, z);
}
heights[x][z] = height;
numPoints++;
}
countWidth = false;
}
System.out.println("Heights have been set up!");
}
#Override
public void update() {
if (!this.hasWorldObj())
return;
if (heightfile!=null) {
for (Entity e : entities) {
if (e != null && !(e.posX < pos.getX() || e.posZ < pos.getZ() || e.posX > pos.getX() + 1
|| e.posZ > pos.getZ() + 1)) {
{
if (heightfile != null) {
double localX = Math.abs(pos.getX() - e.posX);
double localZ = Math.abs(pos.getZ() - e.posZ);
int x = (int) (localX * width);
int y = (int) (localZ * depth);
e.posY = pos.getY() + getHeight(x, y);
}
}
} else
e = null;
}
}
}
public float getHeight(float xf, float zf) {
int x = worldCoordToIndex(xf);
int z = worldCoordToIndex(zf);
System.out.println("getting height for: " + x + ", " + z);
if (x < 0)
x = 0;
if (z < 0)
z = 0;
if (z >= heights.length) {
System.out.println("WARN getHeight z index out of bounds: " + z);
z = heights.length - 1;
}
if (x >= heights[z].length) {
System.out.println("WARN getHeight x index out of bounds: " + x);
x = heights[z].length - 1;
}
System.out.println("Returned height: " + heights[z][x] * heightScale);
return heights[z][x] * heightScale;
}
private int worldCoordToIndex(float f) {
return (int) Math.floor(f / widthScale);
}
i am currently attempting to make a fractal terrain generator using the diamonds and squares algorithm. I have the algorithm completed (I think) but I don't know how to use JFrames very well.
How would I get this to display the tiles?
package fractal.terrain;
import java.util.Random;
public class Main {
public int mapSize = 257;
public static void main(String[] args) {
Main m = new Main();
double tileData[][] = new double[m.mapSize][m.mapSize];
Random r = new Random();
final double seed = r.nextInt(10000 - 500 + 1);
System.out.println("The seed is " + seed);
tileData[0][0] = tileData[0][m.mapSize - 1] = tileData[m.mapSize - 1][0]
= tileData[m.mapSize - 1][m.mapSize - 1] = seed;
double h = 10000.0;
for (int sideLength = m.mapSize - 1; sideLength >= 2; sideLength /= 2) {
int halfSide = sideLength / 2;
for (int x = 0; x < m.mapSize - 1; x += sideLength) {
for (int y = 0; y < m.mapSize - 1; y += sideLength) {
double avg = tileData[x][y]
+ tileData[x + sideLength][y]
+ tileData[x][y + sideLength]
+ tileData[x + sideLength][y + sideLength];
avg /= 4.0;
tileData[x + halfSide][y + halfSide] = avg + (r.nextDouble() * 2 * h) - h;
}
}
for (int x = 0; x < m.mapSize - 1; x += halfSide) {
for (int y = (x + halfSide) % sideLength; y < m.mapSize - 1; y += sideLength) {
double avg = tileData[(x - halfSide + m.mapSize) % m.mapSize][y]
+ tileData[(x + halfSide) % m.mapSize][y]
+ tileData[x][(y + halfSide) % m.mapSize]
+ tileData[x][(y - halfSide + m.mapSize) % m.mapSize];
avg /= 4.0;
avg = avg + (r.nextDouble() * 2 * h) - h;
tileData[x][y] = avg;
if (x == 0) {
tileData[m.mapSize - 1][y] = avg;
}
if (y == 0) {
tileData[x][m.mapSize - 1] = avg;
}
}
}
}
}
}
override the draw method on a JPanel and cycle through the array drawing different colored Rectangles for each value.
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
for (int x = 0; x < m.mapSize; x++) {
for (int y = 0; y < m.mapSize; y++) {
//check each tile and assign in a color based on its value
if(tileData[x][y] == aCertainNumber){
g.setColor();
}
g.fillRect(x*10, y*10, 10, 10);
}
}
this should display each tile 10 pixels by 10 pixels
you could also output to an image with
BufferedImage map = new BufferedImage(mapSize, mapSize,BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < mapSize; x++) {
for (int y = 0; y < mapSize; y++) {
if(mapData[x][y]==aCertainNumber)
map.setRGB(x, y, theColorYouWant.getRGB());
}
}