I really do hate asking for help but here I am asking
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?
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,
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
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
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
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!
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
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!
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:
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 (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() {
}
}
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
So I can say that simply by putting my textures into a textureAtlas, that my fps has effectively doubled
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
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!
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
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
I was reading before about using a chunk of tiles to create one texture as you just described
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
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.
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
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.
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.
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
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?