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 UE4's base class for anything that can be placed in a scene. Your actor will build a maze using the growing tree 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 UE4 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
    • Do this in the editor from the File menu.
    • 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.
    • UE4 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. Add 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 "View Options" 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 Mesh and Material changeable

  1. Add UStaticMesh* and UMaterial* members to your actor class, both tagged as UPROPERTY(EditAnywhere). They'll show up in the detail window, and you'll be able to drag a mesh and material into them.
  2. Override the OnConstruction() virtual function and move the SetStaticMesh call into it to set your UStaticMeshComponent to use the mesh set in the editor. You should be able to change between different meshes.
  3. Use UMeshComponent::SetMaterial() to also be able to change your material. The manipulator uses a dynamic material, which allows run-time animated changes to material parameters. We don't need that, so plain SetMaterial() is fine.
  4. Change the mesh to a plane (or other suitable mesh to use as a wall) and 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 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.
    • 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.
    • Find an FTransform constructor to set the instance rotation (aka orientation) and translation (position offset from the root component).
    • If you use the 100x100 BasicShapes plane for your walls, you'll need to use a 90 degree rotation around X or Y to orient it, and translations that are multiples of 50.
  2. Once you have one plane working, build a grid of them.
    • It is fine for this assignment to make the grid and maze a fixed compile-time constant size.

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. Implement the maze algorithm in your OnConstruction() function.

Grad Students

Undergrads can use a fixed compile-time constant size for the maze (and thus allowing fixed size array data structures). Grad students should make the maze size changeable in the editor as a UPROPERTY(EditAnywhere).

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.