Pacman game

HTML, JavaScript, WebGL, Computer Graphics

Check out the project

tl;dr

  • Created a simple labyrinth with Pacman on top, illuminated by Phong shading and lighting
  • Added dots for Pacman to eat
  • Added jumping capabilities, centering of the camera, and shear view
  • Added simple collision detection to prevent Pacman from going out of bounds

As part of my Foundations of Computer Graphics course, I had to implement a Pacman clone using WebGL. All the models had to be created by ourselves, and the use of libraries such as three.js was prohibited.

Labyrinth and Pacman

I have used Blender for creating the models and exported these in the .obj format. I chose this format because we had to write a .obj parser as part of a previous assignment to render the objects in WebGL, so I wouldn't have to duplicate work. For example, the definition of the ground plane is:

export default class GroundPlaneObject {
    static getFile() {
        const info = "v -20.000000 0.000000 20.000000\n" +
            "v 20.000000 0.000000 20.000000\n" +
            "v -20.000000 0.000000 -20.000000\n" +
            "v 20.000000 0.000000 -20.000000\n" +
            "s off\n" +
            "f 2 3 1\n" +
            "f 2 4 3";

        return info;
    }
}

This info is then collected, parsed, and fed to WebGL. Both the labyrinth and Pacman are exposed as separate classes to keep the logic together.

// Labyrinth.js draw call
draw(projectionMatrix, globalModelMatrix, viewMatrix, lightSource) {
    this.cubes.forEach(cone => cone.draw(projectionMatrix, globalModelMatrix, viewMatrix, lightSource));
    this.groundPlane.draw(projectionMatrix, globalModelMatrix, viewMatrix, lightSource);
}

Each of the objects to draw are implementing a DrawingObject interface which defines a draw method.

Dots to eat

I first rendered the balls by placing them inside the labyrinth and drawing them.

this.balls.forEach(ball => ball.draw(projectionMatrix, globalModelMatrix, viewMatrix, lightSource));

To determine if Pacman collects a ball, I passed the position and checked if a ball is on the same field.

// Pseudo code
collectBall(pacmanPosition) {
  // Go through the ball positions, checking if one is on pacmans position
  // If so
     // Remove that ball from the array
     // Get the current score from the HTML element
     // Add a static number to it
     // Change the innerHTML with the updated score
}

Improvements and Additions

Apart from the requirements above, I had to implement a simple collision detection to avoid Pacman from going out of bounds. The camera then needed to be adapted such that it always focuses on Pacman and moves along.

I also added the functionality for Pacman to jump while implementing additional features such as music, playing as Ms. Pacman, and a welcome screen.

Things I have not implemented

The overall time spent on this project was roughly two weeks next to the general coursework. I left out optional features that would have added more implementation time, such as:

  • Shadows
  • Enemy Ghosts
  • Lives

Additionally, I didn't optimize for mobile screens, which is why it's best on desktop devices.

Learnings

This project was a great way to piece together concepts from previous lectures (e.g., Phong Illumination) and create something fun out of it. I learned more about rounding errors in JavaScript, how buffers in WebGL work, and how a game loop draws entities continuously.

If I would start from scratch again (and not in an academic context), I would experiment with libraries that abstract some of the low-level details instead of using WebGL directly. The latter approach felt like I reinvented the wheel, knowing that great people have already made contributions to making 3D in JavaScript more accessible. However, given the academic context, I was also able to understand the constraints.