Tile grid world creation trouble - java

I'm shooting an idea here and have come across a few problems. Firstly I have a class with a method for creating new worlds.
The 2D tiled world gets created like this:
for(int y = 0; y < WORLDSIZE; y++){
for(int x = 0; x < WORLDSIZE; x++){
tiles.add(new Tile(Sprite.blanktile, x*32, y*32));
}
}
This creates and predefines the entire worlds tile positions and gives them all a blank tile sprite. It's only run once at start. The class where this gets processed is where the problem starts.
Here the tiles are iterated through and skips all tiles that are not on the screen. The xoff and yoff are up, down, left, right offsets for moving the screen around. I'm a bit worried about editing the actual grid locations. I'm sure I will be able to account for that when needed.
This is the general idea.
while(true){
for(int i = 0; i < world.tiles.size(); i++){
world.tiles.get(i).x += xoff;
world.tiles.get(i).y += yoff;
if(world.tiles.get(i).y + yoff > GameWindow.HEIGHT || world.tiles.get(i).y + yoff < -64 || world.tiles.get(i).x + xoff > GameWindow.WIDTH || world.tiles.get(i).x + xoff < -64){
continue;
}else
render.setTilePixel(world.tiles.get(i).sprite, world.tiles.get(i).x, world.tiles.get(i).y);
}
window.draw(render.visualization());
}
It sets each tiles pixels every time it updates. Which is making it quite slow for something that should be less. The way it sets the pixels is like this:
public void setTilePixel(Sprite sprite, int x, int y){
int xa, ya;
for(int hrzt = 0; hrzt < sprite.SIZE; hrzt++){
for(int vtc = 0; vtc < sprite.SIZE; vtc++){
xa = (hrzt + x);
ya = (vtc + y);
if(xa < width && xa > -1){
if (xa + ya * width < width*height && xa + ya * width > 0){
if(sprite.pixels[hrzt + vtc * sprite.SIZE] != 0xff000000 && sprite.pixels[hrzt + vtc * sprite.SIZE] != 0){
visualizer.pixels[xa + ya * width] = sprite.pixels[hrzt + vtc * sprite.SIZE];
}
}
}
}
}
}
The last operations send the new image data to the window to be drawn to the screen.
window.draw(render.visualization());
The issue here is that it is taking a long time each cycle. I've tried a few other ways but its taking a long time, like a whole week. So, I've come here. The more tiles I'm processing causes it to get a lot slower. I'm looking for a new way. Possibly where it does everything in the same section of for loops cutting the reiteration down?
Hope I've got enough up. Let me know what else I should add. Thanks.

A couple of small suggestions:
Firstly, you are ultimately rendering these to a screen, I assume. Can you work out where the tiles are in relation to what will be displayed, and skip the ones that are not going to be rendered? This should cut down your calculation volume significantly.
Secondly, in your setTilePixel method, you loop through the full range of hrzt and vtc values, and then have some checks to see whether to do action or not. You could do something like this, change:
for(int hrzt = 0; hrzt < sprite.SIZE; hrzt++){
for(int vtc = 0; vtc < sprite.SIZE; vtc++){
xa = (hrzt + x);
ya = (vtc + y);
if(xa < width && xa > -1){
if (xa + ya * width < width*height && xa + ya * width > 0){
// ***
}
}
}
}
to:
int maxValidHrzt = width-x;
int minValidHrzt = -1;
int maxValidVtc = etc...
int minValidVtc = etc...
for(int hrzt = minValidHrzt; hrzt < maxValidHrzt; hrzt++){
for(int vtc = minValidVtc; vtc < maxValidVtc; vtc++){
// ***
}
}

Related

BufferedImage slows down performance

I'm working on a game, nothing serious, just for fun.
I wrote a class 'ImageBuilder' to help creating some images.
Everything works fine, except one thing.
I initialize a variabile like this:
// other stuff
m_tile = new ImageBuilder(TILE_SIZE, TILE_SIZE, BufferedImage.TYPE_INT_RGB).paint(0xff069dee).paintBorder(0xff4c4a4a, 1).build();
// other stuff
Then, in the rendering method, i have:
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
g.drawImage(m_tile, x * (TILE_SIZE + m_padding.x) + m_margin.x, y * (TILE_SIZE + m_padding.y) + m_margin.y, null);
}
}
Note: m_padding and m_margin are just two Vector2i
This draws on the screen a simple 16x16 table using that image, but the game is almost frozen, i can't get more than like 10 FPS.
I tried to creating the image without that class, by doing this (TILE_SIZE = 32):
m_tile = new BufferedImage(TILE_SIZE, TILE_SIZE, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < TILE_SIZE; x++) {
for (int y = 0; y < TILE_SIZE; y++) {
if (x == 0 || y == 0 || x + 1 == TILE_SIZE || y + 1 == TILE_SIZE)
m_tile.setRGB(x, y, 0x4c4a4a);
else
m_tile.setRGB(x, y, 0x069dee);
}
}
This time, i get 60 FPS.
I can't figure out with is the difference, i used to creating image using 'ImageBuilder' and all is fine, but not this time.
ImageBuilder class:
// Constructor
public ImageBuilder(int width, int height, int imageType) {
this.m_width = width;
this.m_height = height;
this.m_image = new BufferedImage(m_width, m_height, imageType);
this.m_pixels = ((DataBufferInt) m_image.getRaster().getDataBuffer()).getData();
this.m_image_type = imageType;
}
public ImageBuilder paint(int color) {
for (int i = 0; i < m_pixels.length; i++) m_pixels[i] = color;
return this;
}
public ImageBuilder paintBorder(int color, int stroke) {
for (int x = 0; x < m_width; x++) {
for (int y = 0; y < m_height; y++) {
if (x < stroke || y < stroke || x + stroke >= m_width || y + stroke >= m_height) {
m_pixels[x + y * m_width] = color;
}
}
}
return this;
}
public BufferedImage build() {
return m_image;
}
There are other methods, but i don't call them, so i don't think is necessary to write them
What am i doing wrong?
My guess is that the problem is your ImageBuilder accessing the backing data array of the data buffer:
this.m_pixels = ((DataBufferInt) m_image.getRaster().getDataBuffer()).getData();
Doing so, may (will) ruin the chances for this image being hardware accelerated. This is documented behaviour, from the getData() API doc:
Note that calling this method may cause this DataBuffer object to be incompatible with performance optimizations used by some implementations (such as caching an associated image in video memory).
You could probably get around this easily, by using a temporary image in your bilder, and returning a copy of the temp image from the build() method, that has not been "tampered" with.
For best performance, always using a compatible image (as in createCompatibleImage(), mentioned by #VGR in the comments) is a good idea too. This should ensure you have the fastest possible hardware blits.

Problem with Scan-Line Polygon Filling algorithm in java

(please don't mark this question as not clear, I spent a lot of time posting it ;) )
Okay, I am trying to make a simple 2d java game engine as a learning project, and part of it is rendering a filled polygon as a feature.
I am creating this algorithm my self, and I really can't figure out what I am doing wrong.
My though process is something like so:
Loop through every line, get the number of points in that line, then get the X location of every point in that line,
Then loop through the line again this time checking if the x in the loop is inside one of the lines in the points array, if so, draw it.
Disclaimer: the Polygon class is another type of mesh, and its draw method returns an int array with lines drawn through each vertex.
Disclaimer 2: I've tried other people's solutions but none really helped me and none really explained it properly (which is not the point in a learning project).
The draw methods are called one per frame.
FilledPolygon:
#Override
public int[] draw() {
int[] pixels = new Polygon(verts).draw();
int[] filled = new int[width * height];
for (int y = 0; y < height; y++) {
int count = 0;
for (int x = 0; x < width; x++) {
if (pixels[x + y * width] == 0xffffffff) {
count++;
}
}
int[] points = new int[count];
int current = 0;
for (int x = 0; x < width; x++) {
if (pixels[x + y * width] == 0xffffffff) {
points[current] = x;
current++;
}
}
if (count >= 2) {
int num = count;
if (count % 2 != 0)
num--;
for (int i = 0; i < num; i += 2) {
for (int x = points[i]; x < points[i+1]; x++) {
filled[x + y * width] = 0xffffffff;
}
}
}
}
return filled;
}
The Polygon class simply uses Bresenham's line algorithm and has nothing to do with the problem.
The game class:
#Override
public void load() {
obj = new EngineObject();
obj.addComponent(new MeshRenderer(new FilledPolygon(new int[][] {
{0,0},
{60, 0},
{0, 60},
{80, 50}
})));
((MeshRenderer)(obj.getComponent(MeshRenderer.class))).color = CYAN;
obj.transform.position.Y = 100;
}
The expected result is to get this shape filled up.(it was created using the polygon mesh):
The actual result of using the FilledPolygon mesh:
You code seems to have several problems and I will not focus on that.
Your approach based on drawing the outline then filling the "inside" runs cannot work in the general case because the outlines join at the vertices and intersections, and the alternation outside-edge-inside-edge-outside is broken, in an unrecoverable way (you can't know which segment to fill by just looking at a row).
You'd better use a standard polygon filling algorithm. You will find many descriptions on the Web.
For a simple but somewhat inefficient solution, work as follows:
process all lines between the minimum and maximum ordinates; let Y be the current ordinate;
loop on the edges;
assign every vertex a positive or negative sign if y ≥ Y or y < Y (mind the asymmetry !);
whenever the endpoints of an edge have a different sign, compute the intersection between the edge and the line;
you will get an even number of intersections; sort them horizontally;
draw between every other point.
You can get a more efficient solution by keeping a trace of which edges cross the current line, in a so-called "active list". Check the algorithms known as "scanline fill".
Note that you imply that pixels[] has the same width*height size as filled[]. Based on the mangled output, I would say that they are just not the same.
Otherwise if you just want to fill a scanline (assuming everything is convex), that code is overcomplicated, simply look for the endpoints and loop between them:
public int[] draw() {
int[] pixels = new Polygon(verts).draw();
int[] filled = new int[width * height];
for (int y = 0; y < height; y++) {
int left = -1;
for (int x = 0; x < width; x++) {
if (pixels[x + y * width] == 0xffffffff) {
left = x;
break;
}
}
if (left >= 0) {
int right = left;
for (int x = width - 1; x > left; x--) {
if (pixels[x + y * width] == 0xffffffff) {
right = x;
break;
}
}
for (int x = left; x <= right; x++) {
filled[x + y * width] = 0xffffffff;
}
}
}
return filled;
}
However this kind of approach relies on having the entire polygon in the view, which may not always be the case in real life.

I am trying to add collision detection to this particle system I made

I am doing this in processing which is essentially java and I have never attempted anything like this before. Can't find any examples of collision detection using arrays to map the pixels.
I am not really trying to make them realistic collisions. I was thinking it would have the same response as if it hit a wall which is just for it to change directions in whatever axis is appropriate for the wall it hit.
I have tried checking if the x and y position are the same but can't seem to make that work. I'd appreciate any input on this.
import java.util.Arrays;
int numOfParticles = 10;
float[] x = new float[numOfParticles]; //initial position of y only matters
float[] px = new float[numOfParticles];
float[] y = new float[numOfParticles];
float[] py = new float[numOfParticles];
int speed = 10;//inversly related to speed
float[] xIncrement = new float[numOfParticles]; //the ratio of increments determines the pattern
float[] yIncrement = new float[numOfParticles]; // it is the slope of the line
//float xIncrement = 10/speed; //the ratio of increments determines the pattern
//float yIncrement = 11/speed; // it is the slope of the line
color currentColor;
int alpha = 100;//range of 0-255
//radius of ball
int radius = 1;
//thickness of line behind ball
int thickness = 5;
int rateOfColor = 5; //this is inversely related to rate but also changes the range of colors
int maxColor = 255;
int minColor = 0;
void setup(){
size(500,500);
background(0);
colorMode(HSB);
strokeWeight(thickness);
frameRate(60);
//initialize particles
for(int i = 0;i<numOfParticles;i++){
xIncrement[i] = random(0,100)/speed; //the ratio of increments determines the pattern
yIncrement[i] = random(0,100)/speed; // it is the slope of the line
x[i] = random(0,width);
px[i] = x[i];
y[i] = random(0,height);
py[i] = y[i];
}
//you can either initialize all of them individually or do a random one
//x[0] = 0;
//px[0] = x[0];
//y[0] = 450;
//py[0] = y[0];
//x[1] = width;
//px[1] = x[1];
//y[1] = 450;
//py[1] = y[1];
}
void draw(){
background(0); //comment out for criss cross
for(int i = 0; i < numOfParticles; i++){
particle(i);
}
}
void particle(int particleNum){
currentColor = color(minColor + (x[particleNum]/rateOfColor)%maxColor,255,255,alpha);
stroke(currentColor);
fill(currentColor);
ellipse(x[particleNum],y[particleNum],radius,radius);
line(px[particleNum],py[particleNum],x[particleNum],y[particleNum]);
px[particleNum] = x[particleNum];
py[particleNum] = y[particleNum];
y[particleNum]+= yIncrement[particleNum];
x[particleNum]+= xIncrement[particleNum];
if(x[particleNum] > width + 1 || x[particleNum] < 0){
x[particleNum] -= 2*xIncrement[particleNum];
xIncrement[particleNum]*=-1;
}
if( y[particleNum] > height + 1 || y[particleNum] < 0){
y[particleNum] -= 2*yIncrement[particleNum];
yIncrement[particleNum]*=-1;
}
//if(Arrays.binarySearch(x,x[particleNum]) >= 0 && Arrays.binarySearch(y,y[particleNum]) >= 0){
// xIncrement[particleNum]*=-1;
// yIncrement[particleNum]*=-1;
// print("*\n");
// stop();
//}
print("x[0] = " + x[0] + "\n");
print("x[1] = " + x[1] + "\n");
print("y[0] = " + y[0] + "\n");
print("y[1] = " + y[1] + "\n");
}
Stack Overflow isn't really designed for general "how do I do this" type questions. It's for specific "I tried X, expected Y, but got Z instead" type questions. But I'll try to help in a general sense:
You need to break your problem down into smaller pieces and then take those pieces on one at a time. Don't worry about the whole particle system. Make it work for a single particle. Do some research on collision detection.
Then if you get stuck, you can post a more specific question along with a MCVE. Good luck.

Render gray-scale pixels based on an array of values between 0 and 1

I am trying to render pixels from an array.
I have an array of data that looks like this (except much larger). From this array I would like to somehow render it so that each number in the array corresponds to a pixel with a shade of gray based on the number value. (0.0 would be white, and 1.0 would be black)
I don't know where to start.
For the array you have given; If you know the width and height of the image you want rendered you can do this:
int indx = 0;
for(int x = 0; x < width; x++) {
for(int y = 0; y < height; y++) {
glColor3d(data[indx],data[indx],data[indx]);
//drawing of it goes here; assuming glVertex2d(x,y);
indx++;
}
}
For this to work it should be known that width*height < data.length. Increment index for each pixel drawn to go to the next number in the array and draw it accordingly.
Modify the x and y so it draws where you want. Say if locX = locY = 10 then depending on the viewport you should have already set up, then the image will start rendering 10px away from (probably) either the top left or bottom left corner. This part is simple maths if you have already started to learn how to draw in OpenGl and/or LWJGL.
int locX, locY;
int indx = 0;
for(int x = 0; x < width; x++) {
for(int y = 0; y < height; y++) {
glColor3d(data[indx],data[indx],data[indx]);
glVertex2d(locX + x, locY + y);
indx++;
}
}
Hope this helps.

CS106a Checkerboard optimization

I'm learning Java out of The Art & Science of Java by Roberts (Stanford's CS106a text). I'm using NetBeans as my IDE.
Chapter 4, exercise 14 asks you to expand on a Checkerboard program introduced earlier. Specifically, it asks you to center the checkerboard and draw a set of red & white checks corresponding to the initial state of the game.
I've accomplished as much as requested, but have two issues-
The board is not completely centered in the window. It is closer to the left side of the window than the right side. I am not sure how to center it more. Am I doing this right? Is there a setting in NetBeans I can/should change?
The checkers are supposed to take up a large portion of the tiles they sit on. I assigned the size of my checkers to be dependent on the size of tiles so that the setup would be simple and proportionate. Is there a better way to do this to make the checkers bigger?
import acm.graphics.*;
import acm.program.*;
import java.awt.*;
public class Checkerboard extends GraphicsProgram{
public void run(){
double sqSize = (double)getHeight() / N_ROWS;
for (int i = 0; i < N_ROWS; i++){
for(int j = 0; j < N_COLUMNS; j++){
double x = ((j * sqSize) + (getWidth() / N_COLUMNS)); //centers square??
double y = (i * sqSize);
GRect sq = new GRect( x, y, sqSize, sqSize );
sq.setFilled((i + j) % 2 != 0);
sq.setFillColor(Color.GRAY);
add(sq);
double circleCoord = (sqSize * .33);
double xx = ((j * sqSize) + (getWidth() / N_COLUMNS) + circleCoord);
double yy = ((i * sqSize) + circleCoord);
if((i + j) % 2 != 0 && i < 3 ){
GOval red = new GOval( xx, yy, circleCoord, circleCoord);
red.setFilled(true);
red.setFillColor(Color.RED);
add(red);
} else if((i + j) % 2 != 0 && i > 4 ){
GOval black = new GOval( xx, yy, circleCoord, circleCoord);
black.setFilled(true);
black.setFillColor(Color.BLACK);
add(black);
}
}
}
}
private static final int N_ROWS = 8;
private static final int N_COLUMNS = 8;
}
For 1. The center of the board should be in the center of the width, too. So we know that
The left edge of tile N_COLUMNS/2 = getWidth()/2 e.g. tile 4 in 0 indexing has its left edge in the center
And every tile left or right of that will have a movement of sqSize, so:
double x = getWidth()/2 + (j-N_COLUMNS/2)*sqSize

Categories

Resources