Good evening everyone.
I've been messing a bit with isometric tile worlds and I have a few doubts about rendering the elements on it.
When I build a single height map I render it first and then add the diferent elements on top, and the displacement of the last seem right.
public void render(Graphics2D g2d) {
for(int i = 0; i < tileGrid.length; i++) {
Point isop = NeoMath.getInstance().cartToIso(i % yAmmount, i / yAmmount, GlobalDataStorage.tileWidth, GlobalDataStorage.tileHeight);
TileManager.getInstance().getTileByID(tileGrid[i]).render(g2d, isop.x, isop.y);
}
for(Entity entity : entityList) {
entity.render(g2d);
}
}
(The position of the entity is calculated inside it's update).
With this I have no problems as everything is rendered on the same height, the problem comes when I try to add other floors to it.
Let's say that I want it to have three heights. I have a list of list of tiles instead of the single array, and render every element on them:
public void render(Graphics2D g2d) {
int flag = 0;
for(int i = 0; i < tileGrid.size(); i++) {
Point isop = NeoMath.getInstance().cartToIso(i % yAmmount, i / yAmmount, GlobalDataStorage.tileWidth, GlobalDataStorage.tileHeight);
for(int k = 0; k < tileGrid.get(i).size(); k++) {
TileManager.getInstance().getTileByID(tileGrid.get(i).get(k)).render(g2d, isop.x, isop.y - (GlobalDataStorage.tileZ * k));
}
while(flag < currentList.size() &&
currentList.get(flag).getPosition().equals(new Point(i % yAmmount, i /
yAmmount))) {
currentList.get(flag).render(g2d);
flag++;
}
}
}
Where the currentList is the list of entities.
With this I have the problem that, when the entities move to a new position, they get overlaped by the tiles, as these are rendered after the entity, and the position of the entity does not change until it reached the destiny. I could change the position to the new one before rendering, but that implies that in the other axis the previous tile get rendered after the entity, making it disapear for a second due to the overlap.
This also mess when I try to draw selection rectangle as it get stuck behind the tiles being rendered. I don't want them to overlap the whole map so can't draw them after all the rendering has been done either.
Does someone know of another approach that I can try out?
Thank you beforehand.
Draw your entire floor layer in a first pass. Then in the second pass draw all walls and objects and moving entities.
I could change the position to the new one before rendering,
David Brevik, programmer on Diablo, mentions using this option in his GDC talk Diablo: A Classic Games Postmortem. It was his first "What Went Wrong" example!
Reference: https://www.youtube.com/watch?v=VscdPA6sUkc&t=20m17s
Turns out this is a classic hurdle in isometric games.
Related
I'm currently trying to create a program using processing that draws lines from all mouseclicks to new mouseclicks.
As a side it also should save the clicks in a two-dimensional array.
public void setup() {
size(500,500);
}
int clickcount = 0;
int storedclicks = 10000;
int[] mouseclickX = new int [storedclicks];
int[] mouseclickY = new int [storedclicks];
public void draw() {
background(80);
drawing();
}
public void mousePressed() {
if(clickcount >= storedclicks) {
clickcount = 0;
}
mouseclickX[clickcount] = mouseX;
mouseclickY[clickcount] = mouseY;
clickcount++;
}
public void drawing() {
beginShape(LINES);
for (int i = 0; i < storedclicks; i++) {
vertex(mouseclickX[i], mouseclickY[i]);
}
endShape();
}
}
Something works with the code I have now, but something doesn't add up for me. As it is now, on first click I get a line from the upper left corner, next click that line disappears and I get a new line from the ending point of that line and the first line disappears.
Next click a new line from the corner to clicking point comes (line nr. 2 still present). And then it just continues.
I figured if I changed the storeclicks to a number like 5, it doesn't do the from corner thing, just a new line from every previous click position.
It sounds a little confusing, so here's a picture to help (after 3 clicks):
A few notes:
I'd use a lifo ring buffer (you could simulate it with a linked list) or similar to store the clicks, that way you don't have to check storedclicks separately and it should make drawing easier since the head of the buffer moves when you remove elements from the front.
Additionally I'd only draw lines if there are at least two points in the list/buffer.
Third, to prevent synchronization issues (updating only x or y) I'd use a list/buffer/array of Point objects (make your own or use java.awt.Point) rather than two separate x and y arrays.
As for your drawing code you should loop over the stored points rather than all elements (most of which might be empty), i.e. like this:
When using your code:
if( clickcount > 1 ) {
for (int i = 0; i < clickcount ; i++) {
vertex(mouseclickX[i], mouseclickY[i]);
}
}
When using a list/ring buffer as well as Point objects:
if( list.size() > 1 ) {
for (Point clickpos : list ) {
vertex(clickpos.x, clickpos.y);
}
}
Finally, if processing is similar to OpenGL (I don't know processing) the shape type LINES would draw a line between two vertices, i.e. every uneven vertex is a line start and every even vertex is a line end. What you probably want is something like LINE_STRIP (don't know the name or whether it exists in processing) which makes the renderer draw lines between all vertices (i.e. from 0 to 1, from 1 to 2 etc.)
Edit:
As an explanation for the situation in the image you posted, I assume the clicks are ordered from right to left (indices 0, 1, 2). If that's the case then I'd explain it like this (see above for more info):
The first line is drawn between points 0 and 1, which is ok.
The second line is drawn between points 2 and 3 (see paragraph about the loop as well as LINES resp. line strips), which is not what you want.
The third line (if storedclicks > 5) will be drawn between points 4 and 5, which is a point since both vertices have the coordinates 0/0.
The above bulletpoint is repeated for all following lines (again see the paragraph about the loop).
As you can see there are two problems:
You'd want to draw lines between 0 and 1 and between 1 and 2 which is why I suggested a line strip (if that exists in processing, otherwise you'd have to reuse the last point).
You'd want to stop at index 2, i.e. ignore all indices starting from 3, which is why I mentioned not using storedclicks in the loop condition).
Just to add to the existing great answers, here's a hacky approach that simply takes advantage of not clearing the background:
int lastClickX = -1;
int lastClickY = -1;
void setup(){
size(500,500);
background(80);
}
void draw(){}
void mouseReleased(){
if(lastClickX >= 0 && lastClickY >= 0) line(mouseX,mouseY,lastClickX,lastClickY);
lastClickX = mouseX;
lastClickY = mouseY;
}
You can run this as demo bellow:
var lastClickX = -1;
var lastClickY = -1;
function setup(){
createCanvas(500,500);
background(80);
}
function draw(){}
function mouseReleased(){
if(lastClickX >= 0 && lastClickY >= 0) line(mouseX,mouseY,lastClickX,lastClickY);
lastClickX = mouseX;
lastClickY = mouseY;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.17/p5.min.js"></script>
Bare in mind, although this method doesn't store position, it simply takes advantage of not clearing the screen, which isn't the most flexible of options.
To understand what's happening in your sketch, look to the reference.
This code:
beginShape(LINES);
vertex(30, 20);
vertex(85, 20);
vertex(85, 75);
vertex(30, 75);
endShape();
Generates this image:
(source: processing.org)
So, that's why you're getting gaps where your line segments aren't drawn. Instead of using LINES, you probably want to use the no-args beginShape() function along with the endShape(CLOSE) function:
(source: processing.org)
noFill();
beginShape();
vertex(30, 20);
vertex(85, 20);
vertex(85, 75);
vertex(30, 75);
endShape(CLOSE);
As for why you're getting a point at 0,0: keep in mind that int arrays are initialized to contain all zeroes by default. So when you loop through your entire array, you end up drawing a bunch of points at 0,0. Instead, you probably want a separate variable that keeps track of the total number of clicks (which you stop updating when you reach the maximum number of clicks you want to hold).
As a side it also should save the clicks in a two-dimensional array.
Note that you aren't storing the clicks in a two-dimensional array. You're storing them in two one-dimensional arrays, also known as parallel arrays. This is generally a bad design. Instead, consider using Processing's PVector class along with an ArrayList instead of an array. That way you can keep track of how many valid clicks you have without needing the separate variable I mentioned above.
Putting it all together, it might look something like this:
ArrayList<PVector> clicks = new ArrayList<PVector>();
public void setup() {
size(500, 500);
}
public void draw() {
background(80);
drawing();
}
public void mousePressed() {
clicks.add(new PVector(mouseX, mouseY));
if(clicks.size() > 5){
clicks.remove(0);
}
}
public void drawing() {
noFill();
beginShape();
for(PVector v : clicks){
vertex(v.x, v.y);
}
endShape(CLOSE);
}
draws lines from all mouseclicks to new mouseclicks.
If instead of a closed polygon, you want something like a spider web that connects every point to every other point, then you just have to loop through each click and draw the connecting lines:
public void drawing() {
for (PVector v1 : clicks) {
for (PVector v2 : clicks) {
line(v1.x, v1.y, v2.x, v2.y);
}
}
}
I struggle with problem relayed on code (rendering) optimization.
I have 650 hexagons on screen and I need to move them all together and zoom them freely.
As far I have 2d map that stores hexagon objects that contains some parameters as vertexes, center point etc.
my onDraw method looks like this:
#Override
public void onDraw(final Canvas canvas) {
if(hexMap == null || hexMap.length <= 0) {
populateHexMap();
}
if(radiusChanged) {
changeRadius();
}
for(int i = 0; i < hexMap.length; i++) {
for(int j = 0; j < hexMap[i].length; j++) {
if(hexMap[i][j] != null && hexMap[i][j].isToDraw()) {
paint.setColor(hexMap[i][j].getColor());
drawPath.rewind();
drawPath.moveTo(hexMap[i][j].getPoint(0).x, hexMap[i][j].getPoint(0).y);
drawPath.lineTo(hexMap[i][j].getPoint(1).x, hexMap[i][j].getPoint(1).y);
drawPath.lineTo(hexMap[i][j].getPoint(2).x, hexMap[i][j].getPoint(2).y);
drawPath.lineTo(hexMap[i][j].getPoint(3).x, hexMap[i][j].getPoint(3).y);
drawPath.lineTo(hexMap[i][j].getPoint(4).x, hexMap[i][j].getPoint(4).y);
drawPath.lineTo(hexMap[i][j].getPoint(5).x, hexMap[i][j].getPoint(5).y);
drawPath.lineTo(hexMap[i][j].getPoint(0).x, hexMap[i][j].getPoint(0).y);
canvas.drawPath(drawPath, paint);
}
}
}
}
Drawing works great as far. To zoom I catch pinch event gesture and change radius of hexagon then recalculate its vertexes. To move I simply add shift value to center points and recalculate vertexes.
Everything has worked great when I had for example ~30 hexagons. With 650 everything is super slow.
I've tried to enable hardware acceleration but it didn't help. I've tried to reduce amount of invalidate() calls but in my opinion it's not the problem. The problem lays on drawPath.
Is there any way to speed things up? I don't have much time to switch on opengl right now or maybe you know some opensource solutions for drawing hexagons with opengl?
Thanks in advance!
I would like to know if anybody can help me with a better method of drawing a Tile map for my android game.
Currently, I use a for loop to draw the required Bitmaps to the screen but when doing this for the amount of tiles I need to render at once (enough to cover the screen and a bit more), when the map scrolls, you can notice the map become jolty with its movement, because of the for loop having to loop through all the tiles. The tiles are drawn simular to this:
for(int i = 0; i < 170; i++) {
canvas.drawBitmap(bitmap, x + i * bitmap.getWidth(), y, null);
}
The method I am currently using uses a few bitmaps to save memory, and draws them in different locations.
A different method i can think of to draw the map would be to create a larger Bitmap from the tiles and move the position of that larger bitmap to create movement. The problem with this is that is does require lots of memory and gets out of memory errors.
So, i need to try and find out a method of drawing multiple tiles preferably without the use of a for-loop (I believe the for-loop is causing the jolty, un-smooth movement of the map).
For more details just ask, thanks.
The for loop is the correct way to draw it, your problem is probably the amount of data you are trying to draw in one frame and/or the number of pixels you are trying to push through the system.
If you want good performance on Android your best bet will be to use the graphics hardware it provides. There are various game frameworks available that will make that easier for you and give you much better performance than you will get otherwise.
If not then you will need to break up the drawing and still run the same effective logic but spread out so you draw a few tiles per frame.
is it really required to draw ALL tiles at once?
if it is possible for you, maybe you can determine the 'visible view port' and then just draw those tiles, which need to be drawn?!?
int width = getWidth()/Tiles.width; // do it once before any rendering
// thats the amount of tiles per line
int height = getHeight()/Tiles.height; // do it once before any rendering
// thats the amount of tiles per row
Point pos = ...; // you should know the position of your 'figure' within the map
// could as well be the scroll position
//now draw only those tile from the 'viewport':
for (int dy = 0; dy < width; dy++){
for (int dx = 0; dy < height; dy++){
int xOnMap = pos.x + x;
int yOnMap = pos.y + dy;
int index = yOnMap*height+yOnMap; //index in list
canvas.drawBitmap(bitmap,
x * bitmap.getWidth(), y * bitMap.getHeight(), null);
}
}
so you just have to draw some tile and that would always be the same amount...
i must confess i wrote that code onstackoverflow so there is a chance i did a syntax terror ^^
I have written a particle system applet; currently I am creating, and drawing each particle separately.
(Here is the code)
BufferedImage backbuffer;
Graphics2D g2d;
public void init(){
backbuffer = new BufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_RGB);
g2d = backbuffer.createGraphics();
setSize(WIDTH, HEIGHT);
//creates the particles
for (int i = 0; i < AMOUNTPARTICLES; i++) {
prtl[i] = new particleO();
prtl[i].setX(rand.nextInt(STARTX));
prtl[i].setY(rand.nextInt(STARTY));
prtl[i].setVel(rand.nextInt(MAXSPEED)+1);
prtl[i].setFAngle(Math.toRadians(rand.nextInt(ANGLESPREAD)));
}
//other code
}
public void update(Graphics g) {
g2d.setTransform(identity);
//set background
g2d.setPaint(BGCOLOUR);
g2d.fillRect(0,0,getSize().width,getSize().height);
drawp();
paint(g);
}
public void drawp() {
for (int n = 0; n < AMOUNTPARTICLES; n++) {
if (prtl[n].getAlive()==true){
g2d.setTransform(identity);
g2d.translate(prtl[n].getX(), prtl[n].getY());
g2d.setColor(prtl[n].getColor());
g2d.fill(prtl[n].getShape());
}
}
}
It's performance was alright, I could get ~40FPS with 20,000 particles (although, I have a decent laptop). But after I added collision detection (see below), that number plummeted to less than 2000,
public void particleUpdate(){
for (int i = 0; i < AMOUNTPARTICLES; i++) {
//other update code (posx, posy, angle etc etc)
for (int j = 0; j < AMOUNTPARTICLES; j++) {
if (i!=j && prtl[j].getAlive()==true){
if(hasCollided(i, j)){
prtl[i].setcolor(Color.BLACK);
prtl[j].setcolor(Color.BLACK);
}
}
}
public boolean hasCollided(int prt1, int prt2){
double dx = prtl[prt1].getX() - prtl[prt2].getX();
double dy = prtl[prt1].getY() - prtl[prt2].getY();
int edges = prtl[prt1].getRadius() + prtl[prt2].getRadius();
double distance = Math.sqrt( (dx*dx) + (dy*dy) );
return (distance <= edges);
}
I have searched quite a bit for a better way of drawing the particles to the screen, but the examples either confused me, or were not applicable.
I am doing a boat load of calculations (too many). But I couldn’t think of another way of doing it, suggestions are welcome.
First of all, adding in something like collision detection always takes a lot of memory. However, let's look at your collision detection algorithm
public void particleUpdate(){
for (int i = 0; i < AMOUNTPARTICLES; i++) {
//other update code (posx, posy, angle etc etc)
for (int j = 0; j < AMOUNTPARTICLES; j++) {
if (i!=j && prtl[j].getAlive()==true){
if(hasCollided(i, j)){
prtl[i].setcolor(Color.BLACK);
prtl[j].setcolor(Color.BLACK);
}
}
}
Let's pretend there was only 2 particles, 1 and 2. You will check, in order
1,1
1,2
2,1
2,2
The truth is, you only really needed to check 1 vs 2 in this case. If 1 hits 2, 2 will also hit 1. So change your for loop skip previously tested, and the same number for that matter.
public void particleUpdate(){
for (int i = 0; i < AMOUNTPARTICLES; i++) {
//other update code (posx, posy, angle etc etc)
for (int j = i+1; j < AMOUNTPARTICLES; j++) {
Another thing I notice is that you do a sqrt operation, but only to compare to what looks like a static number. If you remove that, and compare it to the number squared, you'll get a large improvement, especially with something you do so much.
double distance_squared = ( (dx*dx) + (dy*dy) );
return (distance <= edges*edges);
Keep looking for improvements like this. Then you might carefully look at other options, like using a different class, threading, etc, which all might improve the system. But make sure you optimize the code where you can first. Here is a list of other things that I would try, roughly in order.
Check to see if particle i is alive before you even calculate anything else after i comes into view.
Do a quick pass over the pairs, only even bothering to check in detail if they are close. A simple way would be to detect if they are within the x and y dimensions first, before taking a sqrt operation. Always do the cheapest test first, before doing complex ones.
Look at your code, to see if you really use all of the values calculated, or if you might be able to reduce the number of operations somehow.
Perhaps you could cluster the images on a regular basis with a broad stroke, then refine only objects which passed the initial cluster for a period of time, then do a broad cluster algorithm.
You could thread the collision detection. However, if you are going to do this, you should only thread the checking to see if something has collided, and after all of those threads are done, update the objects on the view.
Look into alternative architectures, which might speed things up some.
Painting is a complex process and paint requests can be triggered for any number of reasons, the OS might want the window to update, the repaint manager might want to repaint, the programer might want to repaint.
Updating the particles within the paint process is bad idea. What you want to do is update the particles in a separate thread on a separate buffer. When you're ready, request that the component responsible for painting the buffer perform a repaint, passing a new copy of the buffer to repainted (you don't want to be painting on the buffer that is begin updated to the screen, you'll end up with dirty paints).
It's hard to tell from you code, but it would appear you're using java.awt.Applet, personally, I'd upgrade to javax.swing.JApplet.
I'd move the painting to a java.swing.JPanel. Swing components provide double buffering (as well as other buffering strategies). The only job this panel has is to paint a buffer to the screen when the particles engine has a new frame.
The particles engine is responsible for updating all the particles and painting these results to a backing buffer (BufferedImage), this would then be handed to the panel and the panel would make a copy and schedule an update.
Swing is NOT THREAD SAFE. That is, you shouldn't make changes to the UI from any thread other then the Event Dispatching Thread. To this end, you might like to have a read through Concurrency in Swing for solutions to resync the off screen buffer to the client.
You are checking all particless colliding with all particlesand this is quite a requeriment, of the order of n^2 (2,000 particles means 4,000,000 combinations, for each frame).
The issue is not java but the algorithm. There must be better options, but to begin with you could reduce the comparations; if you have a maximum speed S and the time in your world increments by T, with T*S you get the maximum distance D of a particle that can collide with the particle you are considering. Reduce the search to those particle which are at a distance equal or less than that. Maybe it will be easier to restrict the search to those in an square centered in your particle an with height/widht D (this will include some particles that are too far, but will make the checks easier).
Additionally, in your code you are checking collisions of P1 vs P2 and P2 vs P1 which is the same, that is a redundance that you can avoid easily.
I am using slick for java since a few days and got a serious problem.
If i run a completely empty apllication (it just shows the fps) with a solution of 800x600 i get a fps count between 700 and 800.
If I now draw an array with 13300 entries as a grid of green and white rectangles, the fps drop to something around 70.
With more entries in the array it becomes really slow.
For example in a solution of 1024x768 and an array with 21760 entries the fps drop to 40.
How i draw a single entry:
public void draw(Graphics graphics){
graphics.setColor(new Color(getColor().getRed(), getColor().getGreen(), getColor().getBlue(), getColor().getAlpha()));
graphics.fillRect(getPosition().x, getPosition().y, getSize().x, getSize().y);
Color_ARGB white = new Color_ARGB(Color_ARGB.ColorNames.WHITE);
graphics.setColor(new Color(white.getRed(), white.getGreen(), white.getBlue(), white.getAlpha()));
}
And this is how I draw the complete array:
public void draw(Graphics graphics) {
for (int ix = 0; ix < getWidth(); ix++) {
for (int iy = 0; iy < getHeight(); iy++) {
getGameGridAt(ix, iy).draw(graphics);
}
}
}
In my opinion 21760 is not that much.
Is there anything wrong with my code or is slick just too slow to draw so much rectangles?
You only want to draw rectangles that are on the screen. If your screen bounds go from 0 to 1024 in the x direction and from 0 to 768 in the y direction, then you only want to loop through rectangles that are inside those bounds and then only draw those rectangles. I can't imagine you are trying to draw 21760 rectangles inside those bounds.
If you are, then try creating one static rectangle and then just try drawing that ONE in all of the different positions you need to draw it at rather than creating a new one every time. For example, in a game I am making, I might have 1000 tiles that are "grass" tiles, but all 1000 of those share the same static texture. So I only need to reference one image rather than each tile creating its own.
Each rectangle can still have a unique state. Just make your own rectangle class and have a static final Image that holds a 5*5 image. Each rectangle will use this image when it needs to be drawn. You can still have unique properties for each rectangle. For example, private Vector2f position, private boolean isAlive, etc
You're probably not going to be able to draw individual rectangles any faster than that.
Games that render millions of polygons per second do so using vertex buffer objects (VBO). For that, you'll probably need to code against the OpenGL API (LWJGL) itself, not a wrapper.
Not sure if Slick will allow it, but if this thing looks anything like a chessboard grid... you could draw just 4 rectangles, grab them and use the resulting image as a texture for your whole image. I'm not even a java programmer just trying to come up with a solution.
Since you're only repeatedly using just a few colors creating a new Color object for every single one is bound to be slow... use new only once for each different color used and store the re-usable colors somewhere in your class, than call the functions with those, constantly allocating and freeing memory is very slow.
And while this might not be as much a benefit as not using new each time but have you considered caching the results of all those function calls and rewriting code as
public void draw(Graphics graphics) {
int ixmax = getWidth();
int iymax = getHeight();
for (int ix = 0; ix < ixmax; ix++) {
for (int iy = 0; iy < iymax; iy++) {
getGameGridAt(ix, iy).draw(graphics);
}
}
}
Or if you'd prefer not to declare new variables
public void draw(Graphics graphics) {
for (int ix = getWidth() - 1; ix >= 0; ix--) {
for (int iy = getHeight() - 1; iy >= 0; iy--) {
getGameGridAt(ix, iy).draw(graphics);
}
}
}
Just noticed in another answer you have an integral size grid (5x5) ... in this case the fastest way to go about this would seem to be to draw each item a single pixel (you can do this directly in memory using a 2-dimensional array) and scale it to 500% or use it as a texture and draw a single rectangle with it the final size you desire ... should be quite fast. Sorry for all the confusion caused by previous answers, you should have said what you're doing more clearly from the start.
If scaling and textures are not available you can still draw in memory using something like this (written in c++, please translate it to java yourself)
for( int x = 0; x < grid.width(); x++ ) {
for( int y = 0; y < grid.height(); y++ ) {
image[x*5][y*5] = grid.color[x][y];
image[x*5][y*5 + 1] = grid.color[x][y];
image[x*5][y*5 + 2] = grid.color[x][y];
image[x*5][y*5 + 3] = grid.color[x][y];
image[x*5][y*5 + 4] = grid.color[x][y];
}
memcpy(image[x*5+1], image[x*5], grid.height() * sizeof(image[0][0]) );
memcpy(image[x*5+2], image[x*5], grid.height() * sizeof(image[0][0]) );
memcpy(image[x*5+3], image[x*5], grid.height() * sizeof(image[0][0]) );
memcpy(image[x*5+4], image[x*5], grid.height() * sizeof(image[0][0]) );
}
I'm not sure, but perhaps for graphics the x and y might be represented in the reversed order than used here, so change the code accordingly if it that's the case (you'll figure that out as soon as a few iterations run), also your data is probably structured a bit differently but I think the idea should be clear.