Saturday, December 6, 2008

Switcheroo - An old "Magazine" game in Silverlight, Part 1

In a previous post, I described the game Switcheroo. In this first post we will create a the start splash screen, the game board and the end splash screen.

The splash screen for a very visual game should be striking. Unfortunately, I am not a graphic artist, so the best I could come up with is the name switcheroo rendered using WordArt in Word.

We need to display the splash screen along with a start button. When the start button is clicked, we can then hide the splash screen and display the game board.

If you remember from that previous post, players can either place a piece on the 5x5 gameboard or rotate a row to the left or right, or rotate a column up or down. So, not only do we need a 5x5 checkerboard but we also need a mechanism to allow the players to rotate the columns and rows.

To accomplish this I added an extra column at the start and the end of the gameboard and an extra row to the top and the bottom of the gameboard. I added small golden arrows.

Once the game has been one or lost, on the placement of the winning move, we need to hide the gameboard and show the splash screen again. This time with a button declaring their win or loss and asking if they wish to play again.

The only problem with this is from a aesthetic point. When we place the winning piece, rotate the winning piece into place or our opponents move defeats us, we do not want to immediately flash to the splash page. The player may not have been able to even see that the move was a winning move or what their opponent even did to win, so we need to include a delay upon determining that the game is in fact over.

In the code for this first part, the both splash screens will work as they are supposed to, but since we have not created any game logic yet, we will consider any click on the gameboard to be the winning move and after our short delay the "You lose" version of the splash screen will display.

If you click on the button, the gameboard will display again and the whole process will start again, but the code will rotate between the "You win" and "You lose" messages to verify that both code paths work.

As you can see, instead of a lot of repetitive XAML, the only hardcoded XAML sets up the grid that will contain the gameboard and the elements for the splash screen.
<UserControl x:Class="Switcheroo.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="300" Height="300">
<Grid x:Name="LayoutRoot"
Tag="Bob">
<Grid.ColumnDefinitions>
<ColumnDefinition Width='25' />
<ColumnDefinition Width='50' />
<ColumnDefinition Width='50' />
<ColumnDefinition Width='50' />
<ColumnDefinition Width='50' />
<ColumnDefinition Width='50' />
<ColumnDefinition Width='25' />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height='25' />
<RowDefinition Height='50' />
<RowDefinition Height='50' />
<RowDefinition Height='50' />
<RowDefinition Height='50' />
<RowDefinition Height='50' />
<RowDefinition Height='25' />
</Grid.RowDefinitions>
<Image x:Name="XamlBoard"
Grid.RowSpan='7'
Grid.ColumnSpan='7'
Source='Splash.png' />
<StackPanel VerticalAlignment="Center"
HorizontalAlignment="Center"
Grid.RowSpan="7"
Grid.ColumnSpan="7">
<Button Click="Start_Click"
Content="Start"></Button>
</StackPanel>
</Grid>
</UserControl>

The XAML code also available here!

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace Switcheroo
{
public partial class Page
{
private class MyColors
{
public static Color Aliceblue { get { return GenerateColorStruct(0xFFF0F8FF); } }
public static Color Cyan { get { return GenerateColorStruct(0xFF00FFFF); } }
public static Color Darkcyan { get { return GenerateColorStruct(0xFF008B8B); } }
public static Color Darkgoldenrod { get { return GenerateColorStruct(0xFFB8860B); } }
public static Color Gold { get { return GenerateColorStruct(0xFFFFD700); } }

private static Color GenerateColorStruct(uint color)
{
var mask = 0x000000FF;

var returnval = new Color
{
A = ((byte)((color >> 24) & mask)),
R = ((byte)((color >> 16) & mask)),
G = ((byte)((color >> 8) & mask)),
B = ((byte)((color) & mask))
};

return returnval;
}
}

private class GameBoard
{
internal readonly int[,] Board = new int[5, 5];

public GameBoard()
{
for (var i = 0; i < 5; i++)
{
for (var j = 0; j < 5; j++)
{
Board[i, j] = -1;
}
}
}
}

readonly DispatcherTimer dt = new DispatcherTimer();

private bool GameOver;
private int GameWinner = -1;
private GameBoard Switcheroo = new GameBoard();

public Page()
{
InitializeComponent();
}

private void Start_Click(object sender, RoutedEventArgs e)
{
Switcheroo = new GameBoard();
LayoutRoot.Children.Clear();
CreateGameBoard();
GameOver = false;
}

private void dt_Tick(object sender, EventArgs e)
{
dt.Stop();
DisplayGameOverSplash();
}

private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
GameOver = true;

GameWinner = GameWinner == 0 ? 1 : 0;

if (GameOver)
{
dt.Interval = new TimeSpan(0, 0, 0, 0, 500); // 500 Milliseconds
dt.Tick += dt_Tick;
dt.Start();
}
}

private void CreateGameBoard()
{
var CellColor = false;
Canvas c;

for (var i = 0; i < 7; i++)
{
for (var j = 0; j < 7; j++)
{
if (i != 0 && i != 6 && j != 0 && j != 6)
{
c = new Canvas
{
Tag = (i + ":" + j),
Background =
(CellColor
? new SolidColorBrush(MyColors.Aliceblue)
: new SolidColorBrush(MyColors.Darkcyan))
};
}
else
{
c = new Canvas
{
Tag = (i + ":" + j),
Background = new SolidColorBrush(MyColors.Cyan)
};


if (j == 6 && i != 0 && i != 6)
{
var p = new Polygon
{
Fill = new SolidColorBrush(MyColors.Gold),
Stroke = new SolidColorBrush(MyColors.Darkgoldenrod)
};

p.Points.Add(new Point(10, 5));
p.Points.Add(new Point(40, 5));
p.Points.Add(new Point(25, 15));

c.Children.Add(p);
}
else if (j == 0 && i != 0 && i != 6)
{
var p = new Polygon
{
Fill = new SolidColorBrush(MyColors.Gold),
Stroke = new SolidColorBrush(MyColors.Darkgoldenrod)
};

p.Points.Add(new Point(10, 20));
p.Points.Add(new Point(40, 20));
p.Points.Add(new Point(25, 10));

c.Children.Add(p);

}
else if (i == 6 && j != 0 && j != 6)
{
var p = new Polygon
{
Fill = new SolidColorBrush(MyColors.Gold),
Stroke = new SolidColorBrush(MyColors.Darkgoldenrod)
};

p.Points.Add(new Point(5, 10));
p.Points.Add(new Point(5, 40));
p.Points.Add(new Point(15, 25));

c.Children.Add(p);

}
else if (i == 0 && j != 0 && j != 6)
{
var p = new Polygon
{
Fill = new SolidColorBrush(MyColors.Gold),
Stroke = new SolidColorBrush(MyColors.Darkgoldenrod)
};

p.Points.Add(new Point(20, 10));
p.Points.Add(new Point(20, 40));
p.Points.Add(new Point(10, 25));

c.Children.Add(p);

}
}

c.MouseLeftButtonDown += Canvas_MouseLeftButtonDown;
Grid.SetColumn(c, i);
Grid.SetRow(c, j);

LayoutRoot.Children.Add(c);

CellColor = !CellColor;
}
}
}

private void DisplayGameOverSplash()
{
string ButtonText = GameWinner != 0 ? "You Win! Play Again?" : "You Loose! Play Again?";

const string Splash = "<Image xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' " +
"xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' " +
"x:Name='XamlBoard' Grid.RowSpan='7' Grid.ColumnSpan='7' Source='Splash.png' /> ";

const string Stack = "<StackPanel xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' " +
"xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' VerticalAlignment='Center' " +
"HorizontalAlignment='Center' Grid.RowSpan='7' Grid.ColumnSpan='7' />";

string Button = "<Button xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' " +
"xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' Content='" + ButtonText + "'></Button>";

LayoutRoot.Children.Clear();
LayoutRoot.Children.Add((Image)XamlReader.Load(Splash));
LayoutRoot.Children.Add((StackPanel)XamlReader.Load(Stack));
((StackPanel)LayoutRoot.Children[1]).Children.Add((Button)XamlReader.Load(Button));
((Button)((StackPanel)LayoutRoot.Children[1]).Children[0]).Click += Start_Click;
}
}
}

The C# code also available here!

No comments: