This project implements a procedural voxel-based world using chunked terrain and dynamic tunnel generation. It combines:
- Voxel terrain (3D grid of blocks)
- Procedural tunnel carving
- Runtime chunk streaming
- Player interaction (movement, shooting, destruction)
- Mesh generation and collision updates
The system is designed for a forward-moving game loop, where terrain is generated ahead of the player and removed behind them.
| System | Responsibility |
|---|---|
World |
Chunk streaming and world management |
Chunk |
Voxel data + mesh generation |
TunnelPath |
Procedural tunnel path generation |
ShipController |
Player movement & gameplay |
Projectile |
Terrain interaction (destruction/painting) |
PlayerHealth |
Health system |
Collectible |
Score/health pickups |
public enum BlockTypeDefines the material of each voxel:
| Type | Description |
|---|---|
| Air | Empty space |
| Dirt / Grass / Sand | Surface materials |
| Stone / DeepStone | Underground layers |
| Burned | Modified by projectiles |
| Obsol | Reserved/unused |
Each chunk is a 3D voxel grid:
16 x 64 x 16 (X, Y, Z)- Stored as:
BlockType[,,] blocks;GenerateBlocks()- Fills voxel grid
- Carves tunnels using
TunnelPath - Assigns materials based on height layers:
- Bottom → DeepStone
- Middle → Stone
- Top → Dirt
GenerateMesh()- Converts voxels → mesh
- Only renders visible faces
- Uses:
- Vertices
- UVs (texture atlas)
- Triangles
Optimization:
- Faces between solid blocks are not generated
UpdateCollider()- Rebuilds
MeshCollider - Called after terrain modification
- Tunnel connections (geysers)
- Collectibles
- Stored in:
List<GameObject> registered;Used for cleanup when chunk unloads.
This system generates continuous, branching paths in 2D (X,Z plane).
- Starts at origin
- Moves forward step-by-step
- Slight random turning:
ApplyRandomTurnWithClamping()- Direction constrained by:
maxDeviationBranches are created probabilistically:
branchChance
branchAccumulatorBranch lifecycle:
- Spawn from main path
- Diverge away
- Travel minimum length
- Curve back
- Rejoin main path
Uses grid-based spatial binning:
Dictionary<Vector2Int, List<Vector2>> nodeBuckets;Purpose:
- Fast distance queries
- Avoid checking entire path
DistanceSqToPath(Vector2 point)- Computes shortest distance from point to path
- Used by
Chunkto carve tunnels
In Chunk.GenerateBlocks():
bool carve = distSq < tunnelRadiusSq;If true then block becomes Air
Handles dynamic chunk loading/unloading.
playerChunkX = floor(player.position.x / chunkSize)Within:
renderDistance- Destroy chunk GameObject
- Destroy registered objects (collectibles, effects)
layerX.EnsureLengthUpTo()Guarantees tunnels exist ahead of player.
Handles all player gameplay.
- Physics-based (
Rigidbody) - Forces applied:
rb.AddForce()- Speed clamped to
maxSpeed
Player moves between 3 fixed heights:
| Layer | Height |
|---|---|
| Top | 55 |
| Medium | 33 |
| Bottom | 11 |
Controlled by:
P→ go up (if near geyser)O→ go down (if space available)
- Yaw:
A / D - Tilt: visual banking effect
HandleShooting()- Left mouse button
- Spawns projectile prefab
- Applies forward velocity
Fire modes:
- Default
- Marker
- Large
- Creative
HandleVoxelCollision()- Detects impacted voxel
- Removes spherical region
- Spawns debris
- Updates mesh + collider
Score = distance + destroyedBlocks - crashes + collectiblesHandles terrain interaction.
| Mode | Effect |
|---|---|
| 0 | Small destruction |
| 1 | Destruction + burned shell |
| 2 | Paint (Sand blocks) |
For a sphere:
x² + y² + z² <= r²- Iterates through voxel cube
- Applies modification
- Explosion prefab
- Sound
- Physics impulse
- Continuous health drain:
TakeDamage(Time.deltaTime)-
Damage sources:
- Shooting
- Collisions
-
Healing:
- Collectibles
Types:
- Score bonus
- Health boost
Behavior:
- Rotates continuously
- On trigger:
- Adds score OR heals player
- Destroys itself
- Smooth follow using:
Vector3.Lerp()- Maintains:
- Height offset
- Z offset
- Chunk-based world streaming
- Face culling (only visible faces rendered)
- Spatial hashing for tunnel queries
- Coroutine-based chunk generation
- Non-alloc physics queries (
OverlapSphereNonAlloc)
RuntimeMetrics.Record(...)Tracks:
- Mesh generation time
- Collider updates
- Memory usage (commented)
- Player moves forward
- World streams new chunks
- Tunnels generated ahead
- Player:
- Navigates tunnels
- Shoots terrain
- Collects items
- Terrain updates in real-time
- Score increases over distance
- Infinite tunnel generation
- Smooth branching and merging
- Real-time voxel editing
- Immediate mesh rebuild
- Multi-height navigation system
- Combines:
- Procedural generation
- Physics gameplay
- Mesh-based rendering
- Replace cube meshing with Marching Cubes algorithm for higher visual fidelity of the terrain.
- Implement:
- LOD system
- GPU mesh generation
- Add:
- Biomes
- Noise-based terrain variation
- Optimize collider updates (partial updates instead of full rebuild)
This project demonstrates a complete pipeline for:
- Procedural world generation
- Real-time voxel manipulation
- Dynamic streaming environments
- Integrated gameplay systems
It may be utilized as a strong foundation for:
- Voxel games
- Endless runners
- Destructible environments
Special thanks to Dr hab. sztuki inż. arch. Rafał Szrajber, prof. uczelni for both providing the idea for the game concept, and guiding the creation of the thesis, to which this project is attached.