PROCEDURAL GENERATION SYSTEM

Project Type : University/ Solo Project

Software Used : Unity, JetBrainRider

Languages Used : C#

Context

P.T.W.D is a project I developed during my second year, and it marked a significant milestone in my programming journey. It was through this project that I was introduced to foundational programming concepts, especially Object-Oriented Programming (OOP) and design patterns. Working on it helped me strengthen my skills in writing efficient and well-structured code while gaining a deeper appreciation for best practices. 

Procedural Generation System Structure

The data structures in the project serve as the foundation for managing terrain meshes within the system. They are designed to efficiently organize and store essential information, ensuring the system operates smoothly. Separating the different structures allowed me to adheres to the Single Responsibility Principle (SRP) resulting in a modular, maintainable, and scalable architecture.

 

UML diagram of the Mesh Data Structure

This UML diagram represents the basic structure of the Mesh Data, it was a part of my design process.

The following code demonstrates the structure of the MeshData.

The renderers in the project play a crucial role in bringing the world to life by turning data into visible elements. They work by taking structured data, such as mesh information and chunk details, and transforming it into something that can be displayed on the screen.

UML diagram example relationShips Data Structures and the Chunk Renderer

This UML diagram represents the relationship between the Renderer and the MeshData and ChunkData structures . 

The following code demonstrates how the renderer uses the data structures.

The Generators are here in order to create diverse terrains. Each generator focuses on a specific aspect of the terrain generation , ensuring modularity and adherence to the SRP principle. They allow to produce biomes, terrains and trees dynamically.

UML diagram relationShip Data Structures and Renderer and Generator

This UML Diagram represents the relationship between the ChunkData and  TerrainGenerator.

This part of the TerrainGenerator Script represents how the Chunk data is used in order to generate the terrain.

Design Patterns and Techniques used to improve the System

Why did I implement these techniques?

During the development of the system, I encountered issues with performance. The FPS were low, and the game lagged whenever the player moved to a new edge of the terrain, requiring the system to process chunk generation. After conducting research, I discovered that the root cause was that all the chunk generation processes were being executed on the main thread. This created a bottleneck, as the main thread was overwhelmed.

The way to solve this issue

The solution to this issue was to implement multi-threading for the intensive computations and coroutines for the rendering. By doing so, I enabled real-time terrain generation and rendering without impacting gameplay performance.

Flow chart of the Coroutine and the Threading

This flowchart illustrates how multithreading and coroutines collaborate within the system. It provides a clear overview of the process flow, helping me better understand how they work together effectively.

These methods are parts of the coroutine and threading process

Coroutine

Task

Why did I implement this design pattern?

To improve the system’s performance, I implemented the Object Pooling design pattern. Initially, all chunks in the world were rendered simultaneously during gameplay, which led to significant performance issues. By adopting this pattern, I optimized chunk rendering by activating and displaying only the chunks near the player, while disabling and returning distant chunks to a reusable pool. This approach significantly reduced memory and CPU usage, resulting in smoother gameplay and enhanced overall performance.

Flow chart Explaining the Design Pattern

The following code demonstrates the structure of the worldrenderer which helps the world class in order to use the object pooling.

Why did I implement this design pattern?

I implemented the Chain of Responsibility design pattern to make voxel generation for procedural terrain more efficient and organized. This pattern sets up a pipeline of handlers, where each one is responsible for generating a specific voxel layer, like the surface, underground, or bedrock. If a handler can’t process a particular voxel, it passes the task to the next one in the chain. This approach made my code more reusable, easier to scale, and better aligned with the Single Responsibility Principle by keeping each handler focused on its specific task.

Flow chart Explaining the Design Pattern

The following script shows how the layers work : 

Add Your Heading Text Here

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.

Download link Project Files and Build for the game