Game structure and design

Hello!

Firstly, I’m making a game using slick. I’m going for a top down strategy/rpg style, kind of like “dungeon keeper”. I have two questions.

  1. I’m trying to organize it and give it a good design. In the main class (which is a state) i have update() and render() method and I have references to all Images and objects which I’m using. Right now I’m working on the map, which is an array of tiles, 16*16 pixels big. The update method updates the tiles that are on screen and the render fills them on the screen. However, to get a good design, I’d like for the update and rendering of the map take place in the map class itself, so that my core only has:

update(){
map.update();
//update map, minimap, gamepanel, etc.

}

render(){
map.render();
//etc.

}

So that it is clean, just acting as a spider in the wed.

But when I implement this I get funny results. I suspect that it has to do with threading. My core is runnable, but my map class isn’t. It doesn’t implement or extend anything.

  1. I’m also worried about my FPS. With an empty screen I get 5000FPS. When I fill my screen with my 1000 tiles I get 1000FPS. I’m then going to put in tons more logic, loops animations, objects, effects and so forth, will it be enough, or shall I have to abandon the large-scale tilesystem?

First, try to use the code tags for any code you post in the forum for example,

renders as


public void foo()
{
    System.out.println("bar");
}

And second, never worry about your FPS, 1000 FPS is very huge that is not at all necessary. You can’t really notice any difference between a scene with 1000 fps and the same scene with 120 fps. Monitors do limit the fps to their refresh rate.

So you went from 0.2 milliseconds to 1 millisecond per frame, you still have over 15 left to make 60 FPS, which is enough.

For 2D games, as long as you aren’t either 1) going massive scale (i.e. 1000s of things on screen at any time) or 2) doing very dumb things (i.e. loading from file every frame), you’ll be fine.

Rules of optimization state that you should not worry about performance until you need to. As long as it plays at 60+ FPS, don’t bother.
In fact you should limit framerate to a maximum of 60, as most everyone’s monitor can’t refresh any faster than that anyway.

Thanks!

I am aiming for and going to limit to 60-50 FPS in the end. I’m just worried since drawing the map ate up 2000FPS, and I’m going to add so many more things to the game - objects entities, etc.

But anyway. How do you guys manage to get a clean core class? Do you have render methods in other classes, like in a map class or an object class?

I’ll post some code.

This is my current render method in my core class. It’s working fine.



public void render(GameContainer gc, StateBasedGame sbg, Graphics g)
			throws SlickException {
		//draw tiles	
		int type = 0;
		int firstTileX = upperLeftScreenX /Global.TILESIZE;
		int firstTileY = upperLeftScreenY /Global.TILESIZE;
		int firstX = - (upperLeftScreenX & 0x0F);
		int firstY = - (upperLeftScreenY & 0x0f);
		for (int height = 0; height <= tilesOnScreenY/zoom+1; height++){
			for (int width = 0; width <= tilesOnScreenX/zoom+1; width++){
				type = map.getType(firstTileX+width, firstTileY+height);
				switch (type){
				case 1: im.draw(firstX*zoom+width*Global.TILESIZE*zoom, 
						firstY*zoom +height*Global.TILESIZE*zoom, 
						zoom);
				break;
				case 2: //much more coming
				break;
				default: im.draw(50, 50, zoom);
				}

			}
		}		

	}


however, I’d like this:



public void render(GameContainer gc, StateBasedGame sbg, Graphics g)
			throws SlickException {
		map.draw(gc,sbg,g,);
	}


and the in the map class:



	void draw (GameContainer gc, StateBasedGame sbg, Graphics g){
		for (int height = 0; height <= tilesToUpdateX; height++){
			for (int width = 0; width <= tilesToUpdateY; width++){
				im.draw(getX(), getY());
			}

		}
	}


It works, but there is flickering sometimes. Like if it’s jumping in and out of the method. I’ve tried synchronized, but it doesn’t help.

Try drawing a single tile on the whole screen. It will take 2000fps also (a little bit less). Its not the amount of tiles you are drawing, it the amount of screen you are filling.

I figured it out!

For what i suspect to be threading reasons, the update method in my map-class is sometimes called while I’m in the draw-method. Therefore I needed to create temp variables in my draw-method, point them to my update-variables and then work with them.


package play;

import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;

public class SettlementMap {
	
	private Image im;
	
	private Tile tiles[][];
	//for drawing
	private int firstTileX;
	private int firstTileY;
	private int firstX;
	private int firstY;
	private int tilesToUpdateX;
	private int tilesToUpdateY;
	private float zoom;
	private int tileSize;
	private int renderJump;
	private int tilesToRender;
	private int xCount;
	private int yCount;
	private int maxWidth = 800;
	private int maxHeight = 400;

	public SettlementMap () throws SlickException{
		
		im = new Image("res/menu/path.png", false, Image.FILTER_NEAREST);
		tileSize = Global.TILESIZE;
		tilesToUpdateX = 0;
		tilesToUpdateY = 0;
		firstTileX = 0;
		firstTileY = 0;
		tiles = new Tile[Global.MAPSIZE][Global.MAPSIZE];
		for (int height = 0; height < Global.MAPSIZE; height++){
			for (int width = 0; width < Global.MAPSIZE; width++){
				tiles[height][width] = new Tile (1);
				}
			}
		tiles[5][0].setType(2);
		tiles[10][0].setType(2);
		tiles[50][50].setType(2);
		tiles[75][75].setType(2);
		tiles[100][100].setType(2);
	}
	
	int getType (int width, int height){
		return tiles[height][width].getType();
	}
	
	void update (int upperLeftScreenX, int upperLeftScreenY, float zoom) {
		xCount = 0;
		yCount = 0;
		this.zoom= zoom;
		firstTileX = upperLeftScreenX/tileSize;
		firstTileY = upperLeftScreenY/tileSize;
		firstX = (int) (- (upperLeftScreenX & 0x0F)*zoom);
		firstY = (int) (- (upperLeftScreenY & 0x0f)*zoom);
		renderJump = (int) (tileSize*zoom);
		for (int height = 0; height <= tilesToUpdateY; height++){
			for (int width = 0; width <= tilesToUpdateX; width++){
				tiles[height][width].update();
			}
		}
	}
	
	int getX (){
		return firstX + xCount*renderJump;
	}
	
	int getY (){
		return firstY + yCount*renderJump;
	}
	
	void count (){
		xCount ++;
		if (xCount > tilesToUpdateX){
			xCount = 0;
			yCount ++;
		}
	}
	
	int getTilesToRender(float zoom){
		return tilesToRender;
	}
	
	void draw (Graphics g){
		int tempX;
		int tempY = firstTileY;
		int tempTile;
		for (int y = firstY; y <= maxHeight; y = y + renderJump){
			tempX = firstTileX;
			for (int x = firstX; x <= maxWidth; x = x + renderJump){
				tempTile = tiles[tempY][tempX].getType();
				switch (tempTile){
					case 1: im.draw(x, y,zoom);
				break;
					case 2: 
				break;
				default:
				}
				tempX++;
			}
			tempY++;
		}

		g.drawString("tilesToUpdateX =" + tilesToUpdateX, 200, 100);
		g.drawString("tilesToUpdateY =" + tilesToUpdateY, 200, 200);
		g.drawString("tilesToRender =" + tilesToRender, 200, 300);
	}
	
	
}


p.s. Do you think it’s wise to use the switch-statement when choosing which image to draw. I’m planning on making many, many tiles, perhaps 200 of them (gonna use spritesheet). I’ve read somewhere about some kind of ID-thing. Should I look it to that?

Tile drawing is where you’d use the flyweight pattern: each Tile has a either a reference Image tileImage; or an index int textureIndex; which points to a certain image in a master list of images, known as a spritesheet if it’s one large image that you draw sections of.
Note that the image reference is shared, so all tiles with the same image point to the same image object, you don’t want to allocate 200 identical images when you can just share the reference. That is the flyweight pattern.

Google some of those words to find out more.

It becomes something like this:

void draw (Graphics g){
    <snip>
         for (int x = firstX; x <= maxWidth; x = x + renderJump){
            tileImage = tiles[tempY][tempX].getImage();
            tileImage.draw(...);
    <snip>
}

That said, I don’t know enough about Slick to point you in the “best” direction on how to draw textures.

Don’t think about it in FPS, think about it in time: 5000 FPS to 1000 FPS may be a “drop” of 4000 FPS, but it’s only a difference of 0.8 ms. A drop of 40 FPS from 100 to 60 is a loss of 6.667 ms, which is actually 8.333 times more time lost, despite the FPS drop being 100x less.
You have a 16.6667 millisecond “budget” for each frame, drawing your 1000 (!) tiles uses only 4.8% of that budget, even if you lost 4000 FPS.