Jump to content
  • Advertisement
Sign in to follow this  
Fed3x

Tile based game performance

This topic is 4579 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm making this tile based game inspired by Final Fantasy, at start I'm trying to make it so much like FF IV as possible. But I've ran into some performance problems. The fps only stays at about 20 on this computer and it's even lower on my other crappier comp. I don't have so much experience in game programming so I believe i got some bad coding somewhere. At first I'd like to know what FPS this kind of game could have if made well. I'm using java fullscreen and using bufferStrategy for pageflipping. Is it the java2D that's limiting it or my coding? I run the game at resolution (640, 480, 32) and the tiles are sized 16*16. So it draws like 1200 tiles per frame. Then I'm not really sure if I calculate the FPS right. Now I store the time at start of the game loop. Then i have this code to calculate FPS:
			currentTime = System.currentTimeMillis();
			if (currentTime > nextTime){
				nextTime = currentTime + 1000;
				fps = frameCounter;
				frameCounter = 0;
			}else{
				frameCounter++;
			}

One problem with that FPS is at least that it wont go higher than 60. Does it have something to do with the accuracy of time? Here is my render() method:
	public void render(){
		
		
	do{
		//Get the rendering surface#############################
	    Graphics g = bufferStrategy.getDrawGraphics();
	    //######################################################
	    
	    
	    //Clear the drawing buffer to white#####################
	    g.setColor( Color.white );
	    g.fillRect( 0, 0, screenWidth, screenHeight );
	    //######################################################
	    
	    currentTime1 = System.currentTimeMillis();
	    
	    for(int i = 0; i < tileDrawAmountY ; i++){
	    	for(int j = 0; j < tileDrawAmountX ; j++){
		    	if(loopingMap){
		    			
		    			//If player moves southeast of map
			    		if(i + startDrawTileY > lengthY-1 && j + startDrawTileX > widthX-1){
			    			g.drawImage(tiles[map[i + startDrawTileY - lengthY][j + startDrawTileX-widthX]], (j*tileSize)- additionalCamCordX, (i*tileSize)- additionalCamCordY, null);
			    			
				    	//If player moves south of map
			    		}else if(i + startDrawTileY > lengthY-1){
			    			g.drawImage(tiles[map[i + startDrawTileY - lengthY][j + startDrawTileX]], (j*tileSize)- additionalCamCordX, (i*tileSize)- additionalCamCordY, null);
			    			
				    	//If player moves east of map
			    		}else if(j + startDrawTileX > widthX-1){
			    			g.drawImage(tiles[map[i + startDrawTileY][j + startDrawTileX - widthX]], (j*tileSize)- additionalCamCordX, (i*tileSize)- additionalCamCordY, null);
			    			
			    		}else{
			    			g.drawImage(tiles[map[i + startDrawTileY][j + startDrawTileX]], (j*tileSize)- additionalCamCordX, (i*tileSize)- additionalCamCordY, null);
			    		}
		    		}else{
		    			g.drawImage(tiles[map[i + startDrawTileY][j + startDrawTileX]], (j*tileSize)- additionalCamCordX, (i*tileSize)- additionalCamCordY, null);
		    		}
		    	}
		    }
		    
		    
		//Draw the player#######################################
	    if(halfVisible){
	    	g.drawImage(playerHalfImage[playerHeading],playerDrawCordX, playerDrawCordY ,null);
	    }else{
		    if (playerAnimation < 2 || playerAnimation > 5){
		    	g.drawImage(playerImage[playerHeading],playerDrawCordX,playerDrawCordY ,null);
		    }else{
		    	g.drawImage(playerAnimImage[playerHeading],playerDrawCordX,playerDrawCordY ,null);
		    }
	    }
	    //######################################################
	    
	    frames++;
	    bufferStrategy.show();
	    g.dispose();
	    
	}while(bufferStrategy.contentsLost());
	}

PS. How do you add code tags on this forum? EDIT: Tags added [Edited by - Fed3x on May 6, 2006 2:46:35 PM]

Share this post


Link to post
Share on other sites
Advertisement
I am guessing that you are limited to 60fps because you are running fullscreen and it is using vsync, though I couldn't tell you for sure because I don't tend to use Java fullscreen.

Your fps code looks ok, though System.currentTimeMillis() isn't particularly accurate and if you have Java 1.5 System.nanoTime() might be better for games.

Java2D isn't amazingly fast, but its acceptable. I get about 300 fps in my game with a 640x480 screen, 32x32 tiles (Ath 3200+). I think your main problem is probably the amount of maths and checks you do in your render loop. If the map is a looping map it has to run through those checks for each tile, every frame and with 1200 tiles thats a lot of overhead. Even worse, theres then additional maths to do if the player is on a map seam.

A better method would be to calculate where the player is in relation to the map seams before you enter the for statements, and do what maths you can there, that way you only do the maths once per frame rather than once per tile per frame. This will take a bit more work and I can't think of an easy method right now, but I'm sure the good people in the Isometric forum would be able to help you.

Share this post


Link to post
Share on other sites
So I changed my FPS code to use nanoTimer()

I searched a bit in google and i found this site. Accprding to that you can't remove vsync. Is that true? How can I now then check fps higher than 60?

I don't think moving the code to check when the camera goes over a map seam out from render() method will help much. I tested it without loopingMap and it just gave maybe 2 fps. Then I also removed the code to clear the screen to white which gave a couple additional fps. But it's still below 30, if you got 300fps I have something definately wrong. I'm using A64 3500+.

Share this post


Link to post
Share on other sites
To get rid of vsync don't run it fullscreen, just create your game in a JFrame. eg

public createWindow() {
mainWindow = new JFrame("Game window");
mainWindow.setResizable(false);
screen = new Canvas();
screen.setIgnoreRepaint(true);

JPanel pan = (JPanel) mainWindow.getContentPane();
pan.setPreferredSize(new Dimension(game.SCREEN_WIDTH, game.SCREEN_HEIGHT));
pan.add(screen);

mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWindow.setVisible(true);
mainWindow.pack();

screen.createBufferStrategy(2);
buffStrat = screen.getBufferStrategy();

mainWindow.requestFocus();
}



Your game could be slow due to the number of tiles you are drawing but its only 4x more tiles than I draw, how are you calling the render() method?

Share this post


Link to post
Share on other sites
Wow was suprised how easy it was to make the game run in a window. Just copied your method and changed some stuff and got it working in like a couple of minutes. [smile]

Well now fps goes above 60, when I comment out the tile render code it goes up to 900. Also the game seems to run a little bit faster in window mode, why is this? Shouldn't fullscreen be faster?

You asked how I call the render() method, not really sure what you mean but I'll post my game loop here. Render() gets called at the end of it.


public void gameLoop(){

while (running){

//Counting frames, time and FPS#####################
currentTime = System.nanoTime();
time = currentTime - startTime;
if (currentTime > nextTime){
nextTime = currentTime + 1000000000;
fps = frameCounter;
frameCounter = 0;
}else{
frameCounter++;
}
//##################################################


//Movement##########################################
if(!playerMoved){
if(rightPressed || playerMovingRight){
if(!(playerMovingLeft || playerMovingUp || playerMovingDown)){
playerHeading = 3;
if(!(colMap[playerGridCordY][playerGridCordX >= widthX-1 ? 0 : playerGridCordX + 1] == 1)){
if(playerGridCordX != widthX-1 || loopingMap){
playerMoved = true;
playerMovingRight = true;
additionalCordX = additionalCordX + speed;
playerAnimation++;
if(playerAnimation >= tileSize/speed){
additionalCordX = 0;
playerAnimation = 0;
steps++;
playerGridCordX++;
moveCamGridCord = true;
playerMovingRight = false;
}
}
}
}
}
}
if(!playerMoved){
if(leftPressed || playerMovingLeft){
if(!(playerMovingRight || playerMovingUp || playerMovingDown)){
playerHeading = 1;
if(!(colMap[playerGridCordY][playerGridCordX <= 0 ? 0 : playerGridCordX - 1] == 1)){
if(playerGridCordX != 0 || loopingMap){
playerMoved = true;
playerMovingLeft = true;
additionalCordX = additionalCordX - speed;
playerAnimation++;
if(playerAnimation >= tileSize/speed){
additionalCordX = 0;
playerAnimation = 0;
steps++;
playerGridCordX--;
moveCamGridCord = true;
playerMovingLeft = false;
}
}
}
}
}
}
if(!playerMoved){
if(upPressed || playerMovingUp){
if(!(playerMovingLeft || playerMovingRight || playerMovingDown)){
playerHeading = 2;
if(!(colMap[playerGridCordY <= 0 ? 0 : playerGridCordY - 1][playerGridCordX] == 1)){
if(playerGridCordY != 0 || loopingMap){
playerMoved = true;
playerMovingUp = true;
additionalCordY = additionalCordY - speed;
playerAnimation++;
if(playerAnimation >= tileSize/speed){
additionalCordY = 0;
playerAnimation = 0;
steps++;
playerGridCordY--;
moveCamGridCord = true;
playerMovingUp = false;
}
}
}
}
}
}
if(!playerMoved){
if(downPressed || playerMovingDown){
if(!(playerMovingLeft || playerMovingUp || playerMovingRight)){
playerHeading = 0;
if(!(colMap[playerGridCordY >= lengthY-1 ? 0 : playerGridCordY + 1][playerGridCordX] == 1)){
if(playerGridCordY != lengthY-1 || loopingMap){
playerMoved = true;
playerMovingDown = true;
additionalCordY = additionalCordY + speed;
playerAnimation++;
if(playerAnimation >= tileSize/speed){
additionalCordY = 0;
playerAnimation = 0;
steps++;
playerGridCordY++;
moveCamGridCord = true;
playerMovingDown = false;
}
}
}
}
}
}
playerMoved = false;
//##################################################

//Collission with map bounds when map not looping###
if(loopingMap){
if(playerGridCordX == widthX){
playerGridCordX = 0;
}
if(playerGridCordY == lengthY){
playerGridCordY = 0;
}
if(playerGridCordX == -1){
playerGridCordX = widthX-1;
}
if(playerGridCordY == -1){
playerGridCordY = lengthY-1;
}
}
//##################################################

//Check if player is in woods#######################
if(colMap[playerGridCordY][playerGridCordX] == 3){
halfVisible = true;
}else{
halfVisible = false;
}
//##################################################

//Calculate camera position#########################
camView.x = (playerGridCordX*tileSize+additionalCordX) - (screenWidth / 2);
camView.y = (playerGridCordY*tileSize+additionalCordY) - (screenHeight / 2);

if(loopingMap){

if(camView.x <= 0){
camView.x = (widthX*tileSize) + camView.x;
}
if(camView.y <= 0){
camView.y = (lengthY*tileSize) + camView.y;
}

if(camView.x >= (widthX*tileSize)-1){
camView.x = 0;
}
if(camView.y >= (lengthY*tileSize)-1){
camView.y = 0;
}
}
//##################################################

//Camera collissions if map not looping#############
if(!loopingMap){
if(camView.x < 0){
camView.x = 0;
camXCollided = true;
}
if(camView.y < 0){
camView.y = 0;
camYCollided = true;
}
if(camView.x + screenWidth >= (widthX)*tileSize){
camView.x = ((widthX)*tileSize) - screenWidth;
camXCollided = true;
extraXRow = 0;
}else{
extraXRow = 1;
}
if(camView.y + screenHeight >= (lengthY)*tileSize){
camView.y = ((lengthY)*tileSize) - screenHeight;
camYCollided = true;
extraYRow = 0;
}else{
extraYRow = 1;
}
}
//##################################################

//Camera grid cordinates############################
if(moveCamGridCord){
cameraGridCordX = camView.x/tileSize;
cameraGridCordY = camView.y/tileSize;
moveCamGridCord = false;
}
//##################################################


//Calculate additional Camera cordinates############
additionalCamCordY = 0;
additionalCamCordX = 0;

if (!camXCollided){
if (camView.x > 0){
if (additionalCordX > 0){
additionalCamCordX = additionalCordX;
}else if((additionalCordX < 0)){
additionalCamCordX = 16 + additionalCordX;
}
}else{
additionalCamCordX = 0;
}
}

if (!camYCollided){
if (camView.y > 0){
if (additionalCordY > 0){
additionalCamCordY = additionalCordY;
}else if(additionalCordY < 0){
additionalCamCordY = 16 + additionalCordY;
}
}else{
additionalCamCordY = 0;
}
}
//##################################################


//Fine tuning#######################################
//Removes "jumping" when moving up and left
if(!camXCollided){
if(cameraGridCordX >= 0 && playerMovingLeft){
fineTuningX = 1;
}else{
fineTuningX = 0;
}
}
if(!camYCollided){
if(cameraGridCordY >= 0 && playerMovingUp){
fineTuningY = 1;
}else{
fineTuningY = 0;
}
}
//##################################################

//Calculate where to draw the player###############
playerDrawCordX = (playerGridCordX*tileSize) + additionalCordX - camView.x;
playerDrawCordY = (playerGridCordY*tileSize) + additionalCordY - camView.y;

if(loopingMap){
if(playerDrawCordX < 0){
playerDrawCordX = screenWidth / 2;
}
if(playerDrawCordY < 0){
playerDrawCordY = screenHeight / 2;
}
}
//##################################################

//Calculating player cordinates#####################
playerCordX = (playerGridCordX*tileSize) + additionalCordX;
playerCordY = (playerGridCordY*tileSize) + additionalCordY;
//##################################################


//Calculate where to start and how much to draw#####

startDrawTileX = cameraGridCordX-fineTuningX;
startDrawTileY = cameraGridCordY-fineTuningY;

tileDrawAmountY = ((screenHeight/tileSize)+extraYRow);
tileDrawAmountX = ((screenWidth/tileSize)+extraXRow);

if(startDrawTileY < 0){
startDrawTileY = lengthY-1;
}
if(startDrawTileX < 0){
startDrawTileX = widthX-1;
}

//##################################################
camYCollided = false;
camXCollided = false;


//DRAW
render();
}
}


Share this post


Link to post
Share on other sites
Fullscreen with vsync is generaaly slower since once the image has been drawn it has to wait for the synch signal before it can be displayed on the screen.

So I think we have established then that actually drawing the tiles is what kills the performance, I don't have much else to suggest other than using bigger tiles. 16x16 is tiny, and lots of small tiles=lots of loop interations. Theres one other thing, I've never had to use it myself but it might give you a performance boost its to do with how you create your BufferedImages (I assume that is what you are using)

http://javaalmanac.com/egs/java.awt.image/CreateBuf.html

Share this post


Link to post
Share on other sites
My tile images are just regular Images, would it be faster using BufferedImages?

So does this mean that there's nothing wrong with my code, just that 1200 tiles per frame is too much for java2D? In that case what kind of performance boost would I get by using JOGL (java openGL) or LWJGL(something similar)?

I tried dropping the resolution to 320x240 (300 tiles drawn) which gave me about 100 fps. That means same amount of tiles you had. Wonder how you manage to get three times better fps with a bit slower CPU than I have.

Share this post


Link to post
Share on other sites
Impressive game you got there [smile]

You got like 50 classes in there incredible, my game is only one class.[rolleyes]

Well here is the source for my game. It has to be compiled first since I had some strange problem running the jar file.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!