Algorithm for drawing tiles on screen - java

I have struggled with this problem for several days now, and I just can't solve the problem myself.
I have a Minecraft 2D sort of game. The Blocks are divided into Chunks of 64 * 64 Blocks.
I have this draw function:
#Override
protected void gameDraw(Graphics2D g) {
g.drawImage(Texture.getImage("background"), 0, 0, 800, 600, 0, 0, 800, 600, null);
int pixelXMin = (int) (screenX);
int pixelYMin = (int) (screenY);
int pixelXMax = (int) (canvas.getWidth() + screenX);
int pixelYMax = (int) (canvas.getHeight() + screenY);
int blockXMin = pixelXMin / Block.BLOCK_PIXEL_WIDTH;
int blockYMin = pixelYMin / Block.BLOCK_PIXEL_HEIGHT;
int blockXMax = pixelXMax / Block.BLOCK_PIXEL_WIDTH;
int blockYMax = pixelYMax / Block.BLOCK_PIXEL_HEIGHT;
int diffX = pixelXMin % Block.BLOCK_PIXEL_WIDTH;
int diffY = pixelYMin % Block.BLOCK_PIXEL_HEIGHT;
for (int bx = blockXMin; bx < blockXMax; bx++) {
for (int by = blockYMin; by < blockYMax; by++) {
int chunkNum = bx / Chunk.COLUMNS_IN_CHUNK;
int blockX = bx % Chunk.COLUMNS_IN_CHUNK;
int blockY = by % Chunk.COLUMNS_IN_CHUNK;
Chunk chunk = level.loadChunk(chunkNum);
Block block = chunk.getBlock(blockX, blockY);
if (block == null) { continue; }
BlockData blockData = block.getBlockData();
if (blockData.getType().getTextureName().equals("")) { continue; }
int blockPosX = canvas.getWidth() - (bx * Block.BLOCK_PIXEL_WIDTH + diffX);
int blockPosY = canvas.getHeight() - (by * Block.BLOCK_PIXEL_HEIGHT + diffY);
BufferedImage image = Texture.getImage(blockData.getType().getName());
g.drawImage(image, blockPosX, blockPosY, Block.BLOCK_PIXEL_WIDTH, Block.BLOCK_PIXEL_HEIGHT, null);
}
}
}
Here i tried to calculate the maximum and minimum number of pixels on the screen with the variables screenX and screenY who serve as "camera position". Then I loop through every block that is within these pixels.
The rest is sort of self explanatory.
Now for the problem:
As you can see does the game draw nicely, but when you change the values of screenX and screenY, the "box" of blocks move realtive to the screen, but the blocks stay at the same location.
How can I solve this? Thank you!
Link to dropbox download for .jar file and full sourcecode. gameDraw() is in CubeExplorer.java
https://www.dropbox.com/sh/madienlwmhjt2wh/AADA83aKa00j5UFowhotfmmaa?dl=0
SOLUTION
Here is my solution:
#Override
protected void gameDraw(Graphics2D g) {
g.drawImage(Texture.getImage("background"), 0, 0, 800, 600, 0, 0, 800, 600, null);
int x = (int) (screenX / Block.BLOCK_PIXEL_WIDTH);
int y = (int) (screenY / Block.BLOCK_PIXEL_HEIGHT);
int blockXMin = x;
int blockYMin = y;
int blockXMax = x + canvas.getWidth() / Block.BLOCK_PIXEL_WIDTH + 2;
int blockYMax = y + canvas.getWidth() / Block.BLOCK_PIXEL_WIDTH + 2;
System.out.println(x + "," + y + " " + blockXMax + "," + blockYMax);
for(int blockX = blockXMin; blockX < blockXMax; blockX++) {
for(int blockY = blockYMin; blockY < blockYMax; blockY++) {
int chunkX = blockX / Chunk.COLUMNS_IN_CHUNK;
Chunk chunk = level.loadChunk(chunkX);
int blockChunkPosX = blockX % Chunk.COLUMNS_IN_CHUNK;
int blockChunkPosY = blockY % Chunk.ROWS_IN_CHUNK;
Block block = chunk.getBlock(blockChunkPosX, blockChunkPosY);
if (block == null) { continue; }
BlockData blockData = block.getBlockData();
if (blockData.getType().getTextureName().equals("")) { continue; }
int blockScreenLocX = (int) ((int) + blockX * Block.BLOCK_PIXEL_WIDTH - screenX);
int blockScreenLocY = 600 - (int) ((int) + blockY * Block.BLOCK_PIXEL_HEIGHT - screenY);
BufferedImage image = Texture.getImage(blockData.getType().getName());
g.drawImage(image, blockScreenLocX, blockScreenLocY, Block.BLOCK_PIXEL_WIDTH, Block.BLOCK_PIXEL_HEIGHT, null);
}
}
}

Related

loadpixels from jpg / png incorrect processing

I am trying to draw a grid over an image using the underlaying colours of the image to fill the circles. But some pixels are not getting the correct colour.
In this case the circles are drawn white but they should not be drawn white...
See my code below:
import processing.pdf.*;
PImage img;
color background = color(255);
void setup() {
size(1038, 525);
ellipseMode(CORNER);
noStroke();
//img = loadImage("noise2.jpg");
//img = loadImage("air.png");
img = loadImage("accidents.png");
image(img, 0, 0, width, height);
visualGrid(20, 0.4, false);
}
//void draw() {
// fill(noise.get(mouseX, mouseY));
// rect(width - 100, height - 100, 100, 100);
//}
void visualGrid(int circleSize, float fillSmoothing, boolean debug) {
float halfCircle = circleSize / 2.0;
int amountX = floor(width / circleSize);
int amountY = floor(height / circleSize);
amountY += floor(amountY * 0.1);
float offsetX = (width - (amountX * circleSize + halfCircle)) / 2 + halfCircle;
float offsetY = (height - amountY * circleSize + amountY * circleSize * 0.1) / 2;
for (int x = 0; x < amountX; x++) {
for (int y = 0; y < amountY; y++) {
float styledOffsetX = (y % 2 == 0) ? offsetX - halfCircle : offsetX;
float xpos = x * circleSize + styledOffsetX;
float ypos = circleSize * 0.9 * y + offsetY;
int sectionSize = round(circleSize * fillSmoothing);
float sectionOffset = (circleSize - sectionSize) / 2;
color c = getAvgImgColor(img.get(round(xpos + sectionOffset), round(ypos + sectionOffset), sectionSize, sectionSize));
//fill(noise.get(round(xpos), round(ypos)));
if(debug) {
stroke(255, 0, 255);
strokeWeight(1);
}
fill(c);
ellipse(xpos, ypos, circleSize, circleSize);
if(debug) {
noStroke();
fill(255, 0, 255);
rect(round(xpos + sectionOffset), round(ypos + sectionOffset), sectionSize, sectionSize);
}
}
}
}
color getAvgImgColor(PImage section) {
section.loadPixels();
int avgR = 0, avgG = 0, avgB = 0;
int totalPixels = section.pixels.length;
for (int i = 0; i < totalPixels; i++) {
color pixel = section.pixels[i];
//if(pixel == background) continue;
avgR += red(pixel);
avgG += green(pixel);
avgB += blue(pixel);
}
return color(
round(avgR / totalPixels),
round(avgG / totalPixels),
round(avgB / totalPixels)
);
}
This is what i get when drawing my grid on the image in question:
As you can see in the circled area not all circles should be filled with white... This happens in more places than just the circled are just compare this image with the one below.
I will upload the original image below, so you can use it to debug.
There's a mismatch between the dimensions of your sketch (1038 x 525) and the image you're sampling (2076 x 1048) which might explain the misalignment.
If size(2076, 1048) isn't an option try resizing the image once it's loaded in setup():
...
img = loadImage("accidents.png");
img.resize(width, height);
...

Android canvas. How to set circles at random position?

Below code generated and save position x y to HashMap and check collision two circles;
HashMap<Integer, Float> posX = new HashMap<>();
HashMap<Integer, Float> posY = new HashMap<>();
int numberOfCircle = 8;
for(int i=0; i < numberOfCircle; i ++){
// boolean flag = false;
while (true){
float x =random.nextInt(width - raduis/2) + raduis/2f;
float y =random.nextInt(height - raduis/2) + raduis/2f;
if(!posX.containsValue(x) && !posY.containsValue(y)){
if(i == 0){
posX.put(i, x);
posY.put(i, y);
break;
}
if(i > 0){
double distance = Math.sqrt(((posX.get(i - 1) - x) * (posX.get(i - 1) - x)) + ((posY.get(i - 1) - y) * ( posY.get(i - 1) - y)));
if (distance > raduis+raduis) {
posX.put(i, x);
posY.put(i, y);
Log.d(TAG, i + " xPos=" + posX.get(i) + " yPos=" + posY.get(i) + " distance=" + distance);
break;
}
if(numberOfCircle == posX.size()) break;
}
}
}
}
This code work only if circle count=2; But when circle count > 2 i have collision; How to check current generated position for each in HashMap?
For example:
xPos = {5, 10, 3}
yPos = {10, 33, 5}
generated position x=6, y=10;
calculate distance between x=6, y=10 with all positions in Map. If distance < radius+radius generate new position while distance > radius+radius;
Update ========================>
My code work like
I want like this
output: distance equal between current generated position(X, Y) and previous position(X, Y). I want to check between current generated x, y with all added positons in HashMap.
D/DEBUG DATA ===>: 1 xPos=432.0 yPos=411.0 distance=390.6430595825299
D/DEBUG DATA ===>: 2 xPos=316.0 yPos=666.0 distance=280.1446055165082
D/DEBUG DATA ===>: 3 xPos=244.0 yPos=83.0 distance=587.4291446634223
D/DEBUG DATA ===>: 4 xPos=214.0 yPos=551.0 distance=468.96055271205915
D/DEBUG DATA ===>: 5 xPos=76.0 yPos=1011.0 distance=480.2540994098853
D/DEBUG DATA ===>: 6 xPos=289.0 yPos=868.0 distance=256.55019002136794
D/DEBUG DATA ===>: 7 xPos=494.0 yPos=988.0 distance=237.53947040439405
P.s Sorry so poor English.
Maybe something closer to this will work? I'm not entirely sure if it's what you want or if it will help, but I did a quick rewrite for clarity.
HashMap<Integer, Integer> posX = new HashMap<>();
HashMap<Integer, Integer> posY = new HashMap<>();
final int circlesToPlace = 8;
for(int i = 0 ; i < circlesToPlace ; i++){
// boolean flag = false;
while (true){
final int x = ThreadLocalRandom.current().nextInt((radius/2f), width + 1);
final int y = ThreadLocalRandom.current().nextInt((radius/2f), height + 1);
// Iterate over all other positions to ensure no circle intersects with
// the new circle.
for (int index = 0 ; index < posX.size() ; index++) {
// Calculate distance where d = sqrt((x2 - x1)^2 + (y2 - y1)^2)
final int otherX = posX.get(index);
final int otherY = posY.get(index);
int differenceX = otherX - x;
differenceX *= differenceX;
int differenceY = otherY - y;
differenceY *= differenceY;
final double distance = Math.sqrt(differenceX + differenceY);
if (distance > (radius * 2)) {
posX.put(i, x);
posY.put(i, y);
Log.d(TAG, i + " xPos=" + posX.get(i) + " yPos=" + posY.get(i) + " distance=" + distance);
break;
}
}
}
}
My variant. You don't need to do sqrt to compare distance, you can instead square the constant (d2 in my case).
Random random = new Random();
int numberOfCircle = 8, width = 400, height = 300;
int diameter = 51;
final float radius = diameter * 0.5f;
final float d2 = diameter * diameter;
List<Float> posX = new ArrayList<>(numberOfCircle);
List<Float> posY = new ArrayList<>(numberOfCircle);
while (posX.size() < numberOfCircle) { // till enough generated
// generate new coordinates
float x = random.nextInt(width - diameter) + radius;
float y = random.nextInt(height - diameter) + radius;
System.out.printf("Generated [%3.3f, %3.3f] ... ", x, y);
// verify it does not overlap/touch with previous circles
int j = 0;
while (j < posX.size()) {
float dx = posX.get(j) - x, dy = posY.get(j) - y;
float diffSquare = (dx * dx) + (dy * dy);
if (diffSquare <= d2) break;
++j;
}
// generate another pair of coordinates, if it does touch previous
if (j != posX.size()) {
System.out.println("collided.");
continue;
}
System.out.println("added.");
// not overlapping/touch, add as new circle
posX.add(x);
posY.add(y);
} // while (posX.size() < numberOfCircle)
I resolve like this
private class MyView extends View implements View.OnTouchListener{
List<Circle> randomCircles = new ArrayList<>();
int radius = new Circle().getRadius();
int randomCircleCount = 3;
Random random = new Random();
public MyView(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Integer width = canvas.getWidth();
Integer width = canvas.getWidth();
Integer height = canvas.getHeight() - (radius);
// Integer height = canvas.getHeight()/2 + canvas.getHeight();
///randomCircles.clear();
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.BLACK);
canvas.drawPaint(paint);
Paint paintT = new Paint();
paintT.setTextSize(18f);
paintT.setAntiAlias(true);
paintT.setTextAlign(Paint.Align.CENTER);
while(randomCircles.size() < randomCircleCount){
randomCircle(width, height);
}
for(int i=0; i < randomCircleCount; i ++){
Circle circle = randomCircles.get(i);
float curPosX = randomCircles.get(i).getCx().floatValue();
float curPosY = randomCircles.get(i).getCy().floatValue();
int r = random.nextInt(256);
int g = random.nextInt(256);
int b = random.nextInt(256);
paint.setARGB(175, 77, 2, 200);
//if(r != 0 && g != 0 && b != 0) paint.setARGB(255, r, g, b);
canvas.drawCircle(curPosX, curPosY, radius, paint);
Rect bounds = new Rect();
String text = "" +i;
paintT.getTextBounds(text, 0, text.length(), bounds);
paint.setAntiAlias(true);
canvas.drawText(text, curPosX, curPosY, paintT);
circle.update(width, height);
//Log.d(TAG, "REDRAW");
}
///invalidate();
postInvalidateDelayed(100);
}
private void randomCircle(int width, int height) {
double x = getPosX(width, radius);
double y = getPosY(height,radius);
boolean hit = false;
for (int i = 0; i < randomCircles.size(); i++) {
Circle circle = randomCircles.get(i);
double dx = circle.getCx() - x;
double dy = circle.getCy() - y;
int r = circle.getRadius() + radius;
if (dx * dx + dy * dy <= r * r) {
Log.d(TAG, "dx=" + dx + " dy=" + dy);
hit = true;
}
}
if (!hit) {
Log.d(TAG, "Here!!!!!");
randomCircles.add(new Circle(x, y));
}
}
private Float getPosX(Integer width, Integer radius){
return random.nextInt(width - radius/2) + radius/2f;
}
private Float getPosY(Integer height, Integer radius){
return random.nextInt(height - radius/2) + radius/2f;
}

Dynamic drawing on canvas taking too long

I wanted to draw a grid of quads with an animated colour property but ended up with code that is too slow just to create a basic structure of an image, not to mention to animate it. Also, the image is drawing, not as it finished to compute, but in the process by parts. How can I fix it? Here's onDraw(Canvas canvas) method:
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
final int height = canvas.getHeight();
final int width = canvas.getWidth();
Bitmap.Config conf = Bitmap.Config.RGB_565;
Bitmap bitmap = Bitmap.createBitmap(width, height, conf);
new Thread(() -> {
int cells_amount = 0;
float cells_vertical = 0;
float cells_horizontal = 0;
cells_vertical = (float) height / CELL_SIZE;
int y = 0;
if (cells_vertical % 1 > 0) {
y = Math.round(CELL_SIZE * -(cells_vertical % 1));
cells_vertical = (int) (cells_vertical + 1);
}
cells_horizontal = (float) width / CELL_SIZE;
int x = 0;
if (cells_horizontal % 1 > 0) {
x = Math.round(CELL_SIZE * -(cells_horizontal % 1));
cells_horizontal = (int) (cells_horizontal + 1);
}
cells_amount = (int) (cells_horizontal * cells_vertical);
Canvas c = new Canvas(bitmap);
c.drawColor(Color.BLACK);
int x_preserved = x;
Paint textPaint = new Paint();
textPaint.setColor(Color.parseColor("#EEEEEE"));
for (int i = 0; i < cells_amount; i++) {
Rect rect = new Rect(x, y, x + CELL_SIZE, y + CELL_SIZE);
Paint framePaint = new Paint();
framePaint.setColor(Color.parseColor("#1F1F1F"));
framePaint.setStyle(Paint.Style.STROKE);
framePaint.setStrokeWidth(1);
c.drawRect(rect, framePaint);
if (random.nextBoolean()) {
String output = String.format(Locale.getDefault(), "%d", "+");
int cx = (int) (x + (CELL_SIZE / 2) - (textPaint.measureText(output) / 2));
int cy = (int) ((y + (CELL_SIZE / 2)) - ((textPaint.descent() + textPaint.ascent()) / 2));
c.drawText(output, cx, cy, textPaint);
}
if (i % cells_horizontal == 0 && i >= cells_horizontal) {
y += CELL_SIZE;
x = x_preserved;
} else {
x += CELL_SIZE;
}
}
}).start();
canvas.drawBitmap(bitmap, 0, 0, null);
}

I try to set some values to an array of integers but the values won't stick

Here is my code:
int[][] coordX = new int[10000][10000];
int[][] coordY = new int[10000][10000];
int cX = 0;
int cY = 0;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int width = (int) (screenSize.getWidth());
int height = (int) (screenSize.getHeight());
BufferedImage dirt = null;
{
try {
dirt = ImageIO.read(new File("res/dirt.png"));
} catch (IOException e) {
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
for (int y = 0; y <= height; y = y + 50) {
for (int x = 0; x <= width; x = x + 50) {
g.drawImage(dirt, x, y, 50, 50, Color.BLACK, this);
coordX[cX][cY] = cX;
coordY[cX][cY] = cY;
cX = cX + 1;
System.out.println("X=" + coordX[cX][cY]);
}
cY = cY + 1;
cX = 0;
System.out.println("Y=" + coordY[cX][cY]);
}
}
The console outputs all the "X=" values as zero, and same for the "Y=", however, when I print the "cX" and "cY" values on their own, they are counting as intended. The problem only seems to arise when I try to store the "cX" and "cY" values in an array.
You're incrementing cX prior to printing
cX = cX + 1;
System.out.println("X=" + coordX[cX][cY]);
i.e. you're not printing the element you've just populated. You'll always get the default (uninitialised) int element value (0).
You're incrementing cX and cY before printing. Try this:
coordX[cX][cY] = cX;
coordY[cX][cY] = cY;
System.out.println("X=" + coordX[cX][cY]);
System.out.println("Y=" + coordY[cX][cY]);
cX = cX + 1;

Java heightmap using lwjgl

I have coded a heightmap but it seems to lag the client. I just don't know how to increase the fps. I get about 3-6fps with the heightmap. Im using a quite large bmp for the heightmap, I think its 1024x1024. When i use a smaller on its fine, maybe im just not using the code effectively. Is there a better way to code this heightmap or did I just code it wrong. It is my first time I have worked on a heightmap. Thanks
public class HeightMap {
private final float xScale, yScale, zScale;
private float[][] heightMap;
private FloatBuffer vertices, normals, texCoords;
private IntBuffer indices;
private Vector3f[] verticesArray, normalsArray;
private int[] indicesArray;
private int width;
private int height;
public float getHeight(int x, int y) {
return heightMap[x][y] * yScale;
}
public HeightMap(String path, int resolution) {
heightMap = loadHeightmap("heightmap.bmp");
xScale = 1000f / resolution;
yScale = 8;
zScale = 1000f / resolution;
verticesArray = new Vector3f[width * height];
vertices = BufferUtils.createFloatBuffer(3 * width * height);
texCoords = BufferUtils.createFloatBuffer(2 * width * height);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
final int pos = height * x + y;
final Vector3f vertex = new Vector3f(xScale * x, yScale * heightMap[x][y], zScale * y);
verticesArray[pos] = vertex;
vertex.store(vertices);
texCoords.put(x / (float) width);
texCoords.put(y / (float) height);
}
}
vertices.flip();
texCoords.flip();
normalsArray = new Vector3f[height * width];
normals = BufferUtils.createFloatBuffer(3 * width * height);
final float xzScale = xScale;
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
final int nextX = x < width - 1 ? x + 1 : x;
final int prevX = x > 0 ? x - 1 : x;
float sx = heightMap[nextX][y] - heightMap[prevX][y];
if (x == 0 || x == width - 1) {
sx *= 2;
}
final int nextY = y < height - 1 ? y + 1 : y;
final int prevY = y > 0 ? y - 1 : y;
float sy = heightMap[x][nextY] - heightMap[x][prevY];
if (y == 0 || y == height - 1) {
sy *= 2;
}
final Vector3f normal = new Vector3f(-sx * yScale, 2 * xzScale, sy * yScale).normalise(null);
normalsArray[height * x + y] = normal;
normal.store(normals);
}
}
normals.flip();
indicesArray = new int[6 * (height - 1) * (width - 1)];
indices = BufferUtils.createIntBuffer(6 * (width - 1) * (height - 1));
for (int i = 0; i < width - 1; i++) {
for (int j = 0; j < height - 1; j++) {
int pos = (height - 1) * i + j;
indices.put(height * i + j);
indices.put(height * (i + 1) + j);
indices.put(height * (i + 1) + (j + 1));
indicesArray[6 * pos] = height * i + j;
indicesArray[6 * pos + 1] = height * (i + 1) + j;
indicesArray[6 * pos + 2] = height * (i + 1) + (j + 1);
indices.put(height * i + j);
indices.put(height * i + (j + 1));
indices.put(height * (i + 1) + (j + 1));
indicesArray[6 * pos + 3] = height * i + j;
indicesArray[6 * pos + 4] = height * i + (j + 1);
indicesArray[6 * pos + 5] = height * (i + 1) + (j + 1);
}
}
indices.flip();
}
private float[][] loadHeightmap(String fileName) {
try {
BufferedImage img = ImageIO.read(ResourceLoader.getResourceAsStream(fileName));
width = img.getWidth();
height = img.getHeight();
float[][] heightMap = new float[width][height];
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
heightMap[x][y] = 0xFF & img.getRGB(x, y);
}
}
return heightMap;
} catch (IOException e) {
System.out.println("Nincs meg a heightmap!");
return null;
}
}
public void render() {
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glNormalPointer(0, normals);
glVertexPointer(3, 0, vertices);
glTexCoordPointer(2, 0, texCoords);
glDrawElements(GL_TRIANGLE_STRIP, indices);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}
}
Sorry to bring up an old topic, however i see a lot of people ask this:
Use a display list, instead of re-making the heightmap every time.
TheCodingUniverse has a good tutorial on how to do this.

Categories

Resources