Showing posts with label ue4. Show all posts
Showing posts with label ue4. Show all posts

Saturday, 31 July 2021

UE4 // Book Generator Project

I took a break from C++ programming to focus on something I don't have much experience with; the Unreal Editor. I use Unreal everyday for work but I usually don't open the editor and if I do it's just to test something I've written/modified in C++. I'm not a designer or a gameplay programmer so it's not something I use often.

I decided to come up with a small project that I could easily break down into stages and implement. I like horror games and most horror games tend to have a library in the level. Creating a book and then placing hundreds of them is tedious and a waste of time so I thought a book/library generator would be a nice small and encapsulated project.

After some planning I ended up with 7 stages that started from creating and uv-unwrapping a simple book model to creating a bookshelf blueprint that has books randomly placed when dragged out:

UE4 // Book Generator Project

You can drag out this cube and it has 4 different spawn points on it that will randomly spawn, horizontal or vertical stacks of books. They themselves are also randomly spawned.

Stage 1 - Book Creation
I had 4 tasks for this stage; 
  1. Create a simple book mesh
  2. UV unwrap it
  3. Create a texture for it
  4. Import it into unreal

I had to re-import it a few times as I kept changing where I wanted the pivot to be and blender's UV map exporter is slightly annoying. My book textures are off by 1 pixel in certain places as Blender can't make up it's mind about hard edges in UV maps.

To create the textures I used a combination of Adobe PhotoShop and an online tool of theirs called Adobe Spark. Spark is fantastic for quickly creating social media graphics as it comes with thousands of templates, free images and fonts. Or well, it's free if you have a CC license (which I do).

UE4 // Book Generator Project

The book dimensions are based off a real book off my shelf and the side textures were created by taking a photo of the sides.

Stage 2 - More Textures and a Book Blueprint
This stages tasks:
  1. Create 9 more textures for the book
  2. Create a way to swap the texture via the editor using a drop down menu
  3. When the book is dragged into the editor it will pick a random texture
  4. When the book is dragged into the editor it will pick a random scale.
Creating all the textures took me 11 days and by the end I was a bit sick of texturing.

UE4 // Book Generator Project

For the next tasks, I became acquaintances with the construction script. Basically, the construction script is run anytime the actor is built and actors are rebuilt on many things:
  • Compilation
  • Save
  • Editor Opening
  • Moving/rotating/scaling the actor
  • Changing a variable on the actor
  • Cooking (building the game for shipping)
It's extremely powerful and useful but also very dangerous. For this I created an enum out of my book covers and then created a random number on construction and used that to determine the book cover. I made these variables public so the cover can be picked manually or set to random by the user in the details panel. I did the same for the scale but only allowed a scale between 0.5 and 1 (1 being full size).

UE4 // Book Generator Project

This is pretty comprehensive for a single book but set the foundation for later stages. "Freezing" the settings is a bit complicated as when construction is run, the actor is torn down and rebuilt so any variables on it are set back to default (or whatever they are set to in the details panel). To counteract this I used something called "Random Streams". This way the book is given the exact same seed when FreezeSettings is true, and then when it's rebuilt it will "randomly" choose the exact same random numbers used to set the cover/scale again. When FreezeSettings is false, a new seed is supplied changing the random numbers starting point.

Stage 3 - Horizontal Stacks
The tasks:
  1. Create a book actor that is a horizontal stack of books. Allow users to add/remove books to the stack
  2. Give the books random scales
This was the hardest stage for me. I started this project on the 10th April and only finished Stage 3 on the 25th July. I had work and other things but I was severely blocked on the stack of books having a random scale applied. I calculate where to place the next book based on the depth of each book added together. This works perfectly, however I was doing this in one place in the construction script:

UE4 // Book Generator Project

Yep. I was giving the book a random scale then setting the global bookscale variable with another random scale. I was livid with myself that I made such a stupid mistake. After this progress was much faster and you can bet I triple checked all my blueprint nodes going forward. 

UE4 // Book Generator Project

For this you can just drag in instances of BP_HorizontalStack, freeze the settings, unfreeze, set random scales and alt-drag new instances to create stacks of books in seconds.

To increase/decrease the books I set a public slider in the details and then in the construction used a for loop. When the slider changed, it would call to "Add Book", setting the cover/location/scale for the number of books set by the slider. Thanks to the random stream, when frozen, it appeared as though all the settings of the book had been saved when in reality they hadn't.

Stage 4 - Vertical Stacks
The tasks:
  1. Create a book actor that is a vertical stack of books. Allow users to add/remove books to the stack.
  2. Allow users to align the books left, right and center.
  3. Allow random scales.
  4. Allow random rotations.
  5. "Flip" book so the spine is facing the other way.
The first task didn't take long. I just simple set the rotation of the book to -90 degrees when spawned. This caused all the books to be stacked on top of each other. However they are all very neat with a default right alignment thanks to where the pivot point is. 

To calculate the offsets so they could be aligned differently, I just offset the spawned location on X and Y using 25% of the books length. 

For random rotations I created a random float in the ranges of -90 to 90 degrees on the z angle using a random stream. Again the stream allowed the rotations to be "frozen".

UE4 // Book Generator Project

Stage 5 - Stack Extras
The tasks:
  1. "Flipped" books so the spines are facing the other way.
  2. Rotating some of the books in horizontal stacks
Flipping the books was a bit of head scratcher for me as I realised it was flipping the book at the pivot point so doing a simple rotation wouldn't work. I then remembered that negating the scale of a mesh causes the texture to "flip" without actually changing the scale. Granted the text is backwards if you see it but these books are only, at most, 20cm tall and meant as background decoration so this worked perfectly.

UE4 // Book Generator Project

The next task frustrated me for an evening until maths came to my rescue. The next book spawn location is determined by all the book widths added together but how wide is a book when it's rotated slightly? As I drew out the problem on paper I quickly noticed that it created a right-angled triangle where I knew the hypotenuse and theta so I had to use Pythagoras to determine the adjacent (the side of the triangle I was after). I then added that length to my stack length and now it creates randomly rotated books (from angles 0 - 20 degrees) of varying scale:

UE4 // Book Generator Project

It's not completely perfect and the end book can end up being rotated as I haven't set anything in place for that but you can just randomise it again. The book rotations are on weighted bools (from streams again, any random numbers use streams) so there's only a 0.05% chance of them being rotated. To rotate them the other way, once the length has been determined, I just move the x location of the book to the end point and negate the angle.


Stage 6 - Bookshelves
The task:
  1. Create 2 simple bookshelves to place books on.
For one of the bookshelves I went back to blender and used the exact dimensions of an Ikea Kallax Cube:
UE4 // Book Generator Project

I UV-unwrapped it and created material slots so I could just apply a simple wood material from the UE4 starter content.

For the other bookshelf, I placed a load of cubes in the editor, scaled/rotated them and then converted them to a Blueprint:

UE4 // Book Generator Project

This isn't as perfect as creating the mesh in blender as the sides are warped but it's good enough and not that noticeable when playing in game.

UE4 // Book Generator Project

Stage 7 - Bookshelf Blueprint
The task:
  1. Create a bookshelf that when dragged into the editor has books randomly spawn on it.
I ended up doing this task in 40 minutes as I already had all the other blueprints. I simply created a few "spawn points" on the mesh and then called "Add Child Actor Component" in the construction script. These calls were behind random weighted bools so not all spawn points are always used.

UE4 // Book Generator Project

And that's it! There are so many more things I want to do and feature creep is rearing it's ugly head but I set out to do specific things; I accomplished that and now it's time to go back to C++ land.

I really enjoyed this task; more so as I got used to blueprint. I remember doing beginner tutorials and wondering how on earth you were supposed to remember all the nodes. The truth it you don't; it's like normal programming only you don't know the language so you search for the appropriate node/google it until you get used to it. Blueprint is super fast for iterating through designs and I rather enjoyed not having to build the editor and open it every time I made a "code" change.

Future tasks do include though:
  1. Allowing the blueprints to be cooked out to merged static meshes to reduce draw calls
  2. Re-doing everything in C++/Blueprint (I do some heavy maths in blueprint and it hurts my soul)

Wednesday, 13 January 2021

C++ - Using NatVis files with UE4 & Visual Studio + WinDbg Funtimes

Story time
Recently at work I was debugging through a dump that had crashed in the TickTaskManger. It's the part of the engine that pretty much handles all the ticks in a frame. An object had recently spawned late in the frame, causing it's tick to be added to the NewlySpawnedTickFunctions container. The problem is that this container is a custom UE4 one known as a TSet. Unreal explains what a TSet is here:
https://docs.unrealengine.com/en-US/ProgrammingAndScripting/ProgrammingWithCPP/UnrealArchitecture/TSet/index.html

It's kind of like a map but instead of a <key, value> pair, the data itself is both key and value. When debugging through it, the only data available to me in the watch window was the LevelList (an array of TickTaskLevels for every level in the world). 

A TickTaskLevel contains a few containers itself, including the NewlySpawnedTickFunctions. When ending the frame, there is an assert to make sure that this TSet is empty, however, one of the TickTaskLevels NewlySpawnedTickFunctions had 1 element. It should've been very easy to see what tick function had added itself late in the frame by just looking at the Target member variable on the TickFunction added to the container. Instead, I was left staring at pointers, padding and random characters.

My manager easily identified what the offending UObject was that was trying to spawn and I was sat there staring at my garbage results like "but how??". Turns out that Visual Studio just had no idea how to display the information in a TSet properly and was doing it the best way it knew how. 

Enter NatVis. I had never heard of this before, there's a great post on it here:

Basically, you create a small XML file that tells VS how to display the custom container. Epic provides an XML file with their custom containers. If you have a code version of UE4 installed you can find this file on your pc around here:
[UE4Root]/Engine/Extras/VisualStudioDebugging/UE4.natvis

You need to copy this file into Visual Studio's visualisers folder. Usually located (but not always) in
C:/Users/You/Documents/Visual Studio Version/Visualizers

Or follow some of the steps below on how to add it to your project directly.

We have a tool that automatically does this for you. It's supposed to do it every time you generate project files but some reason it didn't for me. After this, TSet immediately showed me the offending owner of the tick function (it was a particle...).

Enabling NatVis in Your Own Projects
So the above Microsoft post is really straight forward...if you have debugging tools enabled and you know how to use WinDbg. I apparently did not have debugging tools enabled on my personal PC and I've used WinDbg once at work. I went down a rabbit hole trying to figure out how to follow the "straight forward" post. I eventually figured out what to do from this post:

First go to Apps & Features:

I had a few dev kits installed so I chose the newest one. Click on it and press Modify.


Choose change and then press next.


Tick the Debugging Tools For Windows box and hit change. It will then go and install it.

A Visualizers folder will have now appeared. Mine was located around here:
C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\Visualizers

Now, for me Visual Studio had no problem displaying this type without help but this is some good practice with using WinDbg; the Windows 95 looking memory tracker.

I'm following this tutorial here:

In WinDbg
Open up WinDbg, then File->Open Executable and search for the .exe created when compiling the dog example program from the first link in this post.

In the command line enter:
.symfix
.symfix + (the location of the debug folder of your application)

UE4 C++ - Using Visual Studio's NatVis with UE4 (and your own projects) + WinDbg Funtimes
It will show *BUSY* for a few moments in the bottom left and then symbols will have been loaded for your program.

Next type in:
.reload
bu AppName!main        // you may get a warning after this one about verifying checksum, ignore it
g

WinDbg will now breakpoint into the program at main:
UE4 C++ - Using Visual Studio's NatVis with UE4 (and your own projects) + WinDbg Funtimes

Hit Step Into at the top (or press F11)
UE4 C++ - Using Visual Studio's NatVis with UE4 (and your own projects) + WinDbg Funtimes

Step until MyDog has been initialised. Now, even though I had added the natvis file to the visualizations folder it still didn't understand what to do. Eventually I found a command to make it load a specific natvis file from anywhere:
.nvload {filelocation}
UE4 C++ - Using Visual Studio's NatVis with UE4 (and your own projects) + WinDbg Funtimes

If you type in ??MyDog and hit enter it will show you the contents. It's readable but it could be better. Type the following
dx -r1 MyDog

and WinDbg will show the contents in our nice new readable way!

UE4 C++ - Using Visual Studio's NatVis with UE4 (and your own projects) + WinDbg Funtimes

That's great but WinDbg is kind of a pain and only useful for heavyweight developing. There is a way to add natvis files to Visual Studio.

In Visual Studio
Right click on the project in Solution Explorer and go to add new-> new item.
UE4 C++ - Using Visual Studio's NatVis with UE4 (and your own projects) + WinDbg Funtimes

Go to Visual C++->Utility and choose Debugger visualization file (.natvis)
UE4 C++ - Using Visual Studio's NatVis with UE4 (and your own projects) + WinDbg Funtimes

This will create a basic natvis file for you to add code to. When you run the program and breakpoint; there will be no difference when using the MyDog example but Microsoft gives some pretty good examples of when custom natvis files are useful here:
https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2015/debugger/create-custom-views-of-native-objects?view=vs-2015&redirectedfrom=MSDN

It also builds the natvis file into the pdb (symbols) of the project so if you do use WinDbg you don't have to mess around doing the above steps (but now you know how :) ).