Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (512)
Games in Android Showcase (119)
games submitted by our members
Games in WIP (576)
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  
  Storing a level in a picture  (Read 3159 times)
0 Members and 1 Guest are viewing this topic.
Offline nhmllr

Senior Duke


Medals: 1
Projects: 3


slow and steady...


« Posted 2012-07-20 18:56:04 »

I once saw a live stream of Notch making a game, and he planned out his 2D game levels with a picture. Each pixel represented a tile in the level, and I assume that at the start of the game his code reads the picture and translates the different color pixels into different tiles in the game. As in, a black pixel could represent a block of air, a white pixel could represent a solid block that the character can stand on, and a yellow pixel could represent some enemy

That's what I want to do. I want to be able to take a picture, and based on the color of the pixel. It'll make editing level maps so much easier.

So.... What's the fastest way to do this? What functions should I use?
Doesn't seem like it should be too hard

Thanks
Offline jonjava
« Reply #1 - Posted 2012-07-20 19:01:53 »

Basically the only thing you need to do is get the images pixels as an array (it's raster), and then look at each pixel for the rgb color information and based on that info build your map.

I mean basically each pixel can hold an int, so you can have Integer.MAX_VALUE amount of different ID's but it's going to get hard to create the maps if you don't have a fixed number of easily differentiable colors.

Imho, it's too much of a hassle to save the map as an image (because you need to have a list of colors -> id's -> actual object in your game) but it's certainly possible.

Like, what if you have two objects in the same place? Maybe have each byte of the int decide what object to create. So you can have 4 different objects per pixel but then you're only left with 255 id's. Then again, 255 id's is probably way more than enough :}

Offline nhmllr

Senior Duke


Medals: 1
Projects: 3


slow and steady...


« Reply #2 - Posted 2012-07-20 19:26:03 »

My motivation behind this is that I'm making an RPG, and originally I thought I was going to have randomly generating maps, and even though I succeeding in making convincing maps, I decided that carefully thought out and paced levels will add more character and is the better option.

Storing a map in an image makes it easier to edit, I think. And I don't have nearly enough block types for it to be a problem.

What methods would you use to get the RGB values from an image?
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline matheus23

JGO Kernel


Medals: 109
Projects: 3


You think about my Avatar right now!


« Reply #3 - Posted 2012-07-20 19:46:09 »

If you use BufferedImages, use .getRGB()...

See my:
    My development Blog:     | Or look at my RPG | Or simply my coding
http://matheusdev.tumblr.comRuins of Revenge  |      On Github
Offline nhmllr

Senior Duke


Medals: 1
Projects: 3


slow and steady...


« Reply #4 - Posted 2012-07-20 19:56:18 »

Yeah, I found the source code for that game I was talking about, so I'm studying it. getRGB() seems to be the right thing
Especially since you can get it to just return the array, meaning that you don't have to muck around with weird loops
Cool cool, my plan is working just as expected
Offline Rorkien
« Reply #5 - Posted 2012-07-20 20:09:15 »

Well, given the java default tools, reading pixel info from an image is much easier than parsing a xml file or your own data format. Simpler too.
And it is almost perfect for a platformer, since you can work with multiple layers

PS: Are you reading Metagun source code? Cause he ran into some problems using this on Prelude of Chambered, as he wanted to put more decoration on the walls, but you can only have 1 object per tile. But i definitely wouldn't mind it if i had 48h to make a game  Cheesy
Offline gouessej
« Reply #6 - Posted 2012-07-20 21:35:08 »

I once saw a live stream of Notch making a game, and he planned out his 2D game levels with a picture.
I do the same in 3D  Wink

Offline UprightPath
« Reply #7 - Posted 2012-07-20 21:47:11 »

Honestly, it depends on how you want to organize your color information. You can get 256 (0-255) out of each colour channel that you have (Three if it's a 24-bit image, Four if it's a 32-bit). This means that you can end up storing quite a bit of information in one and it's "Easy" to use.

However, remember that once you get to about six or seven colours (Beyond Black/White) it becomes harder and harder for the human eye to differentiate between them, even on a computer screen. This is especially true if they're all shades/tints of the same primary colour. If you're trying to draw it, it gets rather messy rather fast, especially if you are trying to provide several different forms of information in a single image (IE- Red is the logical tile definition, Blue is the graphical tile definition, Green is the item definition, and Alpha is something else).

If you're planning on writing a level editor that would output it to an image file, then you're probably still better off using some other format that isn't constrained that way.

That isn't to say that it's an expressly bad idea to have a parser that will translate an image into level data, for quick prototyping of your level data, it'll work very well (All of the levels that I've been messing around with in my Platformer are stored as graphical data). But when you get to actually attempting to design everything, you'll probably be better off with a different method.

Edit: Another thread on the question of storing levels as images

Offline davedes
« Reply #8 - Posted 2012-07-20 23:31:25 »

PNG and GIF could be very useful storage formats for compressing large amounts of map data, as their algorithms work on repeating adjacent pixels. A typical tiled map may have a lot of neighbouring repetition, which means there will be lots of room for compression. An 8-bit, opaque and indexed colour image would lead to a very small filesize.

You could also very easily upload the map's data to a GL texture and do some really crazy (useless) stuff with shaders.

If you wanted to get really ambitious, you could write your own encoder/decoder based on PNG/GIF compression... but that's just overkill. Tongue

Quote
However, remember that once you get to about six or seven colours (Beyond Black/White) it becomes harder and harder for the human eye to differentiate between them
This is why you should build your own visual editor, or use Tiled. Smiley

Offline UprightPath
« Reply #9 - Posted 2012-07-20 23:38:45 »

This is why you should build your own visual editor, or use Tiled. Smiley

True! However, if you're going to use something like Tiled why not use the XML it generates and the classes it has that read it?

And yes, I did already mention making your own editor. :3 Hah. But like I said, you do get really constrained by the amount of information that you can have within a single pixel. For anything more complicated than "This is this tile", "This item spawns here" and "This enemy spawns here", and only having a maximum of one each in a given pixel, then it starts to get too complicated to use (Yes, you could use several different images, compressed into a single file, to indicate multiple depths of items/possibilities, however that's really getting complicated. xD)

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Rorkien
« Reply #10 - Posted 2012-07-21 00:09:18 »

PNG and GIF could be very useful storage formats for compressing large amounts of map data, as their algorithms work on repeating adjacent pixels. A typical tiled map may have a lot of neighbouring repetition, which means there will be lots of room for compression. An 8-bit, opaque and indexed colour image would lead to a very small filesize.

You could also very easily upload the map's data to a GL texture and do some really crazy (useless) stuff with shaders.

If you wanted to get really ambitious, you could write your own encoder/decoder based on PNG/GIF compression... but that's just overkill. Tongue

Quote
However, remember that once you get to about six or seven colours (Beyond Black/White) it becomes harder and harder for the human eye to differentiate between them
This is why you should build your own visual editor, or use Tiled. Smiley

GIF is 8-bit, so PNG is the way to go, everywhere. Unless you want animation.
Offline Sickan

Senior Duke


Medals: 9



« Reply #11 - Posted 2012-07-21 00:10:39 »

PNG and GIF could be very useful storage formats for compressing large amounts of map data, as their algorithms work on repeating adjacent pixels. A typical tiled map may have a lot of neighbouring repetition, which means there will be lots of room for compression. An 8-bit, opaque and indexed colour image would lead to a very small filesize.

You could also very easily upload the map's data to a GL texture and do some really crazy (useless) stuff with shaders.

If you wanted to get really ambitious, you could write your own encoder/decoder based on PNG/GIF compression... but that's just overkill. Tongue

Quote
However, remember that once you get to about six or seven colours (Beyond Black/White) it becomes harder and harder for the human eye to differentiate between them
This is why you should build your own visual editor, or use Tiled. Smiley

AFAIK, GIF is 8-bit only

PNG is the way to go, everywhere. Unless you want animation.
I wouldn't say that you should store animations for games in .GIF images. It just feels wrong, and there's probably something technical to it too.

Cheers Smiley
Offline Rorkien
« Reply #12 - Posted 2012-07-21 00:13:51 »

I was just listing the advantages of GIF over PNG, internet-wise.

Because if i wanted animation on a game i would separate the frames side-by-side on a big image and then split and animate it. Using PNG.  Grin
Offline davedes
« Reply #13 - Posted 2012-07-21 03:02:26 »

PNG and GIF could be very useful storage formats for compressing large amounts of map data, as their algorithms work on repeating adjacent pixels. A typical tiled map may have a lot of neighbouring repetition, which means there will be lots of room for compression. An 8-bit, opaque and indexed colour image would lead to a very small filesize.

You could also very easily upload the map's data to a GL texture and do some really crazy (useless) stuff with shaders.

If you wanted to get really ambitious, you could write your own encoder/decoder based on PNG/GIF compression... but that's just overkill. Tongue

Quote
However, remember that once you get to about six or seven colours (Beyond Black/White) it becomes harder and harder for the human eye to differentiate between them
This is why you should build your own visual editor, or use Tiled. Smiley

GIF is 8-bit, so PNG is the way to go, everywhere. Unless you want animation.
Many 2D games won't have more than 255 tiles in a tile set, so GIF works fine. The compression algorithm works a bit differently, so it may be beneficial to use GIF in some situations over an 8bit PNG.

Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #14 - Posted 2012-07-21 08:05:46 »

Notch just took the easy out and had a huge if-statement sequence, checking for specific RGB values. Felix (an opensource game, made for you to look at) does the same thing. If you google Felix begins, it should come up. There's only a few classes, so it should be easy to find.

Depending on how developed your game is, you can make it more sophisticated than just a bunch of ifs.

Offline loom_weaver

JGO Coder


Medals: 17



« Reply #15 - Posted 2012-07-21 14:28:39 »

Here's some partial code that shows how to load an image and then iterate over the pixels within it.

TerrainTile2 is a simple class storing an id, symbol, color, and description.

The only other gotcha is the load happens asynchronously.

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  
    public SurfaceLayer() {
        URL url = getClass().getResource("top.gif");
        image = Toolkit.getDefaultToolkit().getImage(url);
       
        terrainTileCache.add(new TerrainTile2(
            new Integer(0), "default x", Color.GREEN, "."));
        terrainTileCache.add(new TerrainTile2(
            new Integer(1), "water 1", new Color(0, 0, 64), "~"));  // ~
        terrainTileCache.add(new TerrainTile2(
            new Integer(2), "water 2", new Color(0, 0, 128), "~"));
        terrainTileCache.add(new TerrainTile2(
            new Integer(3), "water 3", new Color(0, 0, 255), "~"));  // ~
        terrainTileCache.add(new TerrainTile2(
            new Integer(4), "brown 1", new Color(64, 64, 0), "."));
        terrainTileCache.add(new TerrainTile2(
            new Integer(5), "brown 2", new Color(128, 128, 0), "."));
        terrainTileCache.add(new TerrainTile2(
            new Integer(6), "brown 3", new Color(255, 255, 0), "."));
        terrainTileCache.add(new TerrainTile2(
            new Integer(7), "tree 1", new Color(0, 64, 0), "t"));
        terrainTileCache.add(new TerrainTile2(
            new Integer(8), "tree 2", new Color(0, 128, 0), "t"));
        terrainTileCache.add(new TerrainTile2(
            new Integer(9), "tree 3", new Color(0, 255, 0), "t"));
        terrainTileCache.add(new TerrainTile2(
            new Integer(10), "mountain peak 1", new Color(64, 64, 64), "^"));
        terrainTileCache.add(new TerrainTile2(
            new Integer(11), "mountain peak 2", new Color(128, 128, 128), "^"));
        terrainTileCache.add(new TerrainTile2(
            new Integer(12), "mountain peak 3", new Color(255, 255, 255), "^"));
       
        terrain[0][0] = terrainTileCache.get(0);
        elevation[0][0] = 0;
    }
   
    public void load() {
        height = image.getHeight(null);
        width = image.getWidth(null);

        if (height < 0 || width < 0) {
            System.out.println("image not loaded yet, try again");
            return;
        }
       
        // Create empty BufferedImage, sized to Image
        BufferedImage buffImage =
          new BufferedImage(
              width,
              height,
              BufferedImage.TYPE_INT_ARGB);

        // Draw Image into BufferedImage
        Graphics g = buffImage.getGraphics();
        g.drawImage(image, 0, 0, null);
       
        terrain = new TerrainTile2[width][height];
        elevation = new int[width][height];
       
        for (int a = 0;  a < height;  a++) {
            for (int b = 0;  b < width;  b++) {
                int i = b;
                int j = height - a - 1;
               
                int rgba = buffImage.getRGB(b, a);
                int red = (rgba >> 16) & 0xff;
                //int green = (rgba >> 8) & 0xff;
                //int blue = rgba & 0xff;
               
                if (red <= 10) {
                    terrain[i][j] = terrainTileCache.get(1);
                } else if (red <= 25) {
                    terrain[i][j] = terrainTileCache.get(2);
                } else if (red <= 50) {
                    terrain[i][j] = terrainTileCache.get(3);
                } else if (red <= 75) {
                    terrain[i][j] = terrainTileCache.get(4);
                } else if (red <= 100) {
                    terrain[i][j] = terrainTileCache.get(5);
                } else if (red <= 125) {
                    terrain[i][j] = terrainTileCache.get(6);
                } else if (red <= 150) {
                    terrain[i][j] = terrainTileCache.get(7);
                } else if (red <= 175) {
                    terrain[i][j] = terrainTileCache.get(8);
                } else if (red <= 200) {
                    terrain[i][j] = terrainTileCache.get(9);
                } else if (red <= 225) {
                    terrain[i][j] = terrainTileCache.get(10);
                } else if (red <= 250) {
                    terrain[i][j] = terrainTileCache.get(11);
                } else {
                    terrain[i][j] = terrainTileCache.get(12);
                }
               
                elevation[i][j] = red;
            }
        }
    }
Offline gouessej
« Reply #16 - Posted 2012-07-21 15:28:47 »

@loom_weaver Why not using Integer.valueOf(int) instead of new Integer(int)?

Offline Mads

JGO Ninja


Medals: 26
Projects: 3
Exp: 6 years


One for all!


« Reply #17 - Posted 2012-07-21 16:00:36 »

@loom_weaver Why not using Integer.valueOf(int) instead of new Integer(int)?

Why? Does it really make a difference?

Offline sproingie

JGO Kernel


Medals: 202



« Reply #18 - Posted 2012-07-21 16:10:24 »

Integer.valueOf can return the same Integer object, while 'new' is required to always return a new object.   You can't rely on this behavior of valueOf, but it does do this for small ints at least. Here it is in scala (eq is reference equality in scala)

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
scala> val x = java.lang.Integer.valueOf(123)
x: java.lang.Integer = 123

scala> val y = java.lang.Integer.valueOf(123)
y: java.lang.Integer = 123

scala> x eq y
res0: Boolean = true

scala> val a = new java.lang.Integer(123)
a: java.lang.Integer = 123

scala> val b = new java.lang.Integer(123)
b: java.lang.Integer = 123

scala> a eq b
res1: Boolean = false

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.

Longarmx (50 views)
2014-10-17 03:59:02

Norakomi (39 views)
2014-10-16 15:22:06

Norakomi (31 views)
2014-10-16 15:20:20

lcass (36 views)
2014-10-15 16:18:58

TehJavaDev (66 views)
2014-10-14 00:39:48

TehJavaDev (65 views)
2014-10-14 00:35:47

TehJavaDev (55 views)
2014-10-14 00:32:37

BurntPizza (72 views)
2014-10-11 23:24:42

BurntPizza (43 views)
2014-10-11 23:10:45

BurntPizza (84 views)
2014-10-11 22:30:10
Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06
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!