[LibGDX] 2D (top down) procedurally generated terrain.

I really do hate asking for help but here I am asking :stuck_out_tongue:

I find myself a bit stuck with getting myself a level or environment to render using libGDX. I have sprites that can move around in various different ways (mouse/keyboard), but it’s a bit boring on a plain old black screen or a level created in Tiled.

I have worked out how to render a map using TiledMap and the program called Tiled and I understand the process, but I would prefer to work on doing some procedural map generation and I’m pretty well baffled as to where to start.
I’ve looked for hours and hours online, just purely looking for resources, but not doing any work what so ever, and it’s all narrowing down to 1D or 2D Simplex or Perlin noise generation techniques for top down 2D game with several layers of height. I understand the concepts, but as far as the implementation goes, I can’t help but feel like a bit of a dumbo, I see posts on forums talking about it all, and it feels like those talking are already way ahead of where I am at in regards to procedural generation of terrain.

I want to get the ball rolling and start seeing some kind of progression, because right now, it’s very disheartening that I just don’t seem to be getting anywhere. I’m not as smart as most people on these forums (or at least not feeling very smart right now), and I am nearly always tired and burnt out from my day job and gym, so perhaps that’s why I’m finding it difficult to wrap my head around things, I dunno, but I feel as though I need to ask for some help and guidance here.

So before I go to bed, I just wanted to ask where should I get started with 2D procedural terrain generation using libGDX?

Any help is greatly appreciated as always.

Thankyou in advance :slight_smile:

Below are some resources I have found that might be useful to me later on (and to anybody else reading this thread):
https://github.com/matheus23/Utils (this looks very interesting as it has source code for me to run through and read, but I haven’t gone through it just yet)
http://accidentalnoise.sourceforge.net/minecraftworlds.html (this looked great when I skimmed through, but seemed to be more for 2D side on, rather than top down, same rules probably still apply though)
http://freespace.virgin.net/hugo.elias/models/m_perlin.htm (Brain had a melt down right away, probably because it’s overloaded already, could also be the design of the page)
http://pcg.wikidot.com/

Hi,

There are a lot of things you can do, what I assume is, that you just want a level with grass and water (rivers), you can later expand it with bosses, etc.

For example, your grid is 31 x 31, I think the most simple and good enough for now is to make 1 river from the left to the right, random generated I would do it something like this:


for(int x = 0; x < grid.width; x+=6){
if(x!= 0)
    int previousY = y;//we are going to make lines between the new and old y
int y = (int) Math.random() * 21; //number between 0 and 21
grid[x][y] = water;
if(x!=0)
   int heightDifference = y - previousY; // for example y = 5 and previousy = 15
   float averagePerX = heightDifference/6; // -10/6 < -1.5
   for(int tempX = x -6; tempX  <x; tempX ++){
       grid[tempX][y + averagePerX * tempX] = water;
   }
}

Now you can change this to your likings, make the river wider etc,

That’s right.

At this stage, I would be happy to be able to generate just land and sea, or even just grass and sand or just 2 of something. (Just want to figure out the base level of terrain generation and expand from there, just like learning the base level of programming in general, then expanding from there).
I just have to keep plugging away at it like I’ve always done and be confident that I will eventually progress, even if right now it feels like I am not getting anywhere.

I have a day off tomorrow, so should be able to focus more on getting this done and working :slight_smile:

After much brain deadening, I have RESULTS! :smiley:

Using the diamond square algorithm, I have got myself some lovely randomized terrain.
While it’s nothing too spectacular to look at (since you’ve all seen it before many times!), I feel it is a milestone :slight_smile:

Next up, I just need to do some frustrum culling, putting textures into an atlas, map controls, and perhaps even a map generation ui, before I move on to other things such as generating transitional tiles (auto-tiling?), randomized objects based on terrain type, and other cool stuff :slight_smile:

Here is a video of the progress :slight_smile:

XoAxThXeN8I

A little snippet of code to not render tiles outside specific radius.


public static double calcDistance(double x1, double y1, double x2, double y2) {
	return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}

Usage:


final static double RADIUS = 25.0; //25 tiles
final static double TILESIZE = 1.0;
final static double RENDERDISTANCE = TILESIZE * RADIUS;
if(calcDistance(playerPos, tileX, tileY) <= RENDERDISTANCE) {
    //Render tile here.
}

This might not be the fastest way, but it gets the job done.
Hope it helps :slight_smile:

Yeah, looks really good, though its not done yet, I see small dark pieces random in the water and its a bit angular/rugged. However, we see a pretty nice ā€˜world’ instead of one big grassfield! :smiley:

Could you post your code for the terrain generation? I’ve been struggling with noise generation and am interested to see how you managed to implement it in one day!

Yeah, that is partly due to my threshold values on where terrain should be. Since they are float values and 0.001 of a difference can determine if a tile is grass, or sand, it means every now and then there will be odd tiles. I plan on doing some research on how to refine the terrain even more without losing it’s larger details. The code has smoothness control, but smoothing will smooth the ENTIRE terrain out, rather than averaging out smaller details.

Well, I wouldn’t say one day, perhaps 1 day of coding, a week of pulling hairs out of my head trying to find something appropriate to learn from.

It’s difficult to just post a whole bunch of code, as most of the code I have is horribly unoptimized and messy :stuck_out_tongue:

I will say that I came across various articles on simplex noise, perlin noise and mid-point displacement/diamond square algorithm, and all of it, I found myself dumbfounded.

Diamond square was the easier for me to understand but. It really just seems like it is subdividing a square, over and over, refining the details, but also adding it’s own random variations on sub-division.
When I fully understand how it works, or find a simple explanation given, I will be happy to share it! :slight_smile:
This article is what seemed to explain it quite well, and what every other person points out as being the best resource to learn it (still not easy to read when you’re super tired and frustrated but)
http://www.gameprogrammer.com/fractal.html

While it’s difficult to post code, as I have a bunch of classes set up all contributing, I just read through these here:



While he/she uses Artemis or some entity system (and I didn’t), I couldn’t really follow along with his tutorial, but the principles still applied, I read through his code, tried to understand it, and how I could apply it to my already existing code, and then pretty much did a copy/paste (typed out what he had really, as copy/paste doesn’t really help you learn I find…) for the sake of prototyping.

The code that he has on this blog post is where the magic happens but (take the time to read through it and read the comments, it is well documented):

He does the mid-point displacement differently to normal due to limitations (as he explains), and it works well.
The same sort of class could be made for Simplex and Perlin noise, as there seems to be a few people out there who have done a code dump of the actual noise generation code. You just need to store the information in an array using a series of numbers, by setting threshold values to determine what int value goes where on the array(depending on how many textures you have I guess).

Essentially what the code is doing is filling the float[][] map with float values using a modified diamond square algorithm, then it averages the values out so that they are in a range of 0 - 1f (I believe that’s what it is trying to do), before iterating through the values and using the set threshold values to determine what int values go into the int[][] returnMap.
The threshold being this code here:

  deepWaterThreshold = 0.5f;
  shallowWaterThreshold = 0.55f;
  desertThreshold = 0.58f;
  plainsThreshold = 0.62f;
  grasslandThreshold = 0.7f;
  forestThreshold = 0.8f;
  hillsThreshold = 0.88f;
  mountainsThreshold = 0.95f;

which determine the int values down here: (This snippet basically iterates through the float array, asks what the value is at that iteration of the array, and then checks to see if it is below one of the threshold values to determine what int number we assign to returnMap).

  for(int row = 0; row < map.length; row++){
   for(int col = 0; col < map[row].length; col++){
    map[row][col] = (map[row][col]-min)/(max-min);
    if (map[row][col] < deepWaterThreshold) returnMap[row][col] = 0;
    else if (map[row][col] < shallowWaterThreshold) returnMap[row][col] = 1;
    else if (map[row][col] < desertThreshold) returnMap[row][col] = 2;
    else if (map[row][col] < plainsThreshold) returnMap[row][col] = 3;
    else if (map[row][col] < grasslandThreshold) returnMap[row][col] = 4;
    else if (map[row][col] < forestThreshold) returnMap[row][col] = 5;
    else if (map[row][col] < hillsThreshold) returnMap[row][col] = 6;
    else if (map[row][col] < mountainsThreshold) returnMap[row][col] = 7;
    else returnMap[row][col] = 8;
   }
  }

He also creates a getter class used to get the mid point displacement map.

I will show you the not so messy part of my code which determines what texture goes where

Bare in mind, w and h determine the location of tiles on the screen, and they have (float) (x * .5); assigned to them as I have scaled the textures down using a scalex and scaley, which are float values of 0.5f.
What you see in the video shows the scale of the textures are 1f scale, therefore that w = (float) (x * .5) business is normally something like w = x * 32 / 32 or something like that.
It’s just because of the way i’m doing things is not very optimized or object orientated just yet, that my code is a nightmare to look at since it is just a direct approach to prototyping everything.

batch.begin();
		
		for(int x = 0; x < map.length; x++){
			w = (float) (x * .5);
			for(int y = 0; y < map[x].length; y++){
				h =  (float) (y * .5);
				if(map[x][y] == 0){
					batch.draw(deepwater, w, h, scalex, scaley);
				}
				else if (map[x][y] ==1){
					batch.draw(water, w, h, scalex, scaley);
				}
				else if (map[x][y] ==2){
					batch.draw(shallowwater, w, h, scalex, scaley);
				}
				else if (map[x][y] ==3){
					batch.draw(wetsand, w, h, scalex, scaley);
				}
				else if (map[x][y] ==4){
					batch.draw(sand, w, h, scalex, scaley);
				}
				else if (map[x][y] ==5){
					batch.draw(grass, w, h, scalex, scaley);
				}
				else if (map[x][y] ==6){
					batch.draw(darkgrass, w, h, scalex, scaley);					
				}
				else if (map[x][y] ==7){
					batch.draw(lowmountain, w, h, scalex, scaley);
				}
				else if (map[x][y] ==8){
					batch.draw(highmountain, w, h, scalex, scaley);
				}
				else if (map[x][y] ==9){
					batch.draw(mountainpeak, w, h, scalex, scaley);
				}

			}
               batch.end;
		}

If you wish to view my entire messy code for my ā€œWorldRendererā€ class, just because I know the more code, the better when studying something, here it is :stuck_out_tongue: (I hold no responsibility for brain aneurysms as a result of crappy code :P):

[spoiler]

package com.zetabit.otreumsgame.View;

import java.util.Random;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.zetabit.otreumsgame.MapGenerator.GenerateMap;
import com.zetabit.otreumsgame.Model.Follower;
import com.zetabit.otreumsgame.Model.Ship;

public class WorldRenderer implements Screen{
		
	World world;
	SpriteBatch batch;
	Ship ship;
	public OrthographicCamera cam;
	Texture shipTexture, followerTexture;
	int width, height;
	ShapeRenderer sr;
	Follower follow;
	
	GenerateMap mpdis = new GenerateMap();
	
	Texture soldier;
	Texture grass;
	Texture darkgrass;
	Texture wetsand;
	Texture sand;
	Texture deepwater;
	Texture water;
	Texture shallowwater;
	Texture lowmountain;
	Texture highmountain;
	Texture mountainpeak;
	
	public int[][] map;
	
	public float w;
	public float h;
	
	public boolean zoomin, zoomout;
	public boolean isKeyDown;
	
	float scalex;
	float scaley;
	float camscalex;
	float camscaley;
	int CAM_SCALE;
	
	public WorldRenderer(World world){
		this.world = world;
		
		CAM_SCALE = 20;
		camscalex = 1f;
		camscaley = 1f;		
		
		map = mpdis.getMidPointDisplacement();
		
		width = (Gdx.graphics.getWidth() / CAM_SCALE);
		height = (Gdx.graphics.getHeight() / CAM_SCALE);
		
		cam = new OrthographicCamera();
		cam.setToOrtho(false, width * camscalex, height * camscaley);
		cam.update();
		
		batch = new SpriteBatch();
		batch.setProjectionMatrix(cam.combined);
		
		shipTexture = new Texture("data/ship1.png");
//		shipTexture = new Texture("data/rough_soldier.png");
		shipTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
//		shipTexture.setFilter(TextureFilter.Nearest, TextureFilter.Nearest);
		
		followerTexture = new Texture("data/ship1.png");
		followerTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
		
		soldier = new Texture(Gdx.files.internal("data/rough_soldier.png"));
		grass = new Texture(Gdx.files.internal("data/grasstile.png"));
		darkgrass = new Texture(Gdx.files.internal("data/darkgrasstile.png"));
		wetsand = new Texture(Gdx.files.internal("data/wetsandtile.png"));
		sand = new Texture(Gdx.files.internal("data/sandtile.png"));
		deepwater = new Texture(Gdx.files.internal("data/deepwatertile.png"));
		water = new Texture(Gdx.files.internal("data/watertile.png"));
		shallowwater = new Texture(Gdx.files.internal("data/shallowwatertile.png"));
		lowmountain = new Texture(Gdx.files.internal("data/lowmountaintile.png"));
		highmountain = new Texture(Gdx.files.internal("data/highmountaintile.png"));
		mountainpeak = new Texture(Gdx.files.internal("data/mountainpeak.png"));
		
		sr = new ShapeRenderer();
				
		float tex_SCALE = .5f;
		scalex = tex_SCALE;
		scaley = tex_SCALE;
	}
	
	public void render(){
		Gdx.gl.glClearColor(0,0,0,1);
		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
		
		ship = world.getShip();
		follow = world.getFollower();
		cam.position.set(ship.getPosition().x, ship.getPosition().y, 0);
		cam.update();
		batch.setProjectionMatrix(cam.combined);
		batch.begin();
		
		for(int x = 0; x < map.length; x++){
			w = (float) (x * .5);
			for(int y = 0; y < map[x].length; y++){
				h =  (float) (y * .5);
				if(map[x][y] == 0){
					batch.draw(deepwater, w, h, scalex, scaley);
				}
				else if (map[x][y] ==1){
					batch.draw(water, w, h, scalex, scaley);
				}
				else if (map[x][y] ==2){
					batch.draw(shallowwater, w, h, scalex, scaley);
				}
				else if (map[x][y] ==3){
					batch.draw(wetsand, w, h, scalex, scaley);
				}
				else if (map[x][y] ==4){
					batch.draw(sand, w, h, scalex, scaley);
				}
				else if (map[x][y] ==5){
					batch.draw(grass, w, h, scalex, scaley);
				}
				else if (map[x][y] ==6){
					batch.draw(darkgrass, w, h, scalex, scaley);					
				}
				else if (map[x][y] ==7){
					batch.draw(lowmountain, w, h, scalex, scaley);
				}
				else if (map[x][y] ==8){
					batch.draw(highmountain, w, h, scalex, scaley);
				}
				else if (map[x][y] ==9){
					batch.draw(mountainpeak, w, h, scalex, scaley);
				}

			}
		}
		
		batch.draw(shipTexture, ship.getPosition().x -2, ship.getPosition().y -2, 2, 2, ship.getWidth() * 4, ship.getHeight() * 4, 1, 1, ship.getRotation(), 0 , 0,	shipTexture.getWidth(), shipTexture.getHeight(), false, false);
//		batch.draw(followerTexture, follow.getPosition().x -2, follow.getPosition().y -2, 2, 2, follow.getWidth() * 4, follow.getHeight() * 4, 3, 3, follow.getRotation(), 0 , 0,	followerTexture.getWidth(), followerTexture.getHeight(), false, false);
//		batch.draw(shipTexture, ship.getPosition().x - ship.getWidth() / 2, ship.getPosition().y - ship.getHeight() /2, ship.getWidth() / 2, ship.getHeight() / 2, ship.getWidth(), ship.getHeight(), 1, 1, ship.getRotation(), 0 , 0,	shipTexture.getWidth(), shipTexture.getHeight(), false, false);
		batch.draw(followerTexture, follow.getPosition().x -2, follow.getPosition().y -2, 2, 2, follow.getWidth() * 4, follow.getHeight() * 4, 1, 1, follow.getRotation(), 0 , 0,	followerTexture.getWidth(), followerTexture.getHeight(), false, false);

		

				
		batch.end();
		
		sr.setProjectionMatrix(cam.combined);
		sr.begin(ShapeType.Line);

//		sr.rect(ship.getBounds().x - 2, ship.getBounds().y - 2, ship.getBounds().width * 4, ship.getBounds().height * 4);
//		sr.setColor(Color.RED);
//		sr.rect(follow.getBounds().x - 2, follow.getBounds().y - 2, follow.getBounds().width * 4, follow.getBounds().height * 4);
//		sr.setColor(Color.BLUE);
		sr.end();

		}
	
	public void dispose(){
		batch.dispose();
		shipTexture.dispose();

		
		
		
	}

	@Override
	public void render(float delta) {
	}

	@Override
	public void resize(int width, int height) {
		
	}

	@Override
	public void show() {

	}

	@Override
	public void hide() {
	}

	@Override
	public void pause() {
	}

	@Override
	public void resume() {
	}

}

[/spoiler]

Just a bit of an update.

I still haven’t gotten frustrum culling in yet, mainly because I have been busy all day and tired when I sat down to do some programming, but I did pop my textures into a textureAtlas with immediately improved framerate.

Before, my fps was roughly 12-16
After, my fps is around 30-31 fps except on ridiculously huge maps.

On an n scale of 9, my old fps was just 1fps, now it is 2fps :stuck_out_tongue:

So I can say that simply by putting my textures into a textureAtlas, that my fps has effectively doubled :slight_smile:

Wow thank you!! I’m still a little confused as to how the algorithm works exactly but I’ll take a look at all the resources you linked and hopefully I’ll be able to implement it in my game too! Also, why do you think your game is so slow? Just because you don’t use frustum culling? I’m a little confused. Don’t you use frustum culling for 3D? Since you only have one plane to worry about can’t you just check to see if the tile is within the limits of the screen?

Yeah, the algorithm is still a little confusing, but less confusing than Perlin and Simplex (although simplex is supposed to be easy to understand). I didn’t bother to try and understand those algorithms. But I will :stuck_out_tongue:

And it’s running slow simply because it’s running every single tile on the entire map, on every single frame, at the moment, it’s trying to render over 50,000 tiles each frame (depending on the size of the map), if I cull out what is not on the camera, then i’ll probably only be rendering maybe 5000 tiles or less (depending on zoom level, the zoom level I want for my vehicles i’ll probably only be rendering around 2000 tiles + whatever objects I need to render with it.
The video also runs horrible because every tile texture was a seperate texture object, so draw calls were quite high. I popped the tiles into a texture atlas using the texture packer that libgdx comes with, and the frame rate doubled just from cleaning up my code a bit and packing my tiles into a textureAtlas

Frustum culling is still used in 2D space too I believe. So that is the next step.

Without really looking up any techniques, I was thinking of maybe creating a bounding box for the camera, and setting bounding boxes on every tile, or perhaps bounding boxes on chunks of tiles, and if those bounding boxes overlap, then I should render what is inside of them, otherwise don’t render.
I will see if I can try this tonight, but I think right now, I seriously need to clean up my code, make it more object orientated, as currently it’s a bit of a mixture from several tutorials (which don’t use very good object orientated programming techniques :P)

There is probably a better way to do rendering, but I guess half the fun is trying different techniques out.
I’m also looking into how to store map tiles so that they can render as chunks, like how minecraft and other procedural games load in their terrain.

I am definitely open to suggestions if anybody has any though!

I would recommend using a quadtree to speed up the rendering checks. I would then only check chunks on the outside of a tree node to see if it needs to be rendered or not. I’ll have to look into 2D frustum culling I guess I never thought about that! Can’t you just check if the tile’s position is greater than the screen and maybe factor in the offset of the tile somewhere in there? I guess that is frustum culling… Hmm can’t believe I never thought of that!

UPDATE:

Here is the great result of optimization in action :slight_smile:

After putting tiles into a textureAtlas, FPS doubled.

After implementing some frustum culling, FPS is…well, I really don’t know, but it’s running at a steady 60fps, even when zoomed out.

Next up, I need to research Octrees and/or other chunk rendering methods in combination with frustum culling.

8k8oLnWopMw

Why octrees?
Quadtrees are used in 2D space (what your game seems to be).
Another speedup would be to put serveral (eg 100) tiles into one chunk, and generate one texture from this chunk.

Your worlds seems nice, what are you going to make from it?
Adding ports / vegetations maybe?

Ah ok, thankyou for correcting me, I honestly didn’t know :open_mouth:
I just kept seeing the term oct-trees being used a lot when people were talking about rendering chunks of a map.
Thinking about that now, all of the references were made in relation to games like minecraft, not a top down 2d game -_- (massive durp!).

So quad-trees makes more sense, I shall read up about them, thanks for correcting me :slight_smile:

I was reading before about using a chunk of tiles to create one texture as you just described :slight_smile:
I was thinking about getting to work on that tonight, but so far, all i’ve done today, other than a few little optimizations and uploading a youtube video showing results, is reading, reading, reading :stuck_out_tongue:

As for what I am going to do with the terrain, I won’t say just yet, it is a mystery for the time being.
But my intentions are obvious to make something awesome and fresh to the gaming scene (no surprise there!).

Clearly though, it involves sea-faring, and procedurally generated terrain. :stuck_out_tongue:
As I develop this more into a game, more will be revealed, but until I have something that resembles an actual game, I won’t be saying exactly what it is, as much as I would like to :stuck_out_tongue:

After some (light) reading, I am not sure if I should use quad-trees or something else just to render the map itself.
Quad-trees seem to be more so really for any entities on the map such as tree’s, plants, players, moving sprites etc, am I correct?

Currently, if I up the n value of the terrain generation class, it slows things down a hell of a lot, even changing from 7 to 9 slows my fps down dramatically, despite still only rendering tiles within the view port. I do wish to do massive sized game worlds that take a very long time for the player to traverse, and I think the only way i’m going to be able to do that is rendering in chunks. I just have to work out what the best way of doing this is. Is quad-trees sufficient for this? Or should I use some other method?

I understand the concept of quad-trees, it’s pretty straight forward, but once again, it’s the implementation that has me scratching my head (although, I haven’t really TRIED to implement it how I think I would yet).

Currently, this is my code to render tiles within the viewport for those interested:

x0, y0 = bottom left, top left viewport coordinates
x1, y1 = bottom right, top right viewport coordinates

Basically the code is saying that upon that iteration through the world map, if the tile location is within the constraints of the viewport (x0, y0, x1, y1), then draw the tiles to the screen, nothing too fancy going on here.

for(int x = 0; x < map.length; x++){
			w = (float) (x * .5);
			
			for(int y = 0; y < map[x].length; y++){
				h =  (float) (y * .5);
				
				if (w > x0 + 3 && h > y0 + 3 && w < x1 - 3 && h < y1 - 3){ //if x,y position of tile upon this iteration is within viewport, render tiles within loop
					if(map[x][y] == 0){
						batch.draw(deepwater, w, h, scalex, scaley);
					};
		
					if (map[x][y] ==1){
						batch.draw(water, w, h, scalex, scaley);
					}
	
					if (map[x][y] ==2){
						batch.draw(shallowwater, w, h, scalex, scaley);
					};
	
					if (map[x][y] ==3){
						batch.draw(wetsand, w, h, scalex, scaley);
					};
	
					if (map[x][y] ==4){
						batch.draw(sand, w, h, scalex, scaley);
					};
	
					if (map[x][y] ==5){
						batch.draw(grass, w, h, scalex, scaley);
					};
	
					if (map[x][y] ==6){
						batch.draw(darkgrass, w, h, scalex, scaley);					
					};
	
					if (map[x][y] ==7){
						batch.draw(lowmountain, w, h, scalex, scaley);
					};
	
					if (map[x][y] ==8){
						batch.draw(highmountain, w, h, scalex, scaley);
						
					};
	
					if (map[x][y] ==9){
						batch.draw(mountainpeak, w, h, scalex, scaley);
					};
				}
			}
		}

I’ve noticed that you aren’t disposing of your tile textures, causing memory leaks. Also, you should just load a big spritesheet with all of the tiles on it, then get texture regions for the tiles. Other than that, looking very good.

Two things.

First, instead of using a bunch of if statements, you should try using a switch statement. It would make it look like this:


switch(map[x][y]) {
    case 0:
        batch.draw(deepwater, w, h, scalex, scaley);
    case 1:
        batch.draw(water, w, h, scalex, scaley);
    case 2:
        //so on and so forth
}

Secondly, instead of checking to see if the tile is in the view, use math to find where the view is:


for(int x = x0; x < x1; x++) {//Subtract from x0 and add to x1 to render a little outside of the viewport
    for(int y = y0; y < y1; y++) {//Same as above
        //*Insert rendering code*
    }
}

This should make it faster, as you don’t have to check every block in the map.

Edit: I forgot to add in break statements to the switch statement! With them it looks like this:


switch(map[x][y]) {
    case 0:
        batch.draw(deepwater, w, h, scalex, scaley);
        break;
    case 1:
        batch.draw(water, w, h, scalex, scaley);
        break;
    case 2:
        //so on and so forth
}

Breaks are important because they break out of the switch statement, so that you aren’t checking for the value multiple times.

Thanks :slight_smile:

I was considering using switch statements, but wasn’t too sure if they would be as efficient with this sort of things. I actually really like switch statements, having used them in a procedural story generator I created, I just wasn’t sure if people really used them for map rendering.

As for iterating only what is inside of the viewport, I don’t know why I couldn’t quite figure out what to do, but then before bed last night, after a few american honey bourbons and coke, it occured to me that I was checking through every single tile on the map to determine what I should and shouldn’t render, so therefore the larger the map, the slower the performance, so proceeded to do exactly what you have here (literally the same), and it works a treat.
Seeing your post this morning with the added suggestion of using Switch statements helps clean up my code and hopefully run better too.

Since last night, I am able to render massive worlds (which is obviously a longer load time) with the same realtime rendering performance as a smaller world as i’m only checking tiles within the viewport.
For those who like numbers, previously, it struggled to render after generating 6,296,577 tiles, now it is able to render after generating 235,960,321 tiles, and probably more if the terrain generator wouldn’t run out of heap space.
It spits out the message:

[Quote] Exception in thread ā€œLWJGL Applicationā€ com.badlogic.gdx.utils.GdxRuntimeException: java.lang.OutOfMemoryError: Java heap space
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:116)
Caused by: java.lang.OutOfMemoryError: Java heap space
[/quote]
I will still look into quad-trees some more, as I know I will want to use those for entities etc

The wiki seems to be a great resource for this so far, there are a few other websites out there talking about quad-trees, and videos of people showing them off helped me understand in about 10 seconds how they work, where as reading about the quad-trees initially had me scratching my head :stuck_out_tongue:

The wiki does show the pseudo-code for implementation however, so I will create a class and toy around until I get something working some time.

Sorry to necro a thread, but I’m trying to get frustum culling work for my game, and I just honestly copied and pasted the frustum code. I always get a NPE though. This is my code:

@Override
	public void render(float delta) {
		Gdx.gl.glClearColor(0, 0, 1, 1);
		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

		batch.setProjectionMatrix(cam.combined);
		cam.update();
		cam.zoom = 0.5f;
		
		x0 = MathUtils.floor(cam.frustum.planePoints[0].x); 
	    y0 = MathUtils.floor(cam.frustum.planePoints[0].y); 
	    x1 = MathUtils.floor(cam.frustum.planePoints[2].x); 
	    y1 = MathUtils.floor(cam.frustum.planePoints[2].y); 
	      	      
	      if (x0 % 2 == 1){
	         x0 -= 1;
	      }
	      if (x0 < 0){
	         x0 = 0;
	      }
	      if (x1 > tiles.length){
	         x1 = tiles.length;
	      }
	      if (y0 < 0){
	         y0 = 0;
	      }
	      if (y1 > tiles[0].length){
	         y1 = tiles[0].length;
	      }

		batch.begin();
		for (int x = x0; x < x1; x++) {
			for (int y = y0; y < y1; y++) {
				tiles[x][y].getTile().draw(batch);
			}
		}
		batch.end();
		if(Gdx.input.isKeyPressed(Keys.A)) tx -= 16;
		if(Gdx.input.isKeyPressed(Keys.D)) tx += 16;
		if(Gdx.input.isKeyPressed(Keys.W)) ty += 16;
		if(Gdx.input.isKeyPressed(Keys.S)) ty -= 16;
		
		cam.translate(tx, ty, 0);
	}

The line that’s throwing the exception:

tiles[x][y].getTile().draw(batch);

Which corresponds to this function:

public Sprite getTile() {
		return tile;
	}

Tile is just an object of the LibGDX Sprite class. Any idea why its not working?