Jump to content

  • Log In with Google      Sign In   
  • Create Account

Avengers UTD Chronicles



HUD “atmospheric” controls

Posted by , in Odyssey UI, The Odyssey 03 November 2014 - - - - - - · 651 views

Last month has seen little development as I needed to move to another city in the UK where I'm going to start a new job. So for the last weeks I have been living among boxes (and still am to some extents). Further, the new development machine I built had to have some faulty components so development has been delayed.

I am still busy implementing some features needed for the introductory cutscene. I have refactored the UI so that it is scalable. The size of every control is expressed in "Units". At 1920x1080 a unit is 32 pixels long. At different resolutions, the basic unit is scaled accordingly. For example 2560x1440 is 1.333 times bigger than 1920x1080, so a unit would become about 42.6 pixels.
Posted Image

The picture is supposed to represent the Commander's HUD as the ship travels near Mars towards Saturn. The game itself will not be in first person. The rendering seemed a bit empty so I added an "atmospheric" HUD control in the lower right, the one with the caption "Singularity Phase". It is a simple column chart implemented in DirectX. The control derives from the basic ItemsControl in my UI library that allows generating templated child control based on its ItemsSource property, just like in WPF. For now, you can simply assign an array of float values and it will create the appropriate number of columns in the chart, giving a relative height depending on the chart's width, height and minimum and maximum values set for it.

While animated, the chart looks like this:

Posted Image

It was really simple to implement and animate. Basically the RandomColumnChart derives from the base ColumnChart and exposes a Period property. The period represent the number of seconds before new random values are generated. During each update, each column is interpolated from the current start values and the next array of values. After the specified period of time has elapsed, those values are used as the new starting values and new ones are generated. Then the cycle runs again.

I might need another of these controls to display some kind of sin waves for example... But I shouldn't let myself be too distracted, I'd like to start working on the main part of the game soon!


BSplines and Typewriter effects

Posted by , in Odyssey UI, The Odyssey 26 August 2014 - - - - - - · 727 views

In the past days, I've been busy working on several things concerning the implementation of cutscenes during the game. In my mind, the game opens on a depiction of the Solar System where the moments just before the Odyssey leaves Earth Orbit would be shown. Imagine yourself as the commander of this ship, in a sci-fi universe of vector graphics and green terminals that beep when keys are typed. Running through the checklist moments before the Odyssey leaves the Solar System you would probably see something like the following image.

Posted Image

This would be a part of the introductory cutscene that I hope to show you in the near future. These screens would be interleaved with "cinematic" title screens explaining the back story. In order to draw the purple path you see in the picture, I needed to implement a BSpline plotting algorithm. It's not yet perfect as I would ideally be able to implement constant arc length parametrization (!). You can't see it from the picture, but the path is being rendered as a 3D polyline joining various rectangle polygons. The issue is that the curve segments are not spaced equidistantly. Then again, if you can't see it then it's not really a problem. It's not high priority right now but I would like to have more control over it.

The second part of the title gets us to the fundamental typewriter effect we so desperatly need in an universe like this. I have implemented this effect using my UI library:

Posted Image

The speed of the Typewriter can be configured using its CharactersPerSecond property. Using this information we can determine how many characters to reveal at each update. The Typewriter is using a Direct2D TextLayout object. Since this object needs to be instantiated with its text content from the beginning, i.e. the text string is an argument of the constructor, I assumed that recreating it at each character update would not be the best approach performance-wise. What I hope is a better solution was to create this object fully invisible (i.e., transparent). The TextLayout allows us to apply brushes (i.e. colours) to specific text ranges. So that's what the animation is doing. At each update it increases the number of character the green brush is applied, while the others stay transparent. Each update generates a TextRangeChanged event, which in turn is used to update the caret's position. The next step will be to implement a blinking animation for the caret and other nice features such as carriage return, line scrolling and so on.

This is a feature that will be used extensively in the actual game. The log window, or more enthusiastically, THE COMM CHANNEL, will display information about what's going on in the minds of the AI-controlled ships. So, THE COMM CHANNEL, will describe "in prose" what's happening on screen. I enjoy both seeing space dogfights but also reading about them. Hopefully, reading sentences like "Red 5, breaking formation", "ALL HANDS, BATTLESTATIONS!", "IT'S A TRAP", or "I'll see you on the other side" will add to the idea of witnessing a space battle from the command room of a capital ship. Ideally the radio chatter would also be fully voiced, but that maybe is stretching too far the imagination considering the resources currently available.

That's it for now, back to implementing the blinking caret Posted Image


Direct2D Glass Buttons and reinventing the wheel

Posted by , in Odyssey UI 11 August 2014 - - - - - - · 1,584 views
Direct2D, GUI library, SharpDX
I am not aware of any GUI library that specifically targets C#, SharpDX and Direct2D so I had to roll my own and well this gave me the opportunity to learn how Direct2D is used. Direct2D is a wonderful API, although its usage is sometimes... hectic to say the least. Although I can understand some of the reasons behind some design choices, you cannot use it as if you were building a WPF XAML based application.

The game I am building is heavily UI-based as any self-respecting strategic game should be. I am an HCI researcher "by trade" so this was the opportunity of a lifetime to put my PhD to good use! I believe that a well-polished game needs an equally polished UI. I needed it to be able to render high-quality controls and also allow me to animate transition between states and so on. I found that Direct2D would not provide me with that kind of features, unless I implemented them. The library, as I am designing it, isn't intended to be a replacement for XAML or as a tool for data-entry applications built with DirectX (why would anyone think of such a thing? Posted Image). Therefore it is being geared towards situation that commonly occur in a 3D game or application. I'm pleased to present you the ubiquitous Glass Button made with Direct2d, SharpDX and the UI library of the Odyssey engine!

Posted Image


The cool thing about this is that I was able to get this working by following a XAML tutorial. How? Enter the... Odyssey eXstensible Interaction Language (I may need a better name..) I designed this language around XAML as it was the one I am most familiar with. The structure of a OXIL theme (!) consists of two parts. At the start of the theme file, Resource elements are declared. Resources are colors and gradients. For example:
<Resources>
    <SolidColor Name="Transparent" Color="#00000000"/>
    <SolidColor Name="Red" Color="#FFFF0000"/>
    <RadialGradient Name="ButtonFillGlow" RadiusX="1" RadiusY="1" Center="0.5, 0.8">
      <GradientStop Color="{Red}" Offset="0"/>
      <GradientStop Color="{Transparent}" Offset="1.0"/>
    </RadialGradient>
</Resources>
Gradients and colors are declared almost identically as the equivalent XAML syntax. The individual gradient stops are written in curly brackets as they reference color resources previously defined. The second part of the document is the actual theme file. Each control defines how it wants to be rendered. Let's take the above glass button as an example:
<ControlStyle Name="Button" Width="200" Height="50" TextDescriptionClass="Button">
    <VisualState>
      <Shapes>
        <Rectangle Name="Background" Position="0,0" StrokeThickness="1" Width="1" Height="1" Fill="{DimGray}" />
        <Rectangle Name="GlowEffect" Position="0,0" Width="1" Height="1" Fill="{ButtonFillGlow}"/>
        <Rectangle Name="Foreground" Position="0,0" Width="1" Height="1" Fill="{ButtonFillForeground}" Stroke="{DarkGray}"/>
        <Rectangle Name="GlassEffect" Position="0,0" Width="1" Height="0.5" Fill="{ButtonFillShine}"/>
      </Shapes>
      <Animations>
        <Animation Name="Highlighted">
          <Color4Curve TargetName="{Foreground}" TargetProperty="Fill.GradientStops[0].Color">
            <Color4KeyFrame Time="0" Value="{DarkBlue}"/>
            <Color4KeyFrame Time="1" Value="{Red}"/>
          </Color4Curve>
        </Animation>
      </Animations>
    </VisualState>
 </ControlStyle>
The ControlStyle element defines the appearance of the control and any animations attached to it, through the VisualState sub-element. In a distant future it might be possible to also specify DataTemplate elements for databinding purposes (another experimental feature supported by the library). The first part of the VisualState element lists the actual drawing instructions to the 2D renderer. Those shapes are drawn top to bottom with the last one being the foremost shape. Differently from XAML, the Position, Width and Height attributes express values relative to the control whose style this will be applied to. The width and height attributes specified at the beginning of the ControlStyle element represent the preferred values to use in absence of changes. In particular, the position attribute specifies to the renderer any offset to use while rendering the shape. A value of (0, 0) means that the shape will begin in the exact location of the host control; a value of (0, 0.5) would mean that the shape would begin in the vertical center of the control and so on. The values associated to the width and height have a similar purpose. As it stands now, it is possible to render shapes that go outside the logical boundary of the control, but they won't be part of hit-testing. The Fill and Stroke attribute reference gradient resources that were previously defined.

The animation elements can be used to define state transition. TargetName defines the Shape whose TargetProperty will be affected. The two keyframes defined successively define the animation to use. In this particular case, the change will be instantaneous. For example:

protected override void OnPointerEnter(PointerEventArgs e)
{
    base.OnPointerEnter(e);
    string animationName = ControlStatus.Highlighted.ToString();
    var animation = AnimationController[animationName];
    animation.Speed = 1.0f;
    AnimationController.Play(animationName);
}

protected override void OnPointerExited(PointerEventArgs e)
{
    base.OnPointerExited(e);
    string animationName = ControlStatus.Highlighted.ToString();
    var animation = AnimationController[animationName];
    animation.Speed = -1.0f;
    AnimationController.Play(animationName);
}
The negative speed value is used to play the animation backward. Though it would be of course possible to specify a completely different one. Under the hood, there is an object walker class that, through reflection, changes the object properties at run time. Right now, only these discrete instantaneous animations are supported But I'm working towards implementing continuous animations. This is not so easy because of how Direct2D handles resources. In Direct2D gradients are really nothing more than textures. When you specify a Direct2D gradient, it creates a texture. Therefore if you wanted to animate the "glow" color of the radial gradient in the picture, you'd have to create as many intermediate gradients as required by the length of your animation.

This is what it does right now for those two discrete keyframes. It creates a different brush as required, if they don't exist already. For two discrete keyframes it wouldn't constitute a big performance hit. However if you wanted to animate it over the course of one second at 60 fps, you'd probably have to create 60 different brushes, which might or might not be a problem. There might be alternate possibilities, such as animating the opacity of different gradients so that it would give the illusion of colours changing. Another possibility might be animating other gradient properties. For example, it should be possible to animate a RadialGradient's RadiusX and RadiusY properties. These could be used to animate a glow effect in a button that grows or pulsates, without recreating the actual resource. I will experiment on these possibilities and report back later this week. Then again, one of the objectives of this library is to give developers the means, then it will be depend on how good your UI design skills are. Which brings us to another topic....

Cool story bro, but how do I use it in my own projects?

You can check out the experimental branch of the Odyssey Engine. That's where I am currently adding these new features. The UI part of the engine can be used without most of the other libraries (which I am using for my own game). You can check out some samples in the MiniUI project. The engine itself is distributed under the terms of the GPLv3 license. Meaning that it can be freely used in academic and other open source projects. It can be used commercially (although it is still far from being commercially viable) as long as the resulting program is open-source as well (I think). These terms are not set in stone and I'm open to discussion. So if you need a UI library, don't reinvent the wheel but help me provide other SharpDX developers a good and comprehensive Gui library. Most of the groundwork is there, it needs more controls and testing. Drop me a line if you'd like to help.


MVVM in SharpDX? Why not!

Posted by , in Odyssey UI 06 July 2014 - - - - - - · 880 views
SharpDX, UI, GUI
I just started this journal in the hopes that by participating more in the community I'll be able to find the necessary motivations to work on my project. Long story short, a long time ago I started working on a 4X game and the accompanying engine. Unfortunately the scope of the project was too big for my one-man army, so I refocused on a smaller sci-fi turn-based tactical game. I'll talk more about that once I have something to show.

I am also one of those fools who stubbornly refuses to use a 3rd party engine. The good thing about creating your own engine is that you learn a lot, but it is very easy to get distracted by unnecessary things. Such as implementing a DataBinding system in your GUI library.

I am not aware of any major GUI library that provides a DataBinding paradigm similar to XAML's. So, well it was absolutely necessary to implement my own. If anyone is aware of one, please do let me know. It would be interesting to see how more mature implementations were done.

I took my inspiration from Microsoft's "real" implementation of the Binding class. However as a quick look at the disassembled code will tell you, it is really massive and full of unnecessary stuff that will be marginally useful in a game context. A barebone system should be able to at least provide one-way binding of MVVM data to GUI object properties. Most game UI usually just display data without needing the user to change it. After all, a game is not a data-entry application. Therefore, one-way binding will be my main priority as I go along, with two-way bindings to come at a later stage.

First of all we have a DataTemplate class, similar to XAML's. For those not familiar with XAML, in this case the UIElement VisualTree property is a tree of controls that specifies the template of controls to create for each children of the container control this template is applied to.
public class DataTemplate
{
	public Dictionary<string, Binding> Bindings {...}
	public Type DataType { get; set; }
	public string Key { get; set; }
	public UIElement VisualTree { get; set; }
}
However since I guess that WPF determines the bindings while parsing XAML code, I had to manually define bindings through the Bindings property. See the following DataTemplate example:
DataTemplate commandTemplate = new DataTemplate()
{
	DataType = typeof (StackPanel),
	Key = "CommandTemplate",
	VisualTree = new Button() {Width = 64, Height = 64, Margin = new Thickness(4), Name = "Button"}, 
		Content = new Label(){ Name = "Label", TextDescriptionClass = "Small" }},
	Bindings = new Dictionary<string, Binding>
	{
		{"Text", new Binding("CommandName", "Label")},
	}
};
This DataTemplate indicates that for each child item in the container a Button control has to be created, whose Content control is a Label. The Binding tells the system that the CommandName property of the ViewModel has to be bound to the Text property of an element called "Label" . Here is the sample ViewModel:
stackPanel.DataTemplate = commandTemplate;
stackPanel.ItemsSource = new SampleVM[]
{
	new SampleVM() { CommandName = "AA" },
	new SampleVM() { CommandName = "BB" },
};
And here is the result:

Posted Image

It's very crude, but the basic principle seems to be working. And yes, that's a StackPanel with a Horizontal orientation. Controls are laid out automatically. I'm sure that a lot more problems will surface down the road but I am confident that in the long run, a simple MVVM system will save me lots of time. If you have suggestions for any improvement or simply want to know more, let me know!

You can check out the source code in my GitHub repository. Look in the OdysseyUI.sln.





Twitter

August 2016 »

S M T W T F S
 123456
78910111213
14151617181920
212223242526 27
28293031   


PARTNERS