Coconut Express Bonus Content: Low-poly Mini-Tutorials!
How to use flat-shading on a landscape material
This is pretty simple, actually! If it were a Static Mesh instead of a Landscape, you could simply import it without smoothing groups for flat shading, but (as far as I know) there is no way to directly tell a material to ignore mesh smoothing. But you can get around this by deriving the normals from mesh geometry using this setup:
To explain why this works, we’ll have to get a little technical for a moment: The DDX and DDY functions return vectors pointing in the direction of the U and V axes of a pixel’s UV Map. Since those two vectors effectively describe a plane perfectly aligned with the mesh’s surface, their cross product will return a vector perpendicular to this plane – that polygon’s normal vector. Voilà, flat shaded materials!
How to make procedurally animated low-poly water
The ocean in The Coconut Express uses a simple but effective shader applied to a rotating circular mesh. The mesh is a (slightly bent) circle with a lot of subdivisions, of around 8000 polys. This high polycount is needed to provide enough detail when tessellation is applied, as you’ll see in a moment. This is the material setup for the ocean blueprint:
As you can see, it uses the same flat-shading technique as described for the landscape above, so let’s pick apart the other parts! First off, the tessellation. This is pretty standard fare. If you don’t know what it is and how it works, check out this other tutorial I wrote about it a little while ago. The biggest difference is that here, the “heightmap” used for tessellation is not actually a texture, but is procedurally generated from the output of the noise part of this material. So let’s look at that next!
The Noise node is the core of this section. If you click on it, you can adjust a lot of properties, such as algorithm type, minimum and maximum output, quality, etc. In this case, I’ve used Perlin noise with 2 levels of turbulence, a min output of 0 and a max output of 1. That limits the output to the 0.0-1.0 scale used for linear color channels in UE4’s material system.
Now, what about the rest of the noise section? The Noise node’s “position” input can be filled by any three-dimensional vector, but it’s meant to go with positions in world space. You could connect an Absolute World Position node to it and get servicable noise! So what do the rest of these nodes do? First of all, we wanted our water to have animated waves. To achieve that, I used a Time input combined with a factor for animation speed, which works to move the noise’s position input upward over time, creating a nice, subtly bubbling animation.
The Position Transform node (shown as “World to Local”) is used in order to allow us to rotate the mesh, and have its noise function rotate with it. Without it, the noise would stay in place even as the mesh it is applied to moves, which looks really weird. The mesh rotation uses this very simple blueprint setup:
With the rotation rate set to a yaw value of 1 with the rest zeroed out, that combines with the noise material to create a nice, slow panning movement of the waves:
Blueprint: Handling Gameplay Zones
As our core gameplay idea, we wanted the player to have to manage their swallows’ stamina, with different terrain areas having different effects on their speed and rate of energy consumption/recharge. Mountaineous terrain would cost much more energy to cross but would speed you up (because of stronger winds, you see! Look, we don’t really know anything about birds). Towns and forests on the other hand would slow you down but recharge your energy, with forests also acting as shelter from the evil falcons.
To realize this on a tools level, I made a generic “Zone” blueprint. These Zones can overlap each other, can be assigned a priority, and can be rotated and resized as desired by the level designer. When the player character overlaps with a zone, this zone gets registered in an array in the character BP. On end overlap, it is removed again.
The logic to determine the dominant zone is handled entirely within the character itself. Every time a new element is added to or removed from the “Affecting Zones” array in the character, a check is run over the entire array to pick out the zone with the most dominant type and highest priority. That allowed us to handle the swallows’ energy consumption and speed through state changes based on the single dominant zone the player was currently in.
Blueprint: Custom 3D menus with meshes
I’ve had some inquiries about how we made the main menu for this game, so here’s a quick overview for everyone who wants to make their own 3D menus. Ours is a composite blueprint complete with a camera component, all menu items, the selector mesh, and logic for handling input events.
To make this work, the level blueprint enables cinematic mode on Begin Play, and switches the player controller’s view target to the Main Menu blueprint with a gradual blend, to create a nice camera transition.
Since events and functions in level blueprints cannot be called directly from class blueprints, our Menu BP contains an Event Dispatcher. The red line you see in the next picture connects to a “Bind Event to” node for that dispatcher, so that when we call the dispatcher after a round is completed, it calls the “SwitchToMenu” event in the level blueprint and the player’s view is reset to the menu camera.
Input handling is pretty straightforward. The InputActions for up and down change an Integer variable indicating the currently selected item, and the selector mesh’s position is updated accordingly.
When the player hits Enter or the confirmation button on their controller, an action is executed based on the currently selected menu item:
And that’s the gist of it. Don’t forget to enable/disable input on the Menu actor as you switch to/away from it, and you’re golden!
If you have any questions or comments, or just want to connect, just leave a comment or poke me on Twitter!
You can also download and get more info about The Coconut Express here!