Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm developing a endless running game and I'm not really sure on how to make the set. The first approach was to make a BIG set like 10240x3072 pixels so that we have a nice portion of set. After having like 3 or 4 sets that go along with each other I would work on making their elements sequential and repeatable. However this is getting really heavy for the iPad 1 (it's running good in the iPad 2 and the New iPad) even though I'm splitting all the set in slices through Photoshop. For the implementation I'm using Cocos2D. Is there any better approach to make something like this but truly efficient for the iPad memory?

Thank you.

share|improve this question
1  
I'm not sure what you mean by a "game set". A background texture? A level? Please describe a bit more what your precise issue is. –  Laurent Couvidou May 15 '12 at 17:39
    
I mean the scene yes. The whole level that passes by the character while he runs. –  jncunha May 16 '12 at 14:54
1  
How big are the slices? –  bummzack Jun 1 '12 at 14:22
    
Can you break things up into smaller images and procedurally generate some of it? That would make things more interesting with a smaller foot print. Double win! –  Vaughan Hilts Aug 30 '12 at 16:20
add comment

1 Answer 1

Here's how I did it :

I have a camera which is nothing more than a vector, from its position I do find which tiles needs to be drawn; using tile size and viewport size.

In this example less than 10% of the level is drawn :

enter image description here

The whole level is only using 3 textures that are 32x32 pixels, in comparison to your huge texture. However there are some considerations you should care of if using this method : the level generation. Since it's not a huge texture anymore, you can either code a text file that would be the level, each character representing a tile. Or you can use a level editor for this purpose. A quick search for 'mac level editor' brings Tiled.

Here is an excerpt of what I did, that's the part that does find what to draw and draws only that.

The code should be self-explanative, if not, ask.

namespace x
{
    public class LevelComponent : DrawableGameComponent
    {
        public List<Texture2D> Textures { get; private set; }
        public List<Sprite> Sprites { get; private set; }

        private readonly List<Bounds> _bounds;
        private Vector2 _camera;

        private Vector2 _screenCenter;

        private Vector2 _screenSize;
        private Vector3 _size; // level size as tiles
        private Vector2 _sizeAsPixels; // level size ...

        private SpriteBatch _spriteBatch;
        private Vector2 _sizeOfTile;
        private Tile[,,] _tiles; 
        private int _tilesRendered;

        public override void Update(GameTime gameTime)
        {

            /* We will center on the player just like in Super Mario Bros.
             * Also, we clamp the camera to level bounds. */
            Vector2 newPos = Players[0].Position - _screenCenter;
            Vector2 min = Vector2.Zero;
            Vector2 max = SizeAsPixels - _screenSize;
            _camera = Vector2.Clamp(newPos, min, max);

            /* We get level size as tile units. */
            float w = Game.GraphicsDevice.Viewport.Width/SizeOfTile.X;
            float h = Game.GraphicsDevice.Viewport.Height/SizeOfTile.Y;
            var max1 = new Vector2(SizeOfTile.X, SizeOfTile.Y);

            /* Now we are going to find out which level region
             * we just want to render otherwise it'd be unpractical to render the whole level.
             * Example :
             * - If level is 32 * 32 * 3 layers there are 3072 tiles,
             * - Now if our viewport is say 5*5, we only render 5*5*3 or 75 tiles.
             * - While these numbers are low, on a huge level it is critical.
             * - Say on a 512*512*3 or 786432 tiles, tiles to render is only 75.
             * - Yet it is about 0.0001% of the level we render ...
             * - We can thus keep a 60 fps frame rate with this method. */
            Bounds.Clear();
            var layers = (int) Size.Z;
            for (int layer = 0; layer < layers; layer++)
            {
                /* We divide here to make far layers moving more slowly
                 * than layers that are closer to us OR Parallax Scrolling ! */
                Vector2 topLeft = TilesFromPixels(Camera);
                topLeft /= (layer + 1);

                /* We draw what's visible on the screen. */
                Vector2 bottomRight = topLeft + new Vector2(w, h);
                bottomRight = Vector2.Clamp(bottomRight, Vector2.Zero, max1);

                // Now we have bounds of regions to render for all layers !
                Bounds.Add(new Bounds(topLeft, bottomRight));
            }

            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            _spriteBatch.Begin();

            if (Tiles != null)
            {
                /* We're going to draw level from back to front
                 * BUT the most important and a huge CPU saver is that
                 * we're going to draw only what we can see on screen,
                 * thus, we can have gigantic levels and they'll 
                 * still at run 60 fps. */

                _tilesRendered = 0;
                var layers = (int) (Size.Z - 1);
                for (int z = layers; z >= 0; z--)
                {
                    Bounds bounds = Bounds[z];
                    Vector2 topLeft = bounds.TopLeft;
                    Vector2 bottomRight = bounds.BottomRight;
                    Vector2 translation = Camera/(z + 1); // Scrolling difference
                    for (var x = (int) topLeft.X;
                         x < bottomRight.X;
                         x++)
                    {
                        for (var y = (int) topLeft.Y;
                             y < bottomRight.Y;
                             y++)
                        {
                            Tile tile = Tiles[x, y, z];
                            if (tile == null) continue;

                            Vector2 position = PixelsFromTiles(new Vector2(x, y));

                            _spriteBatch.Draw(this.GetTexture(tile.Texture), position - translation, tile.Color);
                            _tilesRendered++;
                        }
                    }
                }
            }

            _spriteBatch.End();

            base.Draw(gameTime);
        }

    }

    public static class Helpers
    {
        /// <summary>
        /// Get pixel position from a tile position, according a tile size.
        /// </summary>
        /// <param name="x">Position horizontal value, as tiles.</param>
        /// <param name="y">Position vertical value, as tiles.</param>
        /// <param name="tileWidth">Tile width, as pixels.</param>
        /// <param name="tileHeight">Tile height, as pixels.</param>
        /// <returns>Position, as pixels.</returns>
        public static Vector2 PixelsFromTiles(float x, float y, float tileWidth, float tileHeight)
        {
            return new Vector2(x * tileWidth, y * tileHeight);
        }

        /// <summary>
        /// Get pixel position from a tile position, according a tile size.
        /// </summary>
        /// <param name="position">Position, as tiles.</param>
        /// <param name="tileSize">Tile size, as pixels.</param>
        /// <returns>Position, as pixels.</returns>
        public static Vector2 PixelsFromTiles(Vector2 position, Vector2 tileSize)
        {
            return PixelsFromTiles(position.X, position.Y, tileSize.X, tileSize.Y);
        }

        /// <summary>
        /// Get tile position from a pixel position, according a tile size.
        /// </summary>
        /// <param name="x">Position horizontal value, as pixels.</param>
        /// <param name="y">Position vertical value, as pixels.</param>
        /// <param name="tileWidth">Tile width, as pixels.</param>
        /// <param name="tileHeight">Tile height, as pixels.</param>
        /// <returns>Position, as tiles.</returns>
        public static Vector2 TilesFromPixels(float x, float y, float tileWidth, float tileHeight)
        {
            return new Vector2(x / tileWidth, y / tileHeight);
        }

        /// <summary>
        /// Get tile position from a pixel position, according a tile size.
        /// </summary>
        /// <param name="position">Position, as pixels.</param>
        /// <param name="tileSize">Tile size, as pixels.</param>
        /// <returns>Position, as tiles.</returns>
        public static Vector2 TilesFromPixels(Vector2 position, Vector2 tileSize)
        {
            return TilesFromPixels(position.X, position.Y, tileSize.X, tileSize.Y);
        }
    }
}
share|improve this answer
1  
Rather than dumping your code (which is in another language) with a picture (that basically just tells us that your world is tiled), you should post pseudo code describing what you did to get it to work. This will be much more helpful for the OP (and future viewers) vs having to sift through your code to find what they're looking for. –  Richard Marskell - Drackir May 31 '12 at 20:34
    
Is it better now ? :-) –  Aybe Jun 1 '12 at 14:14
    
Yes, it's much more concise and applicable to the OP's question. –  Richard Marskell - Drackir Jun 1 '12 at 15:25
    
Okay great :))) –  Aybe Jun 2 '12 at 2:34
    
There is a problem with your example. The sprites are simple and I doubt they will be a problem for a mobile device. In my case I've got tons of sprites with lots of detail. While yours is repetitive, mine is more organic and random. There is not really a cycle between sprites. I'm now using Level Helper to help me. With it I'm able to have a certain number of sprite sheets and I can build the entire level, using only instances of each sprite. The result is very good in terms of quality and a app that was using about 200 images is now using something like 20 or 30 images. –  jncunha Jul 13 '12 at 8:39
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.