Assignment Goals

The primary goals of this assignment are:

  1. Do some user-level C++ coding typical of what would be done for a game.
  2. Get some experience tracking down information in a large codebase.

For this assignment, you'll be creating C++ Actor to build a maze using the recursive division algorithm. In your constructor, you will create a USceneComponent as the root component for the actor, and a UInstancedStaticMeshComponent, for the walls. You will override the virtual OnConstruction() function, to run the maze generation algorithm and add the resulting walls to the UInstancedStaticMeshComponent to create the maze.

Random maze in Unreal Editor

Details

I've given some fairly explicit steps to help you along, but this time I'm expecting you to do a little more this time in terms of following and generalizing examples of certain functionality in the engine code or online.

Create a project

  1. Create a Basic C++ project with no starter content called assn2
    • Put it in top level of your git repository (alongside the Engine directory and your assn1 project directory).
  2. Create a level/map called assn2 and set it as your startup map.

C++ actor

  1. Create a new C++ actor
    • Since the C++ Classes directory won't be visible in the Content Drawer yet, you can do this from the Tools menu.
    • UE5 will create a header and C++ file with starter code. Make sure the class it creates is derived from AActor.
  2. Test that you can drag it into the scene. Look in the "World Outliner" window to make sure it's there.
  3. You can get rid of the BeginPlay() and Tick() functions that it creates for you, since we will not be using them.

Testing and Debugging tips:

At the top of your file, add

#ifdef __clang__
#pragma clang optimize off
#else
#pragma optimize("", off)
#endif

Then at the end of the file, add

#ifdef __clang__
#pragma clang optimize on
#else
#pragma optimize("", on)
#endif

Add a SceneComponent and StaticMeshComponent

The SceneComponent allows the actor to be placed and dragged around the map. A mesh component allows the Actor to be rendered in the scene. We'll start with a UStaticMeshComponent, which renders a single mesh before moving on to the UInstancedStaticMeshComponent.

  1. Create a USceneComponent for actor placement, and set it as the RootComponent.
    • Search all of the Engine cpp files for RootComponent for examples.
    • Some, like PaperTerrainActor.cpp, assign directly into the RootComponent member variable.
    • Others, like ControlRigActor.cpp use the SetRootComponent() member function.
  2. Add a single UStaticMeshComponent* as a new member variable, tagged with the UPROPERTY() macro so it'll be serialized.
    • Manipulator.h and Manipulator.cpp are one example of this, though it does more than you need: you really only need the FObjectFinder, SetupAttachment, and SetStaticMesh.
    • I suggest initially loading a sphere as your mesh, since you don't need to worry about having a two-sided material on it like you do with a plane. In the "Content Drawer" window, you can select "Settings" and enable "Show Engine Content". Then in the "Engine" content folder search for "Sphere". Find the 100x100x100 one from Engine/BasicShapes, right click, and choose "Copy Reference" to get the name to paste in your code in the call to FObjectFinder()
    • You don't really need to worry about setting a material at this stage. If you don't, you'll get the default grid material, which is fine for testing.

Make the Material changeable

  1. Add a UMaterialInterface* member to your actor class, tagged as UPROPERTY(EditAnywhere). It'll show up in the detail window, and you'll be able to drag a material into it.
  2. Override the OnConstruction() virtual function and use MeshComponent->SetMaterial() to change your material. The manipulator example uses a dynamic material, which allows run-time animated changes to material parameters. We don't need that, so plain SetMaterial() is fine.
  3. Change the material to something two-sided (in the pictures here, I made a simple one based on Absolute World Position so the material continues seamlessly from wall to wall).

Fixed-sized Grid of walls

To render many copies of the same mesh, we'll want to switch to a UInstancedStaticMeshComponent. This has a TArray of FInstancedStaticMeshInstanceData, each with a transform providing the position and orientation for that instance.

  1. Switch from the UStaticMeshComponent* to a UInstancedStaticMeshComponent*.
    • You'll need to add at least one instance using AddInstance() to see anything.
    • The FTransform argument to AddInstance is relative to the actor location. The default FTransform() will put the instance exactly at the actor location.
    • Don't forget to ClearInstances() before adding new ones in your OnConstruction() function, or you'll get an ever-growing set on top of each other.
    • If you don't find good examples in the engine source for the UInstancedStaticMesh class (I didn't find any great ones), look at the header that defines it to see what data and member functions are available.
  2. Once you have one the instanced static mesh working, make sure you can place them on the maze grid.
    • Change the shape to the 100x100 BasicShapes Plane
    • The BasicShapes plane is aligned with the X/Y plane. To use it for your walls, you'll need to use a 90 degree rotations to orient it vertically along the X/Z or Y/Z planes, and translations that are multiples of 50.
    • Use the FTransform constructor that takes an FRotator to set the instance orientation and FVector to set the instance position. You'll find those in the templated TTransform base class. The arguments to FRotator are the same as the Rotation in the transform panel, though confusingly not in the same order.
    • I suggest creating helper functions to place a vertical or horizontal wall that handle the necessary rotation and translation for the wall placement.

grid spacing grid

Make your maze

  1. Implement the maze algorithm in your OnConstruction() function.
  2. Add an integer seed property to your class, visible in the editor, that you can change to generate different mazes (or re-create a good previously generated maze).
    • Use a FRandomStream for these random numbers.

Grad Students

Instead of just using the plane object for the wall, make it EditAnywhere and changeable in the actor Detail panel, with the plane as the initial default. Change the grid spacing to fit the size of whatever wall object is set.

Submission

For full credit, you must commit multiple times, showing your incremental development processs.

Capture images of your maze using several different seeds. You can use the windows or mac screen shot tools, or EditorScreenShot console command. Make sure your screen shots are from a view from above showing the full maze.

Add an assn2.txt to the top directory. Tell us what works and what doesn't, and anything else you think we should know for grading. Include links to your screen shot images and make sure they are accessible to both the instructor and grader.

Push to your repository, and tag your final commit with an assn2 tag.