Skip to main content
Logo image

Section 20.5 Moving objects and simple games

If you have access to a VGA monitor, build and test your own bouncing ball system as you work through this section.

Draw a ball.

A 4 \(\times\) 4 red square "ball", whose upper-left coordinate is (x0,y0), drawn on a white background, can be generated by the following VHDL process:
draw_ball: process(pixel_x, pixel_y, x0, y0)
begin
   if (pixel_x >= x0) and (pixel_x < x0+4) 
         and (pixel_y >= y0) and (pixel_y < y0+4) 
   then
      color <= RED;
   else
      color <= WHITE;
   end if;
end process draw_ball;
where pixel_x and pixel_y are coordinates from the controller, and RED and WHITE are 12-bit RGB constants (e.g., x"F00" and x"FFF").

Move the ball.

The ball’s position can be dynamically updated with another VHDL process:
move_ball: process(clk)
begin
   if rising_edge(clk) then
      if move_ball_en = '1' then
         x0 <= x0 + dx;
         y0 <= y0 + dy;
      end if;
   end if;
end process move_ball;
where dx and dy are the horizontal and vertical increments in the square’s position. The enable signal, move_ball_en, is asserted for only one clock cycle in every frame. The ball is drawn in a new position in each successive frame, and moves across the screen. The speed at which the ball moves will be \(60 \sqrt{(dx)^2 + (dy)^2}\) pixels/second.
The best time to assert move_ball_en is during the inactive blanking period between the bottom of the frame and the top of the next frame. A state machine can be designed to watch for the v_blank signal to go low, enable the update for one clock cycle, and then watch for v_blank to go high again.

Checkpoint 20.5.1.

What do you think would happen if move_ball_en were asserted while the image was being displayed, instead of while the image is inactive?

Checkpoint 20.5.2.

What do you think would happen if move_ball_en were asserted for more than one clock cycle in each frame?

Checkpoint 20.5.3.

Draw a state diagram and write a VHDL model for a state machine to update the position of the ball
Answer.
Here is a sketch of the state machine’s combinational logic:
FSM_comb: process(curr_state, v_blank)
begin
   next_state <= curr_state;  -- defaults
   move_ball_en <= '0';

   case curr_state is
      when v_on =>
         if v_blank = '0' then
            next_state <= update;
         end if;

      when update =>  move_ball_en <= '1';
         next_state <= v_off;

      when v_off =>
         if v_blank = '1' then
            next_state <= v_on;
         end if;
    end case;
end process FSM_comb;

Bounce the ball.

Once the ball is moving, its coordinates (x0, y0) will increment indefinitely, and the ball will disappear off the edge of the screen, at least until the values of x0 and y0 roll over. A more interesting behavior is to bounce the ball off the "walls" at the edge of the screen. If the ball is moving to the right and it collides with the right wall, its horizontal increment, dx, should change sign so that the ball will reverse direction. The vertical increment does not change; if the ball is moving up and to the right when it hits the wall, it will be moving up and to the left after it bounces. Detecting a collision must take into account the size of the ball, so that the ball doesn’t move beyond the edge of the screen before bouncing.
Here is a skeleton of a process to detect wall collisions and change directions:
ball_hit_wall: process(clk)
begin
   if rising_edge(clk) then
      -- Hit the right wall
      if (dx  > 0) and (x0 >= 640-4) then
         dx <= -dx;

      -- Check the other walls

      end if;
   end if;
end process ball_hit_wall;
The literal 640-4 should, of course, be replaced by named integer constants for the location of the wall and the size of the ball. The remainder of the process is left as an exercise.

Checkpoint 20.5.4.

Complete the ball_hit_wall process to include collisions with the other three walls.
Combining draw_ball, move_ball, and ball_hit_wall with the update state machine and a VGA controller will draw a ball bouncing around the screen. Once you have this, you can imagine drawing and moving other objects, controlling the movement of objects with user input (like pushbuttons), and colliding one moving object with another. A good project to try is the classic "Pong" game.
A block RAM lookup table can also be dynamically updated. Imagine, for example, making a system that can draw on the screen under user control, like the classic "Etch-A-Sketch" toy. In every frame, the block RAM is read and the contents displayed on the screen by the VGA controller. During the vertical blanking period, the user’s controls can be read, move a cursor, and update the color of the pixel that the cursor points to in the block RAM. The screen is cleared by writing the background color to every memory location.
With these principles in mind, a number of VGA based projects can be designed.