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:
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;
- Create a simple book mesh
- UV unwrap it
- Create a texture for it
- 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).
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:
- Create 9 more textures for the book
- Create a way to swap the texture via the editor using a drop down menu
- When the book is dragged into the editor it will pick a random texture
- 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.
- 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).
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:
- Create a book actor that is a horizontal stack of books. Allow users to add/remove books to the stack
- 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:
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:
- Create a book actor that is a vertical stack of books. Allow users to add/remove books to the stack.
- Allow users to align the books left, right and center.
- Allow random scales.
- Allow random rotations.
- "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".
Stage 5 - Stack Extras
The tasks:
- "Flipped" books so the spines are facing the other way.
- 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.
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:
Stage 6 - Bookshelves
The task:
- 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:
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:
Stage 7 - Bookshelf Blueprint
The task:
- 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.
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:
- Allowing the blueprints to be cooked out to merged static meshes to reduce draw calls
- Re-doing everything in C++/Blueprint (I do some heavy maths in blueprint and it hurts my soul)
No comments:
Post a Comment