Cubix

Project Details:

  • Duration: 4 Weeks
  • Engine: Unreal Engine 5, C++
  • Tools Used: Rider, Github Desktop, Figma, Ableton, Miro
  • Team Size: Solo

Goal of Cubix:

Cubix is intended to be an extremely simple "bare bones" game, allowing me to prioritise improving my C++ capabilities in Unreal Engine. For this reason almost all actors in Cubix were created in C++. With only a few exceptions created using Blueprint visual scripting.

What is Cubix:

Cubix is a short 3D voxel puzzle game where you must rotate and position puzzle pieces to create specific shapes. The game is level based with each level having a "target shape" to make using the provided puzzle pieces. You move pieces by dragging them, and rotate them using UI buttons - all movement is also confined to a grid.

C++ vs Blueprints:

Initially when coming up with the idea to make Cubix, the goal was to solely use C++. But very quickly I realised the limitations of this approach and why many Unreal developers promote using a mixed workflow of both C++ and Blueprints visual scripting.

When I began prototyping and creating the core features of the game (grid based movement, draggable pieces), it became clear that trying to prototype mechanics using C++ significantly slowed down my workflow. This was mostly due to the slower compilation speeds when compared to Blueprints and the added complexity of writing code.

I quickly then shifted my focus on prototyping all my actors in Blueprints first, then later converting the Blueprints into C++ classes. Doing saved much more time and just made sense considering the purpose of Blueprints.

For most actors I defined their class and core behaviour in C++, then creating Blueprints that inherit from said class.

The only classes made entirely without C++ were all the Widget Blueprints, as they only require relatively simple capabilities which I needed to be able to play around with. For this reason I stuck with Blueprints.

Overall after finishing the project it really highlighted why developers say to use Blueprints and C++. Unreal Engine is designed to use both, where each developer can decide where and where not to use each.

They both have benefits and drawbacks, but by using both you can get the best of both worlds. And I plan to keep using both going forward.

Challenges & Solutions:

While creating Cubix (a game which I initially thought to be stupidly simple), I encountered 2 main challenges trying to achieve my vision.

Grid Based Movement:

Creating grid based movement was a fun challenge to overcome, as I had never created and grid based games. For Cubix all puzzle pieces need to be aligned to the grid, alongside any movement or rotation.

The approach I settled on was this...

Each level has a actor named the "Grid Manager". The grid manager has two main variables which are:

  • Grid size (how long and wide is it)
  • Grid cell size (how big is each cell or tile of the grid)

The grid managers role is to keep track of every puzzle pieces state (if they're in the correct spot), and to provide puzzle pieces with location data for where to move to on the grid based on it's desired direction.

So at the start of each game or whenever a puzzle piece needs to move, it tells the grid manager:

"Hey, I want to move in this direction!" or "Hey, can you align me to the grid when the game starts".

Then the grid manager gets the puzzle pieces location and it's desired direction, then it calculates where the puzzle piece should be and sends the info back.

This was a fairly simple and straight forward approach that worked well for Cubix.

Checking for Puzzle Completion:

Figuring out when the puzzle is complete and the pieces are in the correct location was absolutely the biggest challenge for this project.

Initially I planned on having each point in the grid keep track of if there was a puzzle piece on it. Then when you move a piece it checks if the grid has a series of points that represents the "target shape".

But attempting to make this system proved to be a challenge slightly too large for this "simple C++ project".

The thing is you have to account for the fact that the "target shape" can be rotated multiple ways and positioned anywhere within the grid, and it can still be correct. So trying to create a system that took this into account was slightly too complex for this project, so I decided to take another approach.

Instead each puzzle piece has "joints" which are invisible colliders, that when overlapping with another joint tell the gamemode (which controls the win state of the game) whether a puzzle piece is "connected" to the right spot.

Doing this massively simplified the check for whether the puzzle is completed since you're essentially seeing if a hand full of colliders are overlapping.

And as long as you place them in specific points on the puzzle pieces where it would require you the piece to be correctly positioned for the joint to be triggered, then the problem should be solved. Well not exactly.

Taking this approach added it's own complexities, since if a puzzle piece can be mirrored on any axis then you have to make another joint to account for if the puzzle piece gets flipped. And when you do that you also have to make sure now that you actually no longer need all joints to be overlapping only some.

In the end the problem was never fully fixed with this "band-aid" approach, and I ended up getting around it by creating some interestingly shaped puzzle pieces that couldn't be mirrored.

It really showed me how sometimes the simplest things in games can be significantly more complex.

Reflection:

Looking back at Cubix, I feel as though the project certainly pushed me and improved my capabilities in C++. Even if the game is extremely short, visually and mechanically simple. In the end the goal was to improve my C++ development, and I achieved that goal.

Play Cubix Here.