Assignment 4: Minecraft
Due: Apr 16, 2026
This page requires JavaScript to display formulas correctly.
Overview
In this project, you will implement a procedural walking simulator inspired by minecraft. You will implement CPU-side procedural terrain generationa and gameplay with GPU-side on-the-fly procedural texture synthesis using Perlin noise.
Groups
You have been divided into four groups; each group will collaboratively build a single game. I have subdivided each group into four teams responsibility for one specific aspect of the project (see below). Group and team assignments are here.
Getting Started
Installing TypeScript
If you already installed TypeScript during the previous projects, you can skip this section. But if you need a reminder of how to install TypeScript on a new machine:
This project is written in TypeScript and will run in your browser. Before you can download and run the starter code, you will need to install the TypeScript compiler, and a minimalist HTTP server that you can run on your machine and that will serve up the Javascript to your browser.
- First, install the NPM package manager. You can get it here.
- Install the TypeScript compiler
tsc, andhttp-server. If NPM is properly installed, you should be able to do this installation using the commands (might require an Administrator Mode command line on Windows):npm install -g typescript npm install -g http-server npm install @types/offscreencanvas
If the installation is successful, you should be able to run tsc and http-server on your command line (both programs should have been added to your PATH).
WebGL 2.0
This project makes use of several features such as instanced rendering that became available with the release of WebGL 2.0 in 2022. Recent versions of all major browsers support WebGL 2.0, but if you are using a very old machine you may need to update your browser to run the reference solution and starter code.
Starter Code
We have provided you with starter code, written in TypeScript and using WebGL, as a starting point for implementing this project. This code already implements a 3D GUI with mouse and keyboard camera controls (updated to only allow walking horizontally, and with more responsive keyboard controls). I've also given you some starter code for drawing multiple randomly-placed cubes using instanced rendering (look down at program start to see the cubes).
Build
I have written a script for you (make-minecraft.py) which invokes the tsc compiler on the TypeScript source files in src/, and does some post-processing to combine the Javascript files with static files necessary for the web site to function. The complete web package is installed in dist/. Do not directly edit the files in dist/; all development should take place in the source files in src/.
To compile the code, run make-minecraft.py from the folder that contains it (the project root folder).
Running the Code
To run the starter code, first, you need to launch the HTTP server. From the project root directory (containing dist/), run:
http-server dist -c-1
If successful, you should get a confirmation message that the server is now running. You can then launch the app by pointing your browser to http://127.0.0.1:8080.
Submission Instructions
All submissions must be uploaded to Canvas by the project due date and time. Only one group member needs to upload the assignment (but see above about the Collaboration Report). In exceptional cases where Canvas is not working, you may turn in the assignment by emailing the TA a Dropbox link, handing him a USB stick, etc, but there will be no exceptions to the project due date--late submissions will be penalize per the syllabus late policy.
Submit your project as a single .tgz or .zip file. In addition to your complete source code, the .tgz file should include a README file containing, at minimum:
- Your name and the name of your teammate (if any);
- An explanation of any required features that you know are not working completely correctly, but would like to have considered for partial credit;
- Anything the milestone-specific specs below ask you to include.
Tip: after uploading your files to Canvas, it is not a bad idea to download them onto a different computer and check that it still compiles and runs, in case you accidentally leave out some important files from the .tgz. It is also wise to try to upload to Canvas well ahead of the final deadline: again, there will be no exceptions to the lateness policy, not even for technical difficulties.
(Important) Technical Details of Grading
The submitted package should include everything needed to build your project, including
make-minecraft.pyand everything insrc/. We will dock points on submissions that do not build correctly "out of the box."Do not include the contents of
dist/,.gitfolders, or other extraneous files in your submission.
Required Features
Each group has been divided into four teams, with each team loosely responsible for one aspect of the project. It is up to your group to coordinate on how to integrate your contributions together into a working demo and testing the entire system; I recommend planning which set of minimal viable features each team needs from the others to do their own work and then iterating in coarse-to-fine manner on the final feature set.
A minimal demo of terrain synthesis and player physics that you can use for reference can be found here. (Wait a few seconds for the player to drop from the sky.)
Terrain Team
You are responsible for procedurally generating the 3D world, both at program start and on demand whenever the player travels away from their original spawn point.
Cube Landscape
You will procedurally generate a voxellized world out of axis-aligned unit cubes. For this project, terrain is a height field over the \(xz\) plane; you do not need to generate caves or overhangs. In principle, terrain extends infinitely in all horizontal directions; obviously you cannot precompute the terrain geometry in the same way you precomputed the Menger sponger, and instead you will need the ability to generate terrain on the fly as the player moves around your world. To do this, cut up the \(xz\) plane into a grid of \(64\times 64\) chunks.
In the starter code, I've written a Chunk class for you that represents one \(64\times 64\) patch of terrain. The starter code creates a single chunk centered at \((0,0)\) and populates it with cubes at random heights. Please refer to this code for an example of how to generate pseudorandom numbers given a seed in TypeScript, and how to draw multiple cubes using instanced rendering (described below).
Given a random seed, generate a \(64\times 64\) patch of terrain heights. Use at least three octaves of value noise to produce a correlated noise pattern, with both global (mountains and valleys) and local terrain features. Recall that you can create a low-frequency noise octave by generating a small patch of white noise and then repeatedly upsampling it using bilinear interpolation. Tune the precise parameters of the noise function to your taste; you will receive full credit so long as the patch contains correlated noise of various frequencies with heights in the range \([0, 100]\).
These chunks should meet seamlessly at their common boundaries (as if the terrain was a single chunk of size \(192\times 192\)). Guaranteeing seamlessness is probably the trickiest engineering challenge in this assignment: I recommend carefully whiteboarding your planned solution with your partner before starting the implementation, to avoid wasting time extensively refactoring a failed approach.
Cube Types
Use 3D Perlin noise to vary the cube type within your chunk. Create ore veins, caves, etc. Also add lakes and ponds (of water and/or lava) to your terrain synthesis algorithm.
Biomes
As the player travels long distances across the game world, the terrain should transition through different biomes, with varying terrain roughness and block types in different biomes.
Object Placement
Procedurally place grass, shrubbery, rocks, trees, enemies, and other non-cube objects in your world.
Rendering team
You are responsible for visualizing the 3D world.
Cube Rendering
Since this project requires rendering large numbers of cubes with identical geometry, it is an ideal use case for instanced rendering. Instead of sending the GPU the geometry of each individual cube, in instanced rendering you send only one template cube, along with a list of transformations specifying how that one cube should be translated to draw all cubes in the scene. Since you do not need materialize the terrain geometry in RAM, and only need to send a translation vector for each cube rather than a full array of vertices and triangle indices from the CPU to GPU, instanced rendering is a significant performance win. Instanced rendering recently became available as part of the WebGL 2.0 standard, and a tutorial is available here.
I have modified the RenderPass to support instanced rendering. The function drawInstanced(ncopies) tells the GPU to draw a triangle mesh ncopies different times. On its own this is not useful, since all copies will be drawn on top of each other; to pass per-instance data to the GPU, add an instanced vertex attribute buffer using the new function addInstancedAttribute(). Unlike regular attributes, where the vertex shader receives a different attribute value for each different vertex of the object, each vertex in an object instance will receive the same instanced vertex attribute value, with vertices in different object instances receiving different values. For example, the starter code draws 4096 cubes with a single drawInstanced() call. In addition to regular attributes, the starter code passes an array of 4096 per-cube translation vectors in the aOffset instanced vertex attribute buffer. All vertices of the first cube will receive the first translation vector as their aOffset input, all vertices of the second will receive the second, etc.
Turn your patch of height values into a set of cubes drawn by the GPU using instanced rendering. For each chunk grid cell, draw a stack of cubes of the appropriate height. While conceptually each stack extends infinitely downwards, you should only draw cubes that might be visible to the player.
If implemented properly, your application should have no trouble rendering up to nine chunks of terrain in real time on a decently modern machine. Please compare against the reference solution to gauge if your performance is reasonable.
Implement different procedural textures for each cube type in the generated terrain, using Perlin noise as a building block. You can combine Perlin noise with standard mathematical functions, as in the sine examples from class, or combine multiple octaves of Perlin noise. To be clear, you should not materialize a literal texture in GPU memory: implement a shader function which computes a color given \(uv\) coordinates and a random seed, and then use that color to shade pixels in a fragment shader (using a mix of diffuse shading and ambient lighting).
Skybox
Use 3D Perlin noise to equip the world with a procedurally-generated skybox. The skybox should be free of visible of distortions at the corners and edges.
Day-Night Cycle
Implement a day-night cycle. The light location and color and world ambient color should change during the course of the cycle.
Object Rendering
Visualize grass, shrubbery, rocks, etc. and other decorative elements generated by the terrain team with simple billboard sprites. For trees, you may either use sprites, or collaborate with the terrain team on rendering fractal trees generated using L-systems.
Shadows
Implement your choice of ambient occlusion, shadow mapping, or shadow volumes to equip your world with realistic shadows.
Physics Team
You are responsible for maintaining the 3D world and implementing support for player-world interaction.
Dynamic Chunk Loading
As the player moves around the world, dynamically synthesize and delete chunks of terrain so that there is always a \(3\times 3\) chunk region around the player. Again, chunks should meet seamlessly at their boundaries: coordinate with the terrain team to ensure the generated terrain meets this requirement. Your terrain should also be persistent: if the player visits a chunk, moves far away, and then comes back, the chunk should appear the same on the first and second visits.
The R key should return the player to their starting position and reset the chunks being rendered accordingly.
A minor performance stutter is acceptable when the player crosses a chunk boundary and your code generates new terrain, especially on older machines; again, use the reference solution as a benchmark for reasonable performance.
If the player walks back and forth across a chunk boundary or around a chunk corner, performance will significantly degrade, as chunks are repeatedly created and then immediately destroyed. Add some hysteresis logic to chunk creation and destruction to fix this issue.
Collision Detection
Treat the player as a cylinder of radius \(0.4\) that extends two units downward from the camera. Do not allow the user to move in any way that would cause this cylinder to penetrate into a cube: ignore user input during any frame when that input would cause a collision. Hint: you will need to augment your Chunk class with logic for determining the minimum vertical position allowed for the cylinder at a given location on the \(xz\) plane. Just checking the height at the player's current grid cell is not enough, because the cylinder might also overlap one of the cell's eight neighbors. You will need to consider carefully how to fully detect all cases of potential collisions, including at chunk boundaries.
For full credit, the player should not ever "fall through" the world geometry or get stuck partially or wholly inside a cube.
Gravity
Maintain a vertical velocity for the player, and accelerate the player downwards at \(9.8\) units per second squared any time the player's cylindrical body isn't supported by a block underneath. Obviously, your collision detection must prevent gravity from causing penetrations into terrain. Reset velocity to zero whenever the player "lands" on terrain.
Mining and Crafting
Allow the user to remove blocks from the world and place them elsewhere. The changes to the landscape should persist, even if the player moves far away from a chunk and later returns. Coordinate with the gameplay team on the mining and crafting UI.
Blocks that are fully undermined should fall, and similarly you should prevent the player from placing blocks that hover in mid-air. Implement some reasonable logic for block physics.
Fluid Physics
Add some simple fluid physics for updating the water or lava blocks in your 3D world. Fluids should flow from higher elevation to lower over time.
Gameplay Team
You are responsible for player interaction with the virtual world.
FPS Controls
Hovering over the 3D world is useful for debugging, but terrain is meant to be walked on, not admired from a distance. Implement rudimentary logic to walk around your landscape. Implement jumping whenever the player hits spacebar while standing on solid ground. If the physics team has implemented gravity correctly, jumping should simply be a matter of setting the player's velocity to a positive constant (the reference code above uses \(10\) units per second). Do not allow double-jumps while the player is in the air.
Gamification
Add crafting recipes, player goals, environmental hazards, and enemy combat.
HUD
Implement a 2D HUD showing the player's current block inventory. Implement a mouse UI allowing users to craft items, mine blocks, select and place blocks into the world, etc.
Minimap
Show a top-down map of the player's nearby environment in the HUD, indicating major terrain features and nearby enemies.
Enemies
Grab a rigged 3D model (for example, the robot from Assignment 3) and place them in the world as the enemies for the player to fight or avoid. Coordinate with the rendering team on drawing (instanced) 3D geometry for each enemy using linear blend skinning (it is fine to copy-paste code from the previous project to do this.)
Keyframe a simple walk cycle and animate the enemy as it moves around the world.
Enemies should wander around the world and seek out the player, using simple path-finding logic of your choice. You will need to decide how to handle terrain height variation: the enemy should have some way of jumping or climbing across the terrain, but also the player to evade the enemies if desired.
Implement your choice of simple melee or ranged combat against the enemies. Clearly visualize the health of the player and each enemy, with some reasonable logic for player and enemy death.
Secret Ingredient
Your (full) group should coordinate on and implement a secret ingredient: a major additional feature not described in this spec that differentiates your project from those of the other three groups in the class. I will help you brainstorm this secret ingredient during the group check-in (described below).
Grading
Your grade will be determined by:
- a project grade (100 points): the overall quality of the project (and write-up; see below) including completeness of all major features described in this specification; as well as the overall performance, user experience, and robustness of your code;
- your team grade (100 points): an assessment of how well your specific team executed the team-specific required technical features described above.
Except as modified by information in your Collaboration Reports, all students in the same group will receive the same project grade; and all students on the same team will receive the same team grade. Flaking on a group project places significant burden on the other members of your group and team to pick up the slack; flaking or failing to meaningfully contribute to your team or group may result in a project or course grade reduction.
Group Check-In
I will meet with your group in class on Thursday, April 9th to make sure that your group, as well as each team within your group, has a plan for how to implement, integrate, and test their contributions to the project. You should have some idea of how you plan to implement the major features your team is responsible for before this meeting. I will also discuss your group's secret ingredient.
Project Description
Include a write-up (PDF or Word document) describing the major features of your project and how they are implemented. Explain any major design decisions motivating your implementation. (You don't need to parrot anything already described in this specification.) The write-up should have a maximum of one page for each team (so: four pages total). You may include screenshots and other figures as extra pages that don't count against this limit.
Additional Technical Comments
Adding New Files to the Build
In most cases you do not need to modify the build system manually to add new files. The build system will automatically add source files under the following directories to your project:
src/minecraft
On the other hand, files outside of these directories will NOT be added to the
project automatically. For example, if you created a directory called
src/shaders/, and added your custom shaders under this directory, these
source files would NOT be built until you modify the make-minecraft.py as
well.