Header Ads

XNA & Tile Map Editor

Today, I shall discuss about tile map editor generated using Tiled software. Following are some prerequisites before you proceed any further in this tutorial. 


Prerequisites: 

1) Knowledge about Tiled tool.
2) Knowledge about XNA development in windows phone environment.
3) Download xTiled library from download section.

You can explore this library's Documentation for further development. Designing is the most difficult phase in gaming, especially, when developers have to execute the project every single time, whenever, something new is added on the game graphics, Luckily, there are tools like level designers and tile map editors that facilitates the developers with the ease of game designing. For this tutorial, the sample tile map and sprite sheet have been taken from Ray Wenderlich blog which is for Cocos2d-x platform.

You can download the complete source code for this tutorial or you can follow step by step discussion below. The sample code is in Microsoft Visual Studio 2012.

Download Link

Let us begin now.  

1) Create a tile map using Tiled tool.
2) Create new 'Windows Phone Game' project VS 2012 and select OS 7.1 as target.
3) Unzip the xTiled library.
4) Under 'Content' project right-click on reference and click 'Add Reference' browse to 'FuncWorks.XNA.XTiledExtensions.dll' file and click ok.
5) Under 'Code' project right-click on reference and click 'Add Reference' browse to 'FuncWorks.XNA.XTiled.dll' file specific to your project i.e. since we have created windows phone project so we shall add reference to Windows Phone 7 specific dll and click ok.  
6) In your 'Content' project add your tiles map which will have '.tmx' file extension along with sprite.
7) Right-click the '.tmx' file and click 'Properties', then set 'Content Importer' & 'Content Processor' properties to 'TXM Map Importer' & 'TMX Map XTIled' as shown below:


8) Build the project and it will succeed.
9) In your 'Game1.cs' file add following private variables:

    Map map;  
     Rectangle mapView;  
     Vector2 playerPosition;  
     Texture2D player;  
     ObjectLayer objLayer;  
     ObjectLayer wallsLayer;  
     int xDir = 0;  
     int xDirOffset = 0;  
     int yDir = 0;  
     int yDirOffset = 0;  
     bool enableHorizontal = true;  
     bool enableVertical = false;  
     bool enableDiagonal = false;  
     Rectangle mapScreenSize;  
     float duration = 0.08f;  
     int xScreenOffset = 20;  
     int yScreenOffset = 35;  

We have created these variable to load our '.tmx' tile map and give device specific view to the map, some map layers related variables, some touch input related varibales and some map scrolling related variables

10) Now, Add following line in constructor:

this.graphics.IsFullScreen = true;

In above step, we have make sure that our game graphics are on full screen of the device.  

11) Now, add following code in 'Initialize()' method before base call:

this.mapView = this.graphics.GraphicsDevice.Viewport.Bounds; 

We are simple setting our map's view to be displayed on the device.

12) Now, add following lines of code into your 'LoadContent()' method:

       this.map = this.Content.Load<Map>("tmx\\TileMap");  
       this.player = this.Content.Load<Texture2D>("tmx\\Player");  
       // Setting direction.  
       this.xDirOffset = this.map.TileWidth;  
       this.yDirOffset = this.map.TileHeight;  
       this.xDir = this.xDirOffset;  
       this.yDir = this.yDirOffset;  
       // Setting map screen size.  
       int hScroll = this.map.Bounds.Width / this.graphics.PreferredBackBufferWidth;  
       int vScroll = this.map.Bounds.Height / this.graphics.PreferredBackBufferHeight;  
       this.mapScreenSize.Width = ((hScroll) % 2 == 0) ? this.map.Bounds.Width  
                               : hScroll * this.graphics.PreferredBackBufferWidth;  
       this.mapScreenSize.Height = ((vScroll) % 2 == 0) ? this.map.Bounds.Height  
                                : vScroll * this.graphics.PreferredBackBufferHeight;  
       // Setting player position.  
       this.objLayer = this.map.ObjectLayers["Objects"];  
       MapObject mapObj = this.objLayer.MapObjects.Where(p => p.Name.Equals("SpawnPoint", StringComparison.CurrentCultureIgnoreCase)).Select(p => p).First();  
       // Verificaion.  
       if (mapObj != null)  
       {  
         // Get coordinates.  
         Rectangle pos = Map.Translate(mapObj.Bounds, this.mapView);  
         this.playerPosition = new Vector2(pos.X, pos.Y);  
       }  
       // Setting wall layer.  
       this.wallsLayer = this.map.ObjectLayers["Walls"];  

In the above code, we are loading out '.tmx' map file and our player sprite, then setting the player position relative to tile map spawn point, then setting some map scrolling related variables and loading our map's objects layers for player launching point and walls collision detection.  

13) Now, add following lines of code to your 'Update()' method before base call:

      TouchCollection touches = TouchPanel.GetState();  
       foreach (TouchLocation touch in touches)  
       {  
         // Verification.  
         if (touch.State == TouchLocationState.Pressed)  
         {  
           // Update direction.  
           this.UpdateDirection(touch.Position);  
         }  
       }  
       // Scroll updates.  
       this.UpdateHorizontalScroll();  
       this.UpdateVerticalScroll();  
       // Verification.  
       if (this.isWall(this.playerPosition))  
       {  
         // Reverse directions.  
         this.xDir *= -1;  
         this.yDir *= -1;  
       }  
       // Update position.  
       this.UpdatePositions();

In the above code, we have added the screen touch logic, screen scrolling, player's collision with walls and updating player position.

14) Now, add following lines of code into your 'Draw()' method before base call:

      this.spriteBatch.Begin();  
         this.map.Draw(this.spriteBatch, this.mapView);  
       this.spriteBatch.End();  
       // Load player.  
       this.spriteBatch.Begin();  
         this.spriteBatch.Draw(this.player, this.playerPosition, Color.White);  
       this.spriteBatch.End();  

In the above code, we are drawing our map and player.  

15) Now, add 'isWall(...), UpdateHorizontalScroll(), UpdateVerticalScroll(), UpdateDirection(...) & UpdatePositions()' methods into your 'Game1.cs' file, which we have used in our game logic:

     bool isWall(Vector2 pos)  
     {  
       // Initialization.  
       bool isExist = false;  
       // Verification.  
       if (this.wallsLayer != null)  
       {  
         List<MapObject> walls = this.wallsLayer.MapObjects.Where(p => p.Name.Equals("wall", StringComparison.CurrentCultureIgnoreCase)).Select(p => p).ToList<MapObject>();  
         // Wall Collosion.  
         for (int i = 0; i < walls.Count; i++)  
         {  
           // Wall Collosion.  
           Rectangle wallBound = Map.Translate(walls[i].Bounds, this.mapView);  
           // Setting player bounds.  
           Rectangle playerBound = new Rectangle(Convert.ToInt32(pos.X), Convert.ToInt32(pos.Y), this.player.Width, this.player.Height);  
           // Wall Collosion.  
           if (wallBound.Intersects(playerBound))  
           {  
             isExist = true;  
             return isExist;  
           }  
         }  
       }  
       return isExist;  
     }  
     void UpdateHorizontalScroll()  
     {  
       // Initialization.  
       int hScroll = this.map.Bounds.Width - this.mapScreenSize.Width;  
       // Veririfcation.  
       if (this.playerPosition.X >= (this.graphics.PreferredBackBufferWidth - xScreenOffset) &&  
         (this.mapView.X + this.graphics.PreferredBackBufferWidth) < this.map.Bounds.Width)  
       {  
         // Initialization.  
         int xPos = Convert.ToInt32(this.playerPosition.X) - hScroll;   
         // Setting player position relative to screen.  
         this.playerPosition.X = ((this.mapView.X + this.graphics.PreferredBackBufferWidth) < this.mapScreenSize.Width) ? 0 :  
                     (hScroll > 0 && (this.mapView.X + hScroll) < this.map.Bounds.Width) ? xPos : this.playerPosition.X;  
         // Setting map view scroll right.  
         this.mapView.X += ((this.mapView.X + this.graphics.PreferredBackBufferWidth) < this.mapScreenSize.Width) ? this.graphics.PreferredBackBufferWidth :  
                  (hScroll > 0 && (this.mapView.X + hScroll) < this.map.Bounds.Width) ? hScroll : this.mapView.X;  
         // Verification.  
         if (this.playerPosition.X == xPos)  
         {  
           // Setting +ive direction.  
           this.xDir = this.xDirOffset;  
         }  
       }  
       else if (this.playerPosition.X <= 0 &&  
           (this.mapView.X - this.graphics.PreferredBackBufferWidth) >= 0 ||  
           (hScroll > 0 && (this.mapView.X - hScroll) >= 0))  
       {  
         // Initialization.  
         int xPos = Convert.ToInt32(this.playerPosition.X) - hScroll;   
         // Setting player position relative to screen.  
         this.playerPosition.X = ((this.mapView.X - this.graphics.PreferredBackBufferWidth) >= 0) ? this.graphics.PreferredBackBufferWidth - xScreenOffset :  
                     (hScroll > 0 && (this.mapView.X - hScroll) >= 0) ? xPos : this.playerPosition.X;  
         // Setting map view scroll left.  
         this.mapView.X -= ((this.mapView.X - this.graphics.PreferredBackBufferWidth) >= 0) ? this.graphics.PreferredBackBufferWidth :  
                  (hScroll > 0 && (this.mapView.X - hScroll) >= 0) ? hScroll : this.mapView.X;  
         // Verification.  
         if (this.playerPosition.X == xPos)  
         {  
           // Setting -ive direction.  
           this.xDir = -this.xDirOffset;  
         }  
       }  
       // Verification.  
       if (this.playerPosition.X == (this.graphics.PreferredBackBufferWidth - xScreenOffset))  
       {  
         // Setting -ive direction.  
         this.xDir = -this.xDirOffset;  
       }  
       else if (this.playerPosition.X == 0)  
       {  
         // Setting +ive direction.  
         this.xDir = this.xDirOffset;  
       }  
     }  
     void UpdateVerticalScroll()  
     {  
       // Initialization.  
       int vScroll = this.map.Bounds.Height - this.mapScreenSize.Height;  
       // Veririfcation.  
       if (this.playerPosition.Y >= (this.graphics.PreferredBackBufferHeight - yScreenOffset) &&  
         (this.mapView.Y + this.graphics.PreferredBackBufferHeight) < this.map.Bounds.Height)  
       {  
         // Initialization.  
         int yPos = Convert.ToInt32(this.playerPosition.Y) - vScroll;   
         // Setting player position relative to screen.  
         this.playerPosition.Y = ((this.mapView.Y + this.graphics.PreferredBackBufferHeight) < this.mapScreenSize.Height) ? 0 :  
                     (vScroll > 0 && (this.mapView.Y + vScroll) < this.map.Bounds.Height) ? yPos : this.playerPosition.Y;  
         // Setting map view scroll down.  
         this.mapView.Y += ((this.mapView.Y + this.graphics.PreferredBackBufferHeight) < this.mapScreenSize.Height) ? this.graphics.PreferredBackBufferHeight :  
                  (vScroll > 0 && (this.mapView.Y + vScroll) < this.map.Bounds.Height) ? vScroll : this.mapView.Y;  
         // Verification.  
         if (this.playerPosition.Y == yPos)  
         {  
           // Setting +ive direction.  
           this.yDir = this.yDirOffset;  
         }  
       }  
       else if (this.playerPosition.Y <= 0 &&  
           ((this.mapView.Y - this.graphics.PreferredBackBufferHeight) >= 0 ||  
           (vScroll > 0 && (this.mapView.Y - vScroll) >= 0)))  
       {  
         // Initialization.  
         int yPos = Convert.ToInt32(this.playerPosition.Y) + vScroll;  
         // Setting player position relative to screen.  
         this.playerPosition.Y = ((this.mapView.Y - this.graphics.PreferredBackBufferHeight) >= 0) ? this.graphics.PreferredBackBufferHeight - yScreenOffset :  
                     (vScroll > 0 && (this.mapView.Y - vScroll) >= 0) ? yPos : this.playerPosition.Y;  
         // Setting map view scroll up.  
         this.mapView.Y -= ((this.mapView.Y - this.graphics.PreferredBackBufferHeight) >= 0) ? this.graphics.PreferredBackBufferHeight :  
                  (vScroll > 0 && (this.mapView.Y - vScroll) >= 0) ? vScroll : this.mapView.Y;  
         // Verification.  
         if (this.playerPosition.Y == yPos)  
         {  
           // Setting -ive direction.  
           this.yDir = -this.yDirOffset;  
         }  
       }  
       // Verification.  
       if (this.playerPosition.Y == (this.graphics.PreferredBackBufferHeight - yScreenOffset))  
       {  
         // Setting -ive direction.  
         this.yDir = -this.yDirOffset;  
       }  
       else if (this.playerPosition.Y == 0)  
       {  
         // Setting +ive direction.  
         this.yDir = this.yDirOffset;  
       }  
     }  
     void UpdateDirection(Vector2 newPos)  
     {  
       // Setting direction.  
       this.xDir = (newPos.X >= this.playerPosition.X) ? this.xDirOffset : -this.xDirOffset;  
       this.yDir = (newPos.Y >= this.playerPosition.Y) ? this.yDirOffset : -this.yDirOffset;  
       // Initialization.  
       Vector2 minBound = this.playerPosition;  
       Vector2 maxBound = new Vector2(this.playerPosition.X + this.player.Bounds.Width, this.playerPosition.Y + this.player.Bounds.Height);  
       // Verification.  
       if (newPos.Y >= minBound.Y && newPos.Y <= maxBound.Y)  
       {  
         // Enable horizontal.  
         this.enableHorizontal = true;  
         // Diable other direction.  
         this.enableDiagonal = false;  
         this.enableVertical = false;  
       }  
       else if (newPos.X >= minBound.X && newPos.X <= maxBound.X)  
       {  
         // Enable vertical.  
         this.enableVertical = true;  
         // Diable other direction.  
         this.enableDiagonal = false;  
         this.enableHorizontal = false;  
       }  
       else  
       {  
         // Enable diagonal.  
         this.enableDiagonal = true;  
         // Diable other direction.  
         this.enableHorizontal = false;  
         this.enableVertical = false;  
       }  
     }  
     void UpdatePositions()  
     {  
       // Verification.  
       if (this.enableHorizontal)  
       {  
         // Update x position.  
         this.playerPosition.X += this.xDir * duration;  
       }  
       // Verification.  
       if (this.enableVertical)  
       {  
         // Update y position.  
         this.playerPosition.Y += this.yDir * duration;  
       }  
       // Verification.  
       if (this.enableDiagonal)  
       {  
         // Update x & y position.  
         this.playerPosition.X += this.xDir * duration;  
         this.playerPosition.Y += this.yDir * duration;  
       }  
       // Setting direction.  
       this.xDir = (this.playerPosition.X < 0) ? this.xDirOffset : (this.playerPosition.X > (this.graphics.PreferredBackBufferWidth - xScreenOffset)) ? -this.xDirOffset : this.xDir;  
       this.yDir = (this.playerPosition.Y < 0) ? this.yDirOffset : (this.playerPosition.Y > (this.graphics.PreferredBackBufferHeight - yScreenOffset)) ? -this.yDirOffset : this.yDir; ;  
     }

16) Build and Execute the project and you will have following output, also click the screen for touch event:


That's about it.

Enjoy!! Coding.

No comments