Previous Tutorial
Next Tutorial
Posted on: 27-10-2012

Gravity Platformer Tutorial #2 : Tiles

Now that we have got our workspace up and running we can start by examining our game. So lets just look back at what we said:

  • Tiled based platforming
  • Ability to shift gravity by 90 degrees at a time
  • Simple action combat with fantasy weapons

You may not notice it at first, but there are a few essential things missing here, but what are these?

Games exist because of four main elements.

  • Gameplay mechanics
  • Some kind of story or background story
  • The graphics or better said, the look and feel of your game
  • The technology on which the above are build
If you take away any of the above you don't have a game.

Now that we know what a game a based out of, we can tell what is missing. We have a basic gameplay mechanic (the 2d platforming, fighting enemies and the gravity switching) and some technology limitations (2d tile based). But it seems we are missing the look and feel and also a background story.

So lets tackle the look and feel first. We can do this by getting some sketches going or by simply creating some sprites.
I went ahead and made a small tileset for our first map, which you can download here:
download tileset

Now before we go and create the map, lets set up a small directory structure inside our workspace:

Don't worry too much about the tiles directory, this is just the directory I save the individual tiles, we don't really need them but they might be useful later on

Now we create a 32 wide and 18 high map in Tiled. If you don't know the program or don't have it installed, there will be a link below to the website.

To make sure the tutorial does not get too long I will provide you with a sample map that you can use:
download map

If you place both the tileset and the level file in the right folder you should be able to open up the map in the Tiled editor and it will look somewhat like this:

Now the reason that the map size is 32x18 is that this will be a 100% screen filling size, we will be setting up our game to render exactly 32 tiles wide and 18 tiles high, on any resolution.

Lets go and render this amazing map to the screen.

  1. First we want to create a few classes, lets get first rid of our SetupTest.java since we don't need it anymore
  2. We want to create 3 packages and in each of these packages will be one class for now:
  3. This is what our first class Game will look like:

    1. package game;
    2.  
    3. import game.state.LevelState;
    4.  
    5. import org.newdawn.slick.AppGameContainer;
    6. import org.newdawn.slick.GameContainer;
    7. import org.newdawn.slick.SlickException;
    8. import org.newdawn.slick.state.StateBasedGame;
    9.  
    10. public class Game extends StateBasedGame {
    11.  
    12. //set the window width and then the height according to a aspect ratio
    13. public static final int WINDOW_WIDTH = 1280;
    14. public static final int WINDOW_HEIGTH = WINDOW_WIDTH / 16 * 9;
    15. public static final boolean FULLSCREEN = false;
    16.  
    17. //1280x720 is our base, we use 32x32 tiles but we want it to be 40x40 at 1280x720
    18. //so our base scale is not 1 but 1.25 actually
    19. public static final float SCALE = (float) (1.25*((double)WINDOW_WIDTH/1280));
    20. public static final String GAME_NAME = "Gravity Platformer";
    21.  
    22. public Game() {
    23. super(GAME_NAME);
    24. }
    25.  
    26. public void initStatesList(GameContainer gc) throws SlickException {
    27.  
    28. //create a level state, this state will do the whole logic and rendering for individual levels
    29. addState(new LevelState("level_0"));
    30. this.enterState(0);
    31.  
    32. }
    33.  
    34. public static void main(String[] args) throws SlickException {
    35. AppGameContainer app = new AppGameContainer(new Game());
    36.  
    37. //set the size of the display to the width and height and fullscreen or not
    38. app.setDisplayMode(WINDOW_WIDTH, WINDOW_HEIGTH, FULLSCREEN);
    39. //this will attempt to create a framerate of approximately 60 frames per second
    40. app.setTargetFrameRate(60);
    41.  
    42. app.start();
    43. }
    44.  
    45. }

    It should be fairly easy to follow but I would like to clarify some things:

    First thing is that we will be using StateBasedGame instead of BasicGame, this makes it a lot easier to add menu's or credit screens that we can easily swap to. On line 30 you can see us entering state "0" which in this case is the LevelState

    On line 19 we determine the scale, the scale will be 1.25 for 1280x720, which in turn will scale the tiles up to 40x40 pixels instead of 32x32. This will result in 1280/40 = 32 and 720/40 = 18 tiles. Exactly our map width and height. Don't worry we will handle bigger maps soon enough! On 1920x1080 for example this will lead out to: 1920/1280 which is 1.5, then 1.5*1.25 which will be 1.875. 32*1.875 = 60x60 pixels. 1920/60 = 32 once again.

    The main reason behind the scaling is to give people the same experience no matter what resolution we use, this makes it a lot easier for us to make the information you might need to solve the puzzles fit on the screen, on every screen.

  4. Next up is our LeveState class:

    1. package game.state;
    2.  
    3. import game.Game;
    4. import game.level.Level;
    5.  
    6. import org.newdawn.slick.GameContainer;
    7. import org.newdawn.slick.Graphics;
    8. import org.newdawn.slick.Input;
    9. import org.newdawn.slick.SlickException;
    10. import org.newdawn.slick.state.BasicGameState;
    11. import org.newdawn.slick.state.StateBasedGame;
    12.  
    13. public class LevelState extends BasicGameState {
    14.  
    15. Level level;
    16. String startinglevel;
    17.  
    18. public LevelState(String startingLevel){
    19. this.startinglevel = startingLevel;
    20. }
    21.  
    22. public void init(GameContainer container, StateBasedGame sbg) throws SlickException {
    23. //once we initialize our level, we want to load the right level
    24. level = new Level(startinglevel);
    25. }
    26.  
    27. public void update(GameContainer container, StateBasedGame sbg, int delta) throws SlickException {
    28.  
    29. }
    30.  
    31. public void render(GameContainer container, StateBasedGame sbg, Graphics g) throws SlickException {
    32. g.scale(Game.SCALE, Game.SCALE);
    33. level.render();
    34. }
    35.  
    36. //this method is overriden from basicgamestate and will trigger once you press any key on your keyboard
    37. public void keyPressed(int key, char code){
    38. //if the key is escape, close our application
    39. if(key == Input.KEY_ESCAPE){
    40. System.exit(0);
    41. }
    42. }
    43.  
    44. public int getID() {
    45. //this is the id for changing states
    46. return 0;
    47. }
    48.  
    49. }

    Not that much to say here, all we do is load a starting level at first, then we tell OpenGL to scale our game to the scale we defined before and ask the level to render.

  5. Much more interesting is the Level class:

    1.  
    2. package game.level;
    3.  
    4. import org.newdawn.slick.SlickException;
    5. import org.newdawn.slick.tiled.TiledMap;
    6.  
    7. public class Level {
    8.  
    9. private TiledMap map;
    10.  
    11. public Level(String level) throws SlickException{
    12. map = new TiledMap("data/levels/" + level + ".tmx","data/img");
    13. }
    14.  
    15. public void render(){
    16. map.render(0, 0);
    17. }
    18.  
    19. }

    Yep, that is all. Slick2D has a build in parser for Tiled maps. All we have to do, is tell where the map file is, and where the tileset is located and Slick2D will load it for you.

    Lets take a look at the render() method of TiledMap. Currently we use render(int x, int y) to render our entire map starting at the coordinates we supply it with. But this is not really efficient if we had a large map, say 1000x1000 tiles. We can only see a maximum of 32 tiles wide and 18 tiles high. So lets change this to a differnt method:
    render(0, 0, 0, 0, 32, 18)
    What this function does is, it takes once again the starting x and y (in pixels). This is followed by another starting x and y, but this time its the x and y (both in tiles) of the map itself. And then the amount tiles wide and high we want to draw.

    Because it might be a bit confusing to understand here is a small illustration of what this function does:

    The end result

    Closing notes

    And that was all for this tutorial, I hope you learned a bit today, next tutorial we will look at our background story and attempt to get a character in our game

    Links

Categories: Game Design, Game Development, Java, Tutorial

Comments

Kyle said: (17-11-2012)
The map download link doesn't seem to work. I was trying to just make a map using Tiled, but I don't know the dimensions of the tiles in the tileset. Can you tell me what they are?
Frums said: (19-11-2012)
I'm sorry for the late response, the map download seems to work for me, you want to right click and then "save link as".

The dimensions that I use in this game are 32 by 32 pixels.
Michael said: (06-12-2012)
Thanks for the tutorial!
"On like 19 " should be line by the way.
FunPhoneBooks said: (15-08-2013)
> 4. Next up is our LeveState class:
I think you meant LevelState.

Great tutorial!
Hal Crabb IV said: (25-03-2015)
What version of Slick 2D & LWJGL are you using for this tutorial frums? I currently have used the 2.9.3 version and sometimes it doesn't support various functions as I get his error when running it: "Resource not found: 'Data/Images/grassy_tileset.png' ". Obviously, I've renamed data to Data, img to Images but other than that I have everything the same as in your tutorial screenshot (except the individual tiles in the tiles folder but that shouldn't matter - I'm assuming). Do you know what could be causing this error?



What is the name of the website? (to counter the spam)