Anyone who knows me knows that I have a passion for tools, pipelines and making them fit into engines.
About a year ago I decided to become an indie developer. Wanted the experience making the entire game, not just a small section of it. One thing that comes from being an Indie developer is you can’t produce everything yourself. You can’t make an engine, all the tools and the pipeline. It’s just unfeasible. The last few years of development I’ve had to come to the conclusion that you should be working smart. Part of working smart is finding tools that can do 95% of the work and then adapt them using some sort of pipeline to work inside a game scenario. Pre-process as much as possible is the mantra of games development and using existing tools as a pipeline step allow you to achieve this.
One of the tools I found was Microsoft Expression Blend for UI design. You can adapt very easily the UI’s built from Blend and get them into an optimized format ready for your main game pipeline to process. I ended up using a pipeline at Infinite Interactive for the game Puzzle Quest 2, but for my indie tool I went much further.
Windows Presentation Foundation (WPF) is the GUI Framework from Microsoft which I use for generating the screens from. Blend is the editor while WPF is the actual API. Microsoft Expression Blend is used for both developing WPF and Silverlight applications. We are only interested in WPF for the sake of this article because it is a proper .NET assembly. Silverlight is a cutdown version of .NET which has it’s own virtual machine meant for cross-platform support.
In this article I intend to give a very basic introduction to WPF and justification why to use it in your pipeline. I intend to get into the very meaty stuff in the next article and show you have to make a fully configurable pipeline utility. The article is not meant to be an endless resource of Blend and WPF.
WPF has a number of nice features that make it perfect for games use, some of these include:
- Everything is defined in XML; it makes it easier to quickly spot errors etc. Much more like a HTML workflow.
- It has the concept of a style sheet that allows you to customize using either Vector or Raster graphics the look and feel of controls. Here for example is a glass button, as you can see it’s just a series of rectangles which contain different gradients and transparencies:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
<Style x:Key="ButtonStyle1" TargetType="{x:Type Button}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Grid> <Rectangle x:Name="outterRect" Fill="{x:Null}" RadiusY="16" RadiusX="16" Stroke="{TemplateBinding Background}" StrokeThickness="4"/> <Rectangle x:Name="innerRect" Fill="{TemplateBinding Background}" Margin="16.5,8.52" RadiusY="0" RadiusX="0" StrokeThickness="7"/> <Rectangle x:Name="glassRect" RadiusY="16" RadiusX="16" StrokeThickness="7"> <Rectangle.Fill> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0" Opacity="0.5"> <GradientStop Color="Black" Offset="0"/> <GradientStop Color="#FF525252" Offset="1"/> <GradientStop Color="#FF5E5E5E" Offset="0.371"/> <GradientStop Color="#FF5E5E5E" Offset="0.53"/> <GradientStop Color="#FF5E5454" Offset="0.69"/> <GradientStop Color="#FFD0C7C7" Offset="0.181"/> <GradientStop Color="#FF1B1919" Offset="0.832"/> </LinearGradientBrush> </Rectangle.Fill> </Rectangle> <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
- It has full storyboard animation to allow the designer to affect both scene wide animations and animations local to the button styles. For example you could have an animation that occurred when the screen loaded moving controls into view or one that occurred in a button hover state for a individual control style. In the example below, we are changing properties of the button when a mouse over occurs:
1 2 3 4 5 6 7 8 9 10 11 12
<Storyboard x:Key="OnMouseEnter1" RepeatBehavior="Forever"> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Shape.Fill).(Brush.Opacity)" Storyboard.TargetName="glassRect"> <EasingDoubleKeyFrame KeyTime="0" Value="0.5"/> <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="0.8"/> <EasingDoubleKeyFrame KeyTime="0:0:1" Value="0.5"/> </DoubleAnimationUsingKeyFrames> <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[6].(GradientStop.Color)" Storyboard.TargetName="glassRect"> <EasingColorKeyFrame KeyTime="0" Value="#FF1B1919"/> <SplineColorKeyFrame KeyTime="0:0:0.5" Value="#FF2707C6"/> <EasingColorKeyFrame KeyTime="0:0:1" Value="#FF1B1B19"/> </ColorAnimationUsingKeyFrames> </Storyboard>
With the keyframes specified part of the frames will look like the following, notice the blue starting to be introduced:
- It has a easy pipeline to get graphics from Adobe Photoshop or Illustrator into the product. They also provide an easy lightweight tool called Expression Design to draw Vector graphics.
- Relative positioning of UI elements using UI Containers. Such as StackPanels and Grids. They will automatically resize to new resolution making it useful to reuse the same screen between different platforms and have it adjust.The following StackPanel will organise the Buttons to fit one after the other to occupy the entire width of the parent container.
1 2 3 4 5
<StackPanel x:Name="stackPanel" Grid.Column="1" Grid.Row="1"> <Button Style="{DynamicResource ButtonStyle1}" Content="Start Game" Height="33.064" Margin="0,0,0,5" /> <Button Style="{DynamicResource ButtonStyle1}" Content="End Game" Height="33.064" Margin="0,0,0,5" /> <Button Style="{DynamicResource ButtonStyle1}" Content="Load" Height="33.064" Margin="0,0,0,5"/> </StackPanel>
- It’s built on top of Direct3D, which allows you to use your own shader effects. Some of the inbuilt ones include glows and shadow effects. 3D graphics are also supported.
- You can in C# interrogate and extract all metadata about the controls and positioning for a window. You can also generate with only a few lines of code bitmaps for the controls. This can be done at the resolution specified in a configuration file.For instance, you can get create all the Window’s contained inside an assembly, and then extract all the controls out of it using the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
public static T[] ConstructFromAssembly<T>(String assemblyName) { Assembly assembly = Assembly.LoadFrom(assemblyName); List<T> objects = new List<T>(); foreach (Type type in assembly.GetTypes()) { if (type.IsSubclassOf(typeof(T))) { T value = (T)Activator.CreateInstance(type); objects.Add(value); } } return objects.ToArray(); }
You can process the windows from the previous created list of Windows from the assembly. Notice that I Show the window temporarily. This is because unless the window is shown I have found you don’t get accurate positioning information. I tried using the layout update method but didn’t seem to work correctly. Also notice I remove triggers from the window. Triggers force storyboard’s to be activated and when I was extracting information I was getting inaccurate data also due to the fact controls may be off the screen for a load animation for example.
1 2 3 4 5 6 7 8 9 10
foreach (Window window in windows) { // Clear out the triggers, we aren't interested in them at this stage. window.Triggers.Clear(); window.Show(); window.Hide(); ProcessElement(window, ...); }
You can then go through all the controls contained inside the Window through the following:
1 2 3 4 5 6 7 8 9
foreach (FrameworkElement childElement in LogicalTreeHelper.GetChildren(frameworkElement)) { if (childElement == null) { continue; } ProcessElement(childElement, ...); }
The question I hear you all asking is why use Blend over existing utilities like Flash. Some of the main reasons I can think of are:
- As it’s free for students and much cheaper than Flash for non-students.
- It’s much gentler on developers to extract metadata because of .NET reflection. You’re dealing with real objects during your extraction.
- WPF and Blend allow UI designers to code in C# to manipulate the UI. You could use a sandbox environment using Mono for example. Could be useful for frameworks such as Unity.
- I been told by designers it has a nicer workflow. For example, you don’t have to jump through loops to get storyboard animation working, you just select the point for the frame and start moving controls around.
Next time I’ll go more in-depth into the actual concept of a pipeline tool. The main thing to be aware of when it comes to the pipeline is WPF makes it easy to perform a number of optimisations such as converting the control animations into image snapshots and texture atlas’s. My aim with the pipeline tool is to provide end users an alternative from expensive tools for flash pipeline’s and provide a pipeline utility that may have more flexibility for some users.
See you for the next post.