Jacques Jumper

Picture of Jacques Jumper Gameplay

Project Overview

Jacques Jumper is a video game developed using VHDL for FPGA implementation, where players control Jacques, a humanoid frog, as he navigates falling platforms to survive. The game utilizes an NES controller for movement and jumping, with a physics engine that handles dynamics like gravity, collisions, and platform generation. Visual elements, including sprites and backgrounds, were custom-designed and optimized into ROM data for efficient FPGA resource usage. Key modules include VGA for display rendering, Controller I/O for input processing, Pattern Generation for visuals, and a physics engine for gameplay mechanics. Randomized platform spawning was achieved using an LFSR, ensuring engaging and dynamic gameplay. This project integrates hardware design, programming, and art into a cohesive, interactive experience.

Introduction

My group consisted of me, Griffin Faecher, Milo Goldstein, and Jacob Pettigrew.

Jacques Jumper is a game centered on controlling a humanoid frog named Jacques, whose objective is to survive by jumping between falling platforms. The game ends if Jacques comes into contact with the bottom of the screen or jumps too high and leaves the screen. Movement is controlled using an NES controller: the left and right buttons move Jacques laterally, the B button triggers a short, quick jump, and the A button allows for charging up a jump to cross the screen. The game has no specific win condition, focusing instead on strategic jumping and platform selection to maximize survival time.

Figure 1: Abstracted Depiction of How Jacques Jumper Functions

Figure 1 displays an abstracted depiction of how the game works. The project begins at the VGA module, which provides the 60 Hz clock used across most other modules. A game state module manages transitions between starting and ending the game, depending on user input and whether Jacques survives or dies. The physics module handles calculations for positions, movement, and interactions of all entities, including the platforms and Jacques. A pattern generator module renders characters, platforms, and the background based on data stored in ROM files and displays them on the screen according to the current game state.

VGA

The VGA module operates using input from a PLL, a high-speed oscillating clock that generates pulses at a rate of 25 MHz. These pulses are used to drive row and column signals, which control the pixels on the screen. With each pulse, a new pixel is drawn in a column, and after 800 pixels are drawn, the column resets and begins drawing in the next row. This process creates a 640x480 resolution display with a 60 Hz refresh rate.

The module outputs a 60 Hz clock signal, which serves as the timing foundation for other modules. The row and column signals are also output to the Pattern Generation module for difference calculations.

Controller I/O

The controller module processes three main variables: data input, latch, and clock. The clock is derived from the FPGA's internal high-speed clock and slowed down to a pace suitable for the NES controller. A latch signal indicates when the controller should check for inputs.

The NES controller includes eight buttons. When a button is held down after the latch signal, the controller outputs a high signal at a specific time. These outputs are stored in a shift register, with each bit corresponding to a different button input. Figure 2 shows the controller input chart that corresponded with the NES controller used.

Figure 2: Controller Input Chart

Art and Design

All artwork in Jacques Jumper was originally designed and crafted specifically for the game, including the character, platforms, and background. These designs were pixelated, color-corrected, and converted into ROM data for use in VHDL.

Jacques, the humanoid frog, was created by combining a frog mask with a bodybuilder's physique and pixelating the result with a 6-bit color palette. A “transparent color” value was assigned to unused pixels to prevent garbage rendering on-screen. After refining the design, a C++ script converted the pixelated image into ROM-compatible data. Jacques features nine distinct sprites to represent various animations, with left and right sprites mirrored to save memory.

Figure 3: Image Compression on Character Sprite

Figure 4: Nine Different Jacques Sprites Representing Different In-Game Mechanics

The platform was designed using Pixilart, an online pixel art tool, which ensured precise dimensions and colors within the 6-bit color palette. Holes in the platform were initially filled with a temporary "garbage color" (brown) that was later removed in the ROM. Only one platform design was created, as all platforms in the game shared the same appearance. The final platform was rendered in white, with the garbage value aligning with the universal program garbage value used in the Pattern Generation module.

Figure 5: Sprite of Original Jacques Jumper Platform

The background was created in Pixilart with dimensions matching the 640 x 480 resolution of the visible screen. It featured four evenly spaced shades of blue, each occupying 640 x 120 pixels. A repeating pattern was added to the top of each shade (except the upper-most one) to create a gradient-like effect. The use of a repeating pattern minimized memory usage by reusing the base pattern instead of allocating memory for individual pixels. This approach ensured an efficient and visually appealing design.

Figure 6: Jacques Jumper Background

Pattern Generation

The Pattern Generation module, referred to as pgen in the source code, handles all visual elements of the game. This module integrates data from the Physics, Game State, VGA, and ROM files to ensure that the correct colors are displayed on the correct pixels at the correct times.

Inputs and Outputs

The module takes multiple inputs and outputs a single RGB value for the screen display. Inputs include:

  1. Row and Column Positions: Coordinates of the VGA's current pixel, where (0, 0) represents the upper-left corner, and (639, 479) represents the bottom-right. Off-screen pixels (639–800 in x and 479–500 in y) are assigned a black color using a valid signal, which ensures that only on-screen pixels are colored.

  2. Clocks: A 25 MHz clock and a 60 Hz clock synchronize pgen operations with the rest of the system.

  3. Game Data: Controller input, current game state, Jacques’ jump counter, and x- and y-positions for both Jacques and the platforms.

ROM

The ROMs were implemented as sub-modules instantiated exclusively in the Pattern Generation (Pgen) module. Each ROM adhered to a standardized structure, taking the following inputs:

  • X and Y Coordinates: Representing the upper-left pixel of the bounding box where the image was drawn.

  • 25 MHz Clock: Used to synchronize ROM operations.
    Each ROM output an RGB value corresponding to the specified inputs.

A custom C++ program was developed to convert image data, formatted as 6-bit RGB values, into VHDL-compatible ROM code. The program processed input text files containing space-separated pixel values and new lines for each image row. Using a yx-concatenated N-bit address line, the program mapped image data to a compact ROM structure.

The script optimized ROM storage by dynamically sizing address lines based on the image dimensions. For example, some address lines were 7 bits long, while others were 12 bits, ensuring each image used only the necessary memory space. This approach maximized efficiency and minimized resource usage on the FPGA.

Physics and Movement

The physics engine forms the foundation of gameplay, controlling all movement and collisions for Jacques and the platforms. All calculations were handled in a single VHDL file.

Generics
Key constants—gravity, run acceleration, friction, and collision leniency—were defined as generics, serving as the basis for the engine's kinematics.

Structure
The physics engine operates within a single 60 Hz process block. Signals track terminal velocities and collision status to manage Jacques’ interactions and movements.

Horizontal Movement
Jacques accelerates left or right when the controller is pressed until reaching a terminal velocity, determined by a signal that halts further acceleration. Upon releasing the input, frictional deceleration creates a sliding effect, enhancing the fluidity of movement.

Vertical Movement and Platforms
Platforms are spawned using an LFSR (Linear Feedback Shift Register) and move downward at a constant rate of 2 pixels per frame. One platform always spawns in the center of the screen for gameplay balance.

Collisions were managed using lookahead calculations, predicting the next-frame positions of Jacques and platforms. A collision leniency parameter expanded the platform’s collision box to ensure Jacques didn’t phase through platforms, even at high speeds. When collisions occurred, Jacques’ velocity matched the platform’s, locking him to its movement.

Figure 7: Collision Diagram Showcasing Collision Leniency

Jumping Mechanics
Jumping was enabled only when Jacques was "on a platform." A short hop (B button) set his y-velocity to +14, while a charge jump (A button) involved a counter that incremented up to 16. Upon releasing the A button, Jacques’ jump velocity was determined by the counter value plus 10, enabling dynamic, high jumps.

Linear Feedback Shift Register (LFSR)

Platform generation was achieved using a 20-bit LFSR, dividing the screen into 16 sections for platform spawning. The LFSR was driven by the 60 Hz clock, consistent with other modules. Bits 20 and 18 were fed through an XOR gate to produce randomness, maximizing the period of the LFSR. This setup ensured a non-repeating platform pattern for approximately five hours, creating a randomized yet consistent gameplay experience.

Figure 8: Diagram of LFSR Used in Jacques Jumper

Four bits were selected from the LFSR and combined into a 4-bit number. The value of this number determined which of the 16 horizontal screen divisions the platform would spawn in. One division was placed off-screen to ensure the number of platforms on-screen never remained constant. This design increased the challenge, requiring quick thinking and reaction to the changing platform pattern.

Figure 9: Diagram of Multiplexer Used for Platform Spawns

Video Demo