12
\$\begingroup\$

I am creating a side scroller game for android devices but I encountered a problem. The game looks like this:

enter image description here

The black dot is a player. Player's x doesn't change, only the y does. When the player collides with the green terrain the game is over.

I have a problem with how should I handle a map storing and drawing. What I did is store the level file in a large png file (like 1k px height and 20k px width), same with the collision mask (basically the same file but with black colored collision terrain). I guess this approach isn't to good beacuse I was getting the out of memory exceptions due to large bitmap on some devices. I've learned that some people use tile based maps but with this sinusoidal map it could be to hard for me to do small tiles and collsion masks for them. I want the map to be as long as possible and if it's possible random-generated but I don't know what's the best approach to do this with this kind of terrain and how to handle pixel perfect collisions when doing so.

\$\endgroup\$
2
  • \$\begingroup\$ Use something like this: gamedev.stackexchange.com/questions/37887/… Your approach is not very effective. For this kind of world, one would normally opt to use a mesh (vertices) instead of a gigantic image because downloading a 1k * 20k image for a simple game is really an unreasonable demand. It also does not make sense as far a memory management goes. \$\endgroup\$
    – AturSams
    Commented May 14, 2014 at 11:25
  • \$\begingroup\$ Not a duplicate; this isn't just about generating. \$\endgroup\$ Commented May 16, 2014 at 18:14

4 Answers 4

14
\$\begingroup\$

You should definelty use multiple smaller images. However, smaller images doesn't necessarily means something like this:

enter image description here
(source: refrag.com)

You can just have few map parts that fit together nicely. When the player arrives in a new zone you check the collision for this new part. For example you have tiles like this (they can/should be larger):

01 02 03 04![] ![04 05]

1     2       3        4           5 

Your goal is to make them in such a way that the end of one correspond to the beginning of multiple ones. Then, in your game you create the map by putting side by side different tiles. You can mix them randomly as long as one's end fits with the other one's beginning

Here is an example of map that is bigger in total than the images I used.

Without spaces

01010203040405]03

With spaces to see the tiles

01 01 02 03 04 04 05] 03

1     1     2      3          4            4            5      2

It doesn't work perfectly here, but the idea is that tiling isn't just 32x32px squares.

\$\endgroup\$
9
\$\begingroup\$

Disclaimer: If you want to expand the road with more complex imagery - using detailed textures etc - then the following technique isn't very straight forward, but if it's as simple as the example image then this technique is an option. It seemed worth mentioning should anyone else have a similar problem.

Raster Roads

Although your specific game runs right to left, consider the following method which demonstrates how to create those old style pseudo-3d racing tracks; enter image description here

Although not directly what you are after, the principles are the same; describe the road line by line stating it's position/size. You can loop the array rather than have a goal to make it endless.

The principles of this technique can be used to index chunks of terrain types instead of describing just a single pixel width line on the screen.

As you don't require pseudo-3d, you can skip the parts in the tutorial relating to distance relative scaling, but you could do something similar to narrow the road.

Collision detection with this technique is also rather simple, you check the players y position against the line which corresponds to the players x (usually you have a linear mapping from x position to array index, but it depends on what proportion of the x-axis one array element represents).

\$\endgroup\$
8
\$\begingroup\$

Generation

For generating a world, I recommend the Perlin noise algorithm. I'm sure there are dozens of good tutorials and explanations elsewhere on the internet, so I won't go into detail here. There is a less well-known noise generation algorithm, Simplex noise, that is better than Perlin noise, but you are less likely to find a tutorial about that for Android. The advantage Perlin noise has over many other generation methods is that each column is generated independently of the others, so you can generate the column only when you need it.

Storing

So you have used your generation algorithm to make a world like this (except a lot bigger than 10x20):

██▀▀▀▀▀████████▀▀▀██
▀          ▀▀       
                    
      ▄███████▄▄▄   
█▄▄▄████████████████

Look at the first column:

█
▀
 
 
█

By storing it as a basic image, you would need a color for each of the ten pixels. Assuming the image format doesn't support an alpha channel, a color is 24 bits, so a list of ten colors is 240 bits.

(0,  0,  0)
(0,  0,  0)
(0,  0,  0)
(255,255,255)
(255,255,255)
(255,255,255)
(255,255,255)
(255,255,255)
(0,  0,  0)
(0,  0,  0)

Expand that to 1000 pixels tall, and you're using 24,000 bits per column. There are 20,000 columns, so that's 480,000,000 bits or 60,000,000 bytes. You were trying to load about 57 megabytes at once.

Now, what if you just stored it as a bunch of bits, one bit per pixel? Ten pixels would be 10 bits.

1110000011

Expand that out to the entire map and you get 20,000,000 bits or 2,500,000 bytes.

But you can still store it with far less memory. There are only two places where it changes between 1s and 0s, so you could store that like this:

3, 8

If you give each number 10 bits to be able to store values up to 1,000, that's 20 bits per column. Multiply that for all the columns, and that's only 400,000 bits or 50,000 bytes to store the entire map.

However, you don't need to store the entire map in RAM; you can load/generate only the part of the map you need a second before you need it. Assuming you have a 1000 pixel wide screen and you pre-load/-generate another 1000, that's 2000 columns loaded at once. 2,000 x 20 bits is 40,000 bits, or only 5,000 bytes stored on RAM at once. That is less than a ten-thousandth of what you were trying to store originally.


Collision detection

To detect if the player has collided with a wall, look at it column by column. Take the leftmost column that the player is still a part of, i.e. this one:

  ↓
██▀█▀▀▀████████▀▀▀██
▀          ▀▀       
  ▄▄                
  ▀▀  ▄███████▄▄▄   
█▄▄▄████████████████

Check whether the top pixel of the player:

 ▀
  
→▄
 ▀
 ▄

is above the first value stored in the map, which would be about here:

→▀
  
 ▄
 ▀
 ▄

If it is (which it isn't in this case), you have a collision. Next, do the same for the bottom:

 ▀
  
 ▄
→▀
→▄

No collision there, either. Do the same for the rest of the columns the player is a part of. If the player's top pixel is ever above the first value for that column or the bottom pixel is ever below the second value, you know you have a collision.

\$\endgroup\$
1
\$\begingroup\$

One approach could be that you split the big bitmap up in chunks along the y-axis, and show/hide the appropriate chunk when they go in/out of screen. This might also of course also cause the problem you mentioned with too large bitmaps on certain hardware, but is well worth checking out.

https://stackoverflow.com/questions/22471655/what-will-be-the-best-approach-implementing-infinite-repeating-scrolling-world-i

Another approach that will be much more easy on your disk usage is to procedurally generate the world. You "simply" draw the outline of the ground and the roof in your editor of choise, and use this as input to an algorithm that produces ground.

http://www.iforce2d.net/blog/2013-07-20

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged .