RPG Tile Based Map Programming
Does anybody have any experience in this area? I have an online rpg type game that I'm working on and its going fine, the only problem is that the map scrolls too slow so your character moves too slowly. For my map I simply extended a JPanel. For every tile the character moves(one tile = 32 pixels) the screen is redrawn 8 times (each redraw basically moves the whole screen 4 pixels in the desired direction) in this way the screen moves very smoothly and character animation is smooth, but it is also pretty slow. Can anybody offer any suggestions? I would love to talk with people who have done this type of thing before becuase it is my first stab at it. thanks.
[675 byte] By [
kevinp34] at [2007-9-27 18:44:10]

To make it go faster you need to either have it move in less than 8 frames, or to decrease your delay time.
JTeen at 2007-7-6 19:57:07 >

the reason your drawing is slow, is because you are redrawing every cell, every frame. This is unnecessary, as you can reuse the cells you drew in the previous frame, by simply shifting them by the appropriate amount.
let me clarify:
I will use some real numbers to demonstrate the optimisation.
screenWidth=800, screenHeight=640, cellWidth=32, cellHeight=32;
scrollSpeed=8; // << the number of frames it takes to move by 1 complete cell
just to render the background, your current approach calls drawImage this many times every frame
(screenWidth/cellWidth+1)*(screenHeight/cellHeight+1)
e.g. (800/32 +1)*(640/32+1) = 546 cell redraws/frame
There are several approaches you can use to fix this, all require the use of a cellBuffer to cache the previously drawn cells.
1) draw the cellBuffer onto itself, and redraw all the cells that have become corrupt.
Approach will require (screenWidth/cellWidth+1)+(screenHeight/cellHeight+1) cell redraws
e.g. 800/32+1 +640/32+1 = 47 cell redraws/frame
2) have an oversized cellBuffer (with dimensions of screenWidth+cellWidth*2 by screenHeight+cellHeight*2).
With this method, redraw of exposed cells is only necessary every time you cross a cell boundary, not every frame rendered.
Assuming you are moving at a constant 4 pixels/frame as you describe in your question, that will mean
e.g. 800/32+1 +640/32+1 = 47 cell redraws every 8th frame (an average of 5.9 celldraws/frame)
This method has the lowest number of celldraws per frame. However, because you draw all newly exposed cells, in 1 go, you end up with choppy scrolling.
3) This method attempts to solve the problem of choppy scrolling, by using predictive edge cell drawing, and distributing the cell rendering over multiple frames. (giving a more consistant frame rate)
using this method, requires you to have 4 additional buffers (1 for each edge of the screen)
into these buffers, the predicted cells will be cached.
This algorithm has 2 important factors deciding its efficiency, if the viewport scrolls quickly, its efficiency will drop massively.
The accuracy to which you can predict which edge buffer to fill up first also greatly influences the algorithms efficiency - therefor it is better suited to use in an environment where the direction of scrolling doesn't change rapidly.
Best case performance is ((screenWidth/cellWidth+1)+(screenHeight/cellHeight+1))/scrollSpeed (assumes you are moving in both axis, if only moving in 1 axis, the performance would be even better)
e.g. ((800/32+1)+ (640/32+1))/8= 5.9 cells/frame
Worst case performance is ((screenWidth/cellWidth+1)*2 + (screenHeight/cellHeight+1)*2) /scrollSpeed
e.g. ((800/32+1)*2 + (640/32+1)*2)/8 = 52+42/8 = 11.8 cells/frame.
So, as long as you scroll at a constant 1 cell every 8 frames, this algorithm will give you a performance per frame of between
5.9 and 11.8 cells/frame.
As you can see, even the simple method highlighted in method 1) is approx. 9 times faster than your current method.
and, if the choppy scrolling of method 2), or the limitations imposed by method 3) are acceptable for your implementation, you can get an algorithm that will run approx. 50-100 times faster than your current method.
Abuse at 2007-7-6 19:57:07 >

I'm not too sure if I understand your problem completely, but I'll give it a go anyways:
Why re-draw over and over just to move the object/screen? You can instead just redraw whenever you want to, and move the objects/screen a certain distance based on the time elapsed between re-draws. It might be choppy with low-framerates, but the distance moved will be the same no matter how quickly you re-draw.
Try with the new "Full screen mode".
Thanks for all the help. I believe that I'm going in the right direction now thanks to all of you. Quick question, hopefully there's a simple answer. The code in question looks like this,
This is just a sample of the code i'm working with:
int increm = 32;
Image grass;
Image bufferedImage;
public class ImagePanel extends JPanel
{
int increm = 32;
Image grass;
Image bufferedImage;
public void createBufferedImage
{
bufferedImage = createImage(15*increm,14*increm);
Graphics bufferedGraphics = bufferedImage.getGraphics();
bufferedGraphics.drawImage(grass, 0, 0, this);
bufferedGraphics.drawImage(grass, 32,32, this);
}
public void paint(g)
{
createBufferedImage();
g.drawImage(bufferedImage, 0, 0, this);
}
}
I have the Image grass and Image bufferedImage defined as class variables. I am trying to put two grass images into one image (bufferedImage) so that I can do something like this
drawImage(bufferedImage, 0, 0, this);
that will draw the new Image bufferedImage which contains the two grass images. What I hoped is that I could then call the bufferedImage image with other methods to change it, move it, etc. but whenever I call bufferedImage with another method the two grass images don't appear to be there. What am I doing wrong?
In mine I have each tile as a simple Image, then draw them onto a screen size +/- 1 tile background Image. The charachters are draw on top of that. Each time the charachter moves a tile the background is refreshed:
BTW why is bufferedImage better than Image?
Also a good tip - draw the tiles at ((ypos+1)*tileheight)-imageheight, so if an tile is 32px tall and a charachter is 48, it will be drawn 16px overlaying the tile above, so this way it looks at 20 degrees off top down instead of directly top down (top down with side on images dosen't work, 20d off does)
String[][] tiles =
new Image[(screenwidth/tilewidth)+2][(screenheight/tileheight)+2];
remakebg() {
tiles = cutFromArray(wholeWorld, x, y, width, height);
bg = createImage(
screenwidth+(tilewidth*2),
screenheight+(tileheight*2));
g = bg.getGraphics();
for(int i=0; i<tiles.length; i++) {
for(int j=0; j><tiles[0].length; j++) {
g.drawImage(
getImageFromCache(tiles[i][j]),
i*32,
(j*32)+32-(tiles[i][j].getHeight(this),
this);
}
}
}
scrollx(int scroll) {
for(int i=0; i><scroll; i++) { //or i--, can't remember checking func
xoffset++;
repaint();
}
remakebg();
}
paint() {
g.drawImage(bg, xoffset-tilewidth, yoffset-tilewidth, this);
drawchars(xoffset, yoffset, g);
}
This is all from memory, and I can't remember exactly how I put each foreground object on the screen. Also this is a guess at the scroll type you want, I know of 2 main types:
Pokemon (gameboy): the charachter is always in the center, the world and other chars move in relation (the above, slower but a better effect)
Zelda (link's awakening): the background is static and the charachters move around it, scrolling a whole screen at a time when an edge is reached (faster, and easier to make maps for)
>
