Unreal Engine 4, C++, UI, and You


UMG designer with some scribbles over it.

To recap, I’ve been trying to make a Space Invaders clone in UE4 this month for #1GAM. Last time I left off, I had the basic gameplay down - you could shoot enemies and they could shoot you. However, when you died, you had to exit and restart the game to try again. Similarly, when you destroyed all the enemy ships, you’d just sit there with nothing left to do.

What the game really needed was some sort of UI. You know, a main menu, a game over screen, and a “YOU’RE WINNER” screen, displayed at appropriate times. And thus, the stage is set for the story of how I came to hate, then come to terms with, Unreal Motion Graphics.

For reference, I’m on Unreal Engine 4.7.6. The issues might no longer be issues in the future, and the code may become outdated.

UMG is the name for Unreal Engine 4’s UI designer. It’s that thing in the header image that has all my scribbles over it. It’s pretty simple and intuitive - all you do is drag elements around, like text and buttons, to create a menu. Then, you use the blueprint editor to script the things that happen when events are triggered, such as button clicks. I’d compare it to the Visual Basic editor interface, except replace Basic with blueprints.

But wait a second, blueprints? I’m trying to use C++ for everything! Well, maybe I can live with using blueprints just for ui elements. It kinda makes sense - after all, in large teams, UI designers will generally have less expertise in programming. Blueprints are more friendly to learn.

However, while I can script the behavior in blueprints, I can’t spawn the UI through blueprints because menus should be spawned when things happen in C++. For example, when our ship gets destroyed by a laser, we need “game over” to come up. Okay, well, let’s see if we can google up a way to spawn UMG menus in C++. Oh yeah, here’s one! Let’s try it out…

Hours pass…

Holy hell I hate UMG. The documentation sucks, my code is so unclean, and, worst of all, the Visual Studio Intellisense support is completely broken. I finally got my menu to show up, but there are red lines all over my code and I can’t auto-complete anything. Plus, I’m spawning my menu right in my Ship class, which is the worst separation of concerns I’ve ever seen. I can’t shake the feeling that there must be a better way to do it, but there are almost no samples online that deal with C++.

Apparently there’s this thing called Slate which is C++ through-and-through, but it seems almost universally panned by the community. I should probably stick with UMG, but honestly, I need to figure out a better way to do this… well, it’s late and I’m tired. Maybe if I sleep on it, things will become clearer.

For reference, this is what my C++ code to spawn the menu looked like. Just to be clear, while it does work, I don’t recommend using this code - keep reading for a better way to do things.

void AShip::OnBeginOverlap(AActor *otherActor) {
    if (otherActor->IsA(AEnemyLaser::StaticClass())) {
        /* We're going down */
        deathSound->Play();
        mesh->SetVisibility(false);
        bAutoDestroyWhenFinished = true;
        otherActor->Destroy();

        /* Create and display our menu (widget, in UMG terms) */
        UUserWidget *widget = CreateWidget<UUserWidget>(GetWorld()->GetFirstPlayerController(), *GameOverWidget);
        widget->AddToViewport();

        /* Focus it and show the mouse cursor */
        FInputModeUIOnly Mode;
        Mode.SetWidgetToFocus(widget->GetCachedWidget());
        GetWorld()->GetFirstPlayerController()->SetInputMode(Mode);
        GetWorld()->GetFirstPlayerController()->bShowMouseCursor = true;
    }
}

The next day…

Ok, I have an idea. Here’s how it’ll work: I’ll blueprint the ship C++ class. The blueprint will contain a dead-simple script attached to the OnDestroy event that spawns the menu. I know I wrote in the previous post that I’d rather not have behavior mixed within blueprints and C++, but I think this is an exception to that rule - the blueprint doesn’t really contain behavior, after all.

I implement this idea pretty quickly - it’s much easier than I thought. Here’s what the resulting blueprint looks like:

blueprint triggered when ship is destroyed.

As an aside, the above embodies another flaw in blueprints - they’re hard to share. You can copy-paste C++ snippets into a code editor, but you’ll have to manually assemble the blueprint above if you want to reproduce it.

Anyway, this blueprint works beautifully. The ship gets hit by a laser, is destroyed, and then the menu spawns. The only difference is that the menu doesn’t immediately show up - it’s delayed by a small amount of time. I assume this has something to do with how UE4 destroys entities. This is only a tiny problem, though, compared to what we’ve achieved. This, finally, seems like the way I should be doing things. It’s nice and clean and feels UE4-ey.

Doing the same thing for the victory menu gets proves to be a little more tricky. There’s no handy event I can hook into that triggers when all enemies have died. I could, in the blueprint for the enemy, detect if any other enemies remain, and spawn the menu if there aren’t, but that is distasteful to me. An enemy should not need to check if other enemies are still alive.

Plus, I have this EnemyController class that keeps a count of the enemies that remain. It’s written in C++, but there must be a way to trigger an event in C++ that gets passed to a blueprint of the EnemyController. So, I type “blueprint event from C++” into google, and it hands me this article. At first, I don’t believe it - is it actually that easy to do? I add the requisite code to my EnemyController class, and - nope, it doesn’t work, I can’t find the blueprint node for my custom event. Just as I expected! It can’t be that easy!

After a few minutes more of googling, though, I find out that there’s a bug with the interaction of events in UE4 and hot reloading of C++ code. All that I need to do is recompile in Visual Studio and re-launch the editor. I do so, and… wow, there it is.

For reference, here is the C++ code in my EnemyController:

/* EnemyController.h */
class AEnemyController : public AActor {
public:
    UFUNCTION(blueprintImplementableEvent, Category="Events", meta=(FriendlyName="All Enemies Dead"))
    void OnAllEnemiesDead();
    
    /* ... */
}

/* EnemyController.cpp */
/* Bound to all the enemies' onDestroy events */
void AEnemyController::OnEnemyDeath() {
    /* Decrement our internal enemy counter */
    --enemyCount;

    /* Speed up the rest of the enemies when one dies */
    moveTime = minimumMoveTime + (initialMoveTime - minimumMoveTime) * enemyCount / initialEnemyCount;

    /* Check if any enemies remain */
    if (enemyCount <= 0) {
        /* If no enemies remain, trigger the "All Enemies Dead" event */
        OnAllEnemiesDead();
    }
}

And here is the blueprint which triggers when OnAllEnemiesDead() is called (it’s almost identical to the one above):

blueprint triggered when all enemies are destroyed.

Honestly - forget any gripes I had with the blueprint system, that is awesome. When I just started trying out UE4, I thought the additional macros that UE4 had added to C++ were mere clutter; that they were more distraction than functionality. Now that I’m actually implementing something in UE4, though, I can see just how cool the reflection system actually is. My earlier frustration at UMG, too, has given over to feelings of “neato”.

To sum up, the answer to “how do I display menus using C++?” is “don’t”. Binding the menu display code in blueprints to events that are triggered from C++ is much cleaner.

With the UI done, I can just about call my Space Invaders clone finished. It’s got gameplay, and you’re able to win and lose it. It’s even kind of fun. Here’s the UE4 project on github, if you’re interested in taking a look.

It’s time to try to tackle a tougher project. I’ll post more about it when it’s under way.