Custom Extensions - frasergeorgeking/UE4_BP_MazeGen_MIT GitHub Wiki

The generator can easily be extended to include new custom geometry or behaviour. Please see the Demo Application Tutorial for an example of how to extend the functionality of the project from a code and logic perspective. The remainder of this section will focus on the implementation of custom maze tile geometry.

Table of Contents

Required Tile Meshes

The generator works by combining a few tiling meshes together to create a seamless maze. Please find renders of the included tiles in both textured and un-textured form below:

images/custom-extensions/corner_tile_render.png

images/custom-extensions/crossroads_tile_render.png

images/custom-extensions/deadend_tile_render.png

images/custom-extensions/straight_tile_render.png

images/custom-extensions/tjunc_tile_render.png

These modular, tiling meshes can then be slotted together and/or rotated to form mazes - this is effectively what the generator does algorithmically. Please see an example of this below.

images/custom-extensions/example_maze_side.png

images/custom-extensions/example_maze.png

images/custom-extensions/example_maze_annotated.png

Importing Custom Geometry

This section will guide you through the process of adding bespoke geometry from an external modelling program into the generator (this specific guide will utilise Blender, but the same principles apply to other modelling suites, such as 3DS Max or Maya). It is assumed that the geometry and textures of your models are in a game-ready state (e.g. Consistent smoothing groups, no overlapping verts, faces etc...). The example tiles used for this guide can be seen below:

images/custom-extensions/demo_expo_tiles.png

The pictured tiles are purposefully basic as to make this guide as generally useful and wide-purpose as possible. From left to right, the tiles are as follows: Straight, Crossroads, Dead-End, T-Junction & Corner.

It is possible to include more than one of each variation of the five basic tile meshes (for example, having two or more corner tiles with varying levels of cosmetic damage, different textures or swapped props etc...). Having additional variation within your tileset can drastically reduce repetition and provide the illusion that content isn't procedurally generated.

3D Software Pre-Export Checklist

Before we start the process of exporting the geometry out of Blender and into Unreal Engine, we need to double check a few things first! Please perform the following on each of your meshes:

  • Ensure the origin of each of your tiles is in the center of the mesh, snapped adjacent to the bottom-most vertex (as per the image below).

images/custom-extensions/origin_example.png

  • Name each tile object in the scene descriptively and appropriately - no "Cube.049" or "tile_TEMP_FINAL". You'll want to include the tile type (straight, crossroads, tjunc etc...), variation number (if applicable) and perhaps a reference to the aesthetic/setting of the tiles.

    • A good example name for a theoretical tile would be desert_tile_crossroads_var02 with a further sub-variation of the tile being named desert_tile_crossroads_var02_crumbled.
  • Measure the width and length of each tile to ensure they are the same across both axes (remember to measure from the longest point across each individual axis). Make sure you jot this number down as we'll need it later when we import the tiles into UE4!

images/custom-extensions/tile_measurement.png

A measurement of the example straight tile (exactly 2.5m across in both the x and y axes).

  • Create a few quick test mazes by snapping different combinations of tiles together (this will help identify any issues with tile geometry early on). If the final maze has no overlapping tiles or gaps, the tiles should be ready for export.

images/custom-extensions/snapping_test.png

An example maze snapping test. All tiles fit together in varying orientations as expected. These tiles are ready for export.

Export Settings

Whilst the export settings used will largely differ depending on modelling suite, it is always important to consider the units your models are based on. 1 Unreal unit (uu) is equal to 1 centimetre (cm) by default - make sure that you're happy with the resulting scale that this causes.

For reference, the image below demonstrates the settings that were used to export the demo tiles out from Blender. Each tile was exported into its own individual .fbx file, however it is also possible to group the meshes into a single file and separate them inside of Unreal Engine.

images/custom-extensions/blender_export_settings.png

Importing into UE4

Navigate into an appropriate folder where you would like to save the tile meshes. A Content/Geometry/Meshes/Tiles directory already exists in the project and will be used for this example (after a new subfolder DemoTiles has been created).

images/custom-extensions/import_directory.png

Once you've navigated to the desired directory, click the Import button and navigate to the folder where you exported your tiles. Select all the tiles and click Open.

images/custom-extensions/tile_import_window.png

If you exported your assets into an .fbx format, Unreal Engine will open an FBX Import Options window as seen below. Feel free to adjust these options as necessary for your assets before clicking Import All.

images/custom-extensions/fbx_import_options.png

Once the import process has been completed, you may wish to check the collision of your tiles (or separately import this from a dedicated collision model). Due to the simple, low poly nature of these example assets, Collision Complexity has been set to Use Complex Collision As Simple.

images/custom-extensions/tile_collision.png

Lastly, ensure that any textures/materials you have created have also been successfully imported, otherwise you may wish to import and apply them separately. The pictured debug tiles do not have dedicated textures or uniform UVs and, as such, will have a very basic material applied.

images/custom-extensions/m_demo_tiles.png

Blueprint Class Setup

Each tile also requires a corresponding Blueprint class to be created (these classes are then referenced inside of a data table and fetched by the generator as required). The easiest way of creating a corresponding Blueprint class is to right-click on each tile, navigate to Asset Actions and select Create Blueprint Using This....

images/custom-extensions/create_blueprint.png

As with the tile geometry from the previous step, you will need to create an appropriate directory to save the Blueprint Classes. For this example, the DemoTiles folder was created in the Content/Blueprints/Tiles directory. Remember to create the class with an appropriate prefix (all Blueprint classes in the project are prefixed with BP_, however you may wish to use a different convention).

images/custom-extensions/create_blueprint_window.png

The newly created classes should look like the image below, with the base mesh of your tile as the root component.

images/custom-extensions/bp_example.png

As these Blueprint classes are the basis of what is spawned at runtime, feel free to add any necessary props, Blueprint code or components as required for your implementation.

Tile Data Table Setup

With the necessary Blueprint classes created, the final requirement is to create a master data table that references these Blueprint tile actors, as well as the values of the walls (i.e. true or false for the north, east, south and west walls of each tile). The images below demonstrate wall values for the PSXStoneTiles bundled with the project.

images/custom-extensions/corner_tile_walls.png

The default corner tile wall values.

images/custom-extensions/crossroads_tile_walls.png

The default crossroads tile wall values.

images/custom-extensions/deadend_tile_walls.png

The default dead-end tile wall values.

images/custom-extensions/straight_tile_walls.png

The default straight tile wall values.

images/custom-extensions/tjunc_tile_walls.png

The default t-junction tile wall values.

In addition to the default states listed above, we also need to keep track of each tile in it's 90°, 180° & 270° orientations (this provides the generator with the flexibility it needs to find a matching tile for any possible tile cell state). The tile data table for the tiles bundled with the project can be seen below for reference.

images/gen-props/tile_data_table_example.png

Tile data tables such as those seen above can be created in two primary ways - automatically via a BP_TileWallIdentifier actor or by manually creating a data table inside of Unreal Engine natively. The automatic method utilises a line trace approach and, as such, may not work universally with all tiles (for example, tiles with non-uniform gaps may cause issues). Documentation on each method has been broken down into individual sections below.

Automated Tile Wall Identification

Before the step-by-step guide begins, please see here for an overview of the specific details of each variable and event related to the BP_TileWallIdentifier class (this is the class responsible for controlling line traces and serialising wall values to file).

Open the TileWallIdentifierScene found in the root level of the project content folder.

images/custom-extensions/tile_identifier_scene_cb.png

After the level has loaded, navigate to the right island titled 'Automated Tile Wall Identifier', as seen below.

images/custom-extensions/tile_identifier_scene.png

Drag each of the tile blueprints into the scene, spacing them out for visual clarity.

images/custom-extensions/tile_bp_scene.png

With the tile actors in the scene, select the BP_TileWallIdentifier and add a reference to each of the tiles in the TargetTileActors array.

images/custom-extensions/target_tile_actors_scene.png

Add an array element for each tile - the example demo tileset has five tiles, so five array elements have been added. Use the Asset Picker eyedropper to reference the tile blueprints.

images/custom-extensions/target_tile_actors_populated.png

A completed list of Blueprint tile references.

Now that the BP_TileWallIdentifier has a reference to each tile, the execute-in-editor events seen above can be called to ensure that the properties of the identifier are correct. The best place to start is typically to call the Preview Line Trace Range With Debug Lines event to ensure that the Line Trace Range is long enough to reach each wall - an example of this is shown below.

images/custom-extensions/debug_lines_example_01.png

An example of the line trace range being previewed with debug lines. Notice how all debug lines comfortably reach the walls of each tile.

If, after performing a debug line test, the default range is too small to reach the tile's walls, increase the value of Line Trace Range and try again. In the case shown above, the default value of 350 is a bit high and whilst this shouldn't cause issues with the wall identification process, the overlapping lines make it difficult to visualise how the system works. The screenshot below demonstrates the results of lowering the Line Trace Range to 200.

images/custom-extensions/debug_lines_example_02.png

After ensuring a valid Line Trace Range has been defined, it is next important to ensure that the value of ZAxis Line Trace Offset is compatible with your tilset. ZAxis Line Trace Offset is used to create a buffer between the origin of your tile geometry and the origin/starting location of the line trace. For example, the Shinto tile below - graciously provided by Sam McMillan for testing - contains gaps in the geometry which would cause a line trace to miss, thereby invalidating the automatically generated wall values.

images/custom-extensions/shinto_example_01.png

The default line trace missing due to a gap in the tile geometry.

images/custom-extensions/shinto_example_02.png

A close-up of the offending gap.

This issue is rectified by increasing the value of ZAxis Line Trace Offset to 130, thereby causing the line trace to hit the support beam of the geometry - this is demonstrated in the screenshot below.

images/custom-extensions/shinto_example_03.png

The resulting effect of adjusting the ZAxis Line Trace Offset value of the BP_TileWallIdentifier.

With an appropriate Line Trace Range and ZAxis Line Trace Offset defined, the Test Line Trace event can be called to perform a test line trace to verify if the correct walls are hit - an example of this is shown below.

Please remember that any existing debug lines that have been drawn to the screen can be flushed by calling Flush Debug Lines(Tile%20Wall%20Identifier%20Properties#flush-debug-lines).

images/custom-extensions/line_trace_example.png

If all line traces performed by Test Line Trace hit the expected walls, the configuration of BP_TileWallIdentifer is complete and the Exported CSV File Name value can be populated. In this case, a value of DemoTiles_250Units has been selected, whereby DemoTiles is the name of the tileset and 250Units is the width/length of the tiles, as per the pre-export checklist. Naming the file this way isn't strictly necessary, however it will greatly help to manage your tile data tables (especially if you have multiple tilesets)!

images/custom-extensions/exported_csv_file_name.png

The final step in the process is to call the Export Values to CSV event. If successful, a CSV file will be output to a TileDataTableCSVs folder in the root of the project directory, as per below.

images/custom-extensions/tile_data_table_csvs.png

The exported CSV file. The 'TILE_' prefix is automatically added.

The tile CSV can now be imported into the project as a data table of data table row type TileDataStructure, as per below. If you wish to use the Blueprint Utility Widget to generate mazes inside of the editor, you will need to import the data table into the Content/Blueprints/TileDataTables directory. Remember to save the asset after importing it!

images/custom-extensions/import_tile_csv_01.png

images/custom-extensions/import_tile_csv_02.png

images/custom-extensions/import_tile_csv_03.png

Please see Using the Generator for details on both the runtime and in-editor generation process. If you have any difficulties with implementing your automatically generated tile data table, your tileset may be incompatible and a manual table may have to be created.

Manual Tile Identification

If your tileset is incompatible with the automatic tile identification process (e.g. your tile geometry doesn't have clearly defined walls, non-uniform gaps etc...), you can manually create a tile data table by following the steps below.

Open the TileWallIdentifierScene found in the root level of the project content folder.

images/custom-extensions/tile_identifier_scene_cb.png

After the level has loaded, navigate to the left island titled 'Manual Tile Wall Assistant', as seen below.

images/custom-extensions/tile_identifier_scene.png

Drag the first tile blueprint of your tileset into the scene, using the transform section of the 'Details' panel to snap the tile to a location of X: 0.0, Y: 0.0, Z: 0.0. Make sure to double-check that the rotation of the blueprint actor is also at a default value of X: 0.0, Y: 0.0, Z: 0.0.

images/custom-extensions/manual_tile_in_scene.png

Navigate to the Content/Blueprints/TileDataTables directory in the content browser and create a new data table of row structure TileDataStructure, as per below. The name of this data table should be TILE_TilesetName_xUnits whereby TilesetName is the name of your tileset and x is the width/length of the tiles, as per the pre-export checklist. For this example, a data table name of TILE_DemoTiles_250Units will be used.

images/custom-extensions/create_data_table.png

The Data Table class can be found under the 'Miscellaneous' tab of the 'Create Advanced Asset' menu.

images/custom-extensions/create_data_table_row_struct.png

The 'Pick Row Structure' box. Tile data tables must be of row structure type TileDataStructure.

images/custom-extensions/tile_data_table_created.png

The newly created tile data table - remember to save your changes to the project!

Once you have created the data table, open it to be greeted with Unreal Engine's data table interface. This is the interface where we'll be adding all of the necessary actor references and tile values. If possible, you may wish to dock this window to another monitor, or leave it floating above the main editor window, as we will be frequently using both.

images/custom-extensions/tile_data_table_window.png

Click the 'Add' button at the top of the data table interface, as seen below.

images/custom-extensions/tile_data_table_window_add.png

Fill the first two fields (TileName and TileActor) with the name of the current tile in the scene, as well as a reference to the respective blueprint class. In the example below, the current tile in the scene is BP_base_tile_corner - this has been given an abbreviated name of base_corner for the TileName field and a reference to the blueprint class has been established within the TileActor field.

images/custom-extensions/tile_data_table_window_01.png

The remaining fields of NorthWall?, EastWall?, SouthWall?, WestWall? and Rotation all refer to the physical properties of the tile in the scene. Use the text labels of the 'Manual Tile Wall Assistant' to act as a guide to the current wall values. For example, the corner tile shown in the screenshot below would have values of NorthWall? = true, EastWall? = false, SouthWall? = false and WestWall? = true, as the geometry of the tile only has walls in the north and west directions. This logic follows the same pattern as that shown in images from the initial Tile Data Table Setup section.

images/custom-extensions/manual_tile_in_scene_anno.png

Finally, the Rotation field is determined by the current value of the Z axis rotation - this should currently equal 0, as we have not adjusted the rotation of the tile yet. With all fields now populated, the first record looks like the following.

images/custom-extensions/tile_data_table_window_02.png

Next, select the tile in the scene and rotate it by 90° in the Z axis, as seen below.

images/custom-extensions/manual_tile_in_scene_rot_90.png

images/custom-extensions/manual_tile_in_scene_rot_90_transform.png

As previously described in the Tile Data Table Setup section, each individual tile needs to be tracked in it's 0°, 90°, 180° & 270° orientations as to provide the generator with the flexibility it needs. With the tile now rotated in the scene, we repeat the previous steps to name the tile, add a reference to the actor and track the wall and rotation values, resulting in the second record entry seen below.

images/custom-extensions/tile_data_table_window_03.png

This process now needs to be repeated two more times with the tile's Z axis rotation being set to 180° and 270°. The screenshot below demonstrates all of the records for the base_corner tile.

images/custom-extensions/tile_data_table_window_04.png

Finally, repeat this process with each of the remaining tiles within your tileset to complete the data table. Please see the finalised data table below.

images/custom-extensions/tile_data_table_window_05.png

You may have noticed that some of the tile data table records contain duplicate wall values; a good example of this is seen in the numerous rotations of the crossroads tile - a tile with no walls will never have walls, regardless of rotation. These duplicate records are not explicitly required and may even increase the computational overhead of the generator (the generator has to iterate over these data tables to find matching tiles and, as such, more data table records = slower generation). The image below demonstrates the tile data table after it has had records with duplicate wall values removed.

images/custom-extensions/tile_data_table_window_06.png

All duplicate crossroads records have been removed, in addition to the 180° & 270° variants of the straight tile.

Please see Using the Generator for details on both the runtime and in-editor generation process.