Project Info - Hellscape

Hellscape is a proof of concept for a simple RPG shooter made in the UE4 engine. The core systems are programmed in C++, with some expanded upon in blueprints. This project will show off my ability to use C++ to set up UI, inventory systems, player input, and more. You can view this project's source code on my Github.

Scripting

Main Takeaways

Inventory System

inventory

The initial issues with designing the inventory system is how different types of items would be handled. Foci (wands, staves, etc) and spells need to be recognized so that the system knows not to put the wrong item in the wrong slot.

The primary step to handling this smoothly is making every pickup, whether it is a weapon or simply a key item, derived from the ItemBase class. This gives a common set of data the inventory system can read from each of the different types of items. This can help with things such as sorting, but the main value of importance is the ItemType parameter. This stores the item's type as an enum so that the inventory system can see an item's type without needing to try and typecast the item to every class it could possibly be.

The inventory system's Item Slots get their own widget class, which have an ItemType just like items do. All inventory interactions consist of two Item Slots swapping items between each other. This is generally only allowed to occur if their ItemTypes match. While the player's equipment slots have the same ItemType as whatever they are meant to hold, inventory slots have a special "Typeless" value that has its own special rules: If a Typeless slot is holding an item, it uses that item's ItemType value in place of its own, allowing it to swap with equipment slots meant to hold that item. This specific interaction is why items and item slots share the same enum type, as they could not otherwise be compared. Finally, an empty Typeless slot can swap with any other slot regardless of its ItemType, allowing the player to empty their focus and spell slots back into their inventory.

Player Character Setup

User Interface

While the visual elements of the UI are the easiest to arrange in the Editor, there are several areas where the UI must be interacted with from C++. In these cases, a base class for the widget and any needed data or functions are declared in C++ and a blueprint derived from that class is created in the editor where the visual end of the UI can be made.

UIDesignScheme

It is possible to add the derived widget class to the player's viewport from C++. We simply have to declare a pointer to the base class and have Unreal find the derived class in our game files and bind it to that pointer. The code for finding the derived widget is ran in the Player Character's constructor. The widget is actually created and added to the viewport in the Begin Play function.

classFinder creatingWidget

Input

Input for pawns and controllers is initialized in their respective SetupPlayerInputComponent and SetupInputComponent functions. A LOT of functions are made only to be bound to player input, so I put these functions in particular within a collapsible region to keep the file neat.

inputRegion playerInput

The inventory button is bound in the Player Controller instead of the player character. Some actions, like menu buttons, make more sense to be in a Player Controller, which is persistent and unlikely to be destroyed like a player character might. In this particular case, the input being the player controller is also helpful because I can disable the player character's input while they are in their inventory. Since the Player Controller input is unaffected, it remains free to close the player's inventory and re-enable Player Character input when the button is pressed again.

playerControllerInput

Delegates

In the future of Hellscape's design, delegates will play a very important role in how the game's systems work. It is possible that an enemy who takes damage may be affected by one or more buffs, debuffs, weaknesses, or resistances that alter the attack. Such is why I've created a custom delegate that is invoked before enemy damage is resolved.

delegateType delegateDeclaration

The delegate being "Multicast" means that I can bind as many functions to it as I please, at the cost of not being able to have a return value. The delegate being "Dynamic" allows it to be called in blueprints. Though it should not matter in this case (as long as the player can't save mid-combat!) dynamic delegates can also be serialized.