Page view counter

Silverlight Tips of the Day - Blog by Mike Snow

Game Programming with Silverlight

Terrain Tutorial Part 1 – Creating a Scrolling Map

For this tutorial we will be generating a 2D scrolling map. The view is a straight down birds eye view. As shown in Figure 1 below, each terrain tile is represented by a combine upper-left and lower-right polygon. A map can be any width and height of these tiles as shown in Figure 2. Figure 3 and 4 show a section of the map textured, one with a grid overlap toggled on. In Figure 4 you will notice a diagonal “seam”. This is an artifact caused by the way Silverlight deals with anti-aliasing. To avoid the seam, numbers must be integer values with no decimal points. This works fine with straight lines against the X and Y axis, but a diagonal line will have decimal points causing the seam to appear. When we deal with matrix transforms in part 2 of these tutorials we will get rid of the seam by scaling the textures a pixel to overlap with each other, thus hiding the seam.

Here is a demo of what we are creating. Place focus on the control than use the cursor keys to scroll it around.

 image  image

image 

Each tile will be represented by a custom control contained in a class called TerrainTile. Let’s take a look at a few points on this class:

  • Each tile is made up of two polygons.
  • The class constructor takes as parameters the X-position, Y-position, width and height of each tile.
  • Each polygon has one image brush associated with it.

TerrainTile.xaml:

<UserControl x:Class="ScrollingMap.TerrainTile"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Canvas>
        <Polygon x:Name="UpperLeftPoly">
            <Polygon.Fill>
                <ImageBrush x:Name="UpperImage"></ImageBrush>
            </Polygon.Fill>
        </Polygon>
        <Polygon x:Name="LowerRightPoly">
            <Polygon.Fill>
                <ImageBrush x:Name="LowerImage"></ImageBrush>
            </Polygon.Fill>
        </Polygon>
    </Canvas>
</UserControl>

In the code behind, we create the points for each polygon based upon its location. We also have a function to set the image for the tile.

TerrainTile.xaml.cs

public partial class TerrainTile : UserControl
{
    public TerrainTile(int xPos, int yPos, int width, int height)
    {
        InitializeComponent();
 
        UpperLeftPoly.Points = new PointCollection();
        UpperLeftPoly.Points.Add(new Point(xPos, yPos));
        UpperLeftPoly.Points.Add(new Point(xPos+width, yPos));
        UpperLeftPoly.Points.Add(new Point(xPos, yPos+height));
 
        LowerRightPoly.Points = new PointCollection();
        LowerRightPoly.Points.Add(new Point(xPos+width, yPos));
        LowerRightPoly.Points.Add(new Point(xPos + width, yPos+height));
        LowerRightPoly.Points.Add(new Point(xPos, yPos + height));
        
    }
 
    public void SetImage(string imgResource)
    {
        Uri uri = new Uri(imgResource, UriKind.Relative);
        ImageSource imgSrc = new System.Windows.Media.Imaging.BitmapImage(uri);
        UpperImage.ImageSource = imgSrc;
 
        imgSrc = new System.Windows.Media.Imaging.BitmapImage(uri);
        LowerImage.ImageSource = imgSrc;
    }
}

To manage the different layers of the terrain I have created a class called TerrainManager. This class we:

  • We declare a two dimensional array of tiles to represent the ground layer.
  • Add a function called CreateGroundLayer() to create the ground layer.

TerrainManager.cs

public class TerrainManager
{
    const int TILE_WIDTH = 90;
 
    Canvas _mapCanvas;
    private TerrainTile[,] _groundLayer;
 
    public TerrainManager(Canvas mapCanvas)
    {
        _mapCanvas = mapCanvas;
    }
 
    public void CreateGroundLayer(int width, int height)
    {
        _groundLayer = new TerrainTile[height, width];
 
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                _groundLayer[y, x] = new TerrainTile(x * TILE_WIDTH, y * TILE_WIDTH, TILE_WIDTH, TILE_WIDTH);
                _groundLayer[y, x].SetImage("grass.png");
                _mapCanvas.Children.Add(_groundLayer[y, x]);
            }
        }
    }
}

In our Page.xaml we create a ContentControl that will contain our map Canvas. The map Canvas is what we add the tiles to. The ContentControl is what we will scroll.

Page.xaml

<UserControl x:Class="ScrollingMap.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Canvas>
        <ContentControl x:Name="MapContent">
            <Canvas x:Name="Map"></Canvas>
        </ContentControl>
    </Canvas>
</UserControl>

In the constructor of Page.xaml.cs we add two event handlers:

  • this.Loaded: We create the terrain manager and initialize a terrain layer once the page is fully loaded.
  • this.KeyDown: We monitor for keyboard events to scroll the map around. In this demo to move you can use the arrow keys, numpad keys or Q, W, E, D, C, X, Z, and A to go NW, North, NE, East, SE, South, SW and West respectively.

Page.xaml.cs

public partial class Page : UserControl
{
    TerrainManager _terrainMgr;
    double _mapOffsetX = 0;
    double _mapOffsetY = 0;
    int _scrollSpeed = 20;
 
    public Page()
    {
        InitializeComponent();
 
        this.Loaded += new RoutedEventHandler(Page_Loaded);
        this.KeyDown += new KeyEventHandler(Page_KeyDown);
    }
 
    void Page_KeyDown(object sender, KeyEventArgs e)
    {
        switch (e.Key)
        {
            case Key.NumPad8:  //North
            case Key.W:
            case Key.Up:
                _mapOffsetY += _scrollSpeed;
                break;
            case Key.NumPad2: // South
            case Key.X:
            case Key.Down:
                _mapOffsetY -= _scrollSpeed;
                break;
            case Key.NumPad6: // East
            case Key.D:
            case Key.Right:
                _mapOffsetX -= _scrollSpeed;
                break;
            case Key.NumPad4: // West
            case Key.A:
            case Key.Left:
                _mapOffsetX += _scrollSpeed;
                break;
            case Key.NumPad7: // NW
            case Key.Q:
                _mapOffsetX += _scrollSpeed;
                _mapOffsetY += _scrollSpeed;
                break;
            case Key.NumPad9: // NE
            case Key.E:
                _mapOffsetX -= _scrollSpeed;
                _mapOffsetY += _scrollSpeed;
                break;
            case Key.NumPad3: // SE
            case Key.C:
                _mapOffsetX -= _scrollSpeed;
                _mapOffsetY -= _scrollSpeed;
                break;
            case Key.NumPad1: // SW
            case Key.Z:
                _mapOffsetX += _scrollSpeed;
                _mapOffsetY -= _scrollSpeed;
                break;
        }
        MapContent.SetValue(Canvas.LeftProperty, _mapOffsetX);
        MapContent.SetValue(Canvas.TopProperty, _mapOffsetY);
    }
 
    void Page_Loaded(object sender, RoutedEventArgs e)
    {
         _terrainMgr = new TerrainManager(Map);
         _terrainMgr.CreateGroundLayer(10, 10);
    }
}

Thank you,
--Mike Snow

 Subscribe in a reader

Comments

Microsoft Weblogs said:

For this tutorial we will be generating a 2D terrain map. Preview and Run App: http://silverlight.services

# August 8, 2008 6:47 PM

Mirrored Blogs said:

Post: Approved at: Aug-8-2008 Terrain Tutorial Part 1 – Creating a Scrolling Map http://silverlight.net

# August 9, 2008 2:25 AM

Dew Drop - August 10, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop - August 10, 2008 | Alvin Ashcraft's Morning Dew

# August 10, 2008 11:37 AM

Community Blogs said:

Michael Washington with an add-on for SL Desktop already, Mike Snow beginning Terrain tutorials and applying

# August 10, 2008 9:13 PM

Visual Web Developer Team Blog said:

7 new Silverlight blogs have been completed. Check them out and let me know if you have any questions.

# August 14, 2008 2:51 PM

Mike Snows Silverlight Blog said:

For this tutorial we will make use of matrix transforms to skew and scale an image in a terrain tile

# August 18, 2008 1:45 PM

Silverlight Tips of the Day - Blog by Mike Snow said:

The purpose of this post is to create an outline summary all the blogs from my Silverlight tips of the

# January 2, 2009 5:56 PM

o UAU nosso de cada dia said:

essa lista eu copiei desse blog bárbaro (acompanhe por RSS você também): uma lista de dicas super úteis

# January 3, 2009 6:25 AM

Websites tagged "silverlight" on Postsaver said:

Pingback from  Websites tagged "silverlight" on Postsaver

# January 12, 2009 6:47 PM

goldbishop said:

Good tut....alot less code than any of the XNA tut's.  Wonder if silverlight can be implemented in client/server applications :)

# May 16, 2009 12:48 PM

mike.snow said:

i show that in my book :)

# May 16, 2009 10:07 PM