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.
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:
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">
<ImageBrush x:Name="LowerImage"></ImageBrush>
</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:
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"
<ContentControl x:Name="MapContent">
<Canvas x:Name="Map"></Canvas>
</ContentControl>
In the constructor of Page.xaml.cs we add two event handlers:
Page.xaml.cs
public partial class Page : UserControl
TerrainManager _terrainMgr;
double _mapOffsetX = 0;
double _mapOffsetY = 0;
int _scrollSpeed = 20;
public Page()
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;
case Key.NumPad6: // East
case Key.D:
case Key.Right:
_mapOffsetX -= _scrollSpeed;
case Key.NumPad4: // West
case Key.A:
case Key.Left:
_mapOffsetX += _scrollSpeed;
case Key.NumPad7: // NW
case Key.Q:
case Key.NumPad9: // NE
case Key.E:
case Key.NumPad3: // SE
case Key.C:
case Key.NumPad1: // SW
case Key.Z:
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
PingBack from http://mstechnews.info/2008/11/terrain-tutorial-part-1-%e2%80%93-creating-a-scrolling-map/