I had a thought one day: Can ChatGPT be used to code a Minecraft web application? My primary goal with this was to see how well ChatGPT could be used to code a large, complex application when I didn’t have the experience to do it myself. I am a software tester and not a developer. I was hoping I could give ChatGPT requirements as we progressed, and it would give me the code to paste into Visual Studio.
I wanted to use C# for the entire project since that is what I am familiar with, and ChatGPT said that we could code a Minecraft web application in ONLY C#. However, I soon learned that JavaScript and other technologies were necessary to bring the game to life. Despite the hurdles, I learned a lot about SignalR, Razor, JavaScript, and THREE.js along the way. My kids even think I’m a rockstar for creating what I have. Little do they know….
MyCraft
I decided to go with a Minecraft web application because my kids are obsessed with Minecraft. (I started telling my kids I was working on “MyCraft” when they asked me what I was doing.) Since I had no previous experience with coding 3D graphics, I figured ChatGPT and I could figure it out. I was certain there were plenty of examples I could follow if I got stuck because Minecraft is very popular, even after 15 years.
My app is in no way considered a completed game. Not even close. However, after six or seven weeks of working on this, some basic functionality is there: moving the player, jumping, sneaking, rotating the camera, a nine-slot item menu, infinite terrain using chunks, Perlin noise terrain generation as the player moves, 12 different types of blocks with the ability to add more no problem, physics and collision detection, touch screen controls, mining blocks, placing blocks, and some multi-player capability.
Technologies Used
- C#: For server-side logic and game mechanics.
- Razor: For building the web interface.
- JavaScript: For client-side rendering and interactions.
- THREE.js: For 3D graphics and rendering the game world.
- SignalR: For real-time communication between the server and clients.
The only one I was familiar with was C#. As the weeks progressed, I became comfortable understanding the JavaScript ChatGPT was giving me. Yesterday, I asked ChatGPT if THREE.js had something for adding sounds, and it gave me an example. I did further research, found a free sound .mp3, and had my pickaxe going “clink, clink, clink” in like 15 minutes. I added a walking sound effect when the player moved. My kids didn’t think it could get any cooler! Yeah, they are easy to please.
After understanding what ChatGPT was using SignalR for and realizing that I already might have some multiplayer capabilities with the code it gave me, I decided to further investigate how to connect my phone. After asking ChatGPT what I needed to do, which was to add my IP address to the launchSettings.json file, I could run around in my block world on both the computer and the phone.
The Journey
I began with a simple goal: create a Minecraft-like game in C# using ChatGPT to generate the code. I give ChatGPT requirements, it generates some code, I put it into Visual Studio and compile, I do the testing, and then let ChatGPT know what works and doesn’t work. It fixes the code, and I retest. I was eager to see if this could work.
ChatGPT suggested using THREE.js for rendering, which required using JavaScript. This was new territory for me, and initially, it was daunting. However, with ChatGPT’s help and some perseverance, I started to understand what “requirements” I needed to give ChatGPT.
Developing Some Classes
The first major classes were the Block, Chunk, and ChunkService classes, responsible for generating and managing the game’s terrain. I knew from playing Minecraft with my kids that the terrain was divided into 16×16 block chunks and was generated on the fly as the players moved across the world. I had no idea how it did it, but ChatGPT did pretty well generating the code for handling the blocks/terrain.
public class ChunkService
{
public ConcurrentDictionary<(int, int), Chunk> LoadedChunks { get; private set; } = new ConcurrentDictionary<(int, int), Chunk>();
public static FastNoise Noise { get; set; } //Perlin noise library
public static int Seed { get; set; }
...
private async Task<Chunk> GenerateChunkAsync(int chunkX, int chunkZ)
{
byte[,,] blocks = new byte[VoxelData.ChunkWidth, VoxelData.ChunkHeight, VoxelData.ChunkWidth];
Random random = new Random();
int preHeight = -1;
int preHeightDelta = 0;
double percentChange = 0.01;
int[,] heights = new int[VoxelData.ChunkWidth, VoxelData.ChunkWidth];
for (int x = 0; x < VoxelData.ChunkWidth; x++)
{
for (int z = 0; z < VoxelData.ChunkWidth; z++)
{
// Calculate global coordinates
int globalX = chunkX * VoxelData.ChunkWidth + x;
int globalZ = chunkZ * VoxelData.ChunkWidth + z;
// Generate noise at these coordinates
float heightNoise = Noise.GetNoise(globalX / 0.5f, globalZ / 0.5f);
int height = Math.Clamp((int)(Math.Pow((heightNoise + 1) / 2, 2) * VoxelData.ChunkHeight), 0, VoxelData.ChunkHeight - 1);
// Set block types in the chunk
for (int y = 0; y < VoxelData.ChunkHeight; y++)
{
if (y == 0)
{
blocks[x, y, z] = 9;//bedrock
}
else if (y < height - 6)
{
blocks[x, y, z] = 2;//stone
}
else if (y < height-1)
{
blocks[x, y, z] = 6; //dirt
}
else if (y < height)
{
blocks[x, y, z] = 1; //grass
}
else
{
blocks[x, y, z] = 0; //air
}
}
}
}
}
}
}
As the player gets closer to an area of the world that hasn’t been generated yet, the ChunkService creates the chunks that need to be. I started out by having a flat world. After I got that going, ChatGPT and I switched over to using Perlin noise to generate the landscape. I don’t think I had heard of Perlin before that. This was one of the times I was quite amazed with what I had. After finding a couple of Minecraft’s textures, my world looked just like Minecraft!
The code above is a snippet that generates a chunk.
Game Hub and Real-Time Updates
To handle real-time updates between the server and clients, ChatGPT used SignalR. This was a completely new area for me, but with ChatGPT’s guidance, I managed to set up a basic game hub.
public async Task<Position> UpdatePlayer(PlayerUpdateData data)
{
var player = _playerManager.GetPlayer(Context.ConnectionId);
if (player != null)
{
int xDelta = 0, yDelta = 0, zDelta = 0;
if (data.KeyStates.TryGetValue("ArrowUp", out bool arrowUp) && arrowUp)
{
zDelta += 1;
}
if (data.KeyStates.TryGetValue("ArrowDown", out bool arrowDown) && arrowDown)
{
zDelta -= 1;
}
if (data.KeyStates.TryGetValue("ArrowLeft", out bool arrowLeft) && arrowLeft)
{
xDelta -= 1;
}
if (data.KeyStates.TryGetValue("ArrowRight", out bool arrowRight) && arrowRight)
{
xDelta += 1;
}
if (data.KeyStates.TryGetValue("Space", out bool space) && space) //Jump
{
yDelta += 1;
}
if (data.KeyStates.TryGetValue("Sneak", out bool sneak) && sneak)
{//check sneak, run, and then walk
player.CurrentSpeed = player.SneakSpeed;
}
else if (data.KeyStates.TryGetValue("Run", out bool run) && run)
{
player.CurrentSpeed = player.RunSpeed;
}
else
{
player.CurrentSpeed = player.Speed;
}
if (data.KeyStates.TryGetValue("Fly", out bool fly) && fly)
{//flying isn't implemented. increase jump height for now
player.CurrentJumpHeight = 2.2f;
}
else
{
player.CurrentJumpHeight = player.JumpHeight;
}
Vector3 movementDelta = new Vector3(xDelta, yDelta, zDelta);
if (movementDelta != Vector3.Zero)
{
Console.WriteLine("mevementDelta: " + movementDelta);
}
Vector3 lookDirection = new Vector3(data.DirectionX, data.DirectionY, data.DirectionZ);
_physics.HandlePlayerInput(player, movementDelta, lookDirection, data.DeltaTime);
GetChunksToLoad(player);
return player.CameraPosition;
}
return null;
}
The GameHub class uses SignalR to send real-time updates about player positions and chunk data to all connected clients. Once I figured out what this was doing, I realized I could move all functionality except input and rendering to the server and send the updates via SignalR to the client.
The above code gets the camera data, the player’s actions, and the time since the last updates from JavaScript. It sends this data to the physics class to determine collisions and calculate gravity and sends a new position back to the client for it to update the THREE.js scene.
Integrating JavaScript and THREE.js
Initially, I struggled with JavaScript and THREE.js, but with help from ChatGPT and YouTube tutorials, I made progress understanding most of the code that ChatGPT was giving me. Here is a basic example of JavaScript code to render the game world.
async function initialize3DScene(canvasId) {
console.log("Initializing 3D scene...");
const canvas = document.getElementById(canvasId);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
console.log("Camera initialized:", camera, window.innerWidth, window.innerHeight);
renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
controls = new THREE.PointerLockControls(camera, canvas);
document.body.addEventListener('click', () => controls.lock());
scene.add(camera);
await getUVData(); //gets the texture data from the server
await getPlayerHotbarItems(); //gets the players hot bar items (pick and bloks)
animate();
}
function animate() {
requestAnimationFrame(animate); //This is one of the things that I don't understand how it works. It seems to be a callback to the animate function.
sendInputToServer(deltaTime); //calls a function that sends the data to the server
renderer.render(scene, camera); //renders the scene
}
This code sets up a basic 3D scene, demonstrating how THREE.js can be used for rendering. The initialize3DScene() function is called from the main Razor page.
Challenges
Throughout the project, there were times when ChatGPT couldn’t provide an immediate solution. There were times when ChatGPT could not provide a working solution after two days of trying. I had to dig deeper on my own. For example, implementing terrain generation took several iterations and required watching tutorials on C# and Unity, as well as JavaScript and THREE.js to get it to what I wanted it to be. There were other times when I had to keep explaining that I wasn’t using Unity! I think after ChatGPT-40 was released, this issue went away.
Another example of this is when ChatGPT couldn’t give me a solution for removing a block from the THREE.InstancedMeshes. I had to dig, and I finally found a solution from a YouTuber who created a Minecraft web application using only JavaScript and THREE.js. I gave the gist of the code to ChatGPT, and it fit that into my code. These moments were both challenging and frustrating, but also rewarding as they pushed me to learn and adapt.
What I Learned
There are a few things I learned from this endeavor:
- First, since I started this project wondering if ChatGPT was capable of implementing a large project. I learned the answer is “No, not by itself.” It needed lots of coaching. However, when I finally understood what more of the code was doing, things got better. Garbage in, garbage out, right? I do know in many cases that I have mentioned before, ChatGPT can be very beneficial. In this specific case, if ChatGPT had someone who knew what they were doing, I think things would have been even better.
- I started out blindly copying and pasting code from ChatGPT since this was part of my overall goal. I pasted in its code, compiled, and ran the app to see if it worked or if it broke anything. I was the BA, ChatGPT was the developer, and I was the tester. I was okay with this since I was testing. This part was easy for me. I told ChatGPT what was working and what was still broken. It then tried to fix the issues, and I retested. We did this until I concluded things worked. It worked pretty well in this regard, however, there were still those few times when it never gave the correct solution.
- Many times, after ChatGPT failed to fix the issues, I had to figure out how to implement a fix. There were plenty of times when the solution it gave was only half-implemented, and before I pasted the code into Visual Studio and ran it, I told ChatGPT that the solution didn’t account for A, B, or C. It revised the code, and then I tested it or completed the correct implementation myself. In both of these cases, I am glad that I had to figure out the solution and learn.
- ChatGPT tried to fix some issues the wrong way. As an example, when some functionality was not working as it should but caused an exception instead, ChatGPT’s solution for me was to add a try/catch block around the code and told me, “Here, that should resolve the error.” Yeah, it got rid of the error alright…but of course, the functionality still didn’t work.
Conclusion
Building this Minecraft web application with ChatGPT was a fun journey, and I ended up learning some new technologies. Now, I am no expert on any of these by any means, but I know I will be trying to work on the next thing at home using some of the same technologies.
ChatGPT did ok implementing this. Although I was frustrated with it a few times, I can’t complain with what I have at this time–a working version of a Minecraft web application. I eventually embraced JavaScript and learned a lot in the process, but there was an initial frustration since ChatGPT said we could do this project with only C#. I have a deeper understanding of game development and the technologies involved. This project proved that with determination, even the most daunting tasks can be accomplished with ChatGPT.