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 - - - - - - · 676 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!

Animation test video

Posted by , in The Odyssey 07 September 2014 - - - - - - · 773 views

For work-related reasons I have been using the Unity engine for the last few months. There are some things I like: it is incredibly easy to set up something and view it live with a few clicks. The editor is very powerful and it is something that I would really like to have for my engine. However, being an incredibly stubborn developer, Unity is not something that I can feel as being "mine", so I'd rather spend twice as much time (or even more) to build my own engine.

This brings us to the matter at hand. My engine does not (yet?) have a visual editor with which I can script animations and preview them. As you may recall, I implemented animation features for my UI library. I have extended that system to the rest of my Entity-Component-System framework. Using my xml-like language is the next-best thing, if compared to actually writing the code necessary to create the animation objects from scratch. Instead, I can define them with a few lines and the parser will take care of orchestrating everything. I have thus created a Cutscene class that is able to animate all entities in the Scene and is also able to reference other hardcoded animations such as those belonging to UI controls. I need these animation features for two main reasons: first, I'm working on an "announcement" trailer of sorts and second, once I move to the actual ship-to-ship combat part of the game, once you issue an order, it will be visually shown by a contextual cutscene, similar to the way it happens in the recent X-Com game. Here is an example of the Cutscene animation shown in the above video.
  <Animation Name="FocusOnPath" WrapMode="Loop">
    <BoolCurve TargetName="{Overlay[UserInterface[twLocation]]}" TargetProperty="IsVisible" >
      <BoolKeyFrame Time="0.0" Value="True"/>
      <BoolKeyFrame Time="7.0" Value="False"/>
    <QuaternionCurve TargetName="{PerspectiveCamera[Rotation]}" TargetProperty="Orientation">
      <QuaternionKeyFrame Time="8" Value="E0,0,0"/>
      <QuaternionKeyFrame Time="10" Value="E90,0,0"/>
    <BoolCurve TargetName="{Overlay[UserInterface[Prompt]]}" TargetProperty="IsVisible" >
      <BoolKeyFrame Time="0.0" Value="False"/>
      <BoolKeyFrame Time="8.0" Value="True"/>
      <BoolKeyFrame Time="20.0" Value="False"/>
    <BoolCurve TargetName="{Overlay[UserInterface[lVenus]]}" TargetProperty="IsVisible" Function="SquareWave" Options="8">
      <BoolKeyFrame Time="0.0" Value="False"/>
      <BoolKeyFrame Time="12.5" Value="False"/>
      <BoolKeyFrame Time="15.5" Value="True"/>
    <BoolCurve TargetName="{Overlay[UserInterface[Target]]}" TargetProperty="IsVisible" Function="SquareWave" Options="4">
      <BoolKeyFrame Time="20.0" Value="False"/>
      <BoolKeyFrame Time="23.0" Value="True"/>
    <BoolCurve TargetName="{Overlay[UserInterface[lCountdown]]}" TargetProperty="IsVisible" >
      <BoolKeyFrame Time="0.0" Value="False"/>
      <BoolKeyFrame Time="20.0" Value="True"/>
    <FloatCurve TargetName="{Path[PrimitiveReveal]}" TargetProperty="PrimitiveRatio">
      <FloatKeyFrame Time="23" Value="0"/>
      <FloatKeyFrame Time="26" Value="1.0"/>
      <FloatKeyFrame Time="30" Value="1.0"/>
    <PlayAnimationCommand TargetName="{Overlay[UserInterface[twLocation]]}" Time ="2"/>
    <PlayAnimationCommand TargetName="{Overlay[UserInterface[Prompt]]}" Time ="10"/>
The top-level object in my ECS is the Scene, an object containing all entities in the game world. As such it implements an interface that is able to return individual entities by name. So when the parser finds a string value in the TargetName attribute it asks the Scene object to return the correct entity. If the value is in array syntax, the Entity object (which implements the same interface as well) will return the component specified as the index of the array. This can go on for more layers as it can be observed from the boolean curves targeting UI elements. Overlay is the UI entity, while UserInterface is the actual UI component holding a reference to the GUI object; the third index is the name of the individual UI control being referenced. I have also implemented a few functions to avoid generating individual keyframes to approximate some effects, such as the SquareWave function that animates the flickering of the target brackets.

Finally, the Commands section references other animations that are defined elsewhere, either as other cutscenes or as hardcoded animation. In this case those two animations are the built-in animations of the Typewriter effect. This cutscene will activate the first one two seconds into the animation and the other one ten seconds into.

So all things considered, it’s a nice compromise if a visual editor is not available. Now, that typewriter definitely needs some sound effects… Also, I can now work on the animation concerning the Campaign Screen!

BSplines and Typewriter effects

Posted by , in Odyssey UI, The Odyssey 26 August 2014 - - - - - - · 791 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,852 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:
    <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"/>
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">
        <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}"/>
        <Animation Name="Highlighted">
          <Color4Curve TargetName="{Foreground}" TargetProperty="Fill.GradientStops[0].Color">
            <Color4KeyFrame Time="0" Value="{DarkBlue}"/>
            <Color4KeyFrame Time="1" Value="{Red}"/>
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)
    string animationName = ControlStatus.Highlighted.ToString();
    var animation = AnimationController[animationName];
    animation.Speed = 1.0f;

protected override void OnPointerExited(PointerEventArgs e)
    string animationName = ControlStatus.Highlighted.ToString();
    var animation = AnimationController[animationName];
    animation.Speed = -1.0f;
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.

The Odyssey, a turn-based sci-fi tactical game

Posted by , in The Odyssey 27 July 2014 - - - - - - · 1,315 views
indie, sci-fi, turn-based
Posted Image
Campaign map
In short
The Odyssey is a turn-based sci-fi tactical game where you play the role of a Fleet Commander in a space conflict. Why turn-based? I felt that there was an over-abundance of real-time games set in space. Turn-based games give you more time to think and can be very addictive, if done well, as X-Com or the Civilization series have shown. Plus, it opens the possibility of asynchronous multiplayer games.

The story
It is 2282 C.E.: Humanity has colonised the nearby planets of the Solar System and established outposts and research stations in the Trans-Neptunian reaches of space. However, more than 15 billion people now live on Terra and tensions between the Old World and the New Worlds are high. An almost depleted Earth is highly dependent on her colonies, although it remains the sole authority in the Solar System.

The Eye of Titan, a space telescope in orbit around Titan, has identified various planets in the habitable zone around nearby yellow main sequence stars: Tau Ceti, Sigma Draconis and Eta Cassiopeia. Three ships using an experimental Anti-Matter drive have been built. Theory and laboratory studies confirm that it will be able to provide enough power to warp the space between the Solar System and the destination. In practice, it has never been tested before. The UTS Winter Solstice, the UTS Dream of Mankind and the UTS Odyssey are en route towards the far reaches of the Oort Cloud where the Anti-Matter drive will be activated. Each ship will escort a small flotilla of ships carrying colonists in cryogenic stasis, deployable factories and biological arks. The voyage is one-way only. You are the commander of the UTS Odyssey and all of mankind’s hopes are with you. Especially after NASA's Chiron program, a generation ship travelling at 0.1 times the speed of light, failed to contact Terra after its 50 years voyage to Alpha Centauri.

The setting
The game is set at the very beginning of interstellar exploration. No alien civilizations have been contacted yet, if any do exists after all. Humanity’s only enemy is itself. The Odyssey, your ship, is a space carrier. Close to Pluto, it will rendezvous the UTS Voyager, a colony ship; the UTS Gagarin, a transport ship with the resources to build infrastructure upon landing and the UTS Darwin, a biological vessel containing seeds, embryos, livestock and everything else needed to jumpstart life. Its route is set towards Sigma Draconis. It is expected that the warp field generated by the Odyssey’s drive will envelope the other ships as well.

Posted Image
Preliminary render of the battle grid
The Game
Somewhere along the way towards Sigma Draconis, conflict will erupt. One of the key points of the game is that you as commander will not have total control over every single ship in the fleet. Alongside the traditional “Action Points” of turn based games, The Odyssey will build on the concept of “Command Points”. Your character will only be able to influence or advise a limited number of ships in your fleet per turn. The ships you do not choose to advise will act as their own ship captain will decide. Each ship captain will have RPG-like characteristics that will influence its decisions. Bravery, morale, discipline, etc. Each ship will belong to a particular “class” which will define the type of operations it is able to conduct. The Odyssey is a space carrier. It hosts several vessels capable of orbital insertion and interplanetary sublight travel. It is armed but it is something that has been kept hidden to the public. All other ships in the solar system are armed with high precision lasers against space debris, at best. Over the course of the game you will have access to ships capable of boarding and long-range strikes. The general idea is that capital ships do not enjoy the same manoeuvrability of single-pilot vessels. Once in motion, a capital ship will take several turns to “brake”. The game will feature a story-driven campaign, possibly more.

The first screen at the beginning of this post is a preliminary draft of the Campaign Screen. Your fleet will move in-system towards the orbit of Pluto where you will attempt to warp towards Sigma Draconis. In this part of the game, you will control your fleet in a similar way to Mass Effect's own system-level screen. Exploration will be a big part of the game as you will need resources to keep your fleet operational. When all hands will need to man their battlestations, the actual battle will be played on a hexagonal grid.

When will be able to play it? Can I help?
At this point in development it is very early to tell. However I, the developer, have already built a sizeable portion of the game engine. I am at point where the next technical steps to implement will depend on the direction the gameplay will take.
You can help in several ways. First and foremost by following the development of the game. Motivation from the community will be a deciding factor in the success of the game. If you like the idea, if you feel that a turn-based space-themed game is something that you would like to play someday, I would be glad to hear your feedback. More details on the gameplay will emerge as I flesh them out.
Furthermore, if you have art that you would like to contribute such as 2D or 3D art, sounds, music, etc. I would really appreciate it, as it is not one of my main skills. Stay tuned for more updates about the voyage of The Odyssey!

An old-school vector graphics / neon mesh appearance effect

Posted by , in Effects 19 July 2014 - - - - - - · 1,020 views
Effects, Vector graphics, Neon
I mentioned I am developing a turn-based space fleet combat kind of game. Think about Battlestar Galactica meets X-Com. I am explicitly referring to Battlestar Galactica and in particular to the Dradis. I always wanted a game where you are the fleet commander or admiral and your only way to relate to the battlefield is not through some ethereal camera floating in space but through some abstract graphical representation.

To recreate that look & feel my inspiration comes from the combat briefings of various sci-fi games and films. Does anyone remember Wing Commander's briefings? That's how I would like the game to be.

It seems in the future, GPUs will suffer a rapid decline. See for yourself:

Posted Image

Star Wars

Posted Image

Star Trek

Posted Image

The Dradis in BattleStar Galactica

So who am I to argue with all these warning signs? Admittedly, BSG is excused as the low-tech approach in the show was somewhat of a forced choice. Anyway, I am in the process of creating a short teaser video for the game I am working on. I needed an appearance effect and I thought that showing the mesh face by face would be a nice example. These future GPUs need time to load meshes anyway! Posted Image
I received some excellent advice in the forums. As a first attempt I implemented an algorithm that randomizes the order of the indices associated to the mesh. The ratio of primitives displayed is then animated from 0 to 1 over the course of 4 seconds. In order to avoid displaying the triangle edges, I am using an adaptation of a solid wireframe shader. Indices are then reshuffled in groups of six (two triangles, one quad). Clearly this approach works when you know that indices are generated in that fashion. It works well with solid primitives. It might be problematic if the triangles that form the base "polygon" are not consecutive. For example, in the below video I am reshuffling indices quad by quad. If the algorithm acted on triangles, then "L" shaped lines would appear (because one of their edges is hidden by the shader).

There's also a bloom post-processing effect applied (which seems to produce "blocky" results, I'll have to tweak it). Also there's a scanline effect because no retro-style graphics would be complete without scanlines. So yeah, the impending GPU decline will also inevitably affect monitor manufacturers. Such is the way of the future Posted Image

MVVM in SharpDX? Why not!

Posted by , in Odyssey UI 06 July 2014 - - - - - - · 991 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.


January 2017 »

1516171819 20 21