SilverMap – A Silverlight Game Tile Map and Editor

I am happy to announce that I have uploaded a new project called SilverMap to CodePlex. SilverMap is a tile map control for Silverlight games that uses layered maps where the higher layers are drawn over the lower ones. In addition, individual tiles have a z-index position and can be drawn over one another. You can also place the tiles anywhere on the map, instead of in a tight grid, which is both beneficial and kind of a pain. :)

SilverMap makes use of the WriteableBitmapEx library. I also used Danc's Miraculously Flexible Game Prototyping Tiles while testing and included them as a zip with the project.

The maps that are created can be saved as XML and the layering information is stored with them. You can set the opacity of a tile and in the near future will be able to scale and rotate them. Aside from the editor the SilverMap.UIMap control can be included in your game project and has the ability to load maps from a file stream (useful for maps that download on the fly).

The code is freely available under the Microsoft Public License so feel free to use it your games, whether they are free games or not.

SilverMap Editor

12/10/09


Silverlight 3 Book

As Frank LaVigne mentions in this post about a Silverlight 3 Book, I have been asked to co-author the book with him. I am honored that he asked me to help with completing the book and will work to deliver a solid book for any .NET developers out there getting their feet wet in the world of Silverlight business development. The book should give you some proof that Silverlight is a viable business tool and not just something for game developers to play with or to make web sites pretty.

Having already had the privilege of peer reviewing the first few chapters this is going to be a great intro to Silverlight, Frank is all about getting right into code!

9/11/09


A Silverlight Games Star Field

Here is a really nice large star field in Silverlight if you are creating a space based game, or even if you are not it is still pretty cool. :)

8/10/09


Silverlight and Multi-Player Games

I have been working on a multi-player library for Silverlight games over the past few months, well, off and on working on it while continuing Perenthia development.

I hope to be able to get the code on CodePlex before too much longer along with a tutorial on how to use it and a demo game.

The library will start with Duplex services but I do have plans to implement sockets in the future. Silverlight’s duplex and socket support is sufficient for RPGs and RTS or really any kind of turn based game but until Silverlight can support UDP sockets it is probably not suited to real time action games.

The goal is that the library can be used with any game engine such as SilverSprite or PlayBits and that it will provide easy to setup and use networking capabilities for your game.

The library is coming along well and I am building a demo game to help with implementation and testing that I hope to have finished in the next week or so.

8/9/09


Game Objects - My Design

I thought I would share one of the core concepts I implemented in Perenthia that has been working quite well. Early on in development I decided to create templates and instances in regards to game objects. A template would define common, unchangable properties of an object whereas an instance would be the opposite. An example would be a Sword; there is a template for a Sword that defines its damage value, price, required skill level, etc. 

I have one table that stores all the objects for the game. Since objects being actively used are loaded into memory and the table is indexed for those queries it runs pretty fast. Since I store all of the objects into one table I make heavy use of the XML data type in SQL 2005 to provide a bunch of XML Serialized Key/Value pairs that define the properties of an object. On the code side of things a dictionary provides the underlying data store for properties of the object. For instance, the Sword example above has a Power property that is used in combination with the wielder's skill in swords to determine damage. The Power property just fetches the Power value from the properties dictionary using the property name. This works a lot like the dependency properties, just my collection serializes to XML for database storage. When an object is loaded from the database the xml from the template is retrieved and added to the object, then the object instance xml is dropped into the object, overriding any template properties of the same name.

This property dictionary also allows me to store whether or not the property belongs to a template or instance. If the property belongs to a template then when the instance is serialized and saved to the database those template properties are excluded. Likewise saving a template does not serialize and save the instance properties. This structure allows for a lot of flexibility with my objects and has been working out quite well. I probably would not have used serialization and xml the way I am if I was doing a typical call to the database qith each request as the extra overhead would not have been worth the effort. However, since my objects are loaded into memory and my saves are background threaded I experience a lot of benefit of an easy to use object.

I shuld note that players character data is stored in a separate table from the core objects table. It follows the same design buy allows me to migrate and change data in the objects table without overwriting player data, since players are essentially the same as mobiles. 

5/15/09


LINQ Queries

I have spent the last three days writing LINQ qeueries over object collections and have to come to rely on LINQ pretty heavily for most query operations. I have played around some with LINQ to SQL and LINQ to Entities but I mostly use it to just query over some good old fashion collections like so:

 

var groups = proposal.Lines.Where(kvp => kvp.Key.Equals(key.Quarter))
                                           .Select(kvp => kvp.Value.Where(l => l.AvailType.Spot == "N" && l.AvailType.Type == "RE"))
                                           .FirstOrDefault()
                                           .GroupBy(l => new { l.NetworkId, l.UnitLength, l.BrandId });

            List<TimePeriod> weeks = proposal.GetWeeksFromOrderFlightDates(key.Quarter);
            foreach (var group in groups)
            {
                BudgetRow row = AddBudgetRow(budgetDefinition,
                    new List<string>(new string[] { group.Key.NetworkId.ToString(), group.Key.UnitLength.ToString(), group.Key.BrandId.ToString() }),
                    budgetRows, BudgetDefinitionType.WeeklyNetworkAvailTypeImps);

                foreach (var week in weeks)
                {
                    var units = group.SelectMany(l => l.Units.Where(u => week.contains(u.AirDate)));

                    row.setRawValue(week, units.Sum(u => (decimal?)u.Demo1Imps));

                    row.setLockedValue(week, units.Where(u => u.IsLockedOutOfUnitAllocation).Sum(u => (decimal?)u.Demo1Imps));
                }
            }

5/7/09


Perenthia World Builder in Silverlight

I have been working to modify the tool I wrote for building the Perenthia world. The tool started life as a WinForms application, migrated to a WPF application but is now a part of the Silverlight Client UI. Since all of the actions are role based and controlled with permission on the server having it in the main client UI allows me to make use of all of the framework already in place for Perenthia.

The world builder UI connects to the server via WCF services tailored for world building. What this will also give me is the ability to open up the world builder to allow users to create custom dungeons, towns, etc.

Some features of the world builder are a background map of the world so I know where to place rooms, zoom capabilities that scale the map, draggable map and the ability to drag items, creatures, NPCs, etc. onto rooms on the map and configure the properties of each.

4/29/09


Silverlight Window Control

Here is a draggable Window control I wrote in Silverlight for Perenthia. A screen shot of the Window being used can be seen at http://cameronalbert.com/post/2008/11/19/Silverlight-Game-Controls.aspx

Here is the XAML from my generics.xaml file:

 

<!-- Default style for Lionsguard.Window -->
	<Style TargetType="lg:Window">
		<Setter Property="Background" Value="#99000000" />
		<Setter Property="Foreground" Value="#FFFFFFFF" />
		<Setter Property="BorderBrush" Value="#FFC38312" />
		<Setter Property="Template">
			<Setter.Value>
				<ControlTemplate TargetType="lg:Window">
					<Grid x:Name="RootElement">
						<Border HorizontalAlignment="Stretch" Margin="0,24,0,0" Padding="4,4,4,4" VerticalAlignment="Stretch" BorderThickness="2,2,2,2" CornerRadius="0,0,4,4">
							<ContentControl x:Name="ContentElement" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"/>
						</Border>
						<Border Height="24" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Top" CornerRadius="4,4,0,0" BorderThickness="2,2,2,0" x:Name="TitleBarElement" />
						<Button Height="20" HorizontalAlignment="Right" Margin="0,3,3,0" VerticalAlignment="Top" Width="20" Content="X" FontWeight="Bold" FontFamily="Trebuchet MS" FontSize="14" x:Name="CloseButtonElement"/>
						<TextBlock Height="20" Margin="8,3,27,0" VerticalAlignment="Top" Text="Title" TextWrapping="Wrap" x:Name="TitleLabelElement" FontFamily="Georgia" FontSize="16" FontWeight="Bold" HorizontalAlignment="Left"/>
					</Grid>
				</ControlTemplate>
			</Setter.Value>
		</Setter>
	</Style>

 

And here is the code for the class:

 

using System;
using System.Collections.Generic;
using System.Net;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Lionsguard
{
	[TemplatePart(Name = "RootElement", Type = typeof(FrameworkElement))]
	[TemplatePart(Name = "ContentElement", Type = typeof(ContentControl))]
	[TemplatePart(Name = "TitleBarElement", Type = typeof(Border))]
	[TemplatePart(Name = "CloseButtonElement", Type = typeof(Button))]
	[TemplatePart(Name = "TitleLabelElement", Type = typeof(TextBlock))]
	[ContentProperty("Content")]
	public class Window : Control
	{
		public FrameworkElement RootElement { get; set; }
		private ContentControl ContentElement { get; set; }
		private Border TitleBarElement { get; set; }
		private Button CloseButtonElement { get; set; }
		private TextBlock TitleLabelElement { get; set; }

		private Point MousePosition { get; set; }

		/// <summary>
		/// Gets or sets the Content of the Window.
		/// </summary>
		public UIElement Content
		{
			get { return (UIElement)GetValue(ContentProperty); }
			set { SetValue(ContentProperty, value); }
		}
		public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(UIElement), typeof(Window), new PropertyMetadata(null, new PropertyChangedCallback(Window.OnContentPropertyChanged)));
		private static void OnContentPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			(obj as Window).SetControlValues();
		}

		/// <summary>
		/// Gets or sets the Title displayed in the title bar of the window.
		/// </summary>
		public string Title
		{
			get { return (string)GetValue(TitleProperty); }
			set { SetValue(TitleProperty, value); }
		}
		public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(Window), new PropertyMetadata("Title", new PropertyChangedCallback(Window.OnTitlePropertyChanged)));
		private static void OnTitlePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			(obj as Window).SetControlValues();
		}

		/// <summary>
		/// Gets or sets a value indicating whether or not the close button of the title bar should be displayed.
		/// </summary>
		public bool ShowCloseButton
		{
			get { return (bool)GetValue(ShowCloseButtonProperty); }
			set { SetValue(ShowCloseButtonProperty, value); }	
		}
		public static readonly DependencyProperty ShowCloseButtonProperty = DependencyProperty.Register("ShowCloseButton", typeof(bool), typeof(Window), new PropertyMetadata(true, new PropertyChangedCallback(Window.OnShowCloseButtonPropertyChanged)));
		private static void OnShowCloseButtonPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			(obj as Window).SetControlValues();
		}

		/// <summary>
		/// Gets a value indicating whether or not the window is currently open and visible.
		/// </summary>
		public bool IsOpen
		{
			get { return this.Visibility == Visibility.Visible; }
		}

		/// <summary>
		/// An event that is raised when the window is closed.
		/// </summary>
		public event EventHandler Closed = delegate { };

		/// <summary>
		/// Initializes a new instance of the Lionsguard.Window class.
		/// </summary>
		public Window()
		{
			this.DefaultStyleKey = typeof(Window);
		}

		public override void OnApplyTemplate()
		{
			base.OnApplyTemplate();

			this.RootElement = base.GetTemplateChild("RootElement") as FrameworkElement;
			this.ContentElement = base.GetTemplateChild("ContentElement") as ContentControl;
			this.TitleBarElement = base.GetTemplateChild("TitleBarElement") as Border;
			this.CloseButtonElement = base.GetTemplateChild("CloseButtonElement") as Button;
			this.TitleLabelElement = base.GetTemplateChild("TitleLabelElement") as TextBlock;
			
			if (this.RootElement != null)
			{
				this.RootElement.MouseLeftButtonDown += new MouseButtonEventHandler(OnRootElementMouseLeftButtonDown);

				this.TitleBarElement.MouseLeftButtonDown += new MouseButtonEventHandler(OnTitleBarMouseLeftButtonDown);
				this.TitleBarElement.MouseEnter += new MouseEventHandler(OnTitleBarMouseEnter);
				this.TitleBarElement.MouseLeave += new MouseEventHandler(OnTitleBarMouseLeave);

				this.TitleLabelElement.MouseLeftButtonDown += new MouseButtonEventHandler(OnTitleBarMouseLeftButtonDown);
				this.TitleLabelElement.MouseEnter += new MouseEventHandler(OnTitleBarMouseEnter);
				this.TitleLabelElement.MouseLeave += new MouseEventHandler(OnTitleBarMouseLeave);

				this.CloseButtonElement.Click += new RoutedEventHandler(OnCloseButtonClick);

				this.SetControlValues();
			}
		}

		private void SetControlValues()
		{
			if (this.ContentElement != null && this.Content != null)
			{
				this.ContentElement.Content = this.Content;
			}
			if (this.TitleLabelElement != null)
			{
				this.TitleLabelElement.Text = this.Title;
				ToolTipService.SetToolTip(this.TitleLabelElement, this.Title);
			}
			if (this.CloseButtonElement != null)
			{
				if (this.ShowCloseButton) this.CloseButtonElement.Visibility = Visibility.Visible;
				else this.CloseButtonElement.Visibility = Visibility.Collapsed;
			}
		}

		private void OnRootElementMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			// Set the Z-Index of the window to the top most position.
			this.BringToFront();
		}

		private void OnCloseButtonClick(object sender, RoutedEventArgs e)
		{
			this.Close();
		}

		private void OnTitleBarMouseLeave(object sender, MouseEventArgs e)
		{
			this.Cursor = Cursors.Arrow;
		}

		private void OnTitleBarMouseEnter(object sender, MouseEventArgs e)
		{
			this.Cursor = Cursors.Hand;
		}

		private void OnTitleBarMouseMove(object sender, MouseEventArgs e)
		{
			Point position = e.GetPosition(null);

			// Prevent the mouse from moving outside the bounds of the parent canvas.
			if (position.X <= 30) position.X = 30;
			if (position.Y <= 30) position.Y = 30;

			Canvas parent = this.Parent as Canvas;
			if (parent != null)
			{
				if (position.X >= (parent.Width - 30)) position.X = parent.Width - 30;
				if (position.Y >= (parent.Height - 30)) position.Y = parent.Height - 30;
			}

			System.Diagnostics.Debug.WriteLine("position = {0}", position);

			double deltaX = position.X - this.MousePosition.X;
			double deltaY = position.Y - this.MousePosition.Y;
			
			Point newPosition = new Point(
				((double)this.GetValue(Canvas.LeftProperty)) + deltaX,
				((double)this.GetValue(Canvas.TopProperty)) + deltaY);

			this.SetValue(Canvas.LeftProperty, newPosition.X);
			this.SetValue(Canvas.TopProperty, newPosition.Y);

			this.MousePosition = position;
		}

		private void OnTitleBarMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
		{
			((UIElement)sender).ReleaseMouseCapture();

			this.Cursor = Cursors.Arrow;

			this.TitleBarElement.MouseLeftButtonUp -= new MouseButtonEventHandler(OnTitleBarMouseLeftButtonUp);
			this.TitleBarElement.MouseMove -= new MouseEventHandler(OnTitleBarMouseMove);
			this.TitleLabelElement.MouseLeftButtonUp -= new MouseButtonEventHandler(OnTitleBarMouseLeftButtonUp);
			this.TitleLabelElement.MouseMove -= new MouseEventHandler(OnTitleBarMouseMove);
		}

		private void OnTitleBarMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			this.MousePosition = e.GetPosition(null);
			this.Cursor = Cursors.Hand;

			((UIElement)sender).CaptureMouse();

			this.TitleBarElement.MouseLeftButtonUp += new MouseButtonEventHandler(OnTitleBarMouseLeftButtonUp);
			this.TitleBarElement.MouseMove += new MouseEventHandler(OnTitleBarMouseMove);
			this.TitleLabelElement.MouseLeftButtonUp += new MouseButtonEventHandler(OnTitleBarMouseLeftButtonUp);
			this.TitleLabelElement.MouseMove += new MouseEventHandler(OnTitleBarMouseMove);
		}

		/// <summary>
		/// Causes the current window to be re-z-indexed to the top most window.
		/// </summary>
		protected void BringToFront()
		{
			// Search the top most "Window" and swap z-indexes.
			Panel parent = this.Parent as Panel; // Panel is the base for Canvas, Grid and StackPanel.
			if (parent != null)
			{
				int currentZIndex = (int)this.GetValue(Canvas.ZIndexProperty);
				var child = (from c in parent.Children where c is Window select c as Window).OrderByDescending(c => (int)c.GetValue(Canvas.ZIndexProperty)).FirstOrDefault();
				if (child != null)
				{
					int topZIndex = (int)child.GetValue(Canvas.ZIndexProperty);
					if (topZIndex == 0) topZIndex = 1; // If the value has not been set then just default it to 1.
					if (topZIndex > currentZIndex)
					{
						this.SetValue(Canvas.ZIndexProperty, topZIndex);
						child.SetValue(Canvas.ZIndexProperty, currentZIndex);
					}
				}
			}
		}

		public void Show()
		{
			this.Visibility = Visibility.Visible;
			// If content is present show that as well.
			if (this.Content != null) this.Content.Visibility = Visibility.Visible;
		}

		public void Close()
		{
			this.Visibility = Visibility.Collapsed;
			// If content is present hide that as well.
			if (this.Content != null) this.Content.Visibility = Visibility.Collapsed;
			this.Closed(this, EventArgs.Empty);
		}

		public void ToggleWindow()
		{
			if (this.Visibility == Visibility.Visible)
			{
				this.Close();
			}
			else
			{
				this.Show();
			}
		}
	}
}

 

This is how I am using it within the Pernethia UI (The content of window is actually the Character Sheet as displayed in the screenshot from above):

Namespace: xmlns:Lionsguard="clr-namespace:Lionsguard;assembly=Lionsguard.Silverlight"

 

<Lionsguard:Window Height="375" x:Name="diagCharacterSheet" Visibility="Collapsed" Width="634" Canvas.Left="8" Canvas.Top="19" Title="Character Sheet" Style="{StaticResource WindowStyle}" d:IsLocked="True">
    		<Perenthia_Dialogs:CharacterSheetDialog x:Name="diagCharacterSheetContent" SkillChanged="diagCharacterSheetContent_SkillChanged"/>
		</Lionsguard:Window>

 

Enjoy!

 

4/22/09


Silverlight Drag and Drop Manager

I wrote some classes for handling drag and drop operations in Silverlight that I use within Perenthia. I thought I would go ahead and share them since I found them to be very useful in getting drag and drag working quickly in new projects.

The Drag and Drag classes consist of two interfaces (IDroppable, IDropContainer) and a manager class (DragDropManager). The interfaces are defined as such:

namespace Lionsguard
{
    /// <summary>
    /// Defines an object that can be dragged and dropped via the DragDropManager.
    /// </summary>
	public interface IDroppable
	{
		/// <summary>
		/// An event that is raised when the IDroppable item begins the drag operation.
		/// </summary>
		event BeginDragEventHandler BeginDrag;

		/// <summary>
		/// Gets the UIElement that will server as the cursor replacement when dragging the current object.
		/// </summary>
		/// <returns></returns>
		UIElement GetDragCursor();
	}

    /// <summary>
    /// Defines an object that accept IDroppable items as part of a drop operation.
    /// </summary>
	public interface IDropContainer
	{
		/// <summary>
		/// Handles the drop of the specified IDroppable item onto the container.
		/// </summary>
		/// <param name="item"></param>
		void Drop(IDroppable item);
	}
}

The DragDropManager (and and associated delegate and event args class) are defined as such:

namespace Lionsguard
{
    /// <summary>
    /// Represents a class used to manage drag and drop items.
    /// </summary>
	public class DragDropManager
	{
		private Panel Host { get; set; }
		private Point MousePosition { get; set; }
		private UIElement DragCursor { get; set; }	
		private bool HasProcessedEndDrag { get; set; }
		private IDroppable Item { get; set; }	

        /// <summary>
        /// Initializes a new instance of the DragDropManager class.
        /// </summary>
        /// <param name="host">The hosting panel where the drag cursor instance will be added.</param>
		public DragDropManager(Panel host)
		{
			this.Host = host;
			this.Host.LostFocus += new RoutedEventHandler(OnHostLostFocus);
		}

		private void OnHostLostFocus(object sender, RoutedEventArgs e)
		{
			EndDrag(false);
		}

		private void OnDragCursorLostMouseCapture(object sender, MouseEventArgs e)
		{
			EndDrag(false);
		}

		private void OnDragCursorLostFocus(object sender, RoutedEventArgs e)
		{
			EndDrag(false);
		}

		private void OnDragCursorMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
		{
			((UIElement)sender).ReleaseMouseCapture();

			this.MousePosition = e.GetPosition(null);

			this.EndDrag(true);
		}

		private void OnDragCursorMouseMove(object sender, MouseEventArgs e)
		{
			Point position = e.GetPosition(null);

			double deltaX = position.X - this.MousePosition.X;
			double deltaY = position.Y - this.MousePosition.Y;

			Point newPosition = new Point(
				((double)this.DragCursor.GetValue(Canvas.LeftProperty)) + deltaX,
				((double)this.DragCursor.GetValue(Canvas.TopProperty)) + deltaY);

			this.DragCursor.SetValue(Canvas.LeftProperty, newPosition.X);
			this.DragCursor.SetValue(Canvas.TopProperty, newPosition.Y);

			this.MousePosition = position;
		}

        /// <summary>
        /// Begins a drag operation for the specified IDroppable instance starting at the specified mousePosition.
        /// </summary>
        /// <param name="droppable">The IDroppable instance to being dragging.</param>
        /// <param name="mousePosition">The Point at which the drag operation will begin.</param>
		public void BeginDrag(IDroppable droppable, Point mousePosition)
		{
			if (droppable != null && this.Host != null)
			{
				System.Diagnostics.Debug.WriteLine("Begin Drag...");
				this.Item = droppable;

				this.DragCursor = droppable.GetDragCursor();
				this.DragCursor.LostFocus += new RoutedEventHandler(OnDragCursorLostFocus);
				this.DragCursor.LostMouseCapture += new MouseEventHandler(OnDragCursorLostMouseCapture);

				// Add the drag cursor to the host control and set the z-index very high.
				this.DragCursor.SetValue(Canvas.ZIndexProperty, 5000);
				this.Host.Children.Add(this.DragCursor);
				this.Host.Cursor = Cursors.Hand;

				this.MousePosition = mousePosition;
				this.DragCursor.SetValue(Canvas.LeftProperty, this.MousePosition.X);
				this.DragCursor.SetValue(Canvas.TopProperty, this.MousePosition.Y);
				this.DragCursor.Visibility = Visibility.Visible;

				this.HasProcessedEndDrag = false;

				this.DragCursor.CaptureMouse();

				this.DragCursor.MouseLeftButtonUp += new MouseButtonEventHandler(OnDragCursorMouseLeftButtonUp);
				this.DragCursor.MouseMove += new MouseEventHandler(OnDragCursorMouseMove);
			}
		}

		private void EndDrag(bool processDrop)
		{
			// This prevents the mouse up event or the end drag event firing more than once from causing the
			// events to bubble up and the drop be handled more than once.
			System.Diagnostics.Debug.WriteLine("End Drag...");
			if (this.HasProcessedEndDrag) return;
			this.HasProcessedEndDrag = true;

			// Reshow the cursor and hide the source image.
			this.Host.Cursor = Cursors.Arrow;

			// Hide the drag cursor.
			if (this.DragCursor != null)
			{
				this.DragCursor.Visibility = Visibility.Collapsed;

                this.DragCursor.LostFocus -= new RoutedEventHandler(OnDragCursorLostFocus);
                this.DragCursor.LostMouseCapture -= new MouseEventHandler(OnDragCursorLostMouseCapture);
				this.DragCursor.MouseLeftButtonUp -= new MouseButtonEventHandler(OnDragCursorMouseLeftButtonUp);
				this.DragCursor.MouseMove -= new MouseEventHandler(OnDragCursorMouseMove);
			}

			if (processDrop)
			{
				System.Diagnostics.Debug.WriteLine("Process Drop...");
				// Search within host for an IDropContainer instance within the area of the current mouse position.
				var container = (from v in VisualTreeHelper.FindElementsInHostCoordinates(this.MousePosition, this.Host)
								where (v as IDropContainer) != null
								select v as IDropContainer).FirstOrDefault();
				if (container != null)
				{
					System.Diagnostics.Debug.WriteLine("Drop...");
					container.Drop(this.Item);
				}
			}
		}
	}

    /// <summary>
    /// Provides a delegate for handling IDroppable.BeginDrag events.
    /// </summary>
    /// <param name="sender">The IDroppable initiating the drag operation.</param>
    /// <param name="e">Event data for the operation.</param>
	public delegate void BeginDragEventHandler(object sender, BeginDragEventArgs e);

    /// <summary>
    /// Represents event data for a BeginDrag event.
    /// </summary>
	public class BeginDragEventArgs : EventArgs
	{
        /// <summary>
        /// Gets or sets the IDroppable initiating the event.
        /// </summary>
		public IDroppable Droppable { get; set; }

        /// <summary>
        /// Gets or sets the Point at which the drag operation should begin.
        /// </summary>
		public Point MousePosition { get; set; }	
	}
}
To use the manager is your project have the controls wyou wish to drag and drop implement the IDroppable interface and the destinations for the drop operation should implement the IDropContainer. One control could implement both if you have operations where items can be dragged into other items of the same type.
Here is an example of the IDroppable and IDropContainer implementation (Both of which are in the same user control).
The GetDragCursor method creates the item so it looks like you are dragging it across the screen, you can have any kind of cursor replacement UIElement you want with this.

namespace Perenthia.Controls
{
public partial class AvatarListItem : UserControl, IDroppable, IDropContainer
{

#region IDroppable Members

public event BeginDragEventHandler BeginDrag = delegate { };

public UIElement GetDragCursor()
{
Image img = new Image();
img.Source = this.GetImageSource();
img.Width = 16;
img.Height = 16;
Border border = new Border();
border.Background = Brushes.DialogFillBrush;
border.BorderBrush = Brushes.BorderBrush;
border.Padding = new Thickness(2);
border.Width = 18;
border.Height = 18;
border.Child = img;
return border;
}

#endregion

private void NameLabel_MouseLeave(object sender, MouseEventArgs e)
{
if (_details != null)
{
_details.Hide();
}
PopupManager.Remove();
}

#region IDropContainer Members

public void Drop(IDroppable item)
{
// Do stuff when the object is dropped...
}

#endregion
}
}

And here is an example of the usage of the drag and drop manager (Contained within a silverlight Page class):

I declare an instance of the DragDropManager when my control is loaded (I use a Canvas for LayoutRoot as I find the drag operation to be smoother with a Canvas).
private void AdminScreen_Loaded(object sender, RoutedEventArgs e)
{
_dragDropManager = new DragDropManager(LayoutRoot);
}

When my IDroppable control instance is created I register a listener to the BeginDrag event:
avatarItem.BeginDrag += new BeginDragEventHandler(OnDroppableBeginDrag);

When the IDroppable raises the BeginDrag event, on a left mouse button click, this method is called which causes the DragDropManager to being the drag operation for the current IDroppable.

private void OnDroppableBeginDrag(object sender, BeginDragEventArgs e)
		{
			if (_dragDropManager != null)
			{
				_dragDropManager.BeginDrag(e.Droppable, e.MousePosition);
			}
		}

When an IDroppable is dropped onto the very same control this method is executed:

#region IDropContainer Members
		public void Drop(IDroppable item)
		{
                    // Do stuff when the object is dropped...
		}
		#endregion

This is pretty much all there is to it.

 

4/21/09


Perenthia Blog

I have started a Perenthia Blog which will be the official blog of the game, however, I will still probably end up blogging code snippets and the like here and keep the Perenthia blog a little more high level and focused on information the actual players may want to know.

4/16/09


Microsoft Internet Explorer 8

Microsoft announced the release of Internet Explorer 8 this morning, it is available at http://www.microsoft.com/ie8. With this new browser release Microsoft will position themselves on the top of the browser stack again. They have incorporated a lot of safe browsing and ease of browsing features into the new version that will make it the paramount browser for the normal everyday web surfer.

 EDIT: And then I started using it and now am back to using Chrome... 

3/19/09


Heading to MIX

I am flying out to MIX in a few hours and I am really excited. This will be my first time at MIX and I am looking forward to all the great sessions regarding Silverlight 3, mixing it up with other industry professionals and just hanging out in Las Vegas. :)

3/17/09


Working on a Silverlight Multi-Player Library

I have been porting out the multi-player aspects of Perenthia into a separate library I hope to get up on CodePlex before too long. What the library will provide is some interfaces and classes related to handling multi-player environments. A central static class will manage connected clients and three protocol classes for HTTP, Polling Duplex and Sockets will use the static class to retrieve connected clients and push information down to them. A simple set of "tags" are used to send information back and forth and some providers allow for defining your own implementation of handling commands and encrypting data.

The library will be focused on providing an easy way to get setup and implement a multi-player structure. You will still need our own game engine and associated components.

As soon as I get it cleaned up a little and tested out I will put it up on CodePlex and provide a tutorial and sample application showing how to implement and use the component.

3/12/09


Silverlight Dependency Property Snippet

I created this snippet for Dependency Properties in Silverlight. If you are creating custom Silverlight controls you might find this snippet useful. I stored my code snippets here "My DocumentsVisual Studio 2008Code SnippetsVisual C#My Code Snippets" so all you need to do is copy the file to this location. Visual Studio should load the snippet the next time you start it up. If it does not then you can go into "Tools", "Code Snippets Manager" and load it up manually.

Now when you type "propsl" and hit tab you will get a stubbed out property like below, where tabbing through the items allows you to change the type and name of the property, set the owning class and provide a default value.

public int MyProperty
{
	get { return (int)GetValue(MyPropertyProperty); }
	set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty = 
     DependencyProperty.Register("MyProperty", typeof(int), typeof(MyClass), 
     new PropertyMetadata(null, new PropertyChangedCallback(MyClass.OnMyPropertyPropertyChanged)));
private static void OnMyPropertyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{

Here is the code for the snippet and a link to the file:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>propsl</Title>
            <Shortcut>propsl</Shortcut>
            <Description>Code snippet for an automatically implemented dependency property in Silverlight.</Description>
            <Author>Cameron Albert</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>type</ID>
                    <ToolTip>Property type</ToolTip>
                    <Default>int</Default>
                </Literal>
                <Literal>
                    <ID>property</ID>
                    <ToolTip>Property name</ToolTip>
                    <Default>MyProperty</Default>
                </Literal>
                <Literal>
                    <ID>ownerClass</ID>
                    <ToolTip>Owner Class</ToolTip>
                    <Default>MyClass</Default>
                </Literal>
                <Literal>
                    <ID>defaultValue</ID>
                    <ToolTip>Default Value</ToolTip>
                    <Default>null</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp">
                <![CDATA[public $type$ $property$
            {
                get { return ($type$)GetValue($property$Property); }
                set { SetValue($property$Property, value); }
            }
            public static readonly DependencyProperty $property$Property = DependencyProperty.Register("$property$", typeof($type$), typeof($ownerClass$), new PropertyMetadata($defaultValue$, new PropertyChangedCallback($ownerClass$.On$property$PropertyChanged)));
            private static void On$property$PropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
            {
            }
            $end$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

Silverlight Dependency Property Snippet

 

3/9/09


Perenthia Fan Site

Perenthia has a fan site, it is called Beyond Angarath, Angarath being a primary city in within the game world. This is exciting for me and extremely cool! It is very rewarding to see people enjoying your creation enough to put work into a fan site for that creation. The forums have been getting more active lately with folks trying out the Alpha and giving lots of feedback and suggestions. I hope stay close with the community in building this game; I have a vision for it but ultimately I want people to play it and enjoy playing it.

2/19/09


Silverlight for Multi-Player Games

I am hoping to start writing some articles about building multi-player games in Silverlight. I am planning on charting my development progress and ideas in my design and development of Perenthia.

Silverlight is a great platform for .NET developers to be able to write multi-player browser based games. You get a great tool for building a UI, animations, sounds, etc. and can program everything in your .NET language of choice. Silverlight 3 is supposed to support 3D so that will open it up even more for some great games.

Over the next few months, in between working on Perenthia, I am going to work on the first of a series of articles outlining how I built Perenthia. I will start with the Server component, outlining the core communication architecture and design decisions.

Keep checking back for more information.

2/16/09


Going to MIX09

I am planning on attending MIX09 this year, already registered actually. I missed it last year but wanted to be present this time around for the new Silverlight announcements and to meet some of the Silverlight team. Anyway, if you are going I will be there :)

1/27/09


Perenthia Armorial Version 1.0

I uploaded the Perenthia Armorial Version 1.0 to the Perenthia Alpha site tonight. The Perenthia Armorial is a way to provide external access to Perenthia information. The first version includes the Player Bar feature, where you pass a player name to the service and renders back HTML with player details.

The call to http://alpha.perenthia.com/Armorial/Armorial.ashx?n=Aldarian renders the following:

1/18/09


Perenthia Alpha Testing

I did a Character Creation, Movement and Chat test last night with the Perenthia Alpha players. The tests went well, I chatted with a few players, found some bugs and was very encouraged by the whole thing. The next test should start next weekend and will include some combat/spell tests. After that we will do some questing tests, travel tests (boats and stuff) and then I will open up more of the world for the alpha players to help resolve other issues not covered by the main feature tests.

I am enjoying Silverlight as the client UI, it provides a Windows like interface with ability to be run in a browser.

1/16/09


Client/Server Communication Data Formats

While developing Perenthia I tried several different communication formats from sending JSON serialized objects back and forth to sending byte arrays containing mostly integers indicating what should be loaded and displayed, what commands to execute etc. What I eventually settled on was a bit of a hybrid. I've created a simple set of "tags" that can be sent to and from the server. The tags are nothing more than pipe delimited strings contained within curly braces and there are a limited number of tags that provide simplicity yet flexibility with the data that is transmitted. The tags are represented in C# by tag objects allow me to create them, query them, etc.

A simple command tag for the SAY command might look like this: {CMD|SAY|"Hello World!"}

I wrote a custom tag writer class that parses the tag objects into strings to be sent to the client and likewise a tag reader that reads the strings sent from the client and parses them into tag objects.

The client can only send commands to the server but the server sends commands, messages and objects to the client. The commands are all the same, the CMD text then the one word name of the command and then a list of arguments, messages are system, chat and tell messages but objects have a bit more information. For instance, an object tag encompasses several different types starting with a base ACTOR, a PLACE, a PLAYER and a PROP or property. The ACTOR, PLACE and PLAYER tags all define the ID, Name and Descriptions of the objects, with some additional data per type but the PROP tag defines the object id of its owner and a name/value pair. An example of a PLAYER tag with properties for x, y and z coordinates might be:

{OBJ|PLAYER|3756|"Aldarian"|"My character description..."}

{OBJ|PROP|3756|"X"|45}

{OBJ|PROP|3756|"Y"|26}

{OBJ|PROP|3756|"Z"|0}

The client can find and parse the player tag and then find the properties associated with that player instance. The way the server works now it will send the full player tag once logged in and a character is selected and then from then on out it just sends property updates.

Using this type of tag structure allows quite a bit of flexibility for the client. I could choose to write a very simple client that only displays MSG tags, much like a MUD, I could write a simple 2D interface client and display and move sprites about the map using the properties from the server, etc. Once Perenthia is up and running I will probably post some information on the tags and how to talk with the server should someone feel the need to write their own client. :)

1/5/09