You will have to show what you have learned, by reusing multiple patters we have covered along the course. There is no base code, but you are free to reuse any assets and code from your previous solutions. Especially useful are the Switch, Movement and Factory solutions. You may implement your own version as well.
In this task you will have to create a procedural dungeon. The floors and walls of the map should be endlessly generated, loaded and unloaded based on the positions of player characters. New chunks of the map should be generated lazily - a new chunk is generated when player goes near it.
There will exist two different representations of the world map. The first is the generated version held in a class responsible for the map. In this version the tiles are represented only by their type, location and prefab name. It is recommended to make these objects plain old C# classes. This version of the map is permanent until the game is closed. The second version of the map consists of Unity GameObjects with graphics and all other relevant components. Tiles for the second version are loaded into the game by instantiating prefabs and unloaded when the player is more than 1 chunk away. When revisiting a previous location the GameObject version of the map has to be reloaded based on the map data. You can reuse the map and tile classes from factory tasks base package or create your own solution.
An example of what loading and unloading chunks near the player could look like:
Your solution to the task is graded based on the following criteria:
It is actually possible to implement observer pattern in C# with only a few lines of code due to some more advanced language features. Here is example how you would hook up an observer to a player movement script. The example sends Vector3 data to the observer, but you might want to consider sending a reference to the player object instead.
In the class that handles player movement create an Action. Defining an observable event(i.e. subject):
public event Action PlayerMoved; //Methods with a single Vector3 parameter can be attached to this action
In the class observing player create the method that handles any new information from the subject.
public void HandlePlayerMoved(Vector3 position) { /* Here we could handle loading and unloading the map chunks */ }
In the class observing player attach a method to the action. This would be the class responsible for deciding when to load or unload chunks. Attaching methods to the observed event(i.e observers):
player.PlayerMoved += HandlePlayerMoved; //Attaches the method with name HandlePlayerMoved to the action.
In the class responsible for player movement notify all listeners when a movement occurs.
PlayerMoved?.Notify(position); //Avoids sending when there are no listeners
First thing you will need is a class that will take on this responsibility. I've decided to name this as World. Your designs may wary.using UnityEngine;
public class World : MonoBehaviour
{
}
This class should be a monobehaviour, as it will need to listen on messages from the game engine and take action. Other responsibilities of this class may be:
With the general idea in mind of where we are going to keep track of terrain data, we can now decide on how to do it. Again your designs may wary, but one option is to use a Dictionary. The data to be kept there would be the map chunks. Each tile in turn can be within the mapchunk, so we can rest assured that all tiles will be kept track of. The obvious choice for keys of the dictionary would be the positions of chunks.
Benefits of dictionary:
This now only leaves us with the question of keys. As a dictionary has a single key and we have coordinates on two axis, then we need to encapsulate them somehow. Here again we have many different options and your designs can wary. For example we could go with a Unity specific approach and used the Vector2Int as a key.
private Dictionary<Vector2Int, Chunk> chunks = new Dictionary<Vector2Int, Chunk>();
Other notable options would be:
The instructions in this part are not mandatory to follow. You can make up your own solution to the requirements. This section will be expanded based on your questions.
How to stop characters from walking into the walls?