Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (475)
Games in Android Showcase (106)
games submitted by our members
Games in WIP (530)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  [LibGDX] 2D (top down) procedurally generated terrain.  (Read 8653 times)
0 Members and 1 Guest are viewing this topic.
Offline Otreum

Junior Member


Medals: 6



« Posted 2013-07-30 14:12:18 »

I really do hate asking for help but here I am asking 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 Smiley

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/
Online Herjan
« Reply #1 - Posted 2013-07-30 14:38:34 »

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:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
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,

Offline Otreum

Junior Member


Medals: 6



« Reply #2 - Posted 2013-07-31 13:40:33 »

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 Smiley

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Otreum

Junior Member


Medals: 6



« Reply #3 - Posted 2013-08-07 12:16:28 »

After much brain deadening, I have RESULTS! Cheesy

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 Smiley

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 Smiley

Here is a video of the progress Smiley

<a href="http://www.youtube.com/v/XoAxThXeN8I?version=3&amp;hl=en_US&amp;start=" target="_blank">http://www.youtube.com/v/XoAxThXeN8I?version=3&amp;hl=en_US&amp;start=</a>
Offline orogamo
« Reply #4 - Posted 2013-08-07 14:43:05 »

A little snippet of code to not render tiles outside specific radius.
1  
2  
3  
public static double calcDistance(double x1, double y1, double x2, double y2) {
   return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}


Usage:
1  
2  
3  
4  
5  
6  
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 Smiley
Online Herjan
« Reply #5 - Posted 2013-08-07 14:47:15 »

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! Cheesy

Offline opiop65

JGO Kernel


Medals: 153
Projects: 7
Exp: 3 years


JumpButton Studios


« Reply #6 - Posted 2013-08-07 16:51:02 »

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!

Offline Otreum

Junior Member


Medals: 6



« Reply #7 - Posted 2013-08-08 00:01:28 »

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! Cheesy
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.

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!

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 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! Smiley
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:
http://javagamexyz.blogspot.com.au/2013/03/the-game-map-level-2.html
http://javagamexyz.blogspot.com.au/2013/03/the-game-map-pt-2-level-2.html
http://javagamexyz.blogspot.com.au/2013/03/terrain-generation.html

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):
http://javagamexyz.blogspot.com.au/2013/03/terrain-generation.html

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:
1  
2  
3  
4  
5  
6  
7  
8  
  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).

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
  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.


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
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 Tongue (I hold no responsibility for brain aneurysms as a result of crappy code Tongue):

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
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() {
   }

}
Offline Otreum

Junior Member


Medals: 6



« Reply #8 - Posted 2013-08-08 07:59:26 »

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 Tongue

So I can say that simply by putting my textures into a textureAtlas, that my fps has effectively doubled Smiley
Offline opiop65

JGO Kernel


Medals: 153
Projects: 7
Exp: 3 years


JumpButton Studios


« Reply #9 - Posted 2013-08-08 17:00:23 »

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?

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Otreum

Junior Member


Medals: 6



« Reply #10 - Posted 2013-08-09 10:52:24 »

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 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 Tongue)

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!
Offline opiop65

JGO Kernel


Medals: 153
Projects: 7
Exp: 3 years


JumpButton Studios


« Reply #11 - Posted 2013-08-09 12:44:14 »

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!

Offline Otreum

Junior Member


Medals: 6



« Reply #12 - Posted 2013-08-10 06:03:12 »

UPDATE:

Here is the great result of optimization in action Smiley

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.

<a href="http://www.youtube.com/v/8k8oLnWopMw?version=3&amp;hl=en_US&amp;start=" target="_blank">http://www.youtube.com/v/8k8oLnWopMw?version=3&amp;hl=en_US&amp;start=</a>
Offline RobinB

JGO Ninja


Medals: 44
Projects: 1
Exp: 3 years


Spacegame in progress


« Reply #13 - Posted 2013-08-10 09:59:51 »

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?
Offline Otreum

Junior Member


Medals: 6



« Reply #14 - Posted 2013-08-10 11:06:38 »

Ah ok, thankyou for correcting me, I honestly didn't know :O
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 Smiley

I was reading before about using a chunk of tiles to create one texture as you just described Smiley
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 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. 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 Tongue


Offline Otreum

Junior Member


Medals: 6



« Reply #15 - Posted 2013-08-10 14:52:48 »

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.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
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);
               };
            }
         }
      }
Offline Longarmx
« Reply #16 - Posted 2013-08-10 16:28:50 »

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.

Offline Geemili

Senior Member


Medals: 9
Projects: 1
Exp: 2 years


No Games Finished


« Reply #17 - Posted 2013-08-10 18:32:35 »

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:
1  
2  
3  
4  
5  
6  
7  
8  
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:
1  
2  
3  
4  
5  
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:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
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.
Offline Otreum

Junior Member


Medals: 6



« Reply #18 - Posted 2013-08-11 02:51:43 »

Thanks Smiley

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

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 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.

http://en.wikipedia.org/wiki/Quadtree
Offline opiop65

JGO Kernel


Medals: 153
Projects: 7
Exp: 3 years


JumpButton Studios


« Reply #19 - Posted 2013-08-30 19:59:11 »

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:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
@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:
1  
tiles[x][y].getTile().draw(batch);

Which corresponds to this function:
1  
2  
3  
public Sprite getTile() {
      return tile;
   }

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

Offline Otreum

Junior Member


Medals: 6



« Reply #20 - Posted 2013-09-10 14:06:18 »

I'm not as advanced as others here, but I guess any time I have a null pointer exception, I immediately think "what ISN'T initialized properly".

So I guess I would ask how is "tile" initialized?
Offline Several Kilo-Bytes

Senior Member


Medals: 11



« Reply #21 - Posted 2013-09-10 23:19:05 »

At least find out which reference is null. Use a debugger or print something.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
      for (int x = x0; x < x1; x++) {
         for (int y = y0; y < y1; y++) {
            try{
                  tiles[x][y].getTile().draw(batch);
            }
            catch(NullPointerException e)
            {
                  System.out.println(x + ", " + y);
                  System.out.println(Objects.toString(tiles));
                  System.out.println(Objects.toString(batch));
                  System.out.println(tiles == null ? "..." : Objects.toString(tiles[x][y].getTile());
                  e.printStackTrace();
                  System.exit(1);
            }
         }
      }


Also, Hugo Elias's "Perlin noise" is not Perlin noise. Read Ken Perlin's presentation if you care what Perlin noise actually is. Simplex noise was also made by Perlin. Different types of gradient noise are often interchangeable and the concept behind the algorithms are simple, yet it is not necessary to know it. It is absolutely important to know what "Perlin noise" is NOT, so you can wrap your mind around the various totally different concepts people describe which people think are related to Perlin noise. Then you can look at the algorithms as something totally unrelated and just disregard the Perlin part, docking points based on how absurdly they get the terminology wrong.
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

ctomni231 (33 views)
2014-07-18 13:55:21

Zero Volt (29 views)
2014-07-18 06:47:54

danieldean (24 views)
2014-07-18 06:41:23

MustardPeter (26 views)
2014-07-17 06:30:00

Cero (41 views)
2014-07-16 07:42:17

Riven (43 views)
2014-07-15 01:02:53

OpenGLShaders (31 views)
2014-07-14 23:23:47

Riven (30 views)
2014-07-14 18:51:35

quew8 (29 views)
2014-07-13 20:57:52

SHC (64 views)
2014-07-13 00:50:04
HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54

HotSpot Options
by Roquen
2014-05-06 15:03:10

Escape Analysis
by Roquen
2014-04-29 22:16:43

Experimental Toys
by Roquen
2014-04-28 13:24:22
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!