Java 2D array, possible to set individual size? - java

Quick question about Java 2D arrays; For my tile-based, top-down, 2D game (using swing) I use
a 2D array to create a map, like this
public int[][] createMap(){
return new int[][]{
{0, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}};
}
I then use this in my gameComponents class where I draw the individual tiles unto the map, like this
protected void paintComponent(Graphics g){
super.paintComponent(g);
for (int row = 0; row < game.getMap().getWidth(); row++) {
for (int col = 0; col < game.getMap().getHeight(); col++) {
g.drawImage(tile.getTileImage().get(values()[game.getMap().getMapArray()[col][row]]), row * SIZE,
col * SIZE, this);
}
} }
(where size is the size of a tile)
This works, and it correctly draws each tile to the map as expected, however
this also causes a problem for collision detection. As you may have noted, while I do define the size between the tiles in draw method, it is not defined in the array at all. Which, as you'd imagine, raises issues when checking for collision as the drawn tile is not where the tile is in the 2D array (due to size offset).
This is the code I use for checking collision (of course, not working due to ArrayIndexOutofbounds).
public boolean collisionDetected(int xDirection, int yDirection, Game game, Player player){
for (int row = 0; row < game.getMap().getHeight() * 16; row ++){
for (int col = 0; col < game.getMap().getWidth() * 16; col++) {
System.out.println(col + xDirection + player.getPositionX());
if(game.getMap().getTile(col + xDirection + player.getPositionX() ,
row + yDirection + player.getPositionY()) == Tiles.GRASS ){
System.out.println("COLLISION DETECTED");
return true;
}
}
}
return false;
}
This method uses a method within the map class that returns the tile on that
specific coordinate, like this
public Tiles getTile(int col,int row){
return Tiles.values()[mapArray[col][row]];
}
And, of course, as the 2D array doesn't know of the size offset, it just throws
an arrayindexoutofbound.
My question is, is it possible to define a 2D map array with the size of a tile in-mind? I appreciate any help & input I can get, after-all I am here to learn!
Extra clarification: All the tiles are in an enum class (i.e AIR, GRASS, STONE...). Also worth noting that the player position is not bound by an array, I merely move it the amount of pixels I want it to move.
Thanks in advance!

This method uses a method within the map class that returns the tile on that specific coordinate, like this
public Tiles getTile(int col,int row){
return Tiles.values()[mapArray[col][row]];
}
So if you have a "coordinate", why do you call the parameters col/row?
If you have a 10x10 grid and each tile is 20 pixels then the grid size is 200x200 so you could have x/y values in the range 0-199
So if you have a coordinate of 25x35 you would simply calculate the row/col values as:
int row = 35 / 20;
int column = 25 / 20;
So your method would be something like :
public Tiles getTile(int x, int y)
{
int row = y / 20;
int column = x / 20;
return Tiles.values()[mapArray[row][column]];
}

Related

Java Convolution (Array Out Of Index Error)

i am writing a program that will read the element in a convolute and the program will then multiple each element in the convolute with the kernel given. I will need to take the convolute array from other class as well as kernel.
But i am getting this Error message: Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 6
The error will be specified in the comment section with capitalised sentence, which is in my second for loop
The classes (kernel and the convolute) will be provided down after my code.
Thank You!
public static void main(String[] args)
{
Kernel kernel = new Kernel();
Convolute convolute = new Convolute();
int[][] matrixA = convolute.MATRIX_A;
int[][] kernelVertical = kernel.VERTICAL;
int[][] resultArray = new int[4][4];
int tempMatrix = 0, sumMatrix = 0; // declare temporary matrix and sumMatrix to take all the sum of each product array
int matrixRowA = 0, matrixColumnA = 0; // declare variable for matrix row and column
int convoluteRowControl = 2, convoluteColumnControl = 2; // this variable is to control moving of the kernel through the convolute
while(convoluteRowControl < matrixA.length) // while loop stops when convoluteRowControl points to 6
{
for(int kernelRow = 0; kernelRow < kernelVertical.length; kernelRow++) // this loop will stop when kernelRow is 3
{
for(int kernelColumn = 0; kernelColumn < kernelVertical[kernelRow].length; kernelColumn++) // this loop will stop when kernelColumn is 3
{
tempMatrix = matrixA[matrixRowA][matrixColumnA] * kernelVertical[kernelRow][kernelColumn]; // ERROR IS HERE!!
sumMatrix += tempMatrix; // sum all of the matrix calculated
matrixColumnA++; // re-initialize matrixColumnA to move to the next column element in matrixA
}
matrixRowA++; // re-initialize matrixRowA to move to the next row element in matrixA
matrixColumnA = convoluteColumnControl - 2; // re-initialize matrixColumnA back to the original specified range
}
for(int row = 0; row < resultArray.length; row++) // this loop is used to store sumMatrix calculated in the above loop to each element of the array
{ // loop stops at 5
for(int column = 0; column < resultArray[row].length; column++)
{
resultArray[row][column] = sumMatrix; // store sumMatrix into each element of the array
}
}
++convoluteColumnControl; // re-initialize convoluteColumnControl so that the kernel can move to the next column
if(matrixColumnA < 5) // if kernel get to the last column of its size, reset the row and move to the next column again
{ // this will stop when kernel get to the final column of the convolute
matrixRowA = convoluteRowControl - 2; // reset row back to the the top kernel
matrixColumnA = convoluteColumnControl - 2; // set new starting limit for the column in the kernel
}
if(matrixColumnA == 5) // when matrixColumnA is 5 (which means the final column of the convolute) this IF is executed
{ // to set the new row for the kernel
++convoluteRowControl; // convolteRowControl increase by 1 to set a new row limit for the kernel in convolution
matrixRowA = convoluteRowControl - 2; // reset matrixRowA equivalent to the starting of the new kernel
}
}
}
public class Convolute
{
/*
* ARRAY_A contains a 6x6 matrix
*/
public static final int[][] MATRIX_A =
{
{10, 10, 10, 0, 0, 0},
{10, 10, 10, 0, 0, 0},
{10, 10, 10, 0, 0, 0},
{10, 10, 10, 0, 0, 0},
{10, 10, 10, 0, 0, 0},
{10, 10, 10, 0, 0, 0}
};
}
public class Kernel
{
/*
* HORIZONTAL - A kernel that detects horizontal lines.
*/
public static final int[][] HORIZONTAL =
{
{ 1, 1, 1},
{ 0, 0, 0},
{-1, -1, -1}
};
/*
* VERTICAL - A kernel that detects vertical lines.
*/
public static final int[][] VERTICAL =
{
{ 1, 0, -1},
{ 1, 0, -1},
{ 1, 0, -1}
};
}

Poor render performance in LibGDX with hexagonal map

So I am making a simple game using LibGDX which involes a 150*150 hexagonal map, and various types of cell, rocky, clear etc.
Problem is when i load the map, my computer almost completely freezes up and any movement thats supposed to be fluid (character moving, button highlights) take 5+ seconds longer than they should.
Here's the relevant code:
public void render(float deltY){
Gdx.gl.glClearColor(255, 255, 255, 100);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act();
polygonSpriteBatch.begin();
for (int j = 0; j < 150; j++) {
for (int i = 0; i < 150; i++) {
offset = i%2 == 0 ? multipleX/2 : 0;
if (mc.getMap().getRow(i).getTile(j).getTileType().equals(TileType.Rocky)) {
drawCell(Color.BLACK, j, i);}
if (mc.getMap().getRow(i).getTile(j).getTileType().equals(TileType.Clear)) {
drawCell(Color.LIGHT_GRAY, j, i);}
}
}
polygonSpriteBatch.end();
stage.draw();
}
private void drawCell(Color color, int x, int y) {
polySprite = new PolygonSprite(makePoints(color));
polySprite.setX(mc.getMap().getRow(y).getTile(x).getTilePosition().get_x() * multipleX + offset);
polySprite.setY(mc.getMap().getRow(y).getTile(x).getTilePosition().get_y() * multipleY);
polySprite.draw(polygonSpriteBatch);
}
public PolygonRegion makePoints(Color color){
side = 5;
h = CalculateH(side);
r = CalculateR(side);
multipleX = (float)Math.sqrt(3)*side;
multipleY = side+(side/2);
float[] points = { // vertices
x, y,
x+r, y+h,
x+r, y+side+h,
x,y+side+h+h,
x-r, y+side+h,
x-r, y+h};
return new PolygonRegion(new TextureRegion(getTexture(color)),points
, new short[] { //4 triangles using vertices to make hexagon
0, 1, 5,
1, 4, 2,
5, 1, 4,
2, 3, 4});
}
public Texture getTexture(Color color){
Pixmap pix = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pix.setColor(color);
pix.fill();
textureSolid = new Texture(pix);
return textureSolid;
}
I'm new to coding and LibGDX so there's probably something stupid i'm doing. Is there any way to render the map once and only redraw the polygons if they change?
Thanks
Looking at your code, you are computing a square root for each cell, for each rendering pass.
So your code currently involves more than 22500 square root operations for each frame you render and is creating as many objects, that's quite a lot !
You should compute the points for your hexagons only once.

Ice Sliding Puzzle Path Finding

I apologize for the somewhat vague title, I'm unsure what you would call this puzzle.
I'm making a path finding method to find the route with the least moves, not the distance traveled.
The rules of the game are simple, you must traverse from the orange square to the green square, but you can only move in a straight line, and cannot stop moving in that direction until you hit a boundary (either the wall of the arena or an obstacle), as if they were sliding across ice.
Example map, and unless I'm mistaken, the desired path (8 moves)
Arena.java: https://gist.github.com/CalebWhiting/3a6680d40610829b1b6d
ArenaTest.java: https://gist.github.com/CalebWhiting/9a4767508831ea5dc0da
I'm assuming this would be best handled with a Dijkstras or A* path finding algorithm, however I'm not only not very experienced with these algorithms, but also don't know how I would go about defining the path rules.
Thank you for any help in advance.
Here's my solution (Java) in case someone is still interested. As #tobias_k suggested in his comment above, indeed BFS is the way to go:
import java.util.LinkedList;
public class PokemonIceCave {
public static void main(String[] args) {
int[][] iceCave1 = {
{0, 0, 0, 1, 0},
{0, 0, 0, 0, 1},
{0, 1, 1, 0, 0},
{0, 1, 0, 0, 1},
{0, 0, 0, 1, 0}
};
System.out.println(solve(iceCave1, 0, 0, 2, 4));
System.out.println();
int[][] iceCave2 = {
{0, 0, 0, 1, 0},
{0, 0, 0, 0, 1},
{0, 1, 1, 0, 0},
{0, 1, 0, 0, 1},
{0, 0, 0, 1, 0},
{0, 0, 0, 0, 0}
};
System.out.println(solve(iceCave2, 0, 0, 2, 5));
}
public static int solve(int[][] iceCave, int startX, int startY, int endX, int endY) {
Point startPoint = new Point(startX, startY);
LinkedList<Point> queue = new LinkedList<>();
Point[][] iceCaveColors = new Point[iceCave.length][iceCave[0].length];
queue.addLast(new Point(0, 0));
iceCaveColors[startY][startX] = startPoint;
while (queue.size() != 0) {
Point currPos = queue.pollFirst();
System.out.println(currPos);
// traverse adjacent nodes while sliding on the ice
for (Direction dir : Direction.values()) {
Point nextPos = move(iceCave, iceCaveColors, currPos, dir);
System.out.println("\t" + nextPos);
if (nextPos != null) {
queue.addLast(nextPos);
iceCaveColors[nextPos.getY()][nextPos.getX()] = new Point(currPos.getX(), currPos.getY());
if (nextPos.getY() == endY && nextPos.getX() == endX) {
// we found the end point
Point tmp = currPos; // if we start from nextPos we will count one too many edges
int count = 0;
while (tmp != startPoint) {
count++;
tmp = iceCaveColors[tmp.getY()][tmp.getX()];
}
return count;
}
}
}
System.out.println();
}
return -1;
}
public static Point move(int[][] iceCave, Point[][] iceCaveColors, Point currPos, Direction dir) {
int x = currPos.getX();
int y = currPos.getY();
int diffX = (dir == Direction.LEFT ? -1 : (dir == Direction.RIGHT ? 1 : 0));
int diffY = (dir == Direction.UP ? -1 : (dir == Direction.DOWN ? 1 : 0));
int i = 1;
while (x + i * diffX >= 0
&& x + i * diffX < iceCave[0].length
&& y + i * diffY >= 0
&& y + i * diffY < iceCave.length
&& iceCave[y + i * diffY][x + i * diffX] != 1) {
i++;
}
i--; // reverse the last step
if (iceCaveColors[y + i * diffY][x + i * diffX] != null) {
// we've already seen this point
return null;
}
return new Point(x + i * diffX, y + i * diffY);
}
public static class Point {
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
#Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
'}';
}
}
public enum Direction {
LEFT,
RIGHT,
UP,
DOWN
}
}
I think the best solution would probably be the BFS, where you represent the state of the board with a "State" object with the following parameters: number of moves made so far, and coordinates. It should also have a method to find the next states attainable (which should be fairly easy to code, just go N, S, E, W and return an array of the first blocking spots).
Create initial state (0 moves with initial coordinates)
Put in a priority queue (sorting by number moves)
while(priority queue has more states):
Remove node
if it is a goal state:
return the state
Find all neighbors of current state
Add them to priority queue (remembering to increment number of moves by 1)
This uses an implicit graph representation. Optimality is guaranteed because of the priority queue; when the goal state is found, it will have been reached with the fewest moves. If the whole priority queue is exhausted and no state is returned, then no solution exists. This solution takes O(V^2logV) time because of the priority queue, but I think this is the simplest to code. A straight up O(V) BFS solution is possible but you'll have to keep track of what states you have or have not visited yet and the fewest number of moves to reach them, which would take O(V) memory.

LWJGL - Interleaved VBO isn't working as intended

so I've been playing around with the LWJGL and made a 3D space that creates a number of cubes with random velocities and colours, originally using two VBOs (one each for location and colour).
I'm trying to make it work with a single, interleaved VBO, but my attempts so far result in the cubes being draw with 'flashing' colours and multiple colours per cube when they should be single, solid colours. It looks like it's an error to do with the starting position and stride for the vertices/colours but changing them around doesn't appear to help, or causes some very strange effects.
Here's the code from the class creating the VBO (there shouldn't be any errors elsewhere since it works fine with the non-interleaved version and they contain equivalent methods):
package test3D.first.main;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import org.lwjgl.BufferUtils;
import test3D.first.entities.*;
import test3D.first.main.threads.Updater;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
public class InterleavedDrawer {
private int vboHandle;
private static FloatBuffer drawData;
final static int vertexSize = 3;
final static int colorSize = 3;
final static int normalSize = 3;
private static int vertexQuantity;
public InterleavedDrawer() {
initialise();
}
private void initialise() {
drawData = BufferUtils.createFloatBuffer(0);
vboHandle = glGenBuffers();
updateBuffers(Updater.objects);
}
public void draw() {
glBindBuffer(GL_ARRAY_BUFFER, vboHandle);
glVertexPointer(vertexSize, GL_FLOAT, 1, 0L);
glColorPointer(colorSize, GL_FLOAT, 1, 4L);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_TRIANGLES, 0, vertexQuantity);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
}
public void update() {
for (int i = 0; i < Updater.getObjectQuantity(); i++) {
drawData.put(Updater.objects.get(i).getVertices());
drawData.put(Updater.objects.get(i).getColors());
}
drawData.flip();
glBindBuffer(GL_ARRAY_BUFFER, vboHandle);
glBufferData(GL_ARRAY_BUFFER, drawData, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
public void clear() {
drawData.clear();
}
private void updateBuffers(ArrayList<AbstractEntity> objects) {
vertexQuantity = 0;
drawData.clear();
for (int i = 0; i < objects.size(); i++) {
vertexQuantity += objects.get(i).getVertexQuantity();
}
drawData = BufferUtils.createFloatBuffer(vertexSize * vertexQuantity + colorSize * vertexQuantity);
}
}
As I said, I suspect the error comes from the lines:
glVertexPointer(vboHandle, GL_FLOAT, 1, 0L);
glColorPointer(vboHandle, GL_FLOAT, 1, 3L);
Leaving the values as they are causes the odd flashing effects, changing the long value in the glColorPointer() to 4 strangely creates 'regions' within the cubes that are colours corresponding to (0, 0, 0), (1, 0, 0), (0, 1, 0) etc., which makes no sense whatsoever since the colours are entirely random floats, and any other values I've tried don't seem to do anything other than the first effect. Am I misusing the values here or something else? Any help at all would be very much appreciated, thanks in advance :)
Stride & offset are specified in bytes. So the stride should be 6 floats * 4 bytes = 24 and the offset 0 (vertex) and 12 (color) respectively.
Also there's a mistake in update(). Right now vertex and color data are interleaved like this (assuming a single triangle per object):
( object 1 ) ( object 2 )
V1V2V3 C1C2C3 V4V5V6 C4C5C6 ...
Where V# is a vector (x,y,z) and C# is a color (r,g,b).
OpenGL expects them in this order:
( object 1 ) ( object 2 )
V1C1 V2C2 V3C3 V4C4 V5C5 V6C6 ...
So the body of the update loop should be changed to something like this (not tested)
float[] vertData = Update.object.get(i).getVertices();
float[] colorData = Update.object.get(i).getColors();
for(int j = 0; j < vertData.length; j += 3) {
drawData.put(vertData, j, 3)
drawData.put(colorData, j, 3)
}
More information

Call recursion method java

I have defined a recursion method (at least I believe it is recursive) that returns void and want to call it in another method, but don't know how. I know it's very basic, but can someone please help? Thanks.
Recursive method:
private static void recursiveWhiteToBlack(BufferedImage image, int width, int height){
image.getRaster().setPixel(width,height, new int [] {0, 0, 0, 0, 0, 0});
int[][] neighbors = neighborsXY(width,height);
for(int i = 0; i<neighbors.length; i++){
int neighborX = neighbors[i][0];
int neighborY = neighbors[i][1];
int[] neighborColor = image.getRaster().getPixel(neighborX, neighborY, new int[] {0, 0, 0, 0, 0, 0});
if(neighborColor[0] == 1){
recursiveWhiteToBlack(image, neighborX, neighborY);
}
}
}
Calling it:
public static BufferedImage countObjects(BufferedImage image, BufferedImage original, ComponentPanel panel){
BufferedImage target = copyImage(image);
for(int width=1; width<image.getRaster().getWidth()-1; width++){ //Determine the dimensions for the width (x)
for(int height=1; height<image.getRaster().getHeight()-1; height++){ //Determine the dimensions for the height (y)
int[] pixel = image.getRaster().getPixel(width, height, new int[] {0, 0, 0, 0, 0, 0});
if(pixel[0] == 1){
none = recursiveWhitetoBlack(image, width, height); //HOW TO CALL IT HERE!!!//
}
System.out.println("countObjects method called");
return target;
}
You call it like this:
if(pixel[0] == 1){
recursiveWhitetoBlack(image, width, height);
}
since the method has no return type, there is no need for variable assignment.
Remove none = since your method returns void (actually means it does not return anything)
So this should look like :
if(pixel[0] == 1){
recursiveWhitetoBlack(image, width, height);
}
also note that none is not defined as a variable/member so it is invalid to use it.
This could be trouble. I'm not sure you have a true stopping condition. You'll know right away when you get an out of memory error.

Categories