I'm creating a game that has randomly drawn Asteroids (to give the Asteroids a jagged look). After research I've only found that you can fill primitive shapes. Does anyone know a method I could use to fill these shapes?
package view.game_object;
import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
public class Asteroid extends BaseGameObject {
public static final int BASE_SIZE = 10;
private final int fragmentCount;
private final int[][] points;
private final int level;
public Asteroid(int level, Random r) {
this.level = level;
this.setBound(level * Asteroid.BASE_SIZE);
int sizeRange = (int) (this.getBound() / 2);
this.fragmentCount = this.level * 6;
// generate random points to draw
this.setPosition(0, 0);
this.points = new int[fragmentCount][2];
ArrayList<Float> angleList = new ArrayList<>();
for (int i = 0; i < fragmentCount; i++) {
angleList.add(r.nextFloat() * (float) (Math.PI * 2));
}
Collections.sort(angleList);
for (int i = 0; i < this.points.length; i++) {
// base point
float x = r.nextInt(sizeRange) + this.getBound() - sizeRange / 3;
// rotate
float[] point = BaseGameObject.rotate(x, 0, this.getPosx(), this.getPosy(),
angleList.get(i));
this.points[i][0] = (int) point[0];
this.points[i][1] = (int) point[1];
}
}
public int getLevel() {
return level;
}
#Override
public void draw(Graphics g) {
g.setColor(Color.gray);
for (int i = 0; i < this.points.length; i++) {
int nextPoint = i + 1;
if (nextPoint >= this.points.length) {
nextPoint = 0;
}
g.drawLine(Math.round(this.getPosx()) + this.points[i][0],
Math.round(this.getPosy()) + this.points[i][1],
Math.round(this.getPosx()) + this.points[nextPoint][0],
Math.round(this.getPosy()) + this.points[nextPoint][1]
);
}
}
}
you can use the Graphics methods to draw a polygon. but you must 'transform' your points into a proper format getXs(), i didn't point that out, i'm pretty sure you can do this as good as i can =)
#Override
public void draw(Graphics g) {
g.setColor(Color.gray); //fillColor
int[] xPoints = getXs(this.points);
int[] yPoints = getYs(this.points);
int nPoints = xPoints.length;
g.fillPolygon(xPoints, yPoints, nPoints);
}
Related
This is my first question here so take it easy ;)
Ok so, I am trying to make a game in Java completely from scratch. The problem I am having at the moment is drawing on a canvas. So essentially the game takes place in a hexagonal grid, so I am trying to draw a hexagonal grid over the screen. The problem is that sometimes (Maybe half the time) the grid doesn't fully render and everything breaks. Upon clicking play game from the main menu, the user should be greeted with a game screen, which is for now just a canvas. A hexagon grid is immediately drawn. When testing with a 10 x 10 hexagonal grid, some hexagons on the right side (that are rendering last) flicker for a few seconds and then either stop flickering and everything works (I can zoom) or they disappear completely and I can no longer zoom. With a 90 x 90 grid, I can't see any flickering as it is likely off-screen, but zooming in/out never works.
Game.java
public class Game {
private static Game ourInstance = new Game();
public static Game getInstance() {
return ourInstance;
}
public final float MIN_SIZE = 64.0f;
public final float MAX_SIZE = 512.0f;
public final float SCROLL_MULTIPLIER = 0.2f;
private float size;
private Point gridPosition;
private HexGrid hexGrid;
public GraphicsContext gc;
private Timer timer = new Timer();
private Game() {
}
public void startGame(GraphicsContext gc) {
hexGrid = new HexGrid(HexHelper.GRID_ORIENTATION.POINTY, HexHelper.GRID_SIZE.OPEN_SPACE);
size = 128.0f;
gridPosition = new Point(0.0f, 0.0f);
runGame(gc);
}
public void runGame(GraphicsContext gc) {
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
updateGame(gc);
}
}, 0, 1000L / 60L);
}
public void updateGame(GraphicsContext gc) {
repaintGame(gc);
}
public void repaintGame(GraphicsContext gc) {
gc.clearRect(0, 0, 1920, 1080);
gc.setStroke(Color.BLACK);
//TODO change with zoom
gc.setLineWidth(5.0);
hexGrid.drawGrid(gc, size);
}
public void zoomCamera(double delta) {
if(size >= MIN_SIZE && size <= MAX_SIZE) {
size += delta * SCROLL_MULTIPLIER;
size = Math.max(MIN_SIZE, Math.min(MAX_SIZE, size));
}
System.out.println(size);
}
}
HexGrid.java
package hexlib;
import javafx.scene.canvas.GraphicsContext;
public class HexGrid {
private HexHelper.GRID_ORIENTATION orientation;
private Hexagon[][] grid;
public HexGrid(HexHelper.GRID_ORIENTATION o, HexHelper.GRID_SIZE s) {
orientation = o;
generateGrid(s);
}
public void generateGrid(HexHelper.GRID_SIZE s) {
int tiles;
switch (s) {
case OPEN_SPACE:
tiles = 90;
break;
case PLANETARY_ORBIT:
tiles = 10;
break;
default:
tiles = 1;
}
grid = new Hexagon[tiles][tiles];
for (int i = 0; i < tiles; i++) {
for (int j = 0; j < tiles; j++) {
grid[j][i] = new Hexagon(new OffsetCoord(j, i));
}
}
}
public void drawGrid(GraphicsContext gc, float size) {
Point[] points;
for (Hexagon[] row: grid) {
for (Hexagon h : row) {
//Gets the coordinates of vertices of the hexagon located at the given coordinate
points = HexHelper.evenRToPixelHexagonVertices(h.getOffsetCoord(), size);
double[] xPoints = new double[]{points[0].x, points[1].x, points[2].x, points[3].x, points[4].x, points[5].x};
double[] yPoints = new double[]{points[0].y, points[1].y, points[2].y, points[3].y, points[4].y, points[5].y};
gc.strokePolygon(xPoints, yPoints, 6);
}
}
}
public Hexagon[][] getGrid() {
return grid;
}
}
HexHelper.java
package hexlib;
import utils.ExtendedMath;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HexHelper {
public enum GRID_ORIENTATION {
POINTY,
FLAT;
}
public enum GRID_SIZE {
OPEN_SPACE,
PLANETARY_ORBIT,
LARGE_PLANET,
SMALL_PLANET,
ONE;
}
public static Point evenRToPixelHexagonCenter(OffsetCoord o, float size) {
float x, y;
//If row is odd, x = column + 1 (times size)
//Else x = column + 1/2 (times size)
if (o.getCoords()[1] % 2 == 0) {
x = (o.getCoords()[0] + 1.0f) * size;
}
else {
x = (o.getCoords()[0] + 0.5f) * size;
}
//0.75 is 3/4 height between each hexagon
//0.5 is 1/2 height that inherently exists between the top of the first row and their centers
y = (o.getCoords()[1] * 0.75f * size) + (0.5f * size);
return new Point(x, y);
}
public static Point[] evenRToPixelHexagonVertices(OffsetCoord o, float size) {
float x, y;
Point[] points = new Point[6];
Point centerPoint = evenRToPixelHexagonCenter(o, size);
points[0] = new Point(centerPoint.x + (0.00f * size), centerPoint.y + (0.50f * size));
points[1] = new Point(centerPoint.x + (0.50f * size), centerPoint.y + (0.25f * size));
points[2] = new Point(centerPoint.x + (0.50f * size), centerPoint.y - (0.25f * size));
points[3] = new Point(centerPoint.x + (0.00f * size), centerPoint.y - (0.50f * size));
points[4] = new Point(centerPoint.x - (0.50f * size), centerPoint.y - (0.25f * size));
points[5] = new Point(centerPoint.x - (0.50f * size), centerPoint.y + (0.25f * size));
return points;
}
}
gc is a GraphicsContext created in the JavaFX application and passed into Game.getInstance().startGame()
Any help is appreciated, and thank you in advance for your help
Im searching for an algorythem to get the location of a hexagon in a grid.
I found this one but it doesnt seam to work:
for(int i = 0; i < width; i++) {
for(int j = 0; j < height; j++) {
grid[i][j] = new Hexagon(x+(j*((3*Hexagon.S)/2)), y+((j%2)*Hexagon.A)+(2*i*Hexagon.A));
}
}
The output is kind of strange:
output
This is the window-creating class(just a test class):
import java.awt.Container;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Grid extends JPanel {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
int width = 2;
int height = 4;
int x = 100;
int y = 100;
Hexagon[][] grid = new Hexagon[width][height];
JFrame f = new JFrame();
Container cp = f.getContentPane();
for(int i = 0; i < width; i++) {
for(int j = 0; j < height; j++) {
grid[i][j] = new Hexagon(x+(j*((3*Hexagon.S)/2)), y+((j%2)*Hexagon.A)+(2*i*Hexagon.A));
cp.add(grid[i][j]);
}
}
f.setLayout(null);
f.setBounds(100, 100, 300, 300);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
}
The Hexagon.java class:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import javax.swing.JButton;
public class Hexagon extends JButton {
public static final int S = 50;
public static final int A = (int) (Math.sqrt(3)*(S/2));
private static final long serialVersionUID = 1L;
private final int x, y;
private final Polygon shape;
public Hexagon(int x, int y) {
this.x = x;
this.y = y;
this.shape = initHexagon();
setSize(2*S, 2*A);
setLocation(x-S, y-A);
setContentAreaFilled(false);
}
private Polygon initHexagon() {
Polygon p = new Polygon();
p.addPoint(x+(S/2), y-A);
p.addPoint(x+S, y);
p.addPoint(x+(S/2), y+A);
p.addPoint(x-(S/2), y+A);
p.addPoint(x-S, y);
p.addPoint(x-(S/2), y-A);
return p;
}
protected void paintComponent(Graphics g) {
g.setColor(Color.BLACK);
g.drawPolygon(this.shape);
}
protected void paintBorder(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLACK);
g2.setStroke(new BasicStroke(4));
g2.drawPolygon(this.shape);
}
public boolean contains(int x, int y) {
return this.shape.contains(x, y);
}
}
As i said, this class worked just fine using non-rectangular shapes.
There was no clipping or such.
You've posted your definition of Hexagon too late, so I copy-pasted a modified version of a similar class from my collection of code snippets.
Here is one way to generate a hexagonal grid:
import java.awt.Container;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JComponent;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.GridLayout;
import java.util.function.*;
public class Hexagons extends JPanel {
private static final long serialVersionUID = 1L;
/** Height of an equilateral triangle with side length = 1 */
private static final double H = Math.sqrt(3) / 2;
static class Hexagon {
final int row;
final int col;
final double sideLength;
public Hexagon(int r, int c, double a) {
this.row = r;
this.col = c;
this.sideLength = a;
}
double getCenterX() {
return 2 * H * sideLength * (col + (row % 2) * 0.5);
}
double getCenterY() {
return 3 * sideLength / 2 * row;
}
void foreachVertex(BiConsumer<Double, Double> f) {
double cx = getCenterX();
double cy = getCenterY();
f.accept(cx + 0, cy + sideLength);
f.accept(cx - H * sideLength, cy + 0.5 * sideLength);
f.accept(cx - H * sideLength, cy - 0.5 * sideLength);
f.accept(cx + 0, cy - sideLength);
f.accept(cx + H * sideLength, cy - 0.5 * sideLength);
f.accept(cx + H * sideLength, cy + 0.5 * sideLength);
}
}
public static void main(String[] args) {
final int width = 50;
final int height = 50;
final Hexagon[][] grid = new Hexagon[height][width];
for(int row = 0; row < height; row++) {
for(int col = 0; col < width; col++) {
grid[row][col] = new Hexagon(row, col, 50);
}
}
JFrame f = new JFrame("Hexagons");
f.getContentPane().setLayout(new GridLayout());
f.getContentPane().add(new JComponent() {
#Override public void paint(Graphics g) {
g.setColor(new Color(0xFF, 0xFF, 0xFF));
g.fillRect(0,0,1000,1000);
g.setColor(new Color(0,0,0));
final int[] xs = new int[6];
final int[] ys = new int[6];
for (Hexagon[] row : grid) {
for (Hexagon h: row) {
final int[] i = {0};
h.foreachVertex((x, y) -> {
xs[i[0]] = (int)((double)x);
ys[i[0]] = (int)((double)y);
i[0]++;
});
g.drawPolygon(xs, ys, 6);
g.drawString(
"(" + h.row + "," + h.col + ")",
(int)(h.getCenterX() - 15),
(int)(h.getCenterY() + 12)
);
}
}
}
});
f.setBounds(0, 0, 500, 500);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
try {
Thread.sleep(100);
} catch (Throwable e) {
} finally {
f.repaint();
}
}
}
It produces the following output:
Sorry for the lack of anti-aliasing. A few hints:
Height H of an equilateral triangle with unit side length is sqrt(3) / 2
The six offsets from the center are (0, +1), (H, +1/2), (H, -1/2), (0, -1), (-H, -1/2), (-H, +1/2), everything times side length.
Distance between rows is 1.5, distance between columns is 2 * H (times scaling constant = side length).
Every odd row is shifted by (0, H) (times scaling constant).
The position of (row,col)-th hexagon is (1.5 * row, 2 * H * (col + 0.5 * (row % 2))) (times constant).
If you want to rotate the hexagons such that two of their sides are horizontal, you have to flip rows and columns.
I am having trouble replicating the picture for my assignment. I would appreciate any tips or solutions. I believe I have the general idea down, but I am having a hard time figuring out the math to replicate the image and doing everything in a single loop.
My program needs to meet these criteria:
Match the Image
Generate a random color for each pair of parabolic curve
Work with any width or height
Use a single loop to draw the entire figure.
Here is the image:
This is what I have tried so far
public static final int WIDTH = 500;
public static final int HEIGHT = 500;
public static final int LINE_INCREMENT = 5;
public static void main(String[] args) {
DrawingPanel panel = new DrawingPanel(WIDTH, HEIGHT);
Graphics g = panel.getGraphics();
int d = 0;
int iterations = HEIGHT/LINE_INCREMENT;
Random rand = new Random();
int red = 0, green = 0, blue = 0;
red = rand.nextInt(128) + 128;
green = rand.nextInt(128) + 128;
blue = rand.nextInt(128) + 128;
g.setColor(new Color(red,green,blue));
for(int y = 0; y < iterations; y++) {
g.drawLine(0, d, d, HEIGHT);
g.drawLine(WIDTH, d, d, 0);
d += LINE_INCREMENT;
}
red = rand.nextInt(128) + 128;
green = rand.nextInt(128) + 128;
blue = rand.nextInt(128) + 128;
g.setColor(new Color(red,green,blue));
d = 0;
for (int x = 0; x < iterations/2; x++) {
g.drawLine(WIDTH/4, d + HEIGHT/4, d + WIDTH/4, HEIGHT - HEIGHT/4);
g.drawLine(d + WIDTH/4, WIDTH/4, WIDTH - WIDTH/4, d + WIDTH/4);
d += LINE_INCREMENT;
}
}
The output:
The way to implement it is by overriding paintComponent:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ParabolicCurves extends JFrame {
private static final int SIZE = 600;
private static final int LINE_INCREMENT = 5;
private static final int NUM_OF_PATTERNS = 4;
private Random rand = new Random();
ParabolicCurves() {
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
JPanel panel = new DrawingPanel(SIZE);
add(panel, BorderLayout.CENTER);
pack();
setVisible(true);
}
class DrawingPanel extends JPanel{
public DrawingPanel(int size) {
setPreferredSize(new Dimension(size, size));
setBackground(Color.WHITE);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int red, green, blue, delta , iterations;
int height, width ,startX, startY, endX, endY ;
Rectangle boundingRrectangle = getBounds();
for(int pattern = 0 ; pattern < NUM_OF_PATTERNS; pattern++) {
red = rand.nextInt(128) + 128;
green = rand.nextInt(128) + 128;
blue = rand.nextInt(128) + 128;
g.setColor(new Color(red,green,blue));
height = (int) boundingRrectangle.getHeight();
width = (int) boundingRrectangle.getWidth();
startX = (int) boundingRrectangle.getX();
startY = (int) boundingRrectangle.getY();
endX = startX+width;
endY = startY+ height;
iterations = Math.min(width, height)/LINE_INCREMENT;
delta = 0;
for (int x = 0; x < iterations ; x++) {
g.drawLine(startX, startY+delta, startX+delta, endY);
g.drawLine(endX, startY+delta, startX+delta, startY);
delta += LINE_INCREMENT;
}
//change bounding rectangle
boundingRrectangle = new Rectangle(startX+(width/4),
startY+(width/4), width/2, height/2);
}
}
}
public static void main(String[] args) {
new ParabolicCurves();
}
}
Output:
Okay, so I'm in the progress of making a game, and I need the collisions to work. I have an if() { } else if() {} -statement, but BOTH of them are being called. Here is my code:
Inside my Player Class:
public Rectangle[] tileRect;
public void update() {
tileRect = new Rectangle[Level1.tiles.size()];
for (int w = 0; w < Level1.tiles.size(); w++) {
Tile m = (Tile) Level1.tiles.get(w);
tileRect[w] = m.getBounds();
if(tileRect[w].intersects(getRect())) {
System.out.println("intersecting");
dy = 0;
} else if (!tileRect[w].intersects(getRect())){
dy = 4;
}
}
x += dx;
y += dy;
}
public Rectangle getRect() {
return new Rectangle(x, y, 32, 32);
}
Here is my Tile class (the Level1.tiles is an arrayList of tiles):
package level;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Tile extends Rectangle {
// Defines the id of the tile, used for setting textures.
private static final long serialVersionUID = 1L;
public int id;
// Location
public int x;
public int y;
// Variables for the terrain sprite sheet.
public BufferedImage image;
public String imageLocation;
public BufferedImage[] sprite;
public int rows = 16;
public int collumns = 16;
public int width = 16;
public int height = 16;
public Tile(int idVal, int xPos, int yPos) {
x = xPos * 32;
y = yPos * 32;
id = idVal;
setBounds(x, y, 32, 32);
createImages();
}
public BufferedImage getImage() {
return sprite[id];
}
public void createImages() {
imageLocation = "res/tile/terrain.png";
try {
image = ImageIO.read(new File(imageLocation));
} catch (IOException e) {
System.out.println("Unable to find file " + imageLocation
+ ", printing stack trace!");
e.printStackTrace();
}
sprite = new BufferedImage[rows * collumns];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < collumns; j++) {
sprite[(i * collumns) + j] = image.getSubimage(j * width, i
* height, width, height);
}
}
}
public int getXLoc() {
return x;
}
public int getYLoc() {
return y;
}
public void setX(int xPos) {
x = xPos;
}
public void setY(int yPos) {
y = yPos;
}
}
I'm getting the "intersecting" message in the console, but the player is still falling down (because dy = 4). Please help! I've been trying to solve this all morning...
If and Else cannot both be caught at the same time.
Looks like you are looping through, seeing an intersection, then continuing to loop through regardless.
Try adding a break command to your for loop.
if(tileRect[w].intersects(getRect())) {
System.out.println("intersecting");
dy = 0;
break;
} else if (!tileRect[w].intersects(getRect())){
dy = 4;
}
The break will stop your for loop from continuing and you will exit the loop with dy = 0; rather than going onto the next tile and changing it back to dy = 4;
if and else if cannot be called in the same run. If if condition is true, then any else is never run (not even checked). It's probably different runs. Debug or put a log to see the flow.
Also
if(tileRect[w].intersects(getRect())) {
System.out.println("intersecting");
dy = 0;
} else if (!tileRect[w].intersects(getRect())){
dy = 4;
}
is much simpler written as
if(tileRect[w].intersects(getRect())) {
System.out.println("intersecting");
dy = 0;
} else {
dy = 4;
}
While using an ArrayList, I can't seem to figure out how to reset and re-apply the random color for each iteration of the for loop. I am trying to reset and apply my random color every time my XLeft position is changed. This is only a section of one class I am using, and my getMax() was defined by a Scanner input. Any suggestions?
import java.util.ArrayList;
import java.util.Random;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
public class BarChart {
private int width, height;
private ArrayList<Double> values = new ArrayList<Double>();
private Random generator = new Random();
int red = generator.nextInt(255);
int green = generator.nextInt(255);
int blue = generator.nextInt(255);
private Color randomColor = new Color(red, green, blue);
public BarChart(int aWidth, int aHeight) {
width = aWidth;
height = aHeight;
}
public void add(double inputValues) {
values.add(inputValues);
}
public double getMax() {
double max = values.get(0);
for (int i = 1; i < values.size(); i++) {
if ((values.get(i)) > max)
max = values.get(i);
}
return max;
}
public void draw(Graphics2D g2) {
int xLeft = 0;
double barWidth = width / values.size();
for (int i = 0; i < values.size(); i++) {
double barHeight = (values.get(i) / getMax()) * height;
Rectangle bar = new Rectangle(xLeft, height - ((int) barHeight),
(int) barWidth, (int) barHeight);
g2.setColor(randomColor);
g2.fill(bar);
xLeft = (int) (xLeft + barWidth);
xLeft++;
}
}
}
It sounds like you're defining your random color once, before the loop. This means that when you run the loop, it uses the same "random color" each time through. You need to move the definition of the random color into the loop, so that it runs with each iteration.
EDIT (based on your comments):
public void draw(Graphics2D g2) {
int xLeft = 0;
double barWidth = width / values.size();
for (int i = 0; i < values.size(); i++) {
double barHeight = (values.get(i) / getMax()) * height;
Rectangle bar = new Rectangle(xLeft, height - ((int) barHeight),
(int) barWidth, (int) barHeight);
red = generator.nextInt(255);
green = generator.nextInt(255);
blue = generator.nextInt(255);
randomColor = new Color(red, green, blue);
g2.setColor(randomColor);
g2.fill(bar);
xLeft = (int) (xLeft + barWidth);
xLeft++;
}