Java-Gaming.org Hi !
Featured games (90)
games approved by the League of Dukes
Games in Showcase (753)
Games in Android Showcase (228)
games submitted by our members
Games in WIP (842)
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  
  Can I Improve this level loader?  (Read 1668 times)
0 Members and 1 Guest are viewing this topic.
Offline Gibbo3771

JGO Kernel


Medals: 128
Projects: 5
Exp: 1 year


Currently inactive on forums :(


« Posted 2014-02-25 10:39:38 »

I am in the process of practising loading from files, using 2D arrays and such and decided to make a project off it. It will basically be a top down 2D shooter with predefined "puzzle" levels.

At first I tried to create these levels in a text file using certain values for certain tile types, that was just shit to look at.

So decided to, instead draw my levels in Photoshop and read the pixel color data and determine what should be created at that location. I am using Box2D and each of my tiles is 1x1 at the moment, I might make them smaller but anyway, enough talking.

I am looking for ways to improve this, as it stands it can read an image file easy enough, it knows that a pixel of pure black = a wall and white = empty etc etc.

Here it is:

This is my LevelLoader class, it reads an image file (hard coded atm) and reads the pixel data which is then converted to a binary string and stored in 2D array. The Level class calls both methods in here, obviously it calls Load() first then Generate().
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  
public class LevelLoader {

   /** How many cells along the x axis the level has */
   public int width;
   /** How many cells along the y axis the level has */
   public int height;
   /** Pixel information for each cell */
   public String[][] cells;

   /** Image to read the file from */
   Pixmap levelInfo;
   
   // Constructor omitted, it's default


/**
    * Load an image from file and store it in memory, read the information and
    * store it in the array for generation
    */

   public void load() {

      try {
         levelInfo = new Pixmap(
               Gdx.files.internal("data/levels/leveltest.png"));
         width = levelInfo.getWidth();
         height = levelInfo.getHeight();
         cells = new String[width][height];
         for (int x = 0; x < levelInfo.getWidth(); x++) {
            for (int y = 0; y < levelInfo.getHeight(); y++) {
               String val = Integer.toBinaryString(levelInfo
                     .getPixel(x, y));
               cells[x][y] = val;
               // System.out.print(" " + cells[x][y]);
            }
            // System.out.println("");
         }

      } catch (Exception e) {
         e.printStackTrace();
      } finally {
         levelInfo.dispose();
      }

   }

/**
    * Generate the level
    *
    * @param level
    *            give reference to level in order to add the tiles to an array
    */

   public void generate(Level level) {
      for (int x = 0; x < width; x++) {
         for (int y = 0; y < height; y++) {
            TileType type = TileType.getType(cells[x][y]);
            if (type == null)
               continue;
            switch (type) {
            case WALL:
               level.tiles.add(new Wall(x + 1, y + 1));
               break;
            case PLAYER_SPAWN:
               level.player = new Player(x + 1, y + 1);
               break;
            case EMPTY:
               level.tiles.add(new Empty(x + 1, y + 1));
            default:
               break;
            }
         }
      }

   }


The Generate() method is what I am not sure of, it works but is it ideal/efficient? It basically reads the data in the array (which is a bunch of binary data) and then compares to to an enum using a hashmap 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  
public enum TileType {
      PLAYER_SPAWN("111111110000000011111111"),
      WALL("11111111"),
      EMPTY("11111111111111111111111111111111");

      /** Used to search through the ID's of the cell enums */
      private static final HashMap<String, TileType> search = new HashMap<String, TileType>();

      /** Binary ID of the cell */
      String binary;

      private TileType(String binary) {
         this.binary = binary;
      }

      public String getBinary() {
         return binary;
      }

      /** Iterate through enum and assign values to map */
      static {
         for (TileType type : EnumSet.allOf(TileType.class)) {
            search.put(type.getBinary(), type);
         }
      }

      /** Get the type of this cell given its binary data */
      public static TileType getType(String id) {
         return search.get(id);
      }

   }


Is there any improvements you can see on this? At first I used text files, then I used buffered image and read the RBG formats and converted to Hex, that was just horrible. I can easily draw my levels in an image editor this way and any time I want to add a new object to the generation, all I need to do is take the hex value of the colour from the editing program and convert it to binary and add it to the enum.

At the moment all it will be generating is walls, the player and empty cells.

Just to clarify why I am creating empty cells, it just makes it easier to spawn enemies/drops during the game without them landing inside any walls and stuff.

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline trollwarrior1
« Reply #1 - Posted 2014-02-25 12:57:52 »

Writing your own files instead of drawing stuff on images is a lot flexible. If you aren't looking for flexibility, you can just use images, but its not flexible at all.

If you're going to use text files, I hope you didn't manually edit the values. You're supposed to make your level editor or use this one if you're looking for simple editing.
http://www.java-gaming.org/topics/level-editor-tool-for-2d-games/31204/view.html
Offline Gibbo3771

JGO Kernel


Medals: 128
Projects: 5
Exp: 1 year


Currently inactive on forums :(


« Reply #2 - Posted 2014-02-25 15:21:17 »

Writing your own files instead of drawing stuff on images is a lot flexible. If you aren't looking for flexibility, you can just use images, but its not flexible at all.

I think it will be flexible enough for what I plan on using it for, I have created 3 levels varying from 32x32 tiles and 128x128 tiles, seems to be working OK.

So for flexibility I should write my own for that I can parse and read in as a level?

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline TeamworkGuy2

Junior Devvie


Medals: 10



« Reply #3 - Posted 2014-02-25 15:47:33 »

It's not necessarily an optimization (although it kind of is).  When code converts one data type to another there should be a clear reason, the data should be better represented by the newly converted to type.
Converting a pixel, which can quite easily be represented by an integer, to a string feels strange.  What does a string have to do with a pixel?  Even if you want it as a string for readability, I would still store it as an integer and just toString it when you want to see it's value.

In this code...
1  
2  
3  
4  
5  
6  
7  
8  
9  
for (int x = 0; x < levelInfo.getWidth(); x++) {
      for (int y = 0; y < levelInfo.getHeight(); y++) {
            String val = Integer.toBinaryString(levelInfo
                  .getPixel(x, y));
            cells[x][y] = val;
            // System.out.print(" " + cells[x][y]);
      }
      // System.out.println("");
}

I feel like the String[][] cell array could be int[][] instead.

Then in the enum, just change the binary ID of each enum to an integer like so...
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  
public enum TileType {
      PLAYER_SPAWN( Integer.parseInt("111111110000000011111111", 2) ),
      WALL( Integer.parseInt("11111111", 2) ),
      EMPTY( Integer.parseInt("11111111111111111111111111111111", 2) );

      /** Used to search through the ID's of the cell enums */
      private static final HashMap<Integer, TileType> search = new HashMap<Integer, TileType>();

      /** Binary ID of the cell */
      int binary;

      private TileType(int binary) {
         this.binary = binary;
      }

      public int getBinary() {
         return binary;
      }

      /** Iterate through enum and assign values to map */
      static {
         for (TileType type : EnumSet.allOf(TileType.class)) {
            search.put((Integer)type.getBinary(), type);
         }
      }

      /** Get the type of this cell given its binary data */
      public static TileType getType(int id) {
         return search.get((Integer)id);
      }

   }


Granted, this might require a few other changes in your code, but probably not many.
Just my thoughts, not necessarily right or wrong either way.
Offline Gibbo3771

JGO Kernel


Medals: 128
Projects: 5
Exp: 1 year


Currently inactive on forums :(


« Reply #4 - Posted 2014-02-25 16:24:14 »

I had this originally, the problem I had with it was that the value of a 4 channel image (rgba) had some insane value such as -162578543, which looked to me a little sore on the eyes. I chose binary because I could easily take the hex value from the editor and change it binary.

The way you suggested it worked fine but I dunno, just couldn't settle on it

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline Danny02
« Reply #5 - Posted 2014-02-25 16:28:59 »

why don't you write it in directly in hex than

1  
2  
3  
PLAYER_SPAWN(0xFF_00_FF),
WALL(0x00_00_FF),
EMPTY(0xFF_FF_FF);
Offline Gibbo3771

JGO Kernel


Medals: 128
Projects: 5
Exp: 1 year


Currently inactive on forums :(


« Reply #6 - Posted 2014-02-25 16:36:13 »

why don't you write it in directly in hex than

1  
2  
3  
PLAYER_SPAWN(0xFF_00_FF),
WALL(0x00_00_FF),
EMPTY(0xFF_FF_FF);


I'll change it back to hex I think, it gave me weird results for some reason, maybe my code was just buggy and I got impatient.

Will let you know.

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline digdugdiggy

Junior Devvie


Medals: 1



« Reply #7 - Posted 2014-02-25 17:13:51 »

I have a similar system, but I use a program called Tiled to draw my maps.
Natively, Tiled outputs a few specific TMX map formats which you can get a java library to read (complicated), but I have had little success with.

I simply load my spritesheet into tiled, and draw a map. Then I export the .TMX map file as CSV. Once that is done, I simple open the .TMX with notepad, copy the relevant array of integers the program has made for me, and plunk it into my own text file. All I use tiled for is numbering the spritesheet really, though it has all kinds of advanced features.

 This makes it really easy to design maps visually instead of with text.

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  
package Manager.Map;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class MapFileLoader {

    // size of map
    private int numCols, numRows, spriteSheetIndex, layerID, MAPID;

    // holds integer array of read map data
    private int[][] parsedLayer;
   
    private BufferedReader br;
    private final String delims = "\\s+";

    public MapFileLoader(String path) {
        InputStream in = getClass().getResourceAsStream(path);
        br = new BufferedReader(new InputStreamReader(in));

        parseFileHeader(br);
        parseMapFile(br);
        close(br);
       
    }

    // PRIVATE METHODS
    private void parseFileHeader(BufferedReader br) {
        try {
            MAPID = Integer.parseInt(br.readLine());
            numCols = Integer.parseInt(br.readLine());
            numRows = Integer.parseInt(br.readLine());
            spriteSheetIndex = Integer.parseInt(br.readLine());
            layerID = Integer.parseInt(br.readLine());
        } catch (IOException | NumberFormatException e) {
            e.printStackTrace();
        }
    }

    private void parseMapFile(BufferedReader br) {
        try {
            parsedLayer = new int[numRows][numCols];

            for (int row = 0; row < numRows; row++) {
                String line = br.readLine();

                String[] tokens = line.split(delims);
                for (int col = 0; col < numCols; col++) {
                    parsedLayer[row][col] = (Integer.parseInt(tokens[col]));
                                       
                }                
            }
        } catch (IOException | NumberFormatException e) {
            e.printStackTrace();
        }
    }

    private void close(BufferedReader br) {
        try {
            br.close();
        } catch (IOException | NumberFormatException e) {
            e.printStackTrace();
        }
    }
   
    // PUBLIC METHODS

    public int getWidth() {
        return numCols;
    }

    public int getHeight() {
        return numRows;
    }
   
    public int getSpriteSheetIndex(){
        return spriteSheetIndex;
    }

    public int[][] getMapData() {
        return parsedLayer;
    }
    public int getLayerID(){
        return layerID;
    }
    public int getMapID(){
        return MAPID;
    }

}

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  
package Manager.Map;

import gfx.SpriteSheet;
import java.awt.image.BufferedImage;

public class MapTranslator {
   
    // change int[][] into BufferedImage[][] using Spritesheets[]

    private final int[][] parsedLayer;
    private final BufferedImage[][] Map;
    private final SpriteSheet ss1;
   
    public MapTranslator(int[][] inputArray, SpriteSheet inputSS){
        this.parsedLayer = inputArray;
        this.ss1 = inputSS;
        this.Map = new BufferedImage[parsedLayer.length][parsedLayer[0].length];
       
        Translate();
       
    }
   
    private void Translate(){ // int array into image array
        for (int y = 0; y < parsedLayer[0].length; y++){
            for (int x = 0; x < parsedLayer.length; x++){
                Map[x][y] = ss1.getSingleTile(parsedLayer[y][x]);
            }
        }
    }
    public BufferedImage[][] getMap(){
        return Map;
    }
}
Offline Gibbo3771

JGO Kernel


Medals: 128
Projects: 5
Exp: 1 year


Currently inactive on forums :(


« Reply #8 - Posted 2014-02-26 10:18:24 »

I've used Tiled before, briefly and it is a damn powerful tool, however in the name of knowledge I am trying to do some things myself just to get a better understanding. It's just nice to know that if I wanted to make a map editor... I could, im not going to but it's good to say I can Lol.

Helps for portfolio and stuff as well.

Appreciate all the replies, I changed the id of the tile to a hex format but was giving me strange results. I am sticking to binary values just now as it seems to be working, although horrible to look at. I will probably create so sort of algorithm later to resolve it to a more readable format.


"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Pages: [1]
  ignore  |  Print  
 
 

 
ivj94 (584 views)
2018-03-24 14:47:39

ivj94 (48 views)
2018-03-24 14:46:31

ivj94 (382 views)
2018-03-24 14:43:53

Solater (62 views)
2018-03-17 05:04:08

nelsongames (109 views)
2018-03-05 17:56:34

Gornova (159 views)
2018-03-02 22:15:33

buddyBro (703 views)
2018-02-28 16:59:18

buddyBro (92 views)
2018-02-28 16:45:17

xxMrPHDxx (493 views)
2017-12-31 17:17:51

xxMrPHDxx (733 views)
2017-12-31 17:15:51
Java Gaming Resources
by philfrei
2017-12-05 19:38:37

Java Gaming Resources
by philfrei
2017-12-05 19:37:39

Java Gaming Resources
by philfrei
2017-12-05 19:36:10

Java Gaming Resources
by philfrei
2017-12-05 19:33:10

List of Learning Resources
by elect
2017-03-13 14:05:44

List of Learning Resources
by elect
2017-03-13 14:04:45

SF/X Libraries
by philfrei
2017-03-02 08:45:19

SF/X Libraries
by philfrei
2017-03-02 08:44:05
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!