Allright, I'm working on a small game here in Java, and I am using this Simplex Noise generator I found online. The problem that I am facing, is this: I'm generating the world of my game like so:
int width = 100;
int height = 100;
world = new int[width * height];
SimplexNoise noise = new SimplexNoise();
for (int i = 0; i < world.length; i++) {
int x = i % width; // what are the coordinates from i ?
int y = i / width ;
int frequency = 15;
float h = (float) noise.noise((float) x / frequency, (float) y / frequency);
if (h >= -1 && h <= 0) {
world[x + y * width] = 0; // air tile
}
else if (h > 0 && h <= 1) {
world[x + y * width] = 1; // test tile
}
}
which quite obviously gives me 2D noise. The end result looks like this:
As far as I understand noise, 2D noise is for top-down games. The one I'm working on, is a side-scroller (like Terraria, Starbound, Crea and others), though. So I'd need a terragen to give me the topmost layer of the terrain, google tells me that is 1D noise, so here's the question: How to convert this 2D noise into a terrain-looking 1D noise?
Just make it a picture of height = 1. Done :)
Related
I want to sample a height map for my 3D engine. It might happen that there are more vertices on my terrain than pixels in my image. I created following method:
public float interpolateFromImage(BufferedImage image, float x, float y){
This takes the image and the x and y coordinate. The coordinates are given in percent.
My approach was to calculate the distances between the nearby pixels and the given coordinate. This worked fine but I ended up with this:
It can be clearly seen that the edges are not as smooth as they should be.
I used following code:
int topLeftX = (int) (x * (image.getWidth()-1)); //index of topLeft pixel
int topLeftY = (int) (y * (image.getHeight()-1)); //index of topLeft pixel
float[] distances = new float[4];
float total = 0;
for(int j = topLeftX ; j < topLeftX + 2; j++){ //run through all 4 nearby pixels
for(int k = topLeftY ; k < topLeftY + 2; k++){
float dist = (float) Math.sqrt( //pythagoras for the distance
(x- j/ (float)(image.getWidth()-1)) *
(x- j/ (float)(image.getWidth()-1)) +
(y- k/ (float)(image.getHeight()-1)) *
(y- k/ (float)(image.getHeight()-1)));
if(dist < 0.001){
return new Color(image.getRGB(j,k)).getRed();
}
distances[(j-topLeftX) * 2 + (k-topLeftY)] = (1f / image.getWidth()) / (dist * dist);
total += distances[(j-topLeftX) * 2 + (k-topLeftY)];
}
}
float h = 0;
for(int j = topLeftX ; j < topLeftX + 2; j++){
for(int k = topLeftY ; k < topLeftY + 2; k++){
float p = distances[(j-topLeftX) * 2 + (k-topLeftY)] / total;
h+= new Color(image.getRGB(j,k)).getRed() * p;
}
}
return h;
Does anyone know how I need to change my code ?
I am very happy for any advice and help :)
You are weighing the colors based in the inverse squared distance from all four corners. The problem here is this: the pixel colors on the edge are affected by the color of the corners across. The pixel colors on the two sides on the edges differ because they are calculated from a different set of four corners.
Solution is to use some common interpolation, like bilinear or bicubic interpolation.
I am trying to make a 2D game in LWJGL. I am having a problem with terrain generation. I currently have an algorithm to generate terrain but it is always random and I can never get that same world again I would like to make an algorithm that generates a x and y coordinates based on a given number.
My current world generation looks like this:
final float STEP_MAX = 1f;
final float STEP_CHANGE = 1;
final int HEIGHT_MAX = 100;
double height = HEIGHT_MAX;
double slope = STEP_MAX;
for (int x = -WORLDSIZE; x < WORLDSIZE; x++) {
height += slope;
slope += (Math.random() * STEP_CHANGE) * 2 - STEP_CHANGE;
if (slope > STEP_MAX) slope = STEP_MAX;
if (slope < -STEP_MAX) slope = -STEP_MAX;
if (height > HEIGHT_MAX) {
height = HEIGHT_MAX;
slope *= -1;
}
if (height < 0) {
height = 0;
slope *= -1;
}
Tile newTile = new Tile(x*25,(int)height*25,25,25,TileType.Grass);
tiles.add(newTile);
Thank you in advance for your help.
If you create your random number generator yourself (rather than letting Math.random() do so for you), you can specify a seed:
Random random = new Random(yourSeed);
random.nextDouble();
the Random class also has many useful methods you might want to look at.
More info: https://docs.oracle.com/javase/8/docs/api/java/util/Random.html
I have a 2D array with a "light source" placed at a coordinate on it. This light source has a brightness value attached to it. I was wondering how I would make it such that this light illuminated the tiles around it to a distance equal to its brightness, with each illuminated tiles brightness equal to its distance from the source. For example a bulb with brightness 5 would illuminate with a radius of 5 cells, with cells 2 steps away having a brightness of 4 and those 5 steps away with 1.
How do you set a value in a 2D array?
final int SIZE = 30; // size of array
int[][] brightnessOfTiles = new int[SIZE][SIZE];
brightnessOfTiles[x][y] = 1234; // some value
How do you determine value of the light at a specific place?
int centerX = 15; // x coordinate of position of light source
int centerY = 10; // y coordinate of position of light source
float dropoffFactor = 1; // how fast the brightness fades
brightnessOfTiles[x][y] = MAX_BRIGHTNESS / (1 + dropoffFactor * getDistanceSquaredBetweenPoints(x, y, centerX, centerY));
How do you determine the distance between two points?
private int getDistanceSquaredBetweenPoints(x, y, centerX, centerY){
// formula is: distance = sqrt( (x - cx) ^ 2 + (y - cy) ^ 2 )
// so, distance ^ 2 = (x - cx) ^ 2 + (y - cy) ^ 2
return Math.pow(centerX - x, 2) + Math.pow(centerY - y, 2);
}
How would you iterate over a 2D array?
for(int x = 0; x < SIZE; x++){
for(int y = 0; y < SIZE; y++){
brightnessOfTiles[x][y] = ...; // see above
}
}
I am trying to find image in an image. I do this for desktop automation. At this moment, I'm trying to be fast, not precise. As such, I have decided to match similar image solely based on the same average color.
If I pick several icons on my desktop, for example:
And I will search for the last one (I'm still wondering what this file is):
You can clearly see what is most likely to be the match:
In different situations, this may not work. However when image size is given, it should be pretty reliable and lightning fast.
I can get a screenshot as BufferedImage object:
MSWindow window = MSWindow.windowFromName("Firefox", false);
BufferedImage img = window.screenshot();
//Or, if I can estimate smaller region for searching:
BufferedImage img2 = window.screenshotCrop(20,20,50,50);
Of course, the image to search image will be loaded from template saved in a file:
BufferedImage img = ImageIO.read(...whatever goes in there, I'm still confused...);
I explained what all I know so that we can focus on the only problem:
Q: How can I get average color on buffered image? How can I get such average color on sub-rectangle of that image?
Speed wins here. In this exceptional case, I consider it more valuable than code readability.
I think that no matter what you do, you are going to have an O(wh) operation, where w is your width and h is your height.
Therefore, I'm going to post this (naive) solution to fulfil the first part of your question as I do not believe there is a faster solution.
/*
* Where bi is your image, (x0,y0) is your upper left coordinate, and (w,h)
* are your width and height respectively
*/
public static Color averageColor(BufferedImage bi, int x0, int y0, int w,
int h) {
int x1 = x0 + w;
int y1 = y0 + h;
long sumr = 0, sumg = 0, sumb = 0;
for (int x = x0; x < x1; x++) {
for (int y = y0; y < y1; y++) {
Color pixel = new Color(bi.getRGB(x, y));
sumr += pixel.getRed();
sumg += pixel.getGreen();
sumb += pixel.getBlue();
}
}
int num = w * h;
return new Color(sumr / num, sumg / num, sumb / num);
}
There is a constant time method for finding the mean colour of a rectangular section of an image but it requires a linear preprocess. This should be fine in your case. This method can also be used to find the mean value of a rectangular prism in a 3d array or any higher dimensional analog of the problem. I will be using a gray scale example but this can be easily extended to 3 or more channels simply by repeating the process.
Lets say we have a 2 dimensional array of numbers we will call "img".
The first step is to generate a new array of the same dimensions where each element contains the sum of all values in the original image that lie within the rectangle that bounds that element and the top left element of the image.
You can use the following method to construct such an image in linear time:
int width = 1920;
int height = 1080;
//source data
int[] img = GrayScaleScreenCapture();
int[] helperImg = int[width * height]
for(int y = 0; y < height; ++y)
{
for(int x = 0; x < width; ++x)
{
int total = img[y * width + x];
if(x > 0)
{
//Add value from the pixel to the left in helperImg
total += helperImg[y * width + (x - 1)];
}
if(y > 0)
{
//Add value from the pixel above in helperImg
total += helperImg[(y - 1) * width + x];
}
if(x > 0 && y > 0)
{
//Subtract value from the pixel above and to the left in helperImg
total -= helperImg[(y - 1) * width + (x - 1)];
}
helperImg[y * width + x] = total;
}
}
Now we can use helperImg to find the total of all values within a given rectangle of img in constant time:
//Some Rectangle with corners (x0, y0), (x1, y0) , (x0, y1), (x1, y1)
int x0 = 50;
int x1 = 150;
int y0 = 25;
int y1 = 200;
int totalOfRect = helperImg[y1 * width + x1];
if(x0 > 0)
{
totalOfRect -= helperImg[y1 * width + (x0 - 1)];
}
if(y0 > 0)
{
totalOfRect -= helperImg[(y0 - 1) * width + x1];
}
if(x0 > 0 && y0 > 0)
{
totalOfRect += helperImg[(y0 - 1) * width + (x0 - 1)];
}
Finally, we simply divide totalOfRect by the area of the rectangle to get the mean value:
int rWidth = x1 - x0 + 1;
int rheight = y1 - y0 + 1;
int meanOfRect = totalOfRect / (rWidth * rHeight);
Here's a version based on k_g's answer for a full BufferedImage with adjustable sample precision (step).
public static Color getAverageColor(BufferedImage bi) {
int step = 5;
int sampled = 0;
long sumr = 0, sumg = 0, sumb = 0;
for (int x = 0; x < bi.getWidth(); x++) {
for (int y = 0; y < bi.getHeight(); y++) {
if (x % step == 0 && y % step == 0) {
Color pixel = new Color(bi.getRGB(x, y));
sumr += pixel.getRed();
sumg += pixel.getGreen();
sumb += pixel.getBlue();
sampled++;
}
}
}
int dim = bi.getWidth()*bi.getHeight();
// Log.info("step=" + step + " sampled " + sampled + " out of " + dim + " pixels (" + String.format("%.1f", (float)(100*sampled/dim)) + " %)");
return new Color(Math.round(sumr / sampled), Math.round(sumg / sampled), Math.round(sumb / sampled));
}
This is a homework assignment.
Work 19 5/16 is the assignment
http://sites.stuycs.org/home/courses/ml2x/dyrland-weaver/work
I am running this in the program processing, which does not require main methods.
Blob was given to us. We had to make BlobRunner on our own.
Any advice on why my code isn't doing what its supposed to would be appreciated.
FIRST FILE BlobRunner
int popSize = 4;
int wobble = 2;
int numSides = 4;
float rad = 100;
int radInt = (int) rad;
float a = sqrt(popSize);
int rootPop = (int) a;
Blob[][] blobs = new Blob[popSize/rootPop][rootPop];
/*=====================================
The trickiest part of setup is to make
the screen an appropriate size for the
grid of blobs. The grid should be just
big enough to contain all of the blobs.
====================================*/
void setup() {
size ((popSize/rootPop)*(2*(radInt+3)), rootPop*(2*(radInt+3)));
populate();
}
/*=====================================
The main purpose of draw is to go through
the array of blobs and display each.
====================================*/
void draw() {
int createdSoFar = 0;
for (int i = 0; i<rootPop; i++){
for (int j = 0; j<popSize/rootPop; j++){
if (createdSoFar < popSize){
blobs[j][i].display();
}
createdSoFar++;
}
}
}
/*=====================================
Populate the array of blobs.
You can use any values for radius, number of sides
and wobble factor that you'd like, but you must
use x and y coordinates that ensure the blobs
are drawn in a grid without overlaping each other.
Your code should work for any reasonable value
of population (i.e. something that would fit on a
normal monitor).
====================================*/
void populate() {
for (int i = 0; i < rootPop; i++){
float y = 1;
for (int j = 0; j < (popSize/rootPop); j++){
float x = 1;
blobs[j][i] = new Blob (x*(rad+3), y*(rad+3), numSides, radInt, wobble, wobble);
x=x+2;}
y=y+2;}
}
SECOND FILE Blob
/*=====================================
A Blob object is a regular polygon variant that
can have various features.
Instance Variables:
numSides: number of sides
rad: distance from the center of the polygon
to any vertext
x: x coordinate of the center
y: y coordinate of the center
xFactor: "wobble" foctor in the x direction
yFactor: "wobble" factor in the y direction
====================================*/
class Blob {
int numSides;
int rad;
float x;
float y;
int xFactor;
int yFactor;
Blob(float cx, float cy, int sides, int r, int xf, int yf ) {
x = cx;
y = cy;
numSides = sides;
rad = r;
xFactor = xf;
yFactor = yf;
}
void display() {
float nx;
float ny;
int rx, ry;
float sy;
strokeWeight(1);
beginShape();
for( float t = 0; t <= 1; t+=( 1.0/numSides ) ) {
/*
"wobble" effect is created by adding a random number to each
x and y coordinate. The larger the x and y factors, the higher
the possible wobble value could be
*/
rx = (int)random(xFactor);
ry = (int)random(yFactor);
nx = rad * cos( 2 * PI * t ) + x + rx;
ny = rad * sin( 2 * PI * t ) + y + ry;
vertex(nx, ny);
}
endShape();
}
}
Your code runs, thus it is doing what you asked it to do and nothing more.
I asked my cat to check it out though and she was all, "the guy is re-initializing his variables inside each pass of the loop, he'll never get a grid of blobs that way. Tell him to start by moving float y = 1; float x = 1; in populate() outside of the bounds of the two for loops and start debugging from there."
Then she rolled over on to her side and I patted her.