A Minecraft-style voxel world built from scratch in Unity. Features include procedurally generated terrain and caves, chunk streaming around the player, mesh generation and block breaking and placing in first person.
Academic project - Interaction in Virtual Environments (Interação em Ambientes Virtuais), BSc in Computer Science and Multimedia Engineering (LEIM), ISEL.
Terrain. A surface height per column comes from fractal Brownian motion, consiting of NoiseUtils.FBm). A second, volumetric pass samples a pseudo-3D Perlin field (NoiseUtils.Perlin3D) and compares it against a density threshold, which lets the surface produce overhangs rather than a pure heightmap.
Caves. Two mechanisms, applied after the terrain exists. GenerateCaves carves any block whose 3D noise value crosses a threshold, producing scattered pockets. GenerateWorms walks a path through the volume, hollowing a radius around each step, producing connected tunnels. Cave walls are then re-textured so exposed stone reads correctly, and dirt with air above it becomes grass.
Water. Fills to sea level, and is meshed separately from solid blocks.
Each chunk constitutes a single mesh. Chunk.DrawChunk walks the
Face UVs are looked up per block type from a texture atlas (Block.GetUVs), so the whole chunk renders in a single draw call with one material.
WorldManager keeps two dictionaries keyed by chunk coordinate: activeChunks and sleepingChunks.
Update compares the player's chunk coordinate against lastPlayerChunk and does nothing if they match.
On a boundary crossing it stops the running build coroutine, calls RemoveDistantChunks, and starts BuildChunks on the new set. Generation runs as a coroutine, so chunks are built across several frames instead of in one hitch; cancelling it stops a fast-moving player from queueing work for chunks already out of range.
RemoveDistantChunks applies two thresholds. Past render distance, a chunk has its MeshRenderer disabled and its mesh cleared, then moves to sleepingChunks — the GameObject and its collider remain, so terrain that is no longer drawn is still solid. Past renderDistance * 3, the object is destroyed.
UpdateChunkVisibility runs every frame: it builds an AABB per active chunk, tests it against the camera frustum with GeometryUtility.TestPlanesAABB, and sets MeshRenderer.enabled from the result. Chunks behind the camera stay loaded but are not submitted for rendering.
A raycast from the camera highlights the targeted block. Breaking and placing modify the chunk's block array and trigger a remesh; when the edit sits on a chunk border, the neighbouring chunk is redrawn too, so the seam updates. A hotbar UI tracks the selected block type.
Config.cs holds the world constants — chunk size 16, chunk height 64, render distance 3, world height 256, sea level 12. Per-chunk generation parameters (noise scale, octaves, density threshold, cave scale, worm count) are serialized fields on Chunk, tunable from the Inspector without recompiling.
Assets/
├── Scripts/
│ ├── Aula1/GenerateCubes.cs # first experiments (1 GameObject per cube)
│ ├── Aula2/Block.cs # block type, face vertices, atlas UVs<>
│ ├── Aula2/Chunk.cs # generation, texturing, meshing, collision
│ ├── Aula3/NoiseUtils.cs # fBm and pseudo-3D Perlin
│ ├── Aula3/WorldManager.cs # chunk streaming, culling, lifecycle
│ ├── Aula4/CaveGenerator.cs # noise carving and worm tunnels
│ ├── Aula5/BlockInteraction.cs # raycast, break, place, neighbour redraw
│ ├── Config.cs # world constants
│ └── HotbarUI.cs, HotbarSlot.cs # block selection UI
├── Materials/, Shaders/, Sprites/ # block atlas and rendering
├── Prefabs/, Scenes/, Settings/
└── StarterAssets/ # Unity's first-person controller
The AulaN folders follow the course's lesson progression.
Open the project in Unity 6000.3 (URP) and play the main scene in Assets/Scenes. WASD to move, mouse to look, left click to break, right click to place, scroll to change the selected block.
Group project for Interaction in Virtual Environments (Interação em Ambientes Virtuais), BSc in Computer Science and Multimedia Engineering (LEIM), ISEL - DEI, 2025/26. Supervised by Prof. Arnaldo Abrantes.
- Lucas Filipe (51793)
- Bruno Pereira (51811)