Interfacing with UI #1 – Structure and Libraries

This is part of a series of posts revolving around user interface design and development, the introduction and links to the other posts can be found here.

During the development of our current game I’ve been tackling the user interface. This post outlines some issues involved with creating a pixel perfect scalable user interface that also handles the different aspect ratios whilst maintaining a consistent look and feel. (A bit of a mouthful, right?). Some of the article will be specific to Unity as this is our current development engine but, hopefully, even if you’re not using Unity you’ll be able to take something useful away.

 

Libraries vs. Bespoke

The first decision that will affect your ability to achieve the best possible UI will be a typical development question.

Shall I use a library or build my own?

 

Each development team approach this question differently but there are some key points to consider. There is the usual trade off between how big your budget is, how many developers are involved on the project and how long the project schedule is. Even if you can afford to dedicate a developer for a few months to develop a custom UI system from scratch, is that really the best use of their time and your money? I’d say it usually isn’t unless you’re planning a lot of revolutionary features that none of the existing libraries provide. This usually isn’t the case.

Now I’ll play devil’s advocate. If you decide to go with a library, what about the features, maintenance, extendibility and future roadmap? Is there much use using a library that you can’t extend or fix yourself? What if the updates are released further and further apart? Those aren’t ideal situations so each of these points are worth some consideration. Any one of them might have a major impact on your game.

For us, a two person team, building our own UI system just isn’t viable. It would take a single developer many months of full time development to achieve the functionality that is available in existing libraries. In our case we’re happy with functionality provided by some UI libraries, however, we made sure that the source code was available in case we ever wanted to branch development.

 

Vector vs. Raster (Bitmap)

When selecting or building a UI system a choice will need to be made on what image format will be used. Will the system support vector, raster or both? For this article I’ll assume the difference between vector and raster is known but if not there is a summary here.

There seems to be some debate on whether to use of a vector or raster based UI system. When digging deeper into developer’s preferences two Twitter conversations lead to the following comments.

You mean something like Scaleform? Too expensive. NGUI (on which uGUI is based) does a perfect job, if you do it right.

and

Smart UI dev tries to be resolution independent, supports various aspect ratios. With vector based UI, it’s not a problem.

 

The split in opinion comes from the fact that there isn’t a clear right or wrong choice and there very rarely is in software development. Both vector and raster based UI systems have their advantages and disadvantages independent of the file format itself. Straying into Unity specific libraries I’ll try to highlight the differences between the two systems.

 

Raster System (Daikon Forge, NGUI, uGUI)

The raster based libraries that exist for Unity are almost entirely drag and drop or wizard based. This makes for a very designer friendly approach, especially if the designer has limited to no programming experience. Some programmers may become frustrated with such approaches though.

Good: Better effects & depth

Raster art tends to have a much wider range of effects that can be created and applied to them compared to vector art. Very often original files that start out as vectors will eventually be rasterised so effects and textures can be applied to them to give them more depth and smooth blends in colour.

Good: Source code provided

Most, if not all, of the raster libraries provide their source code. This is an extremely important point that cannot be overemphasised. As a general rule of thumb, developers should stay away from making any local edits to a library they are using, however, there will usually be situations where a change will need to be made. With the source code this isn’t a problem but care must be taken to port the change to later versions of the library. Without the source code this turns into a big issue that slows development down and, in the worst case situation, can lead to replacing the library.

Bad: Pixel perfect scaling issues

Pixel perfect scaling for raster libraries can be a real pain. When using libraries like Daikon Forge and NGUI you’re able to turn pixel perfect on by a click of a checkbox, however, this won’t scale yet. You’ll need to combine this with anchors to ensure that the position is correct. It can take a bit of playing to get things right from my experience.

Why do you want a scaled pixel perfect UI? Without being pixel perfect you’ll have a blurry interface on anything except the designed for aspect ratio and resolution. Without scaling then the UI will seem too small or large depending on the resolution being used vs. the designed resolution.

If you are developing a game for mobile and desktop platforms then alternative images may be required for different devices based on resolution requirements. If this is the case then it’s common for sprite atlas and texture swapping to be taken into account.

Bad: Aspect ratio issues

Things tend to get worse when scaling with aspect ratios. Your UI design may be thrown completely out the window if you haven’t taken care to incorporate the supported aspect ratios. A major problem with this is UI stretching. For example, making use of anchors to achieve a scaling UI designed for a 4:3 ratio will cause a lot of stretching when playing in a 16:9 resolution unless a lot of care is taken.

Anchor systems differ considerably from Daikon Forge and NGUI (NGUI v2 and the new NGUI v3+ anchors) and in our case we developed custom anchors to help fill the gaps in functionality.

 

Vector System (Scaleform, NoesisGUI)

The vector based libraries that exist for Unity are a mixture of third party tool designer based and pure code based systems. A more code based approach may appeal to some developers more than a designer based approach so it’s something to consider along with the following points.

Good: Pixel perfect scaling

The better vector based libraries will handle runtime vector loading instead of buildtime vector loading. This means scaling will always be pixel perfect with very little effort on the developer. Compared to raster libraries this could save a fair amount of time and effort. Aspect ratios are still a problem but removing pixel perfect scaling from the situation makes things easier.

Good: Image sizes

Vector art have smaller file sizes than raster art due to vectors being mathematical formula based. If building a game for a platform that has limited file system space or there is a requirement to keep final build size as low as possible then a vector based system will help. Generally it won’t help in terms of memory usage as the vector art will use more memory the more complex it is and it tends to effectively draw a bitmap based off the vector data.

Bad: More expensive in price

If using a Unity library for vector UI then the prices are generally at least twice as much as the next best raster based UI library. The libraries are around the range of £250 so, for a business, this isn’t too much in reality. If your budget can cover this then it’s not a problem.

Bad: Closed source code

As explained above, not having the source code for a library can cause some major problems later in the development process. You’ll usually find the source code is available for these libraries but it’s usually for a large cost and must be negotiated for.

Bad: Cross-platform support issues

Support for multiple platforms, especially Linux, tends to be lacking. Devices like Oculus Rift aren’t supported and NoesisGUI doesn’t support consoles yet.

Neutral: Third party tools for designing layout

Scaleform uses Adobe Flash Studio (and I think a few other tools support it too) for designing the UI and this leads to a flash based UI.

NoesisGUI uses XAML and this can be hand coded or designed using Visual Studio.

While these two points aren’t really bad it does lead to reliance on more tools, some of which you have to pay for. If taking the XAML route then this isn’t a problem as you can hand code the design (or visually design it in Visual Studio) then view the UI in Noesis’ viewer. It’s just more points to be aware of.

 

Closing

So, as you can see there is no real right or wrong choice (or at least that’s my opinion). Whether you develop a bespoke system or use one of the two types of libraries, make sure it matches you and your team’s approach, resources and requirements.

For the next article I’ll go into what UI framework we chose and how we addressed the problems and questions raised above. If you have any questions, want to debate or just to share your experiences – grab me on Twitter at @CWolf.

Thanks for reading! 🙂

Interfacing with UI #0 – Introduction

Over the past few years I’ve been slowly drawn into the user interface tasks that we encounter in our contracts and games. Some areas of UI design and construction have a lot of information written about them whilst other areas have precious little. With the aim of passing knowledge on I’ve decided to outline my experience over a series of UI related articles with the hope that it’ll help others working on user interfaces.

While I still have a lot to learn I’ve also learnt a lot of tricks that can help. I especially have a wealth of information in regards to the different frameworks and libraries that exist out in the wild, especially when it comes to Unity. I’ll be posting the links to the articles here as and when they are written to keep them together in one place.

If anyone wants to get in contact with me feel free to reach me at richard@roguevector.com or my Twitter at @CWolf.

Contents

The IOs of Game Netcode #2 – Threading and Concurrency

This is part of a series of posts revolving around game netcode development, the introduction and links to the other posts can be found here.

 

In the last post of The IOs of Game Netcode (found here), I talked about a few general rules of thumb I usually follow when starting a new netcode framework. Over the next few posts I’m going to go a little deeper into the technical options available to us as netcode developers and what routes I take based on different scenarios. The first topic I’m going to start with is threading, and the resulting issue – concurrency. This post is quite long so I’ve only addressed the issues the developer should keep in mind while working with threads. The solutions to these issues will be covered in the next post 🙂

 

Threading

As any netcode developer will know, the first hurdle you will hit when writing netcode or working with sockets is the threading issue. Normally, you can only execute a single piece of program code at a time. Threading allows you to execute multiple pieces program code simultaneously by running it on different threads. By default, reading and writing via sockets will block current execution of program code until it is complete. This isn’t necessarily an issue with writing to a socket unless you are writing faster than the hardware can handle (network card or modem), but if you’re reading, the operation will block until there is data to be read. One of the ways around this is to check the number of available bytes to be read before reading and if there are bytes available, only read that much. However, even with using this method, the actual reading and writing of bytes will block, even if it’s just a moment, and you don’t want this in your main game or render loop. Another way around this is to use asynchronous read and write operations. These run in their own threads automatically (provided by the socket library you are using) and pass the data back via a callback when complete. They have their uses, such as web services, but for real-time game netcode they can begin to cause problems as you cannot be sure of the order of transmission and you start to encroach of race condition territory.

So, in order to effectively read and write across a network with sockets, without causing the main program to hang while it is doing so, you’ll need at least one additional thread to perform the socket operations on. The reason I say at least one additional thread is because when developing your game client, you only really need a single socket to connect to your server. However, for the server, you’ll need a thread for every connecting client to handle each of the socket operations. For those of you who are now thinking “Why not use non-blocking sockets?”, I’m aware of this and it will be covered in a future post, but for the time being I’m focusing on the standard variety of sockets as there’s a lot more to consider with using non-blocking sockets 🙂

Anyway, as soon as you start working with multiple threads, it opens up a whole new bag of worms in the form of concurrent modifications.

 

Concurrency

Concurrent modifications are when you are reading a value of a variable in one thread, while it is being modified in another. Another example is looping over a collection while different thread is adding or removing an element from the collection. Most languages languages allow this type of access with unpredictable results. This is due to two main reasons.

 

Race Conditions

The first is the race condition, you just don’t know what thread is going to access the variable first. There are three scenarios for a race condition:

  • Read & Read – Both threads want to read the value of a variable. It is unknown which thread reads the variable first but it doesn’t matter as it does not change. Both threads read the same value.

  • Read & Write – One thread reads the variable, while the other writes to it. The final value of the variable will always be what is written, but the value read by the reading thread may be that of the variable before the write, or after. This can lead to the aforementioned unpredictable behaviour and potential crashes.

  • Write & Write – Both threads want to write. No read operations are carried out, but that does not eliminate an unpredictable value being read later. This is because the final value of the variable is unknown. It is the value of whichever thread wrote to the variable last.

The above scenarios are very specific and only show two threads accessing a single variable. However, in reality, these threads would be doing more than just reading and writing to a variable. For example, we have a shared (global or static) float variable called currentSpeed accessed by both threads:

Thread 1 – Anti speed-hacking protection

currentSpeed = player.getVelocity().getMagnitude();
if (currentSpeed > MAX_PLAYER_SPEED) {
    player.disconnect();
}

Thread 2 – Find the fastest moving entity

Entity fastestEntity = null;
currentSpeed = 0;
for (Entity entity : entities) {
    if (entity.getVelocity().getMagnitude() > currentSpeed) {
        currentSpeed = entity.getVelocity().getMagnitude();
        fastestEntity = entity;
    }
}
return fastestEntity;

For the record, you should never share a variable between two different tasks like this, but if you did this is how it might play out. For this example we are going to assume that the player is moving at a speed of 4 and there is one other entity in the world moving at a speed of 10:

// Start with Thread 2
Entity fastestEntity = null;
currentSpeed = 0;
for (Entity entity : entities) {
    if (entity.getVelocity().getMagnitude() > currentSpeed) {
// Switch to Thread 1
currentSpeed = player.getVelocity().getMagnitude();
// Switch to Thread 2
        currentSpeed = entity.getVelocity().getMagnitude();
        fastestEntity = entity;
    }
}
// Switch to Thread 1
if (currentSpeed > MAX_PLAYER_SPEED) {
    player.disconnect();
}

In this scenario, the first time currentSpeed is assigned is after the first switch, where it gets set to 4. Before it can test the value of currentSpeed, the process switches to thread 2, where currentSpeed is set to the value of the fastest moving entity’s speed, which 10. Then the process switches back to thread 1 to perform the test. Oh look, the player is moving at a speed of 10, they must be speed-hacking, better disconnect them!

This occurs because the threads can switch at any point in during normal processing and is always something you need to keep in mind while working with multiple threads. There are mechanisms to get around these issues, but first…

 

Non-Atomic Operations

The second reason concurrent access can cause unpredictable results is due to non-atomic load and store operations. This is a bit more low level and might be harder to grasp for those who aren’t familiar with CPU architecture. There are a couple of definitions when it comes to the atomicity of an operation. It can refer to a single instruction or an operation of multiple instructions. Essentially, an operation is considered atomic if it completes in a single step relative to other threads. Therefore, a non-atomic operation can also result in a race condition as described above, but for the purposes of this section, we’ll be focusing on single instructions.

When you want to run your game (or program), you need to compile it into machine code first. Every developer knows this. During the compilation process, the compiler reads our source code and optimises it internally before outputting machine code, therefore the machine code doesn’t directly reflect the logic that we’ve defined. Most developers know this. One of the optimisations compilers do is to maximise CPU register usage. General purpose CPU registers typically have a size equal to the bit-architecture of the system. Modern day systems are 64 bit architecture and have 64 bit general purpose CPU registers. If you have two 32 bit integers that have some operation performed on them, the compiler will attempt to optimise the machine code to load them both into the same 64 bit register to perform the operation more efficiently. Some developers know this.

Now, the problem lies in the scenario where you attempt to perform an operation on a data type that has a larger bit requirement than the CPU register can handle, or the register already has some active data in it. The data ends up being split into multiple machine code instructions – and this is what causes the problem. Can you remember when I mentioned that threads can switch at any point in normal processing? Well, this happens at the machine code instruction level. So, a simple variable assignment such as:

long timestamp = 1L;

Can be split into two machine code instructions, with a thread switch right in the middle.

Not many developers know this.

This is the very essence of non-atomic operations. If processing switches to another thread during a multi-instruction load or store, the race condition is the least of your worries. Depending on your operation, you’ll either end up with a torn-read or a torn-write. One thread attempts to write a 64 bit integer to a variable but only gets as far as the first 32 bit store instruction, another thread reads the full 64 bit contents of the variable, then the first thread writes the second 32 bit store instruction. What the second thread ends up reading is one-half correct data, one-half bad data and one-whole big problem.

 

Some of you may now be thinking “Holy crap, threads are dangerous, how the hell do programs even function without exploding into a flaming ball of random corruption!?” Well, the answer is yes, they are dangerous, but there are also certain principles you can abide by and mechanisms you can use that prevent this pseudo-random behaviour. However, these topics will be addressed in the next post 😉

Like before, if you have any questions about this post or just want to chat netcode, please comment below or fire me a tweet at @Jargon64.

Thanks for reading! 🙂

Interfacing with UI #4 – Coherent UI

February 5th, 2016

This is part of a series of posts revolving around user interface design and development, the introduction and links to the other posts can be found here. Last I wrote about user interfaces I discussed the new Unity UI system and I wrote about our process of porting from Daikon Forge to it. That was a year and a half ago and a lot has changed since then. To keep things interesting we decided to move from Unity UI (yet another move?!) to Coherent UI and I’ll explain why we did it. Why Move… Again?!... (read more)

@SolitudeGame: Status update: Fuel reserves low. Asteroid mining facility detected on sensors. No response to our communications. On approach station appears to be abandoned and running on emergency power only. Away mission approved. Mission objective: Search and salvage - fuel is a priority.

23/03/2022 @ 11:00am UTC

@RogueVec: And so it begins! #RebootDevelop

19/04/2018 @ 8:05am UTC

@RogueVec: We'll be at @RebootDevelop this year. We can't wait! If you want to hang out just give us a shout! #RebootDevelop2018 #GameDev

16/04/2018 @ 12:06pm UTC

@SolitudeGame: Fullscreen terminals allow you to hook into your ship's guns for fine control! Moddable gun modules, terminals and UI! https://t.co/B5N01jrA70 #GameDev

8/12/2017 @ 4:58pm UTC

@CWolf: Woo! And, now we have a cross-compiled (nix --> win64) @SolitudeGame server in our build and deploy pipeline #RV #GameDev

28/11/2017 @ 3:39pm UTC