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, which is UE5's base class for anything that can be placed in a scene. Your actor will build a maze using the recursive backtracking algorithm. In your constructor, you will create a USceneComponent as the root component for the actor, and a UInstancedStaticMeshComponent, for the walls. In the OnConstruction() function, you will run the maze generation algorithm and add the resulting walls to the UInstancedStaticMeshComponent to create the maze.

maze

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. This time, create a Basic Code 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).
    • Code projects placed there will be picked up by GenerateProjectFiles and show up when you open UE5 in your IDE
    • If you run the assn2 game project from Visual Studio/XCode, you can break and debug either in your code or in the engine code.
  2. Create a level/map called assn2 and set it as your startup map.

C++ actor

  1. Create a new C++ actor
    • You can do this from the Tools menu, or in the Content Drawer using the Add button or right-click in the content pane when the C++ Classed directory is selected.
    • Be careful, an Actor is different from an Actor Component. An Actor is an object that can be placed in a scene, while an Actor Component can be added to an Actor to add some common behaivor.
    • 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

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. This will allow you to drag the position of the actor around in the map.
    • See Manipulator.h and Manipulator.cpp for an example of an actor that creates a USceneComponent and UStaticMeshComponent
  2. Add a single UStaticMeshComponent as a new member variable, tagged with the UPROPERTY() macro so it'll be serialized.
    • 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" 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 UMaterial* 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 UMeshComponent::SetMaterial() to be able 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. At this stage, I'd suggest hard-coding the size.

  1. Change the shape to the 100x100 BasicShapes plane
  2. Switch from the UStaticMeshComponent to a UInstancedStaticMeshComponent.
    • You'll need to add at least one instance using AddInstance() to see anything.
    • Don't forget to reset the array before adding instances 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 this component class (and you won't), look at the header that defines it to see what data and member functions are available.
    • Use the FTransform constructor that takes an FRotator to set the instance orientation and FVector to set the instance position.
    • 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.
  3. Once you have one plane working, build a grid of them.

grid spacing grid

Make your maze

  1. Create a maze data structure
    • You can save yourself a lot of boundary testing if you give your grid data structure a one cell border and mark the border cells as already visited before you start.
    • You can carve yourself an entrance and exit by marking a passage between two maze cells and their adjoining border cells
  2. Change your grid construction to only add the walls needed for the maze.
    • In your grid drawing loop, you can just check if your maze data structure has a wall there before adding it to the instance array
  3. 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).
    • You can use FRandomStream for random numbers.
  4. Add two integer size properties to your class, visible in the editor, that you can change the dimensions of the generated mazes.
    • C++ does not have dynamic 2D arrays, you will either need to do the indexing math into a dynamic 1D array by hand, or build yourself a new dynamic 2D array class.
  5. Implement the maze algorithm in your OnConstruction() function.

Grad Students

Instead of square grids, make your maze using triangular or hexagonal cells.

Submission

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

Capture and commit 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.

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