Click here to Skip to main content
Email Password   helpLost your password?

Contents

Introduction

This article represents something new for me, normally my articles are a bit fanciful and are geared around some loose interest that I have. This time I decided to tackle a really old problem (well not so much of a problem but at least something old). I decided to write a game of Snakes, yes that right a game of snakes, but in WPF. For those of you new to WPF, WPF does not really have any low level rendering methods such as OnPaint(..) (well it does but thats not what WPF is all about). As you will see the games of Snakes is not as easy as one might think. This article will be using a lot of WPF tips and tricks along the way. What I will be doing is showing you a couple of screen shots along the way and discussing the requirements (self impossed) that the Snakes game has, and how and why these requirements were solved the way they are.

Requirements

The basic requirements that I imposed for this WPF version of the seminal Snakes game are as follows:

A Quick Peak At The Demo App

When the application is started there are basically 3 choices, Create a level, Start A Game and Set The Difficulty

When the user chooses to create a level they are presented with a level creation wizard which they may use to customize the levels. This is a 2 page wizard, the 1st page allow the user to pick colors (the color picker is a custom control hosted in a window, so this alone may be of interest to some folk)

And the 2nd wizard page allows users to draw out the level using the mouse

And the game in action looks like the following (in this one the user just died, so the snake exploded, oooh poor snake)

So thats what it looks like, but thats not the fun part, IMHO the fun part is seeing how all this fits together, so the next sections of this article are going to talk about all the ins and outs of the design decisions that I made along the way, in the hope that they will help out some of you.

A Blow By Blow Design Explanation Of Each Of The Requirements

The basic requirements that I imposed for this WPF version of the seminal Snakes game are as follows:

To be able to change the size of the level

I wanted a way that all my changes that affected the entire game, a set of global variables if you like. I could of course use static member variables of some class (perhaps called globals) but I wanted to see if there was a better way. Luckily WPF exposes just such a thing, its called the Application.Current.Properties object. This object is quite cool, as it keeps all your global in a nice an easily accessable Dictionary which you can simply assign and access as follows

Accessing

(Brush)Application.Current.Properties["WallColor"];

Assigning

Application.Current.Properties["WallColor"] = new SolidColorBrush(Colors.Cyan);

Only thing to remember is that is stores objects as Type Object so you need to cast or use the As keyword. But its damn handy. So this is how I set all the global variables that are used within the game. Within the Window1.xaml.cs file attached, within the constructor you will find a code block like

Application.Current.Properties["maxGameSize"] = _maxGameSize;
Application.Current.Properties["initialSnakeSize"] = _initialSnakeSize;
Application.Current.Properties["foodItemsPerLevel"] = _foodItemsPerLevel;
Application.Current.Properties["SnakeStyle"] = _snakeStyle;
Application.Current.Properties["WallColor"] = Brushes.Orange;
Application.Current.Properties["FoodColor"] = Brushes.PaleGreen;
Application.Current.Properties["SnakeColor"] = Brushes.Yellow;

This is where all the game values are initialised, they are changed as required on various other windows. Its nice though as all other files can simply access this one global Dictionary which is a nice feature in my opinion.

To be able to change the color of things like walls/food/snake

One of the requirements was for the user to be able to pick their own color for things like walls, snakes, food etc etc. This sounds simple enough doesnt it. Just show a ColorPicker and store the value. Its more complicated than that as youll soon see. But for the moment lets just stick to the simply fact, that if we pick a color using the color picker (code below if youre interested) that, that is the color well use, and thats the end of the story.

The ColorPickerControl is the grid of color, its a custom control hosted in a XAML window, called ColorPickerWindow.xaml

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Data;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Shapes;
    
    namespace WPF_Snakes
    {
        #region ColorPickerControl CLASS
        /// <summary>

        /// A simple color picker control, with a 

    ///custom event that uses

        /// the standard RoutedEventArgs. 

        /// <br/>

        /// NOTE: I also tried to create a custom event with custom 

    ///inherited

        /// RoutedEventArgs, but this didnt seem to work, so this 

    ///event is commented

        /// out. BUt if anyone knows how to do this please let me 

    ///know, as as far as I know

        /// I am doing everything correctly

        /// </summary>

        public class ColorPickerControl : ListBox
        {
            #region InstanceFields
            //A RoutedEvent using standard RoutedEventArgs, 

        //event declaration

            //The actual event routing

            public static readonly RoutedEvent NewColorEvent =
                        EventManager.RegisterRoutedEvent
        ("NewColor", RoutingStrategy.Bubble,
                        typeof(RoutedEventHandler), 
        typeof(ColorPickerControl));
    
            //A RoutedEvent using standard custaom 

        //ColorRoutedEventArgs, event declaration

    
            ////the event handler delegate

            public delegate void NewColorCustomEventHandler
        (object sender, ColorRoutedEventArgs e);
    
            ////The actual event routing

            public static readonly RoutedEvent NewColorCustomEvent =
                        EventManager.RegisterRoutedEvent
        ("NewColorCustom", RoutingStrategy.Bubble,
                        typeof(NewColorCustomEventHandler), 
        typeof(ColorPickerControl));
            //************************************************************************

    
            //string array or colors

            private string[] _sColors = 
            {
                "Black", "Brown", "DarkGreen", "MidnightBlue", 
                    "Navy", "DarkBlue", "Indigo", "DimGray",
                "DarkRed", "OrangeRed", "Olive", "Green", 
                    "Teal", "Blue", "SlateGray", "Gray",
                "Red", "Orange", "YellowGreen", "SeaGreen", 
                    "Aqua", "LightBlue", "Violet", "DarkGray",
                "Pink", "Gold", "Yellow", "Lime", 
                    "Turquoise", "SkyBlue", "Plum", "LightGray",
                "LightPink", "Tan", "LightYellow", "LightGreen", 
                    "LightCyan", "LightSkyBlue", "Lavender", "White"
            };
            #endregion
            #region Constructor
            /// <summary>

            /// Constructor for ColorPickerControl, 

        ///which is a ListBox subclass

            /// </summary>

            public ColorPickerControl()
            {
                // Define a template for the Items, 

        //used the lazy FrameworkElementFactory

                // method

                FrameworkElementFactory fGrid = new
                    FrameworkElementFactory(
        typeof(System.Windows.Controls.
        Primitives.UniformGrid));
                fGrid.SetValue(System.Windows.Controls.
        Primitives.UniformGrid.ColumnsProperty, 10);
                //update the ListBox ItemsPanel with the new 

        ItemsPanelTemplate just created
                ItemsPanel = new ItemsPanelTemplate(fGrid);
    
    
                // Create individual items

                foreach (string clr in _sColors)
                {
                    // Creat bounding rectnagle for items data

                    Rectangle rItem = new Rectangle();
                    rItem.Width = 20;
                    rItem.Height = 20;
                    rItem.Margin = new Thickness(1);
                    rItem.Fill = (Brush)typeof(Brushes).
            GetProperty(clr).GetValue(null, null);
                    //add rectangle to ListBox Items

                    Items.Add(rItem);
    
                    //add a tooltip

                    ToolTip t = new ToolTip();
                    t.Content = clr;
                    rItem.ToolTip = t;
    
                }
                //Indicate that SelectedValue is Fill property of 

        //Rectangle item.

                //Kind of like an XPath query, this is the string 

        //name of the property 

                //to use as the the selected item value from the 

        //actual item data. The item

                //data being a Rectangle in this case

                SelectedValuePath = "Fill";
            }
            #endregion
            #region Events
            // Provide CLR accessors for the event

            public event RoutedEventHandler NewColor
            {
                add { AddHandler(NewColorEvent, value); }
                remove { RemoveHandler(NewColorEvent, value); }
            }
    
            // This method raises the NewColor event

            private void RaiseNewColorEvent()
            {
                RoutedEventArgs newEventArgs = new 
        RoutedEventArgs(NewColorEvent);
                RaiseEvent(newEventArgs);
            }
    
            // Provide CLR accessors for the event

            public event NewColorCustomEventHandler NewColorCustom
            {
                add { AddHandler(NewColorCustomEvent, value); }
                remove { RemoveHandler(NewColorCustomEvent, value); }
            }
    
            // This method raises the NewColorCustom event

            private void RaiseNewColorCustomEvent()
            {
                ToolTip t = (ToolTip)
        (SelectedItem as Rectangle).ToolTip;
                ColorRoutedEventArgs newEventArgs = 
        new ColorRoutedEventArgs(t.Content.ToString());
                newEventArgs.RoutedEvent = 
        ColorPickerControl.NewColorCustomEvent;
                RaiseEvent(newEventArgs);
            }
            #endregion
            #region Overrides
            /// <summary>

            /// Overrides the OnSelectionChanged 

        ///ListBox inherited method, and

            /// raises the NewColorEvent

            /// </summary>

            /// <param name="e">the event args</param>

            protected override void OnSelectionChanged
        (SelectionChangedEventArgs e)
            {
                base.OnSelectionChanged(e);
                //raise the event with standard 

        //RoutedEventArgs event args

                RaiseNewColorEvent();
                //raise the event with the custom 

        //ColorRoutedEventArgs event args

                RaiseNewColorCustomEvent();
            }
            #endregion
        }
        #endregion
        #region ColorRoutedEventArgs CLASS
        /// <summary>

        /// ColorRoutedEventArgs : a custom event argument class

        /// </summary>

        public class ColorRoutedEventArgs : RoutedEventArgs
        {
            #region Instance fields
            private string _ColorName = "";
            #endregion
            #region Consructor
            /// <summary>

            /// Constructs a new ColorRoutedEventArgs object

            /// using the parameters provided

            /// </summary>

            /// <param name="clrName">the color name string</param>

            public ColorRoutedEventArgs(string clrName)
            {
                this._ColorName = clrName;
            }
            #endregion
            #region Public properties
            /// <summary>

            /// Gets the stored color name 

            /// </summary>

            public string ColorName
            {
                get { return _ColorName; }
            }
            #endregion
        }
        #endregion
    }
  

To be able to change the snake from one style to another fairly easily

Whoa, there you didnt think it was that easy did you. No way.

It gets more complicated than that, as I have allowed the snake Style to vary (at the moment only Rectangle and Ellipse Style snakes are supported). But wait lets just think about that for the moment, each cell of the game could be one of the following based on the rules of the snake game

But part of my requirements was that snakes should be able to change Style easily. At the moment just Rectangle and Ellipse Styles but I didnt mention anything about the other game items having to be different Styles. So how can we do acheive this, we need every cell of the game to be able to be one of the items mentioned above, if required. That means we need something that can be a filled Rectangle or filled Ellipse, or just a blank cell, or maybe a filled cell. Mmmm, interesting.

Well the answer lies in a few areas actually, firstly standard OO inheritence and secondly WPF Styles. What the heck do I mean. Well lets consider the big picture.

Well the big picture is this, we have a collection of cells that can contain any of the following

Of which snakes can be Rectangle and Ellipse Styles. What sort of object could be used to represent this. Well from a OO inheritence point of view we could imagine a simple cell that simply has a color associated with it. We could use this for walls and food. But is there a difference from walls and food, there should be, you cant eat a wall after all. So how about a simple property IsFood this would help us distinguish between food and walls within a cell collection, wouldnt it. But what about snakes, they are a special a kind of cell arent they, they could be a regular type'ish cell, but a snake can also die if it hits something. So dont we need a new type of cell. Well yes actually we do. So with a little bit of OO inheritence pixie dust and voodoo magic we end up with 2 cell types

StandardCell

Which simply stored the selected color, remember this was done in the Wizard page 1, as discusse above, and has the rather handy IsFood property that can be used in determining whether the cell is food or not. We will see why this property is so useful later.

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Shapes;
    using System.ComponentModel;
    
    namespace WPF_Snakes
    {
        public class StandardCell : INotifyPropertyChanged
        {   
            #region Instance Fields
            private Brush _FillColor = Brushes.Pink;
            private bool _IsFood = false;
            #endregion
            #region Ctor
            public StandardCell()
            {
    
            }
            
            public StandardCell(bool isFood)
            {
                this._IsFood = isFood;
            }
            #endregion
            #region Public Prioperties
    
            public bool IsFood
            {
                get { return _IsFood; }
                set { _IsFood = value; }
            }
    
            public Brush FillColor
            {
                get { return _FillColor; }
                set
                {
                    _FillColor = value;
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new 
        PropertyChangedEventArgs("FillColor"));
                    }
                }
            }
            #endregion
            #region INotifyPropertyChanged Members
            public event PropertyChangedEventHandler PropertyChanged;
            #endregion
        }
    }
  

SnakeCell

Which as we decided a minute ago is a special type of cell, that could be a regular StandardCell but it could also be another type of cell, in this case a Elliptical cell.

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Shapes;
    using System.ComponentModel;
    
    namespace WPF_Snakes
    {
        public class SnakeCell : StandardCell, 
            INotifyPropertyChanged
        {
            #region Instance Fields
            private bool _IsAlive = true;
            private string _SnakeShape = "Rectangular";
            #endregion
            #region Ctor
            public SnakeCell(string snakeShape)
            {
                this._SnakeShape = snakeShape;
            }
            #endregion
            #region Public Prioperties
    
            public string SnakeShape
            {
                get { return _SnakeShape; }
                set
                {
                    _SnakeShape = value;
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, 
            new PropertyChangedEventArgs("SnakeShape"));
                    }
                }
            }
    
            public bool IsAlive
            {
                get { return _IsAlive; }
                set
                {
                    _IsAlive = value;
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new 
            PropertyChangedEventArgs("IsAlive"));
                    }
                }
            }
            #endregion
            #region INotifyPropertyChanged Members
            public event PropertyChangedEventHandler PropertyChanged;
            #endregion
        }
    }
  

So thats great, we've now got a bunch of cells that hold various bits of data about the game. Basically the current game state is represented by a collection of cells (CellCollection.cs in the attached demo app). But what can we do with these cells, how do we show them in a UI?

Just what sort of UI element (Visual in WPF speak) could we possibly use to represent a colored Rectangle or Ellipse at any point in time. One might first think of using a Shape which is after all where Rectangle and Ellipse derive from. But Shape is abstract, so cant do what we want. But a Button is a very very flexable control. We can simple swap out its Template to be whatever we want it to be. We can have various Templates that all target Button control, and simply apply the correct one we are interested in a point in time. For example consider the following Template which targets Button controls:

    <!-- STANDARD WALL -->
    <Style x:Key="StandardCell" TargetType="{x:Type Button}">
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type Button}">
            <Rectangle Name="rectangle" 
        Fill="{Binding Path=FillColor}" 
        Stroke="{Binding Path=FillColor}" 
        Width="Auto" Height="Auto"/>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>    
    

Mmmm, as can be seen we can Style a Button to look like a Rectangle, so surely we could also Style a Button to look like a Ellipse using the following code

    <!-- ELLIPSE STYLE -->
    <Style x:Key="StandardCell" TargetType="{x:Type Button}">
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type Button}">
            <Ellipse Name="rectangle" 
        Fill="{Binding Path=FillColor}" 
        Stroke="{Binding Path=FillColor}" 
        Width="Auto" Height="Auto"/>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>    
    

Yep thats ok too. So we are on to something here. Perhaps we should use Button controls to represent our Cell collection (CellCollection.cs in the demo app). In fact this is a good solution. Well do that. So we now have a collection of cells that are represented by Button controls. But how do we associate one of the Cell classes (StandardCell/SnakeCell we talked about earlier with a particular button? This sounds quite tricky doesnt it. Well luckily WPF comes to our rescue again. We can actually set the DataContext of a Button control, to be one of our cell objects (StandardCell/SnakeCell), and then simply apply the correct Style that matches the current object that is applied to the Button control's DataContext. Lets have a look at an example

    //Set button 0,0 to a StandardCell and 

    //update its style to be the correct Style 

    StandardCell sc = new StandardCell(true);
    sc.FillColor = _FoodColor;
    _cells[0, 0].DataContext = sc;
    _cells[0,0].Style = _FoodStyle;    
    

So thats enough to style a Button control and bind it to a cell type object. But how does this cell type object work with the Style? Well thats down to Databinding. Recall that we had a Style like the following

    <!-- STANDARD WALL -->
    <Style x:Key="StandardCell" TargetType="{x:Type Button}">
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type Button}">
            <Rectangle Name="rectangle" Fill="{Binding 
            Path=FillColor}" 
            Stroke="{Binding Path=FillColor}" 
            Width="Auto" Height="Auto"/>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>    
    

The import part to note here is the line, especially the Binding parts.

    <Rectangle Name="rectangle" 
    Fill="{Binding Path=FillColor}" 
    Stroke="{Binding Path=FillColor}" Width="Auto" 
    Height="Auto"/>
    

Where do these come from, well these values are bound to the actual value of the Cell object that is used on the DataContext of the Button control being Styled.Recall there was a property for FillColor in the StandardCell object

        public Brush FillColor
        {
            get { return _FillColor; }
            set
            {
                _FillColor = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, 
            new PropertyChangedEventArgs("FillColor"));
                }
            }
        }    
    

Well thats used in the DataBinding. Neat huh.

To be able to create new level by drawing out the level

Ok so I wanted to be able to draw out a new level. So the thought process starts. Mmmm, I want to be able to draw using the mouse or click a certain area and have it change to a game object such as a wall or a snake or bit of food. So how could I acheive this. Well again isnt a Button control quite a handy control to use. It has event for MouseEnter and Click so it seems to be a logical choice. In fact it turns out to be a perfect choice, we simply subscribe to the MouseEnter and Click events of an array of buttons, and use the value of the currently selected drawing item to Style the button accordingly, just as we just saw above.

So how do we draw different items? Well ive given users the choice of menus or buttons, the menus have more options as UI space had to be considered.

Buttons Available

There are buttons available to :

Menus Available

There are menus available to :

Some of these interections look pretty similar dont they. There is some commonality between what the buttons are doing and what the menus are doing. We could simply call the same method for both these events (one for button click and one for menu click), but WPF exposes a more interesting more extendable technique for dealing with this. This technique is known as Commands. Lets have a look at one of these.

  //define a new command for the SettingsWizard object

  public static readonly RoutedCommand DrawWallCommand = 
    new RoutedCommand("DrawWall", typeof(SettingsWizard));
  //add the command to the command bindings for the SettingsWizard 

  this.CommandBindings.Add(new 
  CommandBinding(SettingsWizard.DrawWallCommand,this.WallDrawing));
  

Then in the XAML, we can get the menu and the button to use this command by doing the following, notice the Command attribute, there is also the x:Static which means look for a static item, and local: simply points to the current assembly namespace

  <MenuItem Name="menWall" Header="Draw Walls"  
  Command="{x:Static local:SettingsWizard.DrawWallCommand}">
  </MenuItem>
  <Button x:Name="btnDrawWall" Width="81" Height="26" 
  Content="Draw Wall" Canvas.Left="8" Canvas.Top="14" 
  Template="{StaticResource toolBarButtons}" 
  Command="{x:Static local:SettingsWizard.DrawWallCommand}"/>
  

So thats how we can bind one or more objects to the same command. But what about disabling these commands under certain conditions, turns out we can also do that with commands. Its just slightly more work. We define the command like

  CommandBinding commandBindingSnakeNorth = 
  new CommandBinding(SettingsWizard.SnakeNorthCommand, 
  this.SnakeNorth);
  commandBindingSnakeNorth.CanExecute += 
  new CanExecuteRoutedEventHandler(
  commandBindingSnakePlacement_CanExecute);
  this.CommandBindings.Add(commandBindingSnakeNorth);
  

But this time there is an event wired up which tells the command whether it can execute or not.

  private void commandBindingSnakePlacement_CanExecute
  (object sender, CanExecuteRoutedEventArgs e)
  {
      e.CanExecute = _snakeInitialized;
  }  
  

To be able to move the snake around the level

Recall I previously stated that the current game state was represented by a array of a Button objects which have the appropriate Style applied to them. But how does the Button know which Style to use. Well thats down to the mouse movement. This is captured on the main window (Window1) and then the movement is sent to the CellCollection to determine what should happen, the logic is as follows:

This is all good so far. But how do we do this updating. We need to do it automatically based on the last move, which implies we need some sort of game timer that applies the last direction until the user changes it. So in comes a WPF Timer, the DispatchTimer. Which expected a time interval and a enabled state to be true in order for it to tick. Basically for each tick the game state is updated using the last registered move direction.

Thats it essentially.

One interesting thing that I did find along the way was that, because the cell objects held internal Brush objects that I am trying to animate (well only for the SnakeCell objects actually) and that these internal Brush objects are classed as being Freezable objects (immutable, not able to change once created) the animations were not working. A little bit of messing about and I got the animations working using a clone type converter, which simply clones the original databound object within the styles databinding and uses the clone to animate. The relevant section of the SnakeCell Style is shown below.

  <Rectangle x:Name="rect" RenderTransformOrigin="0.5,0.5"
      Fill="{Binding Path=FillColor, 
    Converter={x:Static local:CloneConverter.Instance}}"
    Stroke="{Binding Path=FillColor,
    Converter={x:Static local:CloneConverter.Instance}}"
    Width="Auto" Height="Auto" Margin="2,2,2,2">  
  

And the value convertor that does the clone for the databinding is as follows:

    using System;
    using System.Globalization;
    using System.Windows;
    using System.Windows.Data;
    
    namespace WPF_Snakes
    {
        /// <summary>

        /// Used in ControlTemplate that use a Freezable object that

        /// is animated.

        /// This deals with the cannot animate immutable object 

        /// error that will

        /// be seen if you dont clone the immutable object for use 

        ///in the ControlTemplate

        /// ready for it to be animated

        /// In this application the immutable object will be the 

        /// the <see cref="SnakeCell">

        /// SnakeCell</see> FillColor Brush which is animated 

        /// in the ControlTemplate

        /// </summary>

        /// 

        public class CloneConverter : IValueConverter
        {
            #region Instance Fields
            public static CloneConverter Instance = new 
            CloneConverter();
            #endregion
            #region Public Methods
            public object Convert(object value, Type targetType, 
            object parameter, CultureInfo culture)
            {
                if (value is Freezable)
                {
                    value = (value as Freezable).Clone();
                }
                return value;
            }
    
            public object ConvertBack(object value, Type targetType, 
            object parameter, CultureInfo culture)
            {
                throw new NotSupportedException
                ("Can't convert back");
            }
            #endregion
        }
    }
  

I could have probably changed the properties in the cell objects to be dependancy properties and all would have been well, but then I never would have worked this code out now would I.

The last thing of note when dealing with the snake movement is they position of the head. If both the tail and head look the same which was should the user move? I needed something to make the head look different. I could have had a special head Style but I opted for a head Adorner which is applied to the current head Button layer (basically an adorner draws stuff on a layer above the element it associated with). This adorner is called SnakeHeadAdorner and is simply reponsable for drawing the snakes eyes on the Styled Button dependant on the current movement direction. The SnakeHeadAdorner is actually shown below

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Shapes;
    using System.Globalization;
    
    namespace WPF_Snakes
    {
    
        public class SnakeHeadAdorner : Adorner
        {
            private string _moveDirection;
            private Brush _bgBrushColor;
    
            // Be sure to call the base class constructor.

            public SnakeHeadAdorner(UIElement adornedElement, 
                string moveDirection, Brush bgBrushColor)
                : base(adornedElement)
            {
                this._moveDirection = moveDirection;
                this._bgBrushColor = bgBrushColor;
            }
    
    
    
            protected override void OnRender(DrawingContext 
                  drawingContext)
            {
                Rect adornedElementRect = new Rect(
                    this.AdornedElement.RenderSize);
                Pen renderPen = new Pen(_bgBrushColor, 1.0);
                double eyeWidth;
                double eyeHeight;
                Point eye1PointStart;
                Point eye2PointStart;
    
                switch (_moveDirection)
                {
                    case "Up":
                        eyeWidth = adornedElementRect.Width / 5;
                        eyeHeight = eyeWidth;
                        eye1PointStart = new Point(eyeWidth-2, 10);
                        drawingContext.DrawRectangle(_bgBrushColor, 
                             renderPen, new Rect(eye1PointStart, 
                             new Size(eyeWidth, eyeHeight)));
                        eye2PointStart=new Point((eyeWidth*3)+2, 10);
                        drawingContext.DrawRectangle(
                        _bgBrushColor, renderPen, new Rect(
                        eye2PointStart, new Size(eyeWidth, 
                        eyeHeight)));
                        break;
                    case "Down":
                        eyeWidth = (int)adornedElementRect.Width/5;
                        eyeHeight = (int)eyeWidth;
                        eye1PointStart = new Point(eyeWidth - 2, 
                        adornedElementRect.Height - (10 + eyeHeight));
                        drawingContext.DrawRectangle(_bgBrushColor, 
                        renderPen, new Rect(eye1PointStart, 
                        new Size(eyeWidth, eyeHeight)));
                        eye2PointStart = new Point((eyeWidth * 3) 
                        + 2, adornedElementRect.Height - (10 + 
                        eyeHeight));
                        drawingContext.DrawRectangle(
                        _bgBrushColor, renderPen, new 
                        Rect(eye2PointStart, new Size(eyeWidth, 
                        eyeHeight)));
                        break;
                    case "Left":
                        eyeHeight = adornedElementRect.Height / 5;
                        eyeWidth = eyeHeight;
                        eye1PointStart = new Point(10, eyeHeight);
                        drawingContext.DrawRectangle(_bgBrushColor, 
                        renderPen, new Rect(eye1PointStart, 
                        new Size(eyeWidth, eyeHeight)));
                        eye2PointStart = new Point(10, eyeHeight*3);
                        drawingContext.DrawRectangle(_bgBrushColor, 
                        renderPen, new Rect(eye2PointStart, 
                        new Size(eyeWidth, eyeHeight)));
                        break;
                    case "Right":
                        eyeHeight = adornedElementRect.Height / 5;
                        eyeWidth = eyeHeight;
                        eye1PointStart = new Point(
                        adornedElementRect.Width - (eyeWidth+10), 
                        eyeHeight);
                        drawingContext.DrawRectangle(_bgBrushColor, 
                        renderPen, new Rect(eye1PointStart, 
                        new Size(eyeWidth, eyeHeight)));
                        eye2PointStart = new Point(
                        adornedElementRect.Width - (eyeWidth+10), 
                        eyeHeight*3);
                        drawingContext.DrawRectangle(_bgBrushColor, 
                        renderPen, new Rect(eye2PointStart, 
                        new Size(eyeWidth, eyeHeight)));
                        break;
    
                }
            }
        }
    }  
  

And to apply and remove this SnakeHeadAdorner the following code is used

       private void SnakeAdornerEvent(object sender, 
             SnakeAdornerEventArgs e)
        {
            string moveDirection = "";

            switch (e.MoveDirection)
            {
                case CellCollection.MoveDirection.Up:
                    moveDirection = "Up";
                    break;
                case CellCollection.MoveDirection.Down:
                    moveDirection = "Down";
                    break;
                case CellCollection.MoveDirection.Left:
                    moveDirection = "Left";
                    break;
                case CellCollection.MoveDirection.Right:
                    moveDirection = "Right";
                    break;
            }

            SnakeHeadAdorner sa = new SnakeHeadAdorner(e.
               SnakeButtons[0], moveDirection, mainGrid.Background);
            if (alSingle == null)
            {
                alSingle = AdornerLayer.GetAdornerLayer(
                      e.SnakeButtons[0]);
                alSingle.Add(sa);
                _snakesToClear.Add(e.SnakeButtons[0]);
            }
            else
            {
                try
                {
                    alSingle.Remove(alSingle.GetAdorners(
                        e.SnakeButtons[1])[0]);
                    alSingle = AdornerLayer.GetAdornerLayer(
                        e.SnakeButtons[0]);
                    alSingle.Add(sa);
                    _snakesToClear.Add(e.SnakeButtons[0]);
                    _snakesToClear.Add(e.SnakeButtons[1]);
                }
                catch
                {
                }
            }
        }

        private void ClearAdorners()
        {
            foreach (Button btn in _snakesToClear)
            {
                alSingle = AdornerLayer.GetAdornerLayer(btn);
                Adorner[] ads = alSingle.GetAdorners(btn);
                foreach (Adorner ad in ads)
                    alSingle.Remove(ad);
            }
        }  
  

This is SnakeHeadAdorner results in the following rendering:

To be able to run the game in different difficulty levels

I wanted to be able to run the game at various levels of difficulty. As mentioned earlier the basis of moving the snake around the level is based on a DispatchTimer, which is just a timer that has an interval at the end of the day. So couldnt we make the level harder if we shortened the time between automatically calculated move. Well yes we could. In fact lets look at that. We have a timer that we want to effect somehow. We could have some sort of UI element to allow the difficulty to be ramped up or down. Perhaps a Slider may be what we are looking for here. We could then use the value of the slider to adjust the interval for the main level time (the DispatchTimer).

So in the XAML we could have a Slider something like

<Slider x:Name="sliDifficulty" HorizontalAlignment="Center" 
  VerticalAlignment="Center" LargeChange="100" Maximum="600" 
  Minimum="300" SmallChange="100" AutoToolTipPlacement="TopLeft" 
  TickFrequency="100" TickPlacement="BottomRight" Margin="15,0,0,0" 
  Width="177" Height="23"/>
<Label HorizontalAlignment="Center" VerticalAlignment="Center" 
  Width="100" Height="Auto" Content="{Binding Path=Value, 
            ElementName=sliDifficulty, Mode=Default, 
  Converter={x:Static local:DifficultyConvertor.Instance}}" 
  Margin="5,0,0,0" Background="#00474242" Foreground="#FF000000" 
  FontFamily="Agency FB" FontSize="14" />

And then in the code behind we could use the value of the slider to adjust the main level timers interval, just like

private void sliDifficulty_ValueChanged(object sender, 
RoutedPropertyChangedEventArgs<double> e)
{
     _TimerInterval = new TimeSpan(0, 0, 0, 0, 
          (int)sliDifficulty.Value);
     _timer.Interval = _TimerInterval;
}

But wait the actual slider is showing some text telling the user exactly (in words) how hard the level is. Hows that?

The answer is a value convertor. It can be seen in the following line that the label content is bound to the Slider, but that should be a number and we are seeing a text value. mmmm.

<Label ..... Content="{Binding Path=Value, 
  ElementName=sliDifficulty, Mode=Default, 
   Converter={x:Static local:DifficultyConvertor.Instance}}" ... />

The interesting thing here is the Converter part. This tells the XAML that there is a special DataBinding converter that is used to translate the bound value. Lets have a look at the converter shall we?

    using System;
    using System.Globalization;
    using System.Windows;
    using System.Windows.Data;
    
    namespace WPF_Snakes
    {
    
        public class DifficultyConvertor : IValueConverter
        {
            #region Instance Fields
            public static DifficultyConvertor Instance = 
                new DifficultyConvertor();
            #endregion
            #region Public Methods
            public object Convert(object value, Type targetType, 
                object parameter, CultureInfo culture)
            {
                double doubleValue = (double)value;
    
                if (doubleValue >= 300 && doubleValue < 400)
                    return "Hard";
                else if (doubleValue >= 400 && doubleValue < 500)
                    return "Medium";
                else if (doubleValue >= 500)
                    return "Easy";
                else
                    return "Not recognized";
            }
    
            public object ConvertBack(object value, Type 
              targetType, object parameter, CultureInfo culture)
            {
                throw new NotSupportedException
                     ("Can't convert back");
            }
            #endregion
        }
    }
  

Simply huh. But very powerful.

To show some visual animation when the snake died

Recall earlier I stated that we were using Button controls with different Style applied dependant on the cell type, and that I also gave the user the choice of a Rectangular or Elliptical SnakeCell and that snakes can actually die when they hit things. Well lets see how all that is achieved shall we. Well this time its all in XAML really.

    <!-- SNAKE CELL RECTANGULAR  -->
    <Style x:Key="SnakeCellRectangular" TargetType="{x:Type Button}">
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type Button}">
            <ControlTemplate.Resources>
              <Storyboard x:Key="Timeline1">
                <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
            Storyboard.TargetName="rect" 
                        Storyboard.TargetProperty=
            "(UIElement.RenderTransform).
            (TransformGroup.Children)[0].
            (ScaleTransform.ScaleX)">
                  <SplineDoubleKeyFrame KeyTime="00:00:00" 
            Value="6.0"/>
                  <SplineDoubleKeyFrame KeyTime="00:00:00.5000000" 
            Value="4.0"/>
                  <SplineDoubleKeyFrame KeyTime="00:00:01" 
            Value="2.0"/>
                  <SplineDoubleKeyFrame KeyTime="00:00:01.5000000" 
            Value="0.5"/>
                  <SplineDoubleKeyFrame KeyTime="00:00:02" 
            Value="0"/>
                </DoubleAnimationUsingKeyFrames>
                <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
            Storyboard.TargetName="rect" 
                        Storyboard.TargetProperty=
            "(UIElement.RenderTransform).
            (TransformGroup.Children)[0].
            (ScaleTransform.ScaleY)">
                  <SplineDoubleKeyFrame KeyTime="00:00:00" 
            Value="6.0"/>
                  <SplineDoubleKeyFrame KeyTime="00:00:00.5000000" 
            Value="4.0"/>
                  <SplineDoubleKeyFrame KeyTime="00:00:01" 
            Value="2.0"/>
                  <SplineDoubleKeyFrame KeyTime="00:00:01.5000000" 
            Value="0.5"/>
                  <SplineDoubleKeyFrame KeyTime="00:00:02" 
            Value="0"/>
                </DoubleAnimationUsingKeyFrames>
                <ColorAnimationUsingKeyFrames BeginTime="00:00:00" 
            Storyboard.TargetName="rect" 
                        Storyboard.TargetProperty="(Shape.Fill).
            (SolidColorBrush.Color)">
                  <SplineColorKeyFrame KeyTime="00:00:00" 
            Value="#00FFFFFF"/>
                  <SplineColorKeyFrame KeyTime="00:00:00.5000000" 
            Value="#FFE50404"/>
                  <SplineColorKeyFrame KeyTime="00:00:01" 
            Value="#00FFFFFF"/>
                  <SplineColorKeyFrame KeyTime="00:00:01.5000000" 
            Value="#FFE50404"/>
                  <SplineColorKeyFrame KeyTime="00:00:02" 
            Value="#00FFFFFF"/>
                </ColorAnimationUsingKeyFrames>
                <ColorAnimationUsingKeyFrames BeginTime="00:00:00" 
            Storyboard.TargetName="rect" 
                        Storyboard.TargetProperty="(Shape.Stroke).
                     (SolidColorBrush.Color)">
                  <SplineColorKeyFrame KeyTime="00:00:00" 
                     Value="#00FFFFFF"/>
                  <SplineColorKeyFrame KeyTime="00:00:00.5000000" 
                     Value="#FFE50404"/>
                  <SplineColorKeyFrame KeyTime="00:00:01" 
                     Value="#00FFFFFF"/>
                  <SplineColorKeyFrame KeyTime="00:00:01.5000000" 
             Value="#FFE50404"/>
                  <SplineColorKeyFrame KeyTime="00:00:02" 
                     Value="#00FFFFFF"/>
                </ColorAnimationUsingKeyFrames>
              </Storyboard>
            </ControlTemplate.Resources>
            <Rectangle x:Name="rect" RenderTransformOrigin="0.5,0.5"
                     Fill="{Binding Path=FillColor, 
                     Converter={x:Static 
                     local:CloneConverter.Instance}}"
                     Stroke="{Binding Path=FillColor,
                     Converter={x:Static 
                     local:CloneConverter.Instance}}"
                     Width="Auto" Height="Auto" Margin="2,2,2,2">
              <Rectangle.RenderTransform>
                <TransformGroup>
                  <ScaleTransform ScaleX="1" ScaleY="1"/>
                  <SkewTransform AngleX="0" AngleY="0"/>
                  <RotateTransform Angle="0"/>
                  <TranslateTransform X="0" Y="0"/>
                </TransformGroup>
              </Rectangle.RenderTransform>
            </Rectangle>
            <ControlTemplate.Triggers>
              <DataTrigger Binding="{Binding IsAlive}" 
                       Value="False">
                <DataTrigger.EnterActions>
                  <BeginStoryboard 
                       Storyboard="{StaticResource Timeline1}"/>
                </DataTrigger.EnterActions>
              </DataTrigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>  
  

The import part to note here is the DataTrigger section which uses the special IsAlive property to trigger an Storyboard animation. This is basically used to kill the snake. All thanks to data binding and Styles. Good stuff. The IsAlive property is set in the CellCollection class when a collision is detected. This is done for all snake styles buttons, thus they all carry out their own death animations.

As shown here:

To show some visual message when the time expired or when the user completed the level or when the snake died

The idea here is to simply show some text that might popup when some event happens. So I thought to myself, why not have a usercontrol with a peice of text on it which can be set, and that has an animation that flashes the text. So this is what I ended up writing.

    <UserControl
        xmlns="http://schemas.microsoft.com/winfx/2006/
        xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Class="WPF_Snakes.FlashTextControl"
        x:Name="UserControl"
        Width="700" Height="112">
    
      <UserControl.Resources>
        <Storyboard x:Key="OnVisibilityChanged">
          <ColorAnimationUsingKeyFrames BeginTime="00:00:00" 
        Storyboard.TargetName="lblText" 
                Storyboard.TargetProperty=
        "(TextElement.Foreground).(SolidColorBrush.Color)">
            <SplineColorKeyFrame KeyTime="00:00:00" 
        Value="#FFFF0F0F"/>
            <SplineColorKeyFrame KeyTime="00:00:00.1000000" 
        Value="#00FFFFFF"/>
            <SplineColorKeyFrame KeyTime="00:00:00.2000000" 
        Value="#FFFF0F0F"/>
            <SplineColorKeyFrame KeyTime="00:00:00.3000000" 
        Value="#00FFFFFF"/>
            <SplineColorKeyFrame KeyTime="00:00:00.4000000" 
        Value="#FFFF0F0F"/>
            <SplineColorKeyFrame KeyTime="00:00:00.5000000" 
        Value="#00FFFFFF"/>
            <SplineColorKeyFrame KeyTime="00:00:00.6000000" 
        Value="#FFFF0F0F"/>
          </ColorAnimationUsingKeyFrames>
        </Storyboard>
      </UserControl.Resources>
    
      <Grid x:Name="LayoutRoot" Height="Auto">
        <Label x:Name="lblText" HorizontalAlignment="Center" 
    VerticalAlignment="Center" Background="{x:Null}" 
    BorderBrush="{x:Null}" FontFamily="Agency FB" 
    FontSize="109" FontWeight="Bold" Foreground="#FFFF0F0F" 
    Width="695.31" Height="146.18"/>
      </Grid>
    </UserControl>
    .....
    .....
    .....
    .....
    using System;
    using System.IO;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Navigation;
    
    namespace WPF_Snakes
    {
        public partial class FlashTextControl
        {
            #region Ctor
            public FlashTextControl()
            {
                this.InitializeComponent();
                this.IsVisibleChanged += new 
        DependencyPropertyChangedEventHandler(
        FlashTextControl_IsVisibleChanged);
            }
            #endregion
            #region Private Methods
            void FlashTextControl_IsVisibleChanged(object sender< 
        DependencyPropertyChangedEventArgs e)
            {
                if (this.Visibility == Visibility.Visible)
                {
                    ((Storyboard)this.
            Resources["OnVisibilityChanged"]).Begin(this);
                }
            }
            #endregion
            #region Public Properties
            public string FlashText
            {
    
                set { lblText.Content = value; }
            }
            #endregion
        }
    }  
  

Pretty simple control, that does some flashing text.

Thats it

I have tried to make sure that this articles decisions and outcomes were properly explained, and I hope that this article contains enough meat for new and old WPFers out there. I personally think it does, I mean it uses Templates, DataBinding, Resources, Commands, Styles, Adorners, Convertors, Timers...its all there (well no dependancy properties or routed events as I was feeling slightly lazy, but other than it all cool)

So What Do You Think ?

I would just like to ask, if you liked the article please vote for it, and leave some comments, as it lets me know if the article was at the right level or not, and whether it contained what people need to know.

History

v1.0 16/10/07 : Initial issue

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralLicence
Basarat Ali Syed
2:04 8 Mar '10  
Kindly mention the license associated with this code. Thanks
DeX

GeneralRe: Licence
Sacha Barber
2:06 8 Mar '10  
Do what you like with it.
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

GeneralRe: Licence
Basarat Ali Syed
2:28 8 Mar '10  
Thanks Smile Great project by the way .... and I follow your articles regularly (not a big surprise I am sure ... me and another million Big Grin )
DeX

GeneralRe: Licence
Sacha Barber
3:09 8 Mar '10  
Thanks.
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

Generalits awesome
ramnet
20:59 18 Jan '08  
thanks for the work you have done Smile

RamaRao MVS
Software Engineer

GeneralRe: its awesome
Sacha Barber
22:20 18 Jan '08  
If you like this one, try these on for size

MyFriends.aspx 3DExplorer.aspx WCFWPFChat.aspx
They are my best WPF ones

Sacha Barber
  • Microsoft Visual C# MVP 2008
  • Codeproject MVP 2008
Your best friend is you.
I'm my best friend too. We share the same view, and never argue

My Blog : sachabarber.net

GeneralReally cool
MG02
13:13 15 Nov '07  
This is really cool...great job, I'm voting for u
GeneralRe: Really cool
Sacha Barber
21:47 15 Nov '07  
thanks

Sacha Barber A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?

My Blog : sachabarber.net

GeneralThanks
Dr.Luiji
23:44 12 Nov '07  
Thank you for sharing another good article.
5 stars as usual.

Dr.Luiji
Trust and you'll be trusted.
GeneralRe: Thanks
Sacha Barber
0:49 13 Nov '07  
thanks there.

I have also written a WPF treeview one and Silverlight one since this one that you may also like

Sacha Barber A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?

My Blog : sachabarber.net

GeneralExcellent
Expategghead
0:38 30 Oct '07  
Inspired me to open the books I have and try again to get to grips with this stuff.

Expategghead

GeneralRe: Excellent
Sacha Barber
0:48 30 Oct '07  
Expategghead wrote:
Inspired me to open the books I have and try again to get to grips with this stuff.

Cool thats good. Ive got another one, which I did in Silverlight (my 1st Silverlight app) you should look out for that one, if you like WPF type articles.



Sacha Barber A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?

My Blog : sachabarber.net

GeneralNice work
Swader
23:55 23 Oct '07  
Hey man,

Thats very nice work.. Kudos.
I would rate it 4.5 (0.5 for not adding the fruits Wink )

Well.. great work and I hope to make something like that myself.
(Currently I just started leaning Smile )

Cheers.

------------------------------------------------------------------
Life would have been much easier if I had the source-code!!

GeneralRe: Nice work
Sacha Barber
0:42 24 Oct '07  
Thanks very much.


Swader wrote:
0.5 for not adding the fruits

There is food that pops up, is this what you mean?

Sacha Barber A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?

My Blog : sachabarber.net

GeneralRe: Nice work
Swader
0:35 26 Oct '07  
well.. when i played that game.. I and the snake just strolled around looking for something to eat.. but didnt get anything. and eventually.. the snake died of hunger Wink

------------------------------------------------------------------
Life would have been much easier if I had the source-code!!

GeneralRe: Nice work
Sacha Barber
1:14 26 Oct '07  
What I do with the food, is if they popup and the snake already occupies that position (cell) the food is diregarded (removed from a list), so if this happens with all fruit (food) then that could explain what you state here. Would be quite strange though, as the snake is very small to begin with.

Oh well, so its not perfect, hope people learn from it all the same.

Sacha Barber A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?

My Blog : sachabarber.net

GeneralRe: Nice work
Swader
8:16 26 Oct '07  
Sure they will.. and hey.. nobody is perfect Wink

Btw.. This is an awesome work for sure.. since I tried it in DotNet 2 and wasnt able to do even what you did. Its just got too confusing for me Smile

I hope I can code something like this in the near future.. cant think of doing now.. since I just started learning Smile

Cheers.

------------------------------------------------------------------
Life would have been much easier if I had the source-code!!

GeneralRe: Nice work
Sacha Barber
8:37 26 Oct '07  
Swader wrote:
Btw.. This is an awesome work for sure..

thanks man

Swader wrote:
and hey.. nobody is perfect

Yes, but we can try, and my team leader at work, in terms of coding abilities is near perfect. In 2 hours today he knocked up a 3d rotating cube mesh with rotating camera in managed DirectX, Im learning that next/after my current silverlight 1.1 mission. I am very envious.



Sacha Barber A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?

My Blog : sachabarber.net

GeneralRe: Nice work
Swader
23:43 26 Oct '07  
Sacha Barber wrote:
Yes, but we can try

Sure we can Smile


Sacha Barber wrote:
I am very envious.

Me too Big Grin

Goodluck for ur Silverlight mission.. Will be waiting to try it out.

------------------------------------------------------------------
Life would have been much easier if I had the source-code!!

GeneralRe: Nice work
Sacha Barber
2:21 27 Oct '07  
The Silverlight article will be published tomorrow 28/10/07, check it out

Sacha Barber A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?

My Blog : sachabarber.net

GeneralAbsolutely Brilliant
HuntrCkr
21:13 23 Oct '07  
Just absolutely brilliant!

I started something similar a while ago, just to play around with WPF, but mine was never nearly as pretty, or as powerful. (or ever finished for that matter D'Oh! )

Great job as usual Smile
GeneralRe: Absolutely Brilliant
Sacha Barber
0:41 24 Oct '07  
HuntrCkr wrote:
Just absolutely brilliant!

I thank you very much.


HuntrCkr wrote:
I started something similar a while ago, just to play around with WPF, but mine was never nearly as pretty, or as powerful. (or ever finished for that matter )

You know I just wanted to do this to see what its like to write a small game, I dont normally do this sort of thing, I do business type stuff.

I have a friend who suggested a new game. Like PacMan, but the maze is invisible, but can be flashed up for a couple of seconds to show it, But this costs points. I think this is great as you cant see whether the ghosts are just about to get you or not. Now maybe you could do that, it would be most cool.







Sacha Barber A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?

My Blog : sachabarber.net

GeneralCrushed
gajatko
0:32 23 Oct '07  
Cool, but it crushed at "level1.lev". I was just heading to consume 4th cake when WPF_Snakes.exe threw an "unhandled exception and app must exit or debug etc.".

Also, is it possible start the game quicker? Do I have to wait one minute each time?

Greetings - Gajatko

Portable.NET is part of DotGNU, a project to build a complete Free Software replacement for .NET - a system that truly belongs to the developers.

GeneralRe: Crushed
gajatko
0:46 23 Oct '07  
Snake crushed again. Here is screenshot for ya:
http://www.flickr.com/photos/15950996@N08/1706092507/[^]

PS: couldn't debug because of error:
Nie można zlokalizować zasobu „forms/window1.xaml”. ("Couldn't locate a resource '...'")

Exception details:

System.IO.IOException was unhandled
Message="Nie mo&#380;na zlokalizowa&#263; zasobu „forms/window1.xaml”." Source="PresentationFramework" StackTrace:
w MS.Internal.AppModel.ResourcePart.GetStreamCore(FileMode mode, FileAccess access)
w System.IO.Packaging.PackagePart.GetStream(FileMode mode, FileAccess access)
w System.IO.Packaging.PackWebResponse.CachedResponse.GetResponseStream()
w System.IO.Packaging.PackWebResponse.GetResponseStream()
w System.IO.Packaging.PackWebResponse.get_ContentType()
w MS.Internal.WpfWebRequestHelper.GetContentType(WebResponse response)
w System.Windows.Navigation.NavigationService.GetObjectFromResponse(WebRequest request, WebResponse response, Uri destinationUri, Object navState)
w System.Windows.Navigation.NavigationService.DoNavigate(Uri source, NavigationMode f, Object navState)
w System.Windows.Navigation.NavigateQueueItem.Dispatch(Object obj)
w System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter)
w System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)
w System.Windows.Threading.DispatcherOperation.InvokeImpl()
w System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
w System.Threading.ExecutionContext.runTryCode(Object userData)
w System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
w System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
w System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
w System.Windows.Threading.DispatcherOperation.Invoke()
w System.Windows.Threading.Dispatcher.ProcessQueue()
w System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
w MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
w MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
w System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter)
w System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)
w System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Boolean isSingleParameter)
w System.Windows.Threading.Dispatcher.Invoke(DispatcherPriority priority, Delegate method, Object arg)
w MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
w MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
w System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
w System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
w System.Windows.Threading.Dispatcher.Run()
w System.Windows.Application.RunInternal(Window window)
w System.Windows.Application.Run(Window window)
w System.Windows.Application.Run()
w WPF_Snakes.App.Main() w D:\JACEK\Visual Studio 2005\Projects\!Temp\WPF_Snakes\WPF_Snakes\WPF_Snakes\obj\Debug\App.g.cs:wiersz 0


Greetings - Gajatko

Portable.NET is part of DotGNU, a project to build a complete Free Software replacement for .NET - a system that truly belongs to the developers.

GeneralRe: Crushed
Sacha Barber
0:54 23 Oct '07  
Sorry to heat that it crashed. I have tried to repeat it, but cant, so could you be more specfic about what you were doing.

And also one point, this article was more about standard WPF than game development, so it was a bit of fun really. Its not that serious



Sacha Barber A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?

My Blog : sachabarber.net


Last Updated 24 Oct 2007 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010