A proof-of-concept custom controller and hardware HUD for Hardspace Shipbreaker

My original reason for getting into hardware dev was the custom keyboard scene, and while I've largely moved beyond that now, I still like messing around with crazy custom input (and output) devices as game controllers. When I saw the game Hardspace: Shipbreaker, my first thought was that its industrial setting made it perfect for some kind of new project. I liked the idea even more when I saw that the game included ascend/descend and roll left/right in its controls - that isn't super common, and I thought that it would be interesting to see what I could do to implement three-axis controls rather than traditional two-axis thumbsticks.

Read more  ↩︎

Custom Hardspace Controller pt 1: Hardware selection

I wanted to incorporate something into this project to provide proper six-axis controls for player movement as one of the main features. Unfortunately when I went looking for suitable controllers, initially I only found finished flight-stick controllers which had a few issues:

  • Far too expensive at approx $150 AUD
  • They already had integrated controller circuits, somewhat defeating the purpose of custom DIY hardware
  • They already had USB connectors, unsuitable for integration into my project unless I use a more expensive controller with USB host support
Read more  ↩︎

Custom Hardspace Controller pt 2: 20x4 LCD implementation

20x4 LCDs are pretty ubiquitous and cheap these days, costing only a few dollars on Aliexpress and so forth. They usually come with the option of a PCF8574 I2C I/O expander board, which can be soldered on quite easily:

Read more  ↩︎

Custom Hardspace Controller pt 3: PTZ joysticks

As mentioned earlier, one of the central ideas for this project was the use of some off-the-shelf 3-axis PTZ (Pan, Tilt, Zoom) joysticks to provide the analog inputs for the controller. These joysticks have a third analog axis which is controlled by vertically twisting the stick clockwise and counter-clockwise.

These are overall the simplest part of this project to integrate aside from one minor factor when implementing the device-side code that interprets their signals.

Read more  ↩︎

Custom Hardspace Controller pt 4: Switches and buttons and bar graphs, oh my

I wanted to be able to display the vital signs of the player in some way in the hardware HUD, so when I came across an older module I had already built containing three bar graphs and a number of switches and buttons I thought it would be perfect for this.

I had originally built this for another project that was long-since mothballed, so being able to use it on something new was definitely attractive.

Read more  ↩︎

Custom Hardspace Controller pt 5: Of TFTs, and scope creep

I'd toyed with the idea of using a TFT screen earlier during this project, but now that all the core functionality was there, I thought that it was worth revisiting the idea of showing the portrait and text of any incoming communications the player received during gameplay.

On looking at the datasheet for the ST7735-based TFT screens that I had, I realised one problem, though - the controller always wanted more than one byte per pixel of data to be transmitted to the device:

Pixel FormatR BitsG BitsB BitsTotal Bits
R4G4B444412
R5G6B556516
R6G6B666618

Even though the TFT screen was very small-resolution (128x160px), that still means that there's a total of 20480 pixels in the display.

Using the most compact representation (12-bit color), that still requires 30720 bytes of memory just to framebuffer the display. My microcontroller had a total of 20K RAM - clearly that wasn't going to be an option. And that was before considering the performance impact of having to pack the pixels so as to not waste the last 4 bits of each pixel. Even with that aside, unless I wanted to go back to my USB stack and implement bulk endpoints, which I didn't want to do at this time, USB HID Out packets are limited to 64 bytes over USB 1.1, so I'd be needing to transmit 480 packets (at an interval of 1ms each) to get that data over the wire. All of these things suggested I had to try something cleverer.

Read more  ↩︎

Custom Hardspace Controller pt 6: BRAAAAINS

This project spent a very significant amount of development time hooked up to the microcontroller - the 'brains' of the project - via a good quality breadboard.

Even after I ran out of room on the breadboard for the number of attached devices on the I2C bus I continued to use one (and a second microcontroller on another devboard) for the initial testing and R+D of the remaining peripheral drivers. They make attaching logic analyzers and other probes much easier, as adding a bare header to the same rows on the breadboard exposes data lines for easy access with standard-pitch grabbers.

However, as I mentioned, I did eventually run out of room for the number of I2C devices I had connected, and I knew that I wanted to make something a little more sturdy, even if I kept the actual microcontroller board detachable from the base board where everything else was connected.

Read more  ↩︎

Allocators, std::filesystem, and GetLastError oh my

The SDK I'm writing, in an ideal world, would be C++14 compliant rather than require C++17, but there's just so many nice things added in C++17 that are hard to give up - one of which is std::filesystem for handling paths in an agnostic fashion.

So to deal with this, we use ghc::filesystem - a C++14 compliant backport of the std::filesystem namespace. It's really quite nice, we no longer have to worry about a whole bunch of string parsing or dealing with platform-specific path separators or even things like checking if a file exists.

filesystem::path tmpPath("/some/path");
tmpPath /= "somefile.txt";

std::cout << tmpPath.c_str() ; // prints /some/path/somefile.txt
if (filesystem::exists(tmpPath))
{
  //do something to read in the file here
}

Everything's been working so far, until we start wrapping the SDK in an Unreal Engine 4 plugin, for our partners that want to access the SDK via blueprint.

Read more  ↩︎

Data-driven code generation for C++ projects with CMake and inja

My current contract is for a SDK that uses asio as an asynchronous event loop, and standard practice for asio projects is for callbacks to receive a std::error_code as a status indicator. std::error_code is pretty useful, but requires a fair bit of boilerplate if you want to create your own codes.

To ease that process, I've cooked up a code-generation process that can be driven by CMake to produce a nice header for all the error categories and codes we return to consumers.

Read more  ↩︎

PEG (Parsing Expression Grammar) parsers in C++ with peglib

I've been doing a lot of work on a project recently that reads in configuration/modding data via command-line flags and tables stored in a text-based file format. The format is terrible, full of idiosyncrasies and inconsistencies depending on the type of table being read in.

The original parser for these was C-based and used a line-by-line system that stored a pointer into a file and advanced that pointer as tokens and rules were consumed. I really didn't like it at all, and for reasons mentioned below it was a real pain to refactor its dependencies. Clearly I was going to have to use an alternative.

Read more  ↩︎

Waiting for Render Resource initialization in UE4

Opened up my personal project earlier today and ran into a nasty crash when trying to load an instance of my proxy texture asset class. The crash was occurring here:

void* Data = RHILockTexture2D(DynamicResource->GetTexture2DRHI(), 0, RLM_WriteOnly, Stride, false);

DynamicResource was valid, all looked okay, until I could see further in that the result of GetTexture2DRHI was not valid, even though I'd already called to initialize the resource beforehand. What was going on?

Read more  ↩︎

Focus management with UMG transitions in UE4

One little gotcha that isn't really well documented with manual keyboard/controller focus in UMG is that if a UI element is not visible when you attempt to set focus to it, that call to SetUserFocus silently fails. What you're going to need to do is either set visibility first (easy enough) or if there's an animated transition that changes Visibility, you're going to want to wait until that transition is complete before doing so. I ended up making the following Blueprint macro to do this, it has a rather unwieldy name but I wanted to distinguish it from other macros I created for subwidget navigation that didn't require animation transitions :

Read more  ↩︎

Website overhaul, static site generators, Zola

So I finally got around to overhauling this old site. Been meaning to for a long time, but I never really had the opportunity to do so. The old version was all handwritten html, which looked okay, but was a real pain in the behind to update with new work, much less include something like a proper blog.

I'd been looking at static site generators for a while. If you don't know what they are - they basically let you write posts or content in Markdown or some similar 'language' and then process all your posts into static HTML pages your website can serve without needing some kind of special serverside service or database. Hugo and Jekyll are the best known examples I think - Github supports both via github pages.

Read more  ↩︎

Using custom visualizers for Actor components in UE4

If you've got an Actor component that needs some kind of in-editor representation, to make it easier to tweak settings like size or falloff, but you aren't deriving from something that would be rendered in editor like a StaticMeshComponent, you might want to use a custom component visualizer.

Let's say you have something like a ProceduralMeshComponent that draws a flat grid of polygons that will be deformed at runtime. You might want to have a representation of the grid like the following image:

In order to do this we can create a FComponentVisualizer subclass.

Read more  ↩︎

Creating a custom settings page in UE4

Creating a custom project settings page is pretty easy, if you don't want to inherit from UDeveloperSettings and use the automatic registration process.

Read more  ↩︎