I looked all over the internet and researched Perlin noise, however, I am still confused.
I am using java and libgdx. I have got a Perlin class to work and generate noise but I'm not sure if the values its giving are correct. How do I check it actually is outputting Perlin noise?
If my implementation is correct I don't know where to go from there to make random terrain. How would I map Perlin noise to tiles? Currently I have 4 basic tiles; water, sand, rock, and grass.
package com.bracco.thrive.world;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
public class WorldGeneration {
Perlin noise = new Perlin();
private SpriteBatch spriteBatch;
//private boolean debug = false;
private TextureRegion[] regions = new TextureRegion[4];
private Texture texture;
float x = 110;
float y = 120;
float originX = 0;
float originY = 16;
float width = 16;
float height = 16;
float scaleX = 1;
float scaleY = 1;
float rotation = 1;
#SuppressWarnings("static-access")
public void createWorld(){
spriteBatch = new SpriteBatch();
texture = new Texture(Gdx.files.internal("assets/data/textures/basictextures.png"));
regions[0] = new TextureRegion(texture,0,0,16,16); //grass
regions[1] = new TextureRegion(texture,16,0,16,16); //water
regions[2] = new TextureRegion(texture,0,17,16,16); //sand
regions[3] = new TextureRegion(texture,17,17,16,16); //rock
float[][] seed = noise.GenerateWhiteNoise(50, 50);
for (int i = 0;i < seed.length; i++){
for ( int j = 0; j < seed[i].length; j++){
System.out.println(seed[i][j] + " ");
}
}
float[][] seedE = noise.GenerateSmoothNoise( seed, 6);
for (int i = 0;i < seedE.length; i++){
for ( int j = 0; j < seedE[i].length; j++){
System.out.println(seedE[i][j] + " ");
}
}
float[][] perlinNoise = noise.GeneratePerlinNoise(seedE, 8);
for (int i = 0;i < perlinNoise.length; i++){
for ( int j = 0; j < perlinNoise[i].length; j++){
System.out.println(perlinNoise[i][j] + " ");
}
}
}
public void render(){
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
spriteBatch.begin();
//spriteBatch.draw(texture, 0, 0, 16, 16);
for (int i = 0; i < regions.length; i++){
spriteBatch.draw(regions[i],75 * (i + 1),100);
}
spriteBatch.end();
}
}
package com.bracco.thrive.world;
import java.util.Random;
public class Perlin {
public static float[][] GenerateWhiteNoise(int width,int height){
Random random = new Random((long) (Math.round(Math.random() * 100 * Math.random() * 10))); //Seed to 0 for testing
float[][] noise = new float[width][height];
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++){
noise[i][j] = (float)(Math.random() % 1);
}
}
return noise;
}
float[][] GenerateSmoothNoise(float[][] baseNoise, int octave)
{
int width = baseNoise.length;
int height = baseNoise.length;
float[][] smoothNoise = new float[width][height];
int samplePeriod = 1 << octave; // calculates 2 ^ k
float sampleFrequency = 1.0f / samplePeriod;
for (int i = 0; i < width; i++)
{
//calculate the horizontal sampling indices
int sample_i0 = (i / samplePeriod) * samplePeriod;
int sample_i1 = (sample_i0 + samplePeriod) % width; //wrap around
float horizontal_blend = (i - sample_i0) * sampleFrequency;
for (int j = 0; j < height; j++)
{
//calculate the vertical sampling indices
int sample_j0 = (j / samplePeriod) * samplePeriod;
int sample_j1 = (sample_j0 + samplePeriod) % height; //wrap around
float vertical_blend = (j - sample_j0) * sampleFrequency;
//blend the top two corners
float top = Interpolate(baseNoise[sample_i0][sample_j0],
baseNoise[sample_i1][sample_j0], horizontal_blend);
//blend the bottom two corners
float bottom = Interpolate(baseNoise[sample_i0][sample_j1],
baseNoise[sample_i1][sample_j1], horizontal_blend);
//final blend
smoothNoise[i][j] = Interpolate(top, bottom, vertical_blend);
}
}
return smoothNoise;
}
float Interpolate(float x0, float x1, float alpha)
{
return x0 * (1 - alpha) + alpha * x1;
}
float[][] GeneratePerlinNoise(float[][] baseNoise, int octaveCount)
{
int width = baseNoise.length;
int height = baseNoise[0].length;
float[][][] smoothNoise = new float[octaveCount][][]; //an array of 2D arrays containing
float persistance = 0.5f;
//generate smooth noise
for (int i = 0; i < octaveCount; i++)
{
smoothNoise[i] = GenerateSmoothNoise(baseNoise, i);
}
float[][] perlinNoise = new float[width][height];
float amplitude = 1.0f;
float totalAmplitude = 0.0f;
//blend noise together
for (int octave = octaveCount - 1; octave >= 0; octave--)
{
amplitude *= persistance;
totalAmplitude += amplitude;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
perlinNoise[i][j] += smoothNoise[octave][i][j] * amplitude;
}
}
}
//normalisation
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
perlinNoise[i][j] /= totalAmplitude;
}
}
return perlinNoise;
}
}
Correctness of perlin noise
Regarding if your perlin noise is 'correct'; the easiest way to see if your perlin noise (or technically fractal noise based upon several octaves of perlin noise) is working is to use the values of your perlin noise to generate a greyscale image, this image should look like some kind of landscape (rolling hills, or mountains depending on the parameters you chose for the persistance (and to a less extent the number of octaves). Some examples of perlin noise is:
Low Persisance:
or
High Persisance:
or
High Persisance (zoomed out):
These greyscale images are produced by the following code
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ImageWriter {
//just convinence methods for debug
public static void greyWriteImage(double[][] data){
//this takes and array of doubles between 0 and 1 and generates a grey scale image from them
BufferedImage image = new BufferedImage(data.length,data[0].length, BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < data[0].length; y++)
{
for (int x = 0; x < data.length; x++)
{
if (data[x][y]>1){
data[x][y]=1;
}
if (data[x][y]<0){
data[x][y]=0;
}
Color col=new Color((float)data[x][y],(float)data[x][y],(float)data[x][y]);
image.setRGB(x, y, col.getRGB());
}
}
try {
// retrieve image
File outputfile = new File("saved.png");
outputfile.createNewFile();
ImageIO.write(image, "png", outputfile);
} catch (IOException e) {
//o no!
}
}
public static void main(String args[]){
double[][] data=new double[2][4];
data[0][0]=0.5;
data[0][5]=1;
data[1][0]=0.7;
data[1][6]=1;
greyWriteImage(data);
}
}
This code assumes each entry will be between 0 and 1, but perlin noise usually produces between -1 and 1, scale according to your implimentation. Assuming your perlin noise will give a value for any x,y then you can run this using the following code
//generates 100 by 100 data points within the specified range
double iStart=0;
double iEnd=500;
double jStart=0;
double jEnd=500;
double[][] result=new double[100][100];
for(int i=0;i<100;i++){
for(int j=0;j<100;j++){
int x=(int)(iStart+i*((iEnd-iStart)/100));
int y=(int)(jStart+j*((jEnd-jStart)/100));
result[i][j]=0.5*(1+perlinNoise.getNoise(x,y));
}
}
ImageWriter.greyWriteImage(result);
My implimentation expects integer x and y. Feel free to modify if this is not the case for you
Mapping to tiles
This is entirely up to you, you need to define certain ranges of the perlin noise value to create certain tiles. Be aware however that perlin noise is biased towards 0. Assuming 2D you could get nice results by taking the landscape analogy semi literally, low values=water, lowish values=sand, medium values=grass, high values =snow.
Also be aware that in some implementations (eg minecraft biomes and caverns) several random values are combined to create an overall result. See https://softwareengineering.stackexchange.com/questions/202992/randomization-of-biomes/203040#203040
Ideas for improvement
If you find that perlin noise generation is too slow then consider simplex noise, it has very similar properties but is more efficient (especially at higher dimentions). Simplex noise is however considerably more complex mathematically.
I realize this is a somewhat old question, but I'd like to post my solution none the less since I found it hard to find working examples.
I was also researching this problem, at first I found your code useful as it seamed to work, in appearance, but when I wanted to change the size of the image the smooth noise would not scale appropriately and I could not find a way to fix your code.
After more research I found your implementation of SmoothNoise very dodgy and so I re-implemented it from a reliable source (http://lodev.org/cgtutor/randomnoise.html).
Here is my noise class, it can generate and work with any kind of noise :
package com.heresysoft.arsenal.utils;
public class Noise
{
public static double[] blend(double[] noise1, double[] noise2, double persistence)
{
if (noise1 != null && noise2 != null && noise1.length > 0 && noise1.length == noise2.length)
{
double[] result = new double[noise1.length];
for (int i = 0; i < noise1.length; i++)
result[i] = noise1[i] + (noise2[i] * persistence);
return result;
}
return null;
}
public static double[] normalize(double[] noise)
{
if (noise != null && noise.length > 0)
{
double[] result = new double[noise.length];
double minValue = noise[0];
double maxValue = noise[0];
for (int i = 0; i < noise.length; i++)
{
if (noise[i] < minValue)
minValue = noise[i];
else if (noise[i] > maxValue)
maxValue = noise[i];
}
for (int i = 0; i < noise.length; i++)
result[i] = (noise[i] - minValue) / (maxValue - minValue);
return result;
}
return null;
}
public static double[] perlinNoise(int width, int height, double exponent)
{
int[] p = new int[width * height];
double[] result = new double[width * height];
/*final int[] permutation = {151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23,
190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174,
20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230,
220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169,
200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147,
118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44,
154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104,
218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192,
214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24,
72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180};*/
for (int i = 0; i < p.length / 2; i++)
p[i] = p[i + p.length / 2] = (int) (Math.random() * p.length / 2);//permutation[i];
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
double x = i * exponent / width; // FIND RELATIVE X,Y,Z
double y = j * exponent / height; // OF POINT IN CUBE.
int X = (int) Math.floor(x) & 255; // FIND UNIT CUBE THAT
int Y = (int) Math.floor(y) & 255; // CONTAINS POINT.
int Z = 0;
x -= Math.floor(x); // FIND RELATIVE X,Y,Z
y -= Math.floor(y); // OF POINT IN CUBE.
double u = fade(x); // COMPUTE FADE CURVES
double v = fade(y); // FOR EACH OF X,Y,Z.
double w = fade(Z);
int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z, // HASH COORDINATES OF
B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z; // THE 8 CUBE CORNERS,
result[j + i * width] = lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, Z), // AND ADD
grad(p[BA], x - 1, y, Z)), // BLENDED
lerp(u, grad(p[AB], x, y - 1, Z), // RESULTS
grad(p[BB], x - 1, y - 1, Z))),// FROM 8
lerp(v, lerp(u, grad(p[AA + 1], x, y, Z - 1), // CORNERS
grad(p[BA + 1], x - 1, y, Z - 1)), // OF CUBE
lerp(u, grad(p[AB + 1], x, y - 1, Z - 1), grad(p[BB + 1], x - 1, y - 1, Z - 1))));
}
}
return result;
}
public static double[] smoothNoise(int width, int height, double zoom)
{
if (zoom > 0)
{
double[] noise = whiteNoise(width, height);
double[] result = new double[width * height];
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
double x = i / zoom;
double y = j / zoom;
// get fractional part of x and y
double fractX = x - (int) x;
double fractY = y - (int) y;
// wrap around
int x1 = ((int) x + width) % width;
int y1 = ((int) y + height) % height;
// neighbor values
int x2 = (x1 + width - 1) % width;
int y2 = (y1 + height - 1) % height;
// smooth the noise with bilinear interpolation
result[j + i * width] = fractX * fractY * noise[y1 + x1 * width]
+ fractX * (1 - fractY) * noise[y2 + x1 * width]
+ (1 - fractX) * fractY * noise[y1 + x2 * width]
+ (1 - fractX) * (1 - fractY) * noise[y2 + x2 * width];
}
}
return result;
}
return null;
}
public static double[] turbulence(int width, int height, double zoom)
{
// http://lodev.org/cgtutor/randomnoise.html
double[] result = new double[width * height];
double initialZoom = zoom;
while (zoom >= 1)
{
result = blend(result, smoothNoise(width, height, zoom), zoom);
zoom /= 2.0;
}
for (int i = 0; i < result.length; i++)
result[i] = (128.0 * result[i] / initialZoom);
return result;
}
public static double[] whiteNoise(int width, int height)
{
double[] result = new double[width * height];
for (int i = 0; i < width * height; i++)
result[i] = Math.random();
return result;
}
private static double fade(double t)
{
return t * t * t * (t * (t * 6 - 15) + 10);
}
private static double lerp(double t, double a, double b)
{
return a + t * (b - a);
}
private static double grad(int hash, double x, double y, double z)
{
int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE
double u = h < 8 ? x : y, // INTO 12 GRADIENT DIRECTIONS.
v = h < 4 ? y : h == 12 || h == 14 ? x : z;
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}
}
Here is an example of how to use the smoothNoise function :
double[] data = Noise.normalize(Noise.smoothNoise(width, height, 32));
for (int i = 0; i < data.length; i++)
data[i] = 255*data[i];
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
img.getRaster().setPixels(0, 0, width, height, data);
Here is an example of how to use the turbulence function :
double[] data = Noise.normalize(Noise.turbulence(width, height, 32));
for (int i = 0; i < data.length; i++)
data[i] = 255*data[i];
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
img.getRaster().setPixels(0, 0, width, height, data);
Here is an example of how to use the perlinNoise function :
double[] data = Noise.normalize(Noise.perlinNoise(width, height, 7));
for (int i = 0; i < data.length; i++)
data[i] = 255 * data[i];
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
img.getRaster().setPixels(0, 0, width, height, data);
Related
Whenever I run my game, the game window opens up very small, like below, how can I fix this. I believe it is something to do with JFrame.setPreferredSize(new Dimension(x,y)); but I don't know where to implement this in the code for it to work.
This is what it shows:
enter image description here
Where do I implement the code? Like in the class, outside the class, or do I need to show my current code for someone to help me here?
This is my code:
public class RoadAppMain extends JFrame {
private static final int D_W = 1920; //Window dimension
private static final int D_H = 1280; //Window height
int grassWidth = 1920; //Width of grass
int vanishHeight = 768; // Height of vanishing point
int roadWidth = 900; //Width of the road
int rumbleLen = 800; //Length of each track stripe
double camDist = 0.8; //Camera distance
int N; //Size of each row or line
int playerPosX = 0; //Movement of player left and right
int playerPosY = 0; //Movement of player up and down
List<Line> lines = new ArrayList<RoadAppMain.Line>();
List<Integer> listValues = new ArrayList<Integer>();
DrawPanel drawPanel = new DrawPanel();
public RoadAppMain() {
for (int i = 0; i < 1600; i++) {
Line line = new Line();
line.z = i * rumbleLen;
int curveAngle = (int) (Math.random() * 15 + 1);
if (i > 20 && i < 70) {
line.curve = curveAngle;
}
else if (i > 100 && i < 150) {
line.curve = -curveAngle;
}
else if (i > 180 && i < 230) {
line.curve = curveAngle;
}
else if (i > 260 && i < 310) {
line.curve = -curveAngle;
}
else if (i > 340 && i < 390) {
line.curve = curveAngle;
}
else if (i > 400 && i < 420) {
}
lines.add(line);
}
N = lines.size();
//Handles action events by user
ActionListener listener = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
drawPanel.repaint();
}
};
Timer timer = new Timer(1, listener);
timer.start();
add(drawPanel);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
//Moves screen using arrow keys
private class DrawPanel extends JPanel {
public DrawPanel() {
String VK_LEFT = "VK_LEFT";
KeyStroke W = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0);
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW); //necessary
inputMap.put(W, VK_LEFT);
ActionMap actionMap = getActionMap(); //necessary
actionMap.put(VK_LEFT, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
playerPosX -= 200;
drawPanel.repaint();
}
});
String VK_RIGHT = "VK_RIGHT";
KeyStroke WVK_RIGHT = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0);
inputMap.put(WVK_RIGHT, VK_RIGHT);
actionMap.put(VK_RIGHT, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
playerPosX += 200;
drawPanel.repaint();
}
});
String VK_UP = "VK_UP";
KeyStroke WVK_UP = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0);
inputMap.put(WVK_UP, VK_UP);
actionMap.put(VK_UP, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
playerPosY += 200;
drawPanel.repaint();
}
});
String VK_DOWN = "VK_DOWN";
KeyStroke WVK_DOWN = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0);
inputMap.put(WVK_DOWN, VK_DOWN);
actionMap.put(VK_DOWN, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
playerPosY -= 200;
drawPanel.repaint();
}
});
}
//Drawing components feature
protected void paintComponent(Graphics g) {
drawValues(g);
g.setColor(Color.black);
g.fillRect(0, 0, 1920, 395);
}
}
private void drawValues(Graphics g) {
int startPos = playerPosY / rumbleLen;
double x = 0; //Initial X position of screen on road
double dx = 0; //Correlation between the angle of the road and player
double maxY = vanishHeight;
int camH = 1700 + (int) lines.get(startPos).y; //Height of the camera
//Starting position
for (int n = startPos; n < startPos + 300; n++) {
Line l = lines.get(n % N); //Position of line
l.project(playerPosX - (int) x, camH, playerPosY);
x += dx;
dx += l.curve;
if (l.Y > 0 && l.Y < maxY) {
maxY = l.Y;
Color grass = ((n / 2) % 2) == 0 ? new Color(21, 153, 71) : new Color(22, 102, 52); //Color for grass (first is for lighter, second for darker)
Color rumble = ((n / 2) % 2) == 0 ? new Color(255, 255, 255) : new Color(222, 4, 4); // Color for rumble (first is white, second is red)
Color road = new Color(54, 52, 52); // Color of road or asphalt
Color midel = ((n / 2) % 2) == 0 ? new Color(255, 255, 255) : new Color(54, 52, 52); //Color of hashed lines (first for white lines, second for gap)
Color car = new Color(104, 104, 104);
Color tire = new Color(0, 0, 0);
Color stripe = new Color(0, 0, 0);
Color light = new Color(253, 0, 0);
Color hood = new Color(0, 0, 0);
Color frame = new Color(0,0,255);
Line p = null;
if (n == 0) {
p = l;
} else {
p = lines.get((n - 1) % N);
}
draw(g, grass, 0, (int) p.Y, grassWidth, 0, (int) l.Y, grassWidth); //(Graphics g, Color c, int x1, int y1, int w1, int x2, int y2, int w2)
draw(g, rumble, (int) p.X, (int) p.Y, (int) (p.W * 2.03), (int) l.X, (int) l.Y, (int) (l.W * 2.03)); //Affects width of rumble
draw(g, road, (int) p.X, (int) p.Y, (int) (p.W * 1.8), (int) l.X, (int) l.Y, (int) (l.W * 1.8));
draw(g, midel, (int) p.X, (int) p.Y, (int) (p.W * 0.78), (int) l.X, (int) l.Y, (int) (l.W * 0.78)); //ADD HERE
draw(g, road, (int) p.X, (int) p.Y, (int) (p.W * 0.7), (int) l.X, (int) l.Y, (int) (l.W* 0.7)); //To cover the gap in between each midel. Must be after to allow it to colour over it
draw(g, car, 965, 927, 125, 965, 1005, 125);
draw(g, tire, 875, 1005, 35, 875, 1050, 35);
draw(g, tire, 1055, 1005, 35, 1055, 1050, 35);
draw(g, stripe, 1050, 965, 30, 870, 980, 30);
draw(g, stripe, 1050, 950, 30, 870, 965, 30);
draw(g, hood, 965, 880, 90, 965, 927, 125);
draw(g, light, 875, 950, 5, 875, 980, 5);
draw(g, light, 890, 950, 5, 890, 980, 5);
draw(g, light, 905, 950, 5, 905, 980, 5);
draw(g, light, 1025, 950, 5, 1025, 980, 5);
draw(g, light, 1040, 950, 5, 1040, 980, 5);
draw(g, light, 1055, 950, 5, 1055, 980, 5);
draw(g, frame, 965, 874, 86, 965, 880, 90);
draw(g, light, 965, 874, 35, 965, 880, 45);
}
}
}
void draw(Graphics g, Color c, int x1, int y1, int w1, int x2, int y2, int w2) {
Graphics g9d = g;
int[] x9Points = { x1 - w1, x2 - w2, x2 + w2, x1 + w1 };
int[] y9Points = { y1, y2, y2, y1 };
int n9Points = 4;
g9d.setColor(c);
g9d.fillPolygon(x9Points, y9Points, n9Points);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new RoadAppMain();
}
});
}
class Line {
double x, y, z;
double X, Y, W;
double scale, curve;
public Line() {
curve = x = y = z = 0;
}
void project(int camX, int camY, int camZ) {
scale = camDist / (z - camZ);
X = (1 + scale * (x - camX)) * grassWidth / 2;
Y = (1 - scale * (y - camY)) * vanishHeight / 2;
W = scale * roadWidth * grassWidth / 2;
}
}
}
The output of the code at the moment is the rectangle design and the first line of the array repeated. The wanted output is the rectangle design and the whole array rather than just the first line.
public class design
{
public static void main (String[] args)
{
JFrame window = new JFrame ("Game Screen");
window.getContentPane ().add (new drawing ());
window.setSize (500, 500);
window.setVisible (true);
window.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
}
}
class drawing extends JComponent
{
public void paint (Graphics g)
{
int[] [] word = {{5, 3, 0, 0, 7, 0, 0, 0, 0},
{6, 0, 0, 1, 9, 5, 0, 0, 0},
{0, 9, 8, 0, 0, 0, 0, 6, 0},
{8, 0, 0, 0, 6, 0, 0, 0, 3},
{4, 0, 0, 8, 0, 3, 0, 0, 1},
{7, 0, 0, 0, 2, 0, 0, 0, 6},
{0, 6, 0, 0, 0, 0, 2, 8, 0},
{0, 0, 0, 4, 1, 9, 0, 0, 5},
{0, 0, 0, 0, 9, 0, 0, 7, 9}};
int r = 0;
int c = 0;
Graphics2D g2 = (Graphics2D) g;
Rectangle rect;
for (int x = 5 ; x < 450 ; x += 50)
{
for (int y = 5 ; y < 450 ; y += 50)
{
rect = new Rectangle (x, y, 50, 50);
g2.draw (rect);
g.drawString (Integer.toString (word [r] [c]), x + 25, y + 25);
}
c++;
if (c == 9)
{
c = 0;
r++;
}
}
rect = new Rectangle (150, 5, 150, 450);
g2.draw (rect);
rect = new Rectangle (5, 150, 450, 150);
g2.draw (rect);
}
}
Try to put r++; in the second for loop after calling g.drawString. r must also be set to 0 inside the first for loop and before entering the second one.
for (int x = 5 ; x < 450 ; x += 50){
r = 0;
for (int y = 5 ; y < 450 ; y += 50){
rect = new Rectangle (x, y, 50, 50);
g2.draw (rect);
g.drawString (Integer.toString (word [r] [c]), x + 25, y + 25);
r++;
}
c++;
}
It would be more readable (and logical in my opinion) to use only two variables for the loops, x and y in your case, and increment them only by one. You can use them to calculate the positions of your rectangles and place your numbers:
for (int x = 0 ; x < 9 ; x++){
for (int y = 0 ; y < 9 ; y++){
rect = new Rectangle (5+x*50, 5+y*50, 50, 50);
g2.draw (rect);
g.drawString (Integer.toString (word [y] [x]), 5+x*50 + 25, 5+y*50 + 25);
r++;
}
c++;
}
Avoid using magic numbers, it's better to define constant variables and give them an appropriate name. You can then easily change their values, and your code is clearer.
What are "magic numbers" in computer programming?
The issue with your logic is that increment of c doesn't follow increment of y(y-axis). i.e. as per your logic c represents y axis in your 2-dimensional array, but you are incrementing it in the for loop of x axis. Solution to this is, move your logic of c increment in inner for loop
for (int x = 5; x < 450; x += 50) {
for (int y = 5; y < 450; y += 50) {
rect = new Rectangle(x, y, 50, 50);
g2.draw(rect);
g.drawString(Integer.toString(word[r][c]), x + 25, y + 25);
c++;
}
if (c == 9) {
c = 0;
r++;
}
}
As your loop will only increment 9 times, moving of if condition along with c++ is not required.
So ive been working on a 2D rpg game and ive worked out the inventory quite nicely but ive ran into a problem where when I have an item in a slot the slot directly after it doesnt render; its just a black square.
Here is a visual: http://imgur.com/lLbv0fh
Im using immediate mode rendering.
Rendering inventory method:
public void render(Renderer renderer) {
if (shown == false) {
return;
}
//renderer.renderRectangle(0, x, y, 220, 240, Color4f.BLACK);
renderer.setFont(font);
renderer.renderString("Inventory", x + 24, y + 11, Color4f.WHITE);
renderer.setFont(Renderer.DEFAULT_FONT);
int drawSlotsX = x + 25;
int drawSlotsY = y + 50;
int xPos = 0;
int yPos = 0;
int maxX = 4;
int maxY = 4;
for (int i = 0; i < slots.length; i++) {
int renderX = ((xPos * 32) + drawSlotsX) + 2 * xPos;
int renderY = ((yPos * 32) + drawSlotsY) + 2 * yPos;
renderer.renderTexture(slot, renderX, renderY, 32, 32, 0);
if (collisionBoxs[i] == null) {
collisionBoxs[i] = new Rectangle(renderX, renderY, 32, 32);
}
if (slots[i] != null) {
renderer.renderTexture(slots[i].getItem().getIcon().getIcon(), renderX, renderY, 24, 32, 0);
if (slots[i].getAmount() < 10) {
renderer.renderString("" + slots[i].getAmount(), renderX + 22, renderY + 18, Color4f.BLACK);
} else {
renderer.renderString("" + slots[i].getAmount(), renderX + 18, renderY + 18, Color4f.BLACK);
}
}
if (xPos < maxX) {
xPos++;
} else {
xPos = 0;
if (yPos < maxY) {
yPos++;
}
}
}
if (grabbedItem != null) {
renderer.renderTexture(grabbedItem.getItem().getIcon().getIcon(), Mouse.getX(), (Engine.FRAME_HEIGHT - Mouse.getY()), 32, 32, 0);
}
}
Sorry if you cant see the problem within this method!
This question already has answers here:
ActionListener isn't Implementing
(2 answers)
Closed 6 years ago.
This is my code, the relevant part is:
public class Fibannacci extends Applet implements ActionListener {
I want to add a button to this code that will allow me to go repaint the Spiral with different colours (randomly select a set of colours).
I get the error:
Fibannacci is not abstract and does not override abstract method
actionPerformed(java.awt.event.ActionEvent) in
java.awt.event.ActionListener
Any help is greatly appreciated! (PS. I'm aware my formatting isn't very good...I'm going back later to fix it later)
/**
* ArcTest is an applet that will randomly select a set of colors to generate a pattern of colors that will be applied to the Fibannacci Spiral
* OVERALL: The applet displays the Fibannacci spiral
* #author Gareth Sykes
* #version 03/07/2014
*/
import java.awt.*;
import java.applet.*;
import java.awt.event.*;
import java.lang.Math.*;
public class Fibannacci extends Applet implements ActionListener {
public void paint(Graphics g) {
int w = getWidth(); // width of screen
int h = getHeight(); // height of screen
int rectSide = 1; // F1
int rectSidePrev = rectSide; // F2: previous square
int rectX = 5 * w / 8; // X coordinate of a rectangle
int rectY = 5 * h / 8; // Y coordinate of a rectangle
int angleStart = 0; // The angle an arc is drawn from
int DegreesAround = 90; // Degrees around an arbitrary circle
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(6));// Sets thickness of rectangles/arcs
// drawn
// Draw a segment of the Fibannacci Spiral
g.setColor(Color.black);
// g.drawArc (x, y, rectWidth, rectHeight, StartingAngleANTI-CLOCKWISE,
// DegreesAroundCircleANTI-CLOCKWISE)
g.drawArc(rectX - rectSide, rectY, rectSide * 2, rectSide * 2,
angleStart, DegreesAround);
angleStart = angleStart + 90;
g.drawArc(rectX - rectSide, rectY, rectSide * 2, rectSide * 2,
angleStart, DegreesAround);
// Draws the first two rectangles
// g.setColor (Color.black); FOR FUTURE REFERENCE: In the case that the
// Color of the Rectangle wants to be modified
g.drawRect(rectX, rectY, rectSide, rectSide);
g.drawRect(rectX - rectSide, rectY, rectSide, rectSide);
// Draws the nth rectangles in the fibannacci series
int n = 0;
int x = 1;
int FirstPrevX = 5 * w / 8 - rectSide; // The rectangle previously
// constructed's X coordinate
int FirstPrevL = rectSide; // The rectangle previosly constructed's side
// length
int FirstPrevY = 5 * h / 8; // The rectangle previously constructed's Y
// coordinate
int SecondPrevX = 5 * w / 8; // The rectangle consturcted before the
// previously constructed rectangles' X
// coordinate
int SecondPrevL = rectSide; // The rectangle consturcted before the
// previously constructed rectangles' side
// length
int SecondPrevY = 5 * h / 8; // The rectangle consturcted before the
// previously constructed rectangles' Y
// coordinate
int arcX = FirstPrevX; // The X coordinate of the arc
int arcY = FirstPrevY; // The Y coordinate of the arc
// Randomly Generates a pattern of four colors that the the rectangles
// and arcs will be colored
int[] color;
color = new int[5];
int m = 0;
boolean dup;
while (m < 4) {
dup = false;
int rn = (int) (Math.random() * 6);
for (int i = 0; i < m; i++) {
if (rn == color[i]) {
dup = true;
}
}
if (dup == false) {
color[m] = rn;
m = m + 1;
}
}
color[4] = color[0];
// Randomly picks which set of colors to use for the Fib. Spiral
int set = (int) (Math.random() * 4) + 1;
int[] r = new int[7];
int[] gr = new int[7];
int[] b = new int[7];
// NOTE: Three dimensional array?
switch (set) {
case (1):
r = new int[] { 51, 60, 112, 231, 177, 173, 3 };
gr = new int[] { 26, 87, 112, 235, 189, 202, 92 };
b = new int[] { 97, 132, 112, 240, 205, 255, 155 };
break;
case (2):
r = new int[] { 237, 255, 254, 155, 58, 230, 112 };
gr = new int[] { 83, 185, 235, 202, 187, 230, 112 };
b = new int[] { 20, 42, 81, 62, 201, 230, 112 };
break;
case (3):
r = new int[] { 9, 35, 50, 146, 154, 255, 112 };
gr = new int[] { 46, 79, 99, 204, 239, 231, 112 };
b = new int[] { 32, 50, 66, 71, 63, 97, 112 };
break;
case (4):
r = new int[] { 120, 171, 246, 222, 170, 73, 112 };
gr = new int[] { 120, 170, 235, 206, 128, 43, 112 };
b = new int[] { 47, 99, 208, 191, 103, 41, 112 };
break;
}
// While loop will draw both rectangles and the Fibannacci Spiral
while (n < 12) {
n = n + 1;
int NextL = FirstPrevL + SecondPrevL; // Side length of Next
// rectanlge in series
switch (x) {
case (1):
rectX = FirstPrevX;
rectY = FirstPrevY + rectSide;
arcX = rectX;
arcY = rectY - NextL;
x = x + 1;
break;
case (2):
rectX = FirstPrevX + rectSide;
rectY = FirstPrevY - SecondPrevL;
arcX = rectX - NextL;
arcY = rectY - NextL;
x = x + 1;
break;
case (3):
rectX = FirstPrevX - SecondPrevL;
rectY = FirstPrevY - NextL;
arcX = rectX - NextL;
arcY = rectY;
x = x + 1;
break;
case (4):
rectX = FirstPrevX - NextL;
arcX = rectX;
arcY = rectY;
x = 1;
break;
}
g.setColor(new Color(r[color[x]], gr[color[x]], b[color[x]]));
g.fillRect(rectX, rectY, NextL, NextL); // Draws a rectangle
g.setColor(Color.black);
g.drawRect(rectX, rectY, NextL, NextL); // Outlines the rectangle
angleStart = angleStart + 90;
// re-assigns variables such that they are ready for the next time
// through the loop
SecondPrevX = FirstPrevX;
SecondPrevL = FirstPrevL;
SecondPrevY = FirstPrevY;
FirstPrevX = rectX;
rectSide = NextL;
FirstPrevL = rectSide;
FirstPrevY = rectY;
// Draws a segment of the fibannacci spiral
g.setColor(new Color(r[color[x - 1]], gr[color[x - 1]],
b[color[x - 1]]));
g.fillArc(arcX, arcY, NextL * 2, NextL * 2, angleStart,
DegreesAround);
g.setColor(Color.black);
g.drawArc(arcX, arcY, NextL * 2, NextL * 2, angleStart,
DegreesAround);
}
}
}
Your class claims to implement ActionListener. This interface requires that you implement actionPerformed. Something like this:
public void actionPerformed(ActionEvent e) {
//code that reacts to the action...
}
See ActionListener.actionPerformed for the javadoc.
Ok so i need help changing the hue of this slider. I cant seem to figure it out. Please no #override. I need something that will run on Ready to Program. The hue will change back to normal when the slider is back at 0. I dont need to get too complex. Just a simple Hue slider will be great. Thanks!
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.applet.Applet;
import javax.swing.event.*;
import java.applet.*;
public class test extends Applet implements ActionListener, ChangeListener
{
//Widgets, Panels
JSlider slider;
Panel flow;
int colorr;
int colorg;
int colorb;
int stars;
//House Coordinates, initialized to 1. (Top Right and No Scaling)
public void init ()
{ //Set Up Input Fields for House Coordinates
resize (380, 240);
setBackground (new Color (102, 179, 255));
slider = new JSlider ();
slider.setValue (0);
slider.setBackground (new Color (102, 179, 255));
slider.setForeground (Color.white);
slider.setMajorTickSpacing (20);
slider.setMinorTickSpacing (5);
slider.setPaintTicks (true);
slider.addChangeListener (this);
//Set up layout, add widgets
setLayout (new BorderLayout ());
flow = new Panel (new FlowLayout ());
flow.add (slider);
add (flow, "South");
}
public void paint (Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
Color color1 = getBackground ();
Color color2 = color1.darker ();
int x = getWidth ();
int y = getHeight () - 30;
GradientPaint gp = new GradientPaint (
0, 0, color1,
0, y, color2);
g2d.setPaint (gp);
g2d.fillRect (0, 0, x, y);
stars (10, 10);
}
public void stars (int x, int y)
{
Graphics g = getGraphics ();
//sun
g.setColor (new Color (139, 166, 211));
g.fillOval (-200, 170, 1000, 400);
g.setColor (new Color (206, 75, 239));
g.fillOval (x, y, 10, 10); //First medium star
g.drawLine (x + 5, y, x + 5, 0);
g.drawLine (x, y + 5, 0, y + 5);
g.drawLine (x + 5, y + 10, x + 5, y + 20);
g.drawLine (x + 10, y + 5, x + 20, y + 5);
g.fillOval (x + 80, y + 30, 12, 12); //Middle medium star
g.drawLine (x + 86, y + 30, x + 86, y + 18);
g.drawLine (x + 80, y + 36, x + 68, y + 36);
g.drawLine (x + 92, y + 36, x + 104, y + 36);
g.drawLine (x + 86, y + 42, x + 86, y + 52);
colorr = (int) (Math.random () * 255) + 1;
colorg = (int) (Math.random () * 255) + 1;
colorb = (int) (Math.random () * 255) + 1;
int randomx = (int) (Math.random () * 300) + 10;
int randomy = (int) (Math.random () * 150) + 10;
stars = 50; //Change for more stars
int ax[] = new int [stars];
int ay[] = new int [stars];
for (int i = 0 ; i < stars ; i++)
{
g.setColor (new Color (colorr, colorg, colorb));
colorr = (int) (Math.random () * 255) + 1;
colorg = (int) (Math.random () * 255) + 1;
colorb = (int) (Math.random () * 255) + 1;
while ((randomx > 88 && randomx < 116) && (randomy < 65 && randomy > 15))
{
randomx = (int) (Math.random () * 300) + 10;
randomy = (int) (Math.random () * 150) + 10;
}
while ((randomx > 0 && randomx < 25) && (randomy > 5 && randomy < 35))
{
randomx = (int) (Math.random () * 300) + 10;
randomy = (int) (Math.random () * 150) + 10;
}
g.drawOval (randomx, randomy, 5, 5);
randomx = (int) (Math.random () * 300) + 10;
randomy = (int) (Math.random () * 150) + 10;
}
g.setColor (Color.white);
g.drawLine (320, 0, 315, 40);
g.drawLine (320, 0, 325, 40);
g.drawLine (320, 120, 315, 80);
g.drawLine (320, 120, 325, 80);
g.drawLine (260, 60, 300, 55);
g.drawLine (260, 60, 300, 65);
g.drawLine (380, 60, 340, 55);
g.drawLine (380, 60, 340, 65);
fillGradOval (280, 20, 80, 80, new Color (254, 238, 44), new Color (255, 251, 191), g);
g.setColor (new Color (255, 251, 191));
fillGradOval (300, 40, 40, 40, new Color (255, 251, 191), new Color (254, 238, 44), g);
}
public void fillGradOval (int X, int Y, int H2, int W2, Color c1, Color c2, Graphics g)
{
g.setColor (c1);
g.fillOval (X, Y, W2, H2);
Color Gradient = c1;
float red = (c2.getRed () - c1.getRed ()) / (W2 / 2);
float blue = (c2.getBlue () - c1.getBlue ()) / (W2 / 2);
float green = (c2.getGreen () - c1.getGreen ()) / (W2 / 2);
int scale = 1;
int r = c1.getRed ();
int gr = c1.getGreen ();
int b = c1.getBlue ();
while (W2 > 10)
{
r = (int) (r + red);
gr = (int) (gr + green);
b = (int) (b + blue);
Gradient = new Color (r, gr, b);
g.setColor (Gradient);
W2 = W2 - 2 * scale;
H2 = H2 - 2 * scale;
X = X + scale;
Y = Y + scale;
g.fillOval (X, Y, W2, H2);
}
}
public void actionPerformed (ActionEvent e)
{
}
public void stateChanged (ChangeEvent e)
{
JSlider source = (JSlider) e.getSource ();
if (!source.getValueIsAdjusting ())
{
}
}
}
I wasn't sure what 'color' you were referring to, so I made some guesses. Here is an hybrid application/applet (much easier for development and testing) that links the color of the bottom panel, as well as the bottom color of the gradient paint, to a hue as defined using the slider.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
/* <applet code=HueSlider width=380 height=240></applet> */
public class HueSlider extends JApplet
{
public void init() {
add(new HueSliderGui());
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
HueSliderGui hsg = new HueSliderGui();
JOptionPane.showMessageDialog(null, hsg);
}
};
SwingUtilities.invokeLater(r);
}
}
class HueSliderGui extends JPanel implements ChangeListener {
//Widgets, Panels
JSlider slider;
JPanel flow;
int colorr;
int colorg;
int colorb;
Color bg = new Color (102, 179, 255);
int stars;
//House Coordinates, initialized to 1. (Top Right and No Scaling)
Dimension prefSize = new Dimension(380, 240);
HueSliderGui() {
initGui();
}
public void initGui()
{
//Set Up Input Fields for House Coordinates
// an applet size is set in HTML
//resize (380, 240);
setBackground (bg);
slider = new JSlider ();
slider.setValue (0);
slider.setBackground (new Color (102, 179, 255));
slider.setForeground (Color.white);
slider.setMajorTickSpacing (20);
slider.setMinorTickSpacing (5);
slider.setPaintTicks (true);
slider.addChangeListener (this);
//Set up layout, add widgets
setLayout (new BorderLayout ());
flow = new JPanel (new FlowLayout ());
flow.add (slider);
add (flow, "South");
validate();
}
#Override
public Dimension getPreferredSize() {
return prefSize;
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
Color color1 = getBackground ();
Color color2 = color1.darker ();
int x = getWidth ();
int y = getHeight () - 30;
GradientPaint gp = new GradientPaint (
0, 0, color1,
0, y, flow.getBackground());
g2d.setPaint (gp);
g2d.fillRect (0, 0, x, y);
stars (10, 10, g2d);
}
public void stars (int x, int y, Graphics g)
{
// Graphics g = getGraphics (); we should never call getGraphics
//sun
g.setColor (new Color (139, 166, 211));
g.fillOval (-200, 170, 1000, 400);
g.setColor (new Color (206, 75, 239));
g.fillOval (x, y, 10, 10); //First medium star
g.drawLine (x + 5, y, x + 5, 0);
g.drawLine (x, y + 5, 0, y + 5);
g.drawLine (x + 5, y + 10, x + 5, y + 20);
g.drawLine (x + 10, y + 5, x + 20, y + 5);
g.fillOval (x + 80, y + 30, 12, 12); //Middle medium star
g.drawLine (x + 86, y + 30, x + 86, y + 18);
g.drawLine (x + 80, y + 36, x + 68, y + 36);
g.drawLine (x + 92, y + 36, x + 104, y + 36);
g.drawLine (x + 86, y + 42, x + 86, y + 52);
colorr = (int) (Math.random () * 255) + 1;
colorg = (int) (Math.random () * 255) + 1;
colorb = (int) (Math.random () * 255) + 1;
int randomx = (int) (Math.random () * 300) + 10;
int randomy = (int) (Math.random () * 150) + 10;
stars = 50; //Change for more stars
int ax[] = new int [stars];
int ay[] = new int [stars];
for (int i = 0 ; i < stars ; i++)
{
g.setColor (new Color (colorr, colorg, colorb));
colorr = (int) (Math.random () * 255) + 1;
colorg = (int) (Math.random () * 255) + 1;
colorb = (int) (Math.random () * 255) + 1;
while ((randomx > 88 && randomx < 116) && (randomy < 65 && randomy > 15))
{
randomx = (int) (Math.random () * 300) + 10;
randomy = (int) (Math.random () * 150) + 10;
}
while ((randomx > 0 && randomx < 25) && (randomy > 5 && randomy < 35))
{
randomx = (int) (Math.random () * 300) + 10;
randomy = (int) (Math.random () * 150) + 10;
}
g.drawOval (randomx, randomy, 5, 5);
randomx = (int) (Math.random () * 300) + 10;
randomy = (int) (Math.random () * 150) + 10;
}
g.setColor (Color.white);
g.drawLine (320, 0, 315, 40);
g.drawLine (320, 0, 325, 40);
g.drawLine (320, 120, 315, 80);
g.drawLine (320, 120, 325, 80);
g.drawLine (260, 60, 300, 55);
g.drawLine (260, 60, 300, 65);
g.drawLine (380, 60, 340, 55);
g.drawLine (380, 60, 340, 65);
fillGradOval (280, 20, 80, 80, new Color (254, 238, 44), new Color (255, 251, 191), g);
g.setColor (new Color (255, 251, 191));
fillGradOval (300, 40, 40, 40, new Color (255, 251, 191), new Color (254, 238, 44), g);
}
public void fillGradOval (int X, int Y, int H2, int W2, Color c1, Color c2, Graphics g)
{
g.setColor (c1);
g.fillOval (X, Y, W2, H2);
Color Gradient = c1;
float red = (c2.getRed () - c1.getRed ()) / (W2 / 2);
float blue = (c2.getBlue () - c1.getBlue ()) / (W2 / 2);
float green = (c2.getGreen () - c1.getGreen ()) / (W2 / 2);
int scale = 1;
int r = c1.getRed ();
int gr = c1.getGreen ();
int b = c1.getBlue ();
while (W2 > 10)
{
r = (int) (r + red);
gr = (int) (gr + green);
b = (int) (b + blue);
Gradient = new Color (r, gr, b);
g.setColor (Gradient);
W2 = W2 - 2 * scale;
H2 = H2 - 2 * scale;
X = X + scale;
Y = Y + scale;
g.fillOval (X, Y, W2, H2);
}
}
public void stateChanged (ChangeEvent e)
{
JSlider source = (JSlider) e.getSource ();
if (!source.getValueIsAdjusting ())
{
int i = source.getValue();
System.out.println(i);
float[] hsb = Color.RGBtoHSB(bg.getRed(),bg.getGreen(),bg.getBlue(),null);
int colorHue = Color.HSBtoRGB((float)i/100f, hsb[1], hsb[2]);
Color c = new Color(colorHue);
flow.setBackground(c);
this.repaint();
}
}
}