2d array HELP

Hello,

I’m new to monogame and i have a school project to make a sokoban.

I dont get how i can create a 2d array where i’ll put my differents sprites (wall,player and objective),
i want to create a map (8 by 8) all i can do right now is draw multiple times the same sprite in a row but how do i create this the 2d array i want.

I’m really lost, please help

EDIT: i realise i dont need an array, i need to make a tile

You can do a class item like this:

class mytile
{
 int typeoftile; //for collisions for ex
 Texture2D spritetodraw;
 
//etc with your data
}

then you can use a 2D array of “mytile” to fill the world of sokoban.
or a 1D array, using y* number of columns + x to access the position you are looking for to draw, test collision etc…

mytile[] world = new mytile[32*24]; //for ex

In your draw method: (pseudocode)

for(int y = 0; y < 24; y++)
for(int x = 0; x < 32; x++)
 draw(mytile[y*32+x].spritetodraw, ...etc

If your Tiles are going to use different textures at some point I would use a texture atlas and an index in the Tile class.

public struct Tile
{
    public int TextureIndex { get; set; }  
}

Then in your Map class or something similar do…

public class Map
{
    private Texture2D textureAtlas;
    private List<Rectangle> sourceRects;
    private int tileSize;
    private int tilesPerRow;
    private Tile[,] tiles;


    private Rectangle TileWorldRect(int x,int y)
    {
        return new Rectangle(x * tileSize, y * tileSize, tileSize, tileSize);
    }

    private Rectangle TileSourceRect(int textureIndex)
    {
        var rect = new Rectangle(
            (textureIndex % tilesPerRow) * tileSize,
            (textureIndex / tilesPerRow) * tileSize,
            tileSize,
            tileSize);

        return rect;
    }

    private void CalculateTileSourceRects()
    {
        //assuming that your are using a textureAtlas with a power of 2
        var numTiles = tilesPerRow * tilesPerRow;
        for (int i = 0; i < numTiles; i++)
        {
            sourceRects.Add(TileSourceRect(i));
        }
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        for (int x = 0; x < mapWidth; x++)
        {
            for (int y = 0; y < mapHeight; y++)
            {
                spriteBatch.Draw(textureAtlas,
                    TileWorldRect(x, y),
                    sourceRects[tiles[x, y].TextureIndex],
                    Color.White);
            }
        }
    }
}

For a multidimensional array.
if you are not simulating this with a one dimensional array which is more efficient.

Then

        // Declaration.

        private Texture2D testTexture;
        Texture2D[,] TextureArray2d;
        Rectangle[,] RectangleArray2d;

        protected override void LoadContent()
        {
            // ... load textures ..

            // Instantiation, creation of a 2d array

            TextureArray2d = new Texture2D[8, 8];
            RectangleArray2d = new Rectangle[8, 8];

            // Getting one of a arbitrary 2d dimensions length. 
            // In this case dimension x y is row column.

            int xlength = TextureArray2d.GetLength(0);
            int ylength = TextureArray2d.GetLength(1);
            
            // Programatically looping and assigning

            int tileSizeX = 32;
            int tileSizeY = 32;
            for (int x = 0; x < xlength; x++)
            {
                for (int y = 0; y < ylength; y++)
                {
                    // This is nearly always assigned thru a loop

                    RectangleArray2d[x, y] = new Rectangle(x * tileSizeX, y * tileSizeY, tileSizeX, tileSizeY);

                    // This would usually be done in some other way.

                    TextureArray2d[x, y] = testTexture;
                }
            }

            // Manual assignment of the tile textures.
        
            Texture2D t = testTexture;

            TextureArray2d = new Texture2D[,]
            {
                {t, t, t, t, t, t, t, t},
                {t, t, t, t, t, t, t, t},
                {t, t, t, t, t, t, t, t},
                {t, t, t, t, t, t, t, t},
                {t, t, t, t, t, t, t, t},
                {t, t, t, t, t, t, t, t},
                {t, t, t, t, t, t, t, t},
                {t, t, t, t, t, t, t, t}
            };

       }

            // ...
            // Drawing.

            spriteBatch.Begin();
            for (int x = 0; x < xlength; x++)
            {
                for (int y = 0; y < ylength; y++)
                {
                    Texture2D t = TextureArray2d[x, y];
                    Rectangle r = RectangleArray2d[x, y];

                    spriteBatch.Draw(t, r, Color.White);
                }
            }
            SpriteBatch.End();

Hi, thank you for your replies, i start to get it but it’s still not completly clear in my head on where i need to put things.

Can i put everything about the map in one Class (seperate from Game1) ? or i need multiple Class just to have the map ?

Why in Game1 i cant call my Class ? for exemple i have :

Public Class Map

{
static public Texture2D TileSetTexture;

        static public Rectangle GetSourceRectangle(int tileIndex)
        {
            return new Rectangle(tileIndex * 32, 0, 32, 32);
        }
} 

but in Game1 i cant call Map.GetSourceRectangle

EDIT:by the way, i want to make a Sokoban game.
How can i move my player in this type of 2d array?
Will i need to have the texture of the player in the tileset and move the tile index to move my player ?

Why static ? If you use static, to call a member you need to do

Map.GetSourceRectangle(...)

instead of

Map mymap = new Map();
mymap.GetSourceRectangle(...)

It’s not entirely bad to use a static map class in this case however it should then probably have a setup map to create load or save map levels.

I would recommend a hierarchical organization like so in the case of a static class…

// psuedo code
public static class Map
{
  Rectangle[,] mapDestinationRectangle;
  Texture2D[,] mapDestinationTexture;

   //
   // Static methods for setting and getting a maps tile texture and rectangle. 
   // by its row column [x,y] position.
   //

   public static SetupMap( string levelname )
   {
     // load or create array or whatever as previously shown. 
     // Using the inner class objects created and then assigned to the array.
     
   }
   
   public static Draw(SpriteBatch spriteBatch)
   {
     // same as previously shown drawing the arrays objects rectangles and textures
   }
}

...Then in Game1...

public class Game1 : base
{
  Map map;

  protected void LoadContent
  {
     map.SetUpMap( "level1" );
  }

  protected void Draw( ... )
  {
    spriteBatch.Begin();
    map.Draw(spriteBatch);
    spriteBatch.End();
  }
}
1 Like

Here is a very good tutorial about how you are supposed to organize a game in MG/XNA.

Add a public Vector2 Position; in your Player Class.

player.Position = new Vector2(1,3); // position player at Cell (1,3).

If you have your Player as a DrawableGameComponent, you draw it in the Draw() method.

public override void Draw(GameTime gameTime)
  {
    spriteBatch.Begin();
    spriteBatch.Draw(_txSprite, Position, Color.White);
    spriteBatch.End();
  }

It’s best to have a separate Class and use DrawableGameComponent as the base class. Just like with Player.
Set the .DrawOrder in the components to make sure that the player is drawn on top of the map.

map.DrawOrder = 0;
player.DrawOrder = 1;

You can avoid to call Begin() End().all the time, it’s enough to call them once in Game.Draw()

public override void Draw(GameTime gameTime)
  {
    device.Clear(Color.Black);

    spriteBatch.Begin();
    base.Draw(gameTime); // Draw all DrawableGameComponent components
    spriteBatch.End();
  }
2 Likes