LOG #3
Had a long-form explanation of the game design ideas and my thought process but I’m keen to keep these blogs more concise so just gonna jump into it.
Concept Art
Took a break from unreal and created some key art. Some kind of tower point of interest in a dense area. Wanted to get some material shaders going in engine to having at least some kind of visual inspiration for designing a few textures was helpful. See a full timelapse video below the image, used blender and krita.
Substance Designer Materials
Created some materials for ground terrain, and tree bark. Vegetation assets were made using Tree it. Have displayed in varying roughness/wetness.
The Mega Uber Super Material Shader
An efficient workflow for authoring and managing materials is quite crucial for me as I’m only one person and as the asset count increases, so too does the workload associated with managing and editing my materials. For this reason, and to reduce GPU draw calls, I am using a mega material shader that will work on almost all of my props, armours, weapons, buildings etc. The key feature of this shader is it’s ability to use up to 16 texture sets (16 * 3, Diffuse, Normal and Occlusion/Roughness/Metallic Packed) so that material texture sets (wood, metal, cloth etc.) can be mixed and matched within one unreal engine material instance.
For example, I have made some ‘scrap’ assets that includes some buildings and props. These asset all use some combination wood, metal and cloth texture sets. In order to use the same texture sets on all of the assets the most basic approach commonly seen is to use multiple unreal engine material slots, one for each texture set. This increases draw calls, and will continue to increase as assets that use more texture sets are used. One material slot per asset is the way to go to minimise draw calls, however, simply baking a bespoke texture for each asset also isn’t optimal for two reasons: each asset will incur draw calls for each of their textures, reducing the effect of having only one material slot in the first place, and also means that asset creation, visual alterations and design-lead changes will require work that linearly increases with the amount of assets, as each asset will need to have their textures edited and re-exported individually. Essentially there’s no easy way to globally change the textures.
The mega shader at it’s core is just an atlas system that uses a 2nd UV Channel as it’s key. By default I have split the atlas into 16, but this can be any value. See the screenshots of the material and it’s atlasing function. The material looks expensive with so many nodes however unreal engine’s shader compiler is very good and these nodes get optimised nicely, notice also that there are switch parameters for each ‘material’ - when these switches are set to false, unreal does not run any process that feeds into that switch, so by only enabling the required amount of ‘materials’ in any shader instance I can optimise further. Below are examples of Mega Material instances for to scrap buildings, one of them does not need to use the cloth texture set, so it has only two materials enabled, wood and metal.By using this shader, I can create assets much faster and easier by using a standardised set of textures. I can also make global changes to the look of my textures easily, along with creating in-game asset variants. Lastly, the material has support for using only 1 texture set across the asset, ignoring the atlas system to allow for a more standard, uniquely baked texture set.
Voxel Terrain Generation + Foliage
In addition, a simple graph for spawning foliage, with a clustering system, is also here - right now just using one tree. It’s very basic right now but it’s all seeded and generation performs well. NPCs use collision invokers, and generate shared local navigation meshes at runtime.
All-in-one NPCs
For now, NPC behaviour is simple but with parameters to allow for many different feeling behaviours that all share the same behaviour tree and Ai Controller. NPCs have a few states: idle/roam, combat, aggro combat and flee. Parameters for these include settings for roaming behaviour, combat styles/ranges, health thresholds for triggering flee or aggro combat states. NPCs also have assigned teams, and defined hostile teams which determines their behaviour when perceiving players and other NPCs. Teams use gameplay tags so theoretically this can support any number of teams - I have three right now, team1,2,3.
NPCs are defined by NPC_info structs. These include all parameters for setting up an NPC, heavy data such as skeletal meshes, materials and animation classes are soft reference pointers to reduce the NPC datatable memory footprint and are asynchronously loaded before the NPC is ‘spawned’. In addition, NPCs use the gameplay ability system much like players, allowing for custom animations, abilities and actions to be also async loaded and granted to the NPCs. In future I want to have an additional set of Hard NPC IDs and info saved in the GodActor NPC Manager (explained in next section), so that this gets queried and Hard NPCs can be loaded with priority over Soft NPCs, along with their internal variables being peristent - this also allows potential to access information and perform some kind of simulation or interactive logic between Hard NPCs even at long distances when an actual NPC_character is not active for them and they do not 'exist' in the game world.
NPC Manager
The NPC Manager is one the first components that belongs to the GodActor. The GodActor, as it’s name suggests, is our omnipresent, all seeing overlord who will manage almost all world processes, including NPC management, world state, voxel edits, and much much more, all with proper networking. The NPC Manager component is responsible for creating our NPC_Character pool at start-up and managing them as we play, right now it handles basic world 'spawning', in future the NPC Manager will also be responsible for tracking and broadcasting NPC Group Behaviour. All NPC_Characters are loaded and disabled at start. Simple parameters such as spawn radius, max NPC limit, and process tick time are all editable for fine control over the manager system.
NPCs will be split into two categories, Hard and Soft. Hard NPCs are NPCs that will have a unique ID, and will have persistence when we return to their location. Soft NPCs are designed to be fire and forget, which do not have unique IDs. Hard NPCs are not yet implemented. For soft NPCs, the NPC manager will ‘spawn’ NPCs if there are NPC Characters available in the ‘backstage’ pool (hidden, not ticking, dormant) and placed at a set distance from players, intialised and 'spawned'. After 'spawning', the NPC Manager cleans up any NPCs that are outside of our ‘despawn’ radius and returns them to the dormant pool, ready for use on the next cycle.
Overall this pooling method works far better than spawning and destroying actors, networking is minimised as only essential info is sent across the network for NPC initialisation on all clients, rather than an entire actor spawning process and all associated networking events that come with it. As NPC Manager develops, it will query information such as biome and other world states to select appropriate NPC Data.
The End!
Thanks for getting through all that, but luckily that's it for this update, there was a lot of interesting bits to focus on that I neglected to ramble about tooo much so please let me know if there's any area you would like see in more detail in the next update. I am doing this blog to share a development journey that hopefully has a good ending, but also as a way to bring some more value from the development process. I am definitely not an expert, or even very experienced but maybe sharing the journey step-by-step will inspire someone to do the same, or at the very least offer a unique reading experience for anyone who enjoyed the game (fingers crossed).
Comments
Post a Comment