Monday, May 23, 2016

Learnings in UE4

Back at it again folks.

Hey loyal (and not so loyal) readers!

This time, I've been meaning to do some work in Unreal Engine 4. I've wanted to much around in that playground for ages and this May long weekend, I thought I'd take a stab at it again.

And man, has it *ever* been a long time since I've been in this ecosystem. I've forgotten a lot. So I'm going to use this blog as kind of a dumping ground for things I've re-learned or learned anew.

So, what I'm trying to do, out of the gate, is fairly ambitious, so we'll see how far we get. Something near and dear to my heart is conversations systems, so I think I'm going to try and take a stab at building something along the lines of a conversation system you'd find in a Telltale game.

So here's the high-level goals:\
  1. Create something that allows for rapid generation of a conversation - from pre-vis all the way through to final, production ready conversations.
  2. Make it multi-user friendly. This is a big part of the workflow. A writer should be able to use it as well as an editor and the cinematic composer. We should also be able to track the state of these conversations along the way.
  3. Allow for localization.

That's not all going to happen at once; this will take a long time to flesh out along the way.

Anyway, let's get started.


One of the things that we're going to need is a 'stage' onto which the conversation actors are going to be able to deliver their lines. This isn't actually a physical stage, but a container for everything you'd need for a conversation. Of course, we'll need the actors who deliver their lines, but you need other things, like cameras, sequencers for animation playback (if necessary) as well as an audio streaming system for line playback and subtitles.

So, off into the land of Unreal Engine we go!

Setting the stage!

It's been a very long time since I've written any Unreal code, and I have next to no experience in UE4. That's kind of what this project is about, to be honest. So, as an upfront warning, there will no doubt be a number of problems with what I create today. So expect a fair number of changes as we go.

The class hierarchy in UE4 is fairly complex, so expect to spend a fair bit of time digging through the code, and tutorials to get a feeling for the engine. But, in all honesty, all modern engines have a fair bit of complexity to them, so this isn't unexpected.

Fire up the editor in UE4 and create a new, C++ project based off the 'Third Person' template. You could use any other, but for the type of game I'm thinking of, that one makes the most sense. Once you've done that, you'll want to add a new C++ class; do this under the 'File->New C++ Class' menu.

There's a number of options here and I'll investigate more later, but for now, let's just stick with the 'Actor'. Eventually we'll also look into the Actor Component, but I don't see getting at that this time around.

The class I'm creating is called AConversationStage (following the UE4 class naming convention). It's intended use is as a origin 'locator' for the conversation (or cinematic) that you can move and all the actors will move with. This is invaluable is something in the level changes and you want to change where the conversation takes place.

For the most part, anything that you interact with in UE4 is derived off an AActor class (there are others and eventually we'll look at them). So that's where we'll start.

Go ahead an create the class. You can find the newly created classes in the Content Browser. It looks like this:


So, once created, you can right-click on the new actor and select 'edit' - you'll then open up the Visual Studio (or Xcode) project with the code ready to go.

Here's what we have at this point:

AConversationStage.h



AConversationStage.cpp



That's able to compile now. Don't actually try to compile it via Visual Studio/Xcode - go back to the Unreal Editor and select the compile button (between the build and play buttons)

Now you can drag that class into your scene. Go ahead and do it.

You don't see anything, right?  Yeah, that's normal. However, if you look at the world outliner, you should see and entry for 'AConversationStage1'. Here's what it looks like:

So, it's important to note that right now there's nothing in this actor. Nothing to render, no real attributes, no real data.  Let's start by creating a visual for this object that we can actually select.

How that works is you need a 'RootComponent'. That's a representation of this object in the world. RootComponents are an instance of a USceneComponent. So what's that? It's essentially a class that defines a transformation (location, rotation, scale) and supports 'attachments'. More on the attachments bits later. UE4 USceneComponent Reference

OK, so how do we display something in-editor? You add a scene component! So, we're looking for anything derived off a USceneComponent class (look in the UnrealEngine\Engine\Source\Runtime\Engine\Classes\Components folder for some examples).  I decided to choose the UBoxComponent as a test. Thus we end up with the following code:

AConversationStage.h 



AConversationStage.cpp




Compile again. Now drag the actor into the scene and now you see a wireframe outline of a box with the standard control widget:
 
Left-click somewhere else, so that the ConversationStageActor isn't selected anymore:

That's just to reiterate that the widget isn't part of the object.

Now select the CoinversationStageActor again by clicking on it in the editor.

Not even remotely easy, is it? Like, dangerously hard to select.  Let's fix that by actually making the scene object used to represent the object an actual static mesh.

First off, crack open your favourite 3D modelling package and create a mesh about 10x10 across (it doesn't have to be very tall). Save it as an FBX and import it into the engine. For now, just save it into your game project (later, we'll make this a plugin). I've stored mine into the Content/Geometry/Meshes folder and called it 'stage'.  At any point in time, you can right click on the asset in the Content Browser and select 'Copy Reference' to get a fully qualified reference to that object; we'll need that later. Please note! If you do not have a static mesh imported for the visual representation of this mesh, you will not see the object in editor!

Next, we have to create a scene object that represents that mesh. That's where the UStaticMeshComponent comes into play. From it, we can get a bunch of geomtry information from it, including collision information.

Anyway, for the object, we need to add said UStaticMeshComponent. So let's modify our class to contain one:

 AConversationStage.h



As you can see, all we've done is add a new private member for the stage, which is a UStaticMeshComponent. We keep this around so we can reference it (and we will).

To create it:

AConversationStage.cpp


And with that, when you drag an conversation stage over now, you get this:


Try re-selecting that again; notice how much easier it is now? That's a huge improvement.

OK, so to talk about the code for a bit. First, notice that we create a static StageMeshAsset. That just means that we only incur creating the object once, not each time we re-instantiate the class. It also means that it's loaded into memory all the time, so you want that asset to be as lightweight as possible.

Secondly, we've actually named this subobject 'm_StageVisualComponent' as part of the CreateDefaultSubobject. This is part of UE4's internal bookkeeping (I believe we can search the loaded scene components for these and get a list, but I have to investigate that later).

Thirdly, we set the static mesh for the instance of the m_StageVisualComponent to the Object that was loaded into the StageMeshAsset. This is just a reference to that object, not a new instance (and duplicate geometry) - Static Meshes, FTW.

So, why all that?

The transformation on the m_StageVisualComponent (and thus the root component) will be used to drive the  offsets for the additional components we're going to add later. eg, the cameras, stage marks for actors, and anything else we want to add.

Exciting times!

No comments: