Context: Study at Beuth Hochschule, Embedded Systems with Prof. Dr.-Ing. Detlef Heinemann
Concept
Description
Project team: Yvonne Wöstefeld, Manuel Jacob, Alexander Wegner
Pong is a computer game which is comparable to tennis. It's supposed to be the first computer game and was created by Atari in 1972 (Pong Arcade).
Two players are using a bat to punch a moving ball to the opponent's field. If a player is missing the ball, the other one is scoring one point. The game is over if one players scores up to nine points.
The bats are symbolized by rectangles which can be moved up and down on the screen. The ball is bouncing from the bats or from the upper and lower screen border. If the ball hits the left or right border, the player of the opposite site scores.
The score is visualized in the upper screen centre.
Figure: Pong Arcade by Atari from 1972 (Source: Telegamez.de)
Hardware and graphic concept
A Nexys2 board with a FPGA chip is used to realize the game. The hardware is descript and synthesized in VHDL
The graphical output, with the screen resolution of 640 pixel times 480 pixels, is done via the VGA port of the Nexys2 board.
Different colours are used to improve the game experience. Score and bat have different colours for each player (Player 1: red, player 2: blue). The ball and the middle line are displayed in white.
Controlling the bats is the done via four input pins of the Nexys2 board (Up and Down movement)
Figure: Design concept
Game physics
The positions of the bat, the ball and further objects are defined trough their origin. The origin point is placed in the upper left corner of each object. This point is the reference for the game physics and the display on the screen. All objects are defined by their origin point and their height and width. A bat for example has the dimensions 15x70 pixels and the ball 15x15 pixels.
Only the origin might change during the game. The score board and the middle line have fixed origin points on the screen.
The horizontal axis is called the x-axis and the vertical axis the y axis. The origin of both axis is placed in the upper left corner of the screen. The values on the axis are increased due moving along in rightward or downward direction.
The bats can be moved vertically. Each player can move his bat parallel to the y-axis due pressing the corresponding button. The maximal movement speed is set by the game. The bats can't be moved outside the visual area.
The ball is moved in x and y direction by vector addition. The ball is moved on a random vector (28 different vectors are possible). Direction, angel and speed are set by the game. The position of the ball is manipulated after a variable time period. The vector of the movement consists of a x and a y component (values form -3 up to 3).
Figure: 28 different vectors for the ball movement
If the ball hits the upper or lower screen border, the y component of the moving vector is negated. The ball seems to bounce from the wall.
If the ball hits one of the bats (Y position is in the range of the bat's height, x position is the range of the bat's width), the x component is changed and the movement speed of the ball is increased (refresh time of the ball position is lowered).
It might happen that the ball is missed by a player. If the ball hits the left or right border, the opposite player scores, the ball position is reset to the middle of the screen, the movement speed is set to default and a new moving vector is created.
The game is over if one player scores up to 10 points. The game is resetted afterwards.
VGA port
VGA port uses basically five wires. Three of them are used to describe the colour (red, green, blue / RGB) and two wires are used to synchronize the electron beam in the monitor (Horizontal synchronization and vertical synchronisation / H-Synch, V-Synch). The electron beam is moving line by line from upper left to lower right and is displaying the pixels on the screen. The colour is changed in parallel to the movement. The current pixel's colour can be changed due simultaneously altering the colour signals.
The colours are limited to 8bit on the Nexys2 board. There are three bits for red, three bits for green and two bits for blue.
Figure: VGA port and connections on the Nexys2 board (Source: Nexys2 Reference Manual)
H-Synch is used for the end of the line and V-Synch for the end of the whole frame.
Figure: Electron beam and synchronization (Source: Nexys2 Reference Manual)
The timing tables from the manual can be used to synchronize the clock signals for the different timing signals.
Figure: Timing table (Source: Nexys2 Reference Manual)
The Nexys2 reference manual holds an example of a VGA driver as well.
Figure: Block diagram of a VGA driver (Source: Nexys2 Reference Manual)
Realization
Figure: Top-Schematic of the pong project
VGA driver (VGADriver)
I decided to use an open source VGA driver. This component was written by some student of „Department of Electrical and Computer Engineering“ from of university of Alberta (Source: [http://www.ece.ualberta.ca/~elliott/ee552/studentAppNotes/1998_w/Altera_UP1_Board_Map/vga.html ualberta.ca]).
Figure: Function block of the VGA driver
Functionality
The VGA driver holds four inputs, five outputs (VGA signals: R, G, B, H-Synch, V-Synch) and two vectors as outputs. It's powered by a clock signal of 25MHz. The timing table is integrated into this component and is used to generate the timed signals H-Synch and V-Synch. The VGA driver is counting column and row (x/y position) of the current pixel and is outputting this position via the output vectors. The colour inputs are directly connected to the colour outputs. An external logic is needed to set the right colour to the right time.
Library IEEE;
use IEEE.STD_Logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity VGAdrive is
port( clock : in std_logic; -- 25Mhz clock
red, green, blue : in std_logic; -- input values for RGB signals
row, column : out std_logic_vector(9 downto 0); -- for current pixel
Rout, Gout, Bout, H, V : out std_logic); -- VGA drive signals
-- The signals Rout, Gout, Bout, H and V are output to the monitor.
-- The row and column outputs are used to know when to assert red,
-- green and blue to color the current pixel. For VGA, the column
-- values that are valid are from 0 to 639, all other values should
-- be ignored. The row values that are valid are from 0 to 479 and
-- again, all other values are ignored. To turn on a pixel on the
-- VGA monitor, some combination of red, green and blue should be
-- asserted before the rising edge of the clock. Objects which are
-- displayed on the monitor, assert their combination of red, green and
-- blue when they detect the row and column values are within their
-- range. For multiple objects sharing a screen, they must be combined
-- using logic to create single red, green, and blue signals.
end;
architecture behaviour1 of VGAdrive is
-- names are referenced from Altera University Program Design
-- Laboratory Package November 1997, ver. 1.1 User Guide Supplement
-- clock period = 39.72 ns; the constants are integer multiples of the
-- clock frequency and are close but not exact
-- row counter will go from 0 to 524; column counter from 0 to 799
subtype counter is std_logic_vector(9 downto 0);
constant B : natural := 93; -- T_pw - horizontal blank: 3.77 us
constant C : natural := 45; -- T_bp - front guard: 1.89 us
constant D : natural := 640; -- T_disp - horizontal columns: 25.17 us
constant E : natural := 22; -- T_fp - rear guard: 0.94 us
constant A : natural := B + C + D + E; -- one horizontal sync cycle: 31.77 us
constant P : natural := 2; -- T_pw - vertical blank: 64 us
constant Q : natural := 32; -- T_bp - front guard: 1.02 ms
constant R : natural := 480; -- T_disp - vertical rows: 15.25 ms
constant S : natural := 11; -- T_fp - rear guard: 0.35 ms
constant O : natural := P + Q + R + S; -- one vertical sync cycle: 16.6 ms
begin
Rout <= red;
Gout <= green;
Bout <= blue;
process
variable vertical, horizontal : counter; -- define counters
begin
wait until clock = '1';
-- increment counters
if horizontal < A - 1 then
horizontal := horizontal + 1;
else
horizontal := (others => '0');
if vertical < O - 1 then -- less than oh
vertical := vertical + 1;
else
vertical := (others => '0'); -- is set to zero
end if;
end if;
-- define H pulse
if horizontal >= (D + E) and horizontal < (D + E + B) then
H <= '0';
else
H <= '1';
end if;
-- define V pulse
if vertical >= (R + S) and vertical < (R + S + P) then
V <= '0';
else
V <= '1';
end if;
-- mapping of the variable to the signals
-- negative signs are because the conversion bits are reversed
row <= vertical;
column <= horizontal;
end process;
end architecture;
Clock divider (Clk_div_2)
The VGA driver needs a clock signal of 25MHz, which is realized with a 2:1 divider - the board clock with 50MHz is used.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity Clk_div_2 is
Port ( Clk_in : in STD_LOGIC;
Clk_out : inout STD_LOGIC);
end Clk_div_2;
architecture Behavioral of Clk_div_2 is
begin
process (Clk_in)
begin
if Clk_in'event and Clk_in='1' then
if (Clk_out = '0') then
Clk_out <= '1';
else
Clk_out <= '0';
end if;
end if;
end process;
end Behavioral;
Game logic and graphics (vgatest)
The VGA driver is used to display the game on a monitor via the VGA port of the Nexys2 board. It's important to set the right colour values at the right time/pixel to display the correct frame on the screen. The positioning of the objects is done with their origin coordinates, which might change during the game. Each frame is display separately on the screen. If the position of the objects is changed a bit from frame to frame, the human eye realizes a moving object
Figure: Main program
This module expects a 25MHz clock signal and the control buttons as inputs and outputs the VGA signals.
The component vgatest consists of function blocks, which are used for the graphical output, the game physics and the VGA port control.
The VGA port control is done by the VGA driver, which structure is descript in this component.
component vgadrive is
port( clock : in std_logic; -- 25.175 Mhz clock
red, green, blue : in std_logic; -- input values for RGB signals
row, column : out std_logic_vector(9 downto 0); -- for current pixel
Rout, Gout, Bout, H, V : out std_logic); -- VGA drive signals
end component;
VGA : component vgadrive -- Einbindung des VGA Drivers
port map ( clock => clock, red => red, green => green, blue => blue,
row => row, column => column,
Rout => R, Gout => G, Bout => B, H => H, V => V);
Graphics and game physics are controlled by parallel processes.
Graphics
This process is responsible for the correct colour signals for the current pixel. The origins and dimensions of all objects are considered. The position of the moving objects is described by variables (Internal signal in the architecture of the module) which can be altered during the game.
signal row, column : std_logic_vector(9 downto 0); -- X,Y-Position aktuellen Bildpixels
signal red, green, blue : std_logic := '0'; -- Farbsteuerung
-- Balkenposition (Y) für Spieler links und rechts
signal beam_pos_left, beam_pos_right: integer range 0 to 650 := 0;
-- Ballkoordinaten
signal ball_pos_x, ball_pos_y: integer range 0 to 650 := 0;
-- Spielstand für links und rechts
signal s_l, s_r : integer range 0 to 10 := 0;
Process:
RGB : process(row, column) -- VGA Ausgabe
variable rgb : std_logic_vector (2 downto 0) := "000"; -- rot, grün, blau für VGADriver
begin
-- linker Balken (15x70 Pixel)
if (row > beam_pos_left and row < (beam_pos_left + 70) and column > 0 and column < 15) then
rgb := "100"; -- Farbe: rot 1, grün 0, blau, 0
else
-- rechter Balken (15x70 Pixel)
if (row > beam_pos_right and row < (beam_pos_right + 70) and column > 624 and column < 639) then
rgb := "001";
else
-- Mittellinie (6 Pixel breit)
if (row > 0 and row < 479 and column > 317 and column < 323) then
rgb := "111";
else
-- linker Punktestand - Seg A --
if ((row > 20 and row < 45 and column > 275 and column < 280) and (s_l = 0 or s_l = 1 or s_l = 2 or s_l = 3 or s_l = 4 or s_l = 7 or s_l = 8 or s_l = 9)) then
rgb := "100";
else
-- linker Punktestand - Seg B --
if ((row > 50 and row < 75 and column > 275 and column < 280) and (s_l = 0 or s_l = 1 or s_l = 5 or s_l = 6 or s_l = 3 or s_l = 4 or s_l = 7 or s_l = 8 or s_l = 9)) then
rgb := "100";
else
-- linker Punktestand - Seg C --
if ((row > 75 and row < 80 and column > 250 and column < 275) and (s_l = 0 or s_l = 2 or s_l = 3 or s_l = 5 or s_l = 6 or s_l = 8 or s_l = 9)) then
rgb := "100";
else
-- linker Punktestand - Seg D --
if ((row > 50 and row < 75 and column > 245 and column < 250) and (s_l = 0 or s_l = 2 or s_l = 6 or s_l = 8)) then
rgb := "100";
else
-- linker Punktestand - Seg E --
if ((row > 20 and row < 45 and column > 245 and column < 250) and (s_l = 0 or s_l = 4 or s_l = 5 or s_l = 6 or s_l = 8 or s_l = 9)) then
rgb := "100";
else
-- linker Punktestand - Seg F --
if ((row > 15 and row < 20 and column > 250 and column < 275) and (s_l = 0 or s_l = 2 or s_l = 3 or s_l = 5 or s_l = 6 or s_l = 7 or s_l = 8 or s_l = 9)) then
rgb := "100";
else
-- linker Punktestand - Seg G --
if ((row > 45 and row < 50 and column > 250 and column < 275) and (s_l = 2 or s_l = 3 or s_l = 4 or s_l = 5 or s_l = 6 or s_l = 8 or s_l = 9)) then
rgb := "100";
else
-- rechter Punktestand - Seg A --
if ((row > 20 and row < 45 and column > 390 and column < 395) and (s_r = 0 or s_r = 1 or s_r = 2 or s_r = 3 or s_r = 4 or s_r = 7 or s_r = 8 or s_r = 9)) then
rgb := "001";
else
-- rechter Punktestand - Seg B --
if ((row > 50 and row < 75 and column > 390 and column < 395) and (s_r = 0 or s_r = 1 or s_r = 5 or s_r = 6 or s_r = 3 or s_r = 4 or s_r = 7 or s_r = 8 or s_r = 9)) then
rgb := "001";
else
-- rechter Punktestand - Seg C --
if ((row > 75 and row < 80 and column > 365 and column < 390) and (s_r = 0 or s_r = 2 or s_r = 3 or s_r = 5 or s_r = 6 or s_r = 8 or s_r = 9)) then
rgb := "001";
else
-- rechter Punktestand - Seg D --
if ((row > 50 and row < 75 and column > 360 and column < 365) and (s_r = 0 or s_r = 2 or s_r = 6 or s_r = 8)) then
rgb := "001";
else
-- rechter Punktestand - Seg E --
if ((row > 20 and row < 45 and column > 360 and column < 365) and (s_r = 0 or s_r = 4 or s_r = 5 or s_r = 6 or s_r = 8 or s_r = 9)) then
rgb := "001";
else
-- rechter Punktestand - Seg F --
if ((row > 15 and row < 20 and column > 365 and column < 390) and (s_r = 0 or s_r = 2 or s_r = 3 or s_r = 5 or s_r = 6 or s_r = 7 or s_r = 8 or s_r = 9)) then
rgb := "001";
else
-- rechter Punktestand - Seg G --
if ((row > 45 and row < 50 and column > 365 and column < 390) and (s_r = 2 or s_r = 3 or s_r = 4 or s_r = 5 or s_r = 6 or s_r = 8 or s_r = 9)) then
rgb := "001";
else
-- Ball (15x15 Pixel)
if (row > ball_pos_y and row < (ball_pos_y + 15) and column > ball_pos_x and column < (ball_pos_x + 15)) then
rgb := "111";
else
rgb := "000";
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
-- Ausgabe der rgb Werte
red <= rgb(2);
green <= rgb(1);
blue <= rgb(0);
end process;
Game physics
This process is responsible for the movement of objects, for the score board and for processing the user's input.
The moving vector for the ball is generated by a separate procedure.
-- Procedure zur zufälligen Ermittlung der Ballbewegung (in rand: Zufallszahl, out x,y: Vektor)
procedure rand_vec(rand : in integer range 0 to 28 := 0; x,y : out integer range -3 to 3) is
begin
case (rand) is
when 0 =>
x := -1;
y := -1;
when 1 =>
x := 1;
y := -1;
when 2 =>
x := 1;
y := 1;
when 3 =>
x := -1;
y := 1;
when 4 =>
x := -2;
y := -1;
when 5 =>
x := -1;
y := -2;
when 6 =>
x := 1;
y := -2;
when 7 =>
x := 2;
y := -1;
when 8 =>
x := 2;
y := 1;
when 9 =>
x := 1;
y := 2;
when 10 =>
x := -1;
y := 2;
when 11 =>
x := -2;
y := 1;
when 12 =>
x := -3;
y := -1;
when 13 =>
x := -1;
y := -3;
when 14 =>
x := 1;
y := -3;
when 15 =>
x := 3;
y := -1;
when 16 =>
x := 3;
y := 1;
when 17 =>
x := 1;
y := 3;
when 18 =>
x := -1;
y := 3;
when 19 =>
x := -3;
y := 1;
when 20 =>
x := -3;
y := -2;
when 21 =>
x := -2;
y := -3;
when 22 =>
x := 2;
y := -3;
when 23 =>
x := 3;
y := -2;
when 24 =>
x := 3;
y := 2;
when 25 =>
x := 2;
y := 3;
when 26 =>
x := -2;
y := 3;
when 27 =>
x := -3;
y := 2;
when others =>
x := -1;
y := -1;
end case;
end procedure rand_vec;
Process:
control: process(clock, start) -- Spielphysik und Steuerung
variable random : integer range 0 to 28 := 0; -- Zufallszahl
variable dir_x, dir_y : integer range -3 to 3; -- Bewegungsvektor des Balls
-- Zählvariablen für Zeitteilung vom Clocksignal
variable cnt_ball, cnt_balken, cnt_ball_mod : integer range 0 to 101 := 0;
variable cnt : integer range 0 to 2002 := 0;
begin
if (clock'event and clock = '1') then
if (cnt /= 2000) then -- Runterskalierung des Clocksignals
cnt := cnt +1;
else
cnt := 0;
cnt_ball := cnt_ball + 1; -- weiter Skalierung des Takts
cnt_balken := cnt_balken + 1; -- weiter Skalierung des Takts
random := random + 1; -- Zufallszahl wird durchgezählt
if (random = 28) then -- Zurücksetzten der Zufallszahl (0..27)
random := 0;
end if;
if (cnt_ball = 101) then --Zurücksetzten des Zählers
cnt_ball := 0;
end if;
if (cnt_balken = 76) then --Zurücksetzten des Zählers
cnt_balken := 0;
end if;
-- Resetverhalten
if (start = '0') then
ball_pos_x <= 320; -- Ball wird in die Mitte positioniert
ball_pos_y <= 240;
s_l <= 0; -- Sore zurücksetzen
s_r <= 0;
cnt_ball_mod := 100; -- Ballgeschwindigkeit auf langsam
-- Auruf der Zufallsfunktion um zufälligen Bewegungsverktor zu erhalten
rand_vec(rand => random, x => dir_x, y => dir_y);
else
-- Spielstart
-- Ballbewegung
if (cnt_ball >= cnt_ball_mod) then
ball_pos_y <= ball_pos_y + dir_y; -- Ballbewegung Y
ball_pos_x <= ball_pos_x + dir_x; -- Ballbewegung X
cnt_ball := 0; -- Counter zurücksetzten
-- oberer Rand detektiert
if (ball_pos_y <= 3) then -- Abfragung vorher, weil Bewegung >1 sein kann
dir_y := (-1*dir_y); -- "Abprallen" - Y Vektor negiert
ball_pos_y <= 4;
end if;
-- unterer Rand detektiert
if (ball_pos_y >= (479-15)) then -- Abfragung vorher, weil Bewegung >1 sein kann
dir_y := (-1*dir_y); -- "Abprallen" - Y Vektor negiert
ball_pos_y <= 479-16;
end if;
-- Ball trift auf Balken (rechts)
if (ball_pos_x >= (623-15) and dir_x > 0 and ball_pos_y > (beam_pos_right - 14) and ball_pos_y < (beam_pos_right + 70 + 14)) then
dir_x := (-1*dir_x); -- "Abprallen" - X Vektor negiert
if (cnt_ball_mod >= 5) then -- Erhöhung der Ballgeschwindigkeit
cnt_ball_mod := cnt_ball_mod - 3;
end if;
end if;
-- Ball trift auf Balken (links)
if (ball_pos_x <= 16 and dir_x < 0 and ball_pos_y > (beam_pos_left - 14) and ball_pos_y < (beam_pos_left + 70 + 14)) then
dir_x := (-1*dir_x); -- "Abprallen" - X Vektor negiert
if (cnt_ball_mod >= 5) then -- Erhöhung der Ballgeschwindigkeit
cnt_ball_mod := cnt_ball_mod - 3;
end if;
end if;
-- Ball trift auch rechten Rand (linker Spieler punktet)
if (ball_pos_x >= (639-15-10)) then
ball_pos_x <= 320; -- Ball in die Mitte
ball_pos_y <= 240; -- Ball in die Mitte
s_l <= s_l + 1; -- Punkte erhöhen
cnt_ball_mod := 100; -- Ballgeschwindigkeit wieder auf langsam
-- Zufallsvektor für Ballbewegung bestimmen
rand_vec(rand => random, x => dir_x, y => dir_y);
end if;
-- Ball trift auch linkten Rand (rechter Spieler punktet)
if (ball_pos_x <= 10) then
ball_pos_x <= 320; -- Ball in die Mitte
ball_pos_y <= 240; -- Ball in die Mitte
s_r <= s_r + 1; -- Punkte erhöhen
cnt_ball_mod := 100; -- Ballgeschwindigkeit wieder auf langsam
-- Zufallsvektor für Ballbewegung bestimmen
rand_vec(rand => random, x => dir_x, y => dir_y);
end if;
end if;
-- Score wird zurückgesetzt wenn ein Spieler gewinnt
if (s_l = 10) or (s_r = 10) then
s_l <= 0;
s_r <= 0;
end if;
-- Bewegung der Balken bei Tastendruck, Geschwindigkeit durch cnt_balken
-- einstellbar. Randpositionen werden berücksichtigt.
if (cnt_balken >= 75) then
if (left_up = '1' and left_down = '0' and beam_pos_left > 1) then
beam_pos_left <= beam_pos_left - 2; -- linker Balken - aufwärts
end if;
if (left_up = '0' and left_down = '1' and beam_pos_left < 408) then
beam_pos_left <= beam_pos_left + 2; -- linker Balken - abwärts
end if;
if (right_up = '1' and right_down = '0' and beam_pos_right > 1) then
beam_pos_right <= beam_pos_right - 2; -- rehter Balken - aufwärts
end if;
if (right_up = '0' and right_down = '1' and beam_pos_right < 408) then
beam_pos_right <= beam_pos_right + 2; -- rechter Balken - abwärts
end if;
end if;
end if;
end if;
end if;
end process;
Complete source code
Library IEEE;
use IEEE.STD_Logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity vgatest is
port(clock, left_up, left_down, right_up, right_down, start : in std_logic;
R, G, B, H, V : out std_logic);
end entity;
architecture test of vgatest is
component vgadrive is
port( clock : in std_logic; -- 25.175 Mhz clock
red, green, blue : in std_logic; -- input values for RGB signals
row, column : out std_logic_vector(9 downto 0); -- for current pixel
Rout, Gout, Bout, H, V : out std_logic); -- VGA drive signals
end component;
-- Procedure zur zufälligen Ermittlung der Ballbewegung (in rand: Zufallszahl, out x,y: Vektor)
procedure rand_vec(rand : in integer range 0 to 28 := 0; x,y : out integer range -3 to 3) is
begin
case (rand) is
when 0 =>
x := -1;
y := -1;
when 1 =>
x := 1;
y := -1;
when 2 =>
x := 1;
y := 1;
when 3 =>
x := -1;
y := 1;
when 4 =>
x := -2;
y := -1;
when 5 =>
x := -1;
y := -2;
when 6 =>
x := 1;
y := -2;
when 7 =>
x := 2;
y := -1;
when 8 =>
x := 2;
y := 1;
when 9 =>
x := 1;
y := 2;
when 10 =>
x := -1;
y := 2;
when 11 =>
x := -2;
y := 1;
when 12 =>
x := -3;
y := -1;
when 13 =>
x := -1;
y := -3;
when 14 =>
x := 1;
y := -3;
when 15 =>
x := 3;
y := -1;
when 16 =>
x := 3;
y := 1;
when 17 =>
x := 1;
y := 3;
when 18 =>
x := -1;
y := 3;
when 19 =>
x := -3;
y := 1;
when 20 =>
x := -3;
y := -2;
when 21 =>
x := -2;
y := -3;
when 22 =>
x := 2;
y := -3;
when 23 =>
x := 3;
y := -2;
when 24 =>
x := 3;
y := 2;
when 25 =>
x := 2;
y := 3;
when 26 =>
x := -2;
y := 3;
when 27 =>
x := -3;
y := 2;
when others =>
x := -1;
y := -1;
end case;
end procedure rand_vec;
signal row, column : std_logic_vector(9 downto 0); -- X,Y-Position aktuellen Bildpixels
signal red, green, blue : std_logic := '0'; -- Farbsteuerung
-- Balkenposition (Y) für Spieler links und rechts
signal beam_pos_left, beam_pos_right: integer range 0 to 650 := 0;
-- Ballkoordinaten
signal ball_pos_x, ball_pos_y: integer range 0 to 650 := 0;
-- Spielstand für links und rechts
signal s_l, s_r : integer range 0 to 10 := 0;
begin
VGA : component vgadrive -- Einbindung des VGA Drivers
port map ( clock => clock, red => red, green => green, blue => blue,
row => row, column => column,
Rout => R, Gout => G, Bout => B, H => H, V => V);
control: process(clock, start) -- Spielphysik und Steuerung
variable random : integer range 0 to 28 := 0; -- Zufallszahl
variable dir_x, dir_y : integer range -3 to 3; -- Bewegungsvektor des Balls
-- Zählvariablen für Zeitteilung vom Clocksignal
variable cnt_ball, cnt_balken, cnt_ball_mod : integer range 0 to 101 := 0;
variable cnt : integer range 0 to 2002 := 0;
begin
if (clock'event and clock = '1') then
if (cnt /= 2000) then -- Runterskalierung des Clocksignals
cnt := cnt +1;
else
cnt := 0;
cnt_ball := cnt_ball + 1; -- weiter Skalierung des Takts
cnt_balken := cnt_balken + 1; -- weiter Skalierung des Takts
random := random + 1; -- Zufallszahl wird durchgezählt
if (random = 28) then -- Zurücksetzten der Zufallszahl (0..27)
random := 0;
end if;
if (cnt_ball = 101) then --Zurücksetzten des Zählers
cnt_ball := 0;
end if;
if (cnt_balken = 76) then --Zurücksetzten des Zählers
cnt_balken := 0;
end if;
-- Resetverhalten
if (start = '0') then
ball_pos_x <= 320; -- Ball wird in die Mitte positioniert
ball_pos_y <= 240;
s_l <= 0; -- Sore zurücksetzen
s_r <= 0;
cnt_ball_mod := 100; -- Ballgeschwindigkeit auf langsam
-- Auruf der Zufallsfunktion um zufälligen Bewegungsverktor zu erhalten
rand_vec(rand => random, x => dir_x, y => dir_y);
else
-- Spielstart
-- Ballbewegung
if (cnt_ball >= cnt_ball_mod) then
ball_pos_y <= ball_pos_y + dir_y; -- Ballbewegung Y
ball_pos_x <= ball_pos_x + dir_x; -- Ballbewegung X
cnt_ball := 0; -- Counter zurücksetzten
-- oberer Rand detektiert
if (ball_pos_y <= 3) then -- Abfragung vorher, weil Bewegung >1 sein kann
dir_y := (-1*dir_y); -- "Abprallen" - Y Vektor negiert
ball_pos_y <= 4;
end if;
-- unterer Rand detektiert
if (ball_pos_y >= (479-15)) then -- Abfragung vorher, weil Bewegung >1 sein kann
dir_y := (-1*dir_y); -- "Abprallen" - Y Vektor negiert
ball_pos_y <= 479-16;
end if;
-- Ball trift auf Balken (rechts)
if (ball_pos_x >= (623-15) and dir_x > 0 and ball_pos_y > (beam_pos_right - 14) and ball_pos_y < (beam_pos_right + 70 + 14)) then
dir_x := (-1*dir_x); -- "Abprallen" - X Vektor negiert
if (cnt_ball_mod >= 5) then -- Erhöhung der Ballgeschwindigkeit
cnt_ball_mod := cnt_ball_mod - 3;
end if;
end if;
-- Ball trift auf Balken (links)
if (ball_pos_x <= 16 and dir_x < 0 and ball_pos_y > (beam_pos_left - 14) and ball_pos_y < (beam_pos_left + 70 + 14)) then
dir_x := (-1*dir_x); -- "Abprallen" - X Vektor negiert
if (cnt_ball_mod >= 5) then -- Erhöhung der Ballgeschwindigkeit
cnt_ball_mod := cnt_ball_mod - 3;
end if;
end if;
-- Ball trift auch rechten Rand (linker Spieler punktet)
if (ball_pos_x >= (639-15-10)) then
ball_pos_x <= 320; -- Ball in die Mitte
ball_pos_y <= 240; -- Ball in die Mitte
s_l <= s_l + 1; -- Punkte erhöhen
cnt_ball_mod := 100; -- Ballgeschwindigkeit wieder auf langsam
-- Zufallsvektor für Ballbewegung bestimmen
rand_vec(rand => random, x => dir_x, y => dir_y);
end if;
-- Ball trift auch linkten Rand (rechter Spieler punktet)
if (ball_pos_x <= 10) then
ball_pos_x <= 320; -- Ball in die Mitte
ball_pos_y <= 240; -- Ball in die Mitte
s_r <= s_r + 1; -- Punkte erhöhen
cnt_ball_mod := 100; -- Ballgeschwindigkeit wieder auf langsam
-- Zufallsvektor für Ballbewegung bestimmen
rand_vec(rand => random, x => dir_x, y => dir_y);
end if;
end if;
-- Score wird zurückgesetzt wenn ein Spieler gewinnt
if (s_l = 10) or (s_r = 10) then
s_l <= 0;
s_r <= 0;
end if;
-- Bewegung der Balken bei Tastendruck, Geschwindigkeit durch cnt_balken
-- einstellbar. Randpositionen werden berücksichtigt.
if (cnt_balken >= 75) then
if (left_up = '1' and left_down = '0' and beam_pos_left > 1) then
beam_pos_left <= beam_pos_left - 2; -- linker Balken - aufwärts
end if;
if (left_up = '0' and left_down = '1' and beam_pos_left < 408) then
beam_pos_left <= beam_pos_left + 2; -- linker Balken - abwärts
end if;
if (right_up = '1' and right_down = '0' and beam_pos_right > 1) then
beam_pos_right <= beam_pos_right - 2; -- rehter Balken - aufwärts
end if;
if (right_up = '0' and right_down = '1' and beam_pos_right < 408) then
beam_pos_right <= beam_pos_right + 2; -- rechter Balken - abwärts
end if;
end if;
end if;
end if;
end if;
end process;
RGB : process(row, column) -- VGA Ausgabe
variable rgb : std_logic_vector (2 downto 0) := "000"; -- rot, grün, blau für VGADriver
begin
-- linker Balken (15x70 Pixel)
if (row > beam_pos_left and row < (beam_pos_left + 70) and column > 0 and column < 15) then
rgb := "100"; -- Farbe: rot 1, grün 0, blau, 0
else
-- rechter Balken (15x70 Pixel)
if (row > beam_pos_right and row < (beam_pos_right + 70) and column > 624 and column < 639) then
rgb := "001";
else
-- Mittellinie (6 Pixel breit)
if (row > 0 and row < 479 and column > 317 and column < 323) then
rgb := "111";
else
-- linker Punktestand - Seg A --
if ((row > 20 and row < 45 and column > 275 and column < 280) and (s_l = 0 or s_l = 1 or s_l = 2 or s_l = 3 or s_l = 4 or s_l = 7 or s_l = 8 or s_l = 9)) then
rgb := "100";
else
-- linker Punktestand - Seg B --
if ((row > 50 and row < 75 and column > 275 and column < 280) and (s_l = 0 or s_l = 1 or s_l = 5 or s_l = 6 or s_l = 3 or s_l = 4 or s_l = 7 or s_l = 8 or s_l = 9)) then
rgb := "100";
else
-- linker Punktestand - Seg C --
if ((row > 75 and row < 80 and column > 250 and column < 275) and (s_l = 0 or s_l = 2 or s_l = 3 or s_l = 5 or s_l = 6 or s_l = 8 or s_l = 9)) then
rgb := "100";
else
-- linker Punktestand - Seg D --
if ((row > 50 and row < 75 and column > 245 and column < 250) and (s_l = 0 or s_l = 2 or s_l = 6 or s_l = 8)) then
rgb := "100";
else
-- linker Punktestand - Seg E --
if ((row > 20 and row < 45 and column > 245 and column < 250) and (s_l = 0 or s_l = 4 or s_l = 5 or s_l = 6 or s_l = 8 or s_l = 9)) then
rgb := "100";
else
-- linker Punktestand - Seg F --
if ((row > 15 and row < 20 and column > 250 and column < 275) and (s_l = 0 or s_l = 2 or s_l = 3 or s_l = 5 or s_l = 6 or s_l = 7 or s_l = 8 or s_l = 9)) then
rgb := "100";
else
-- linker Punktestand - Seg G --
if ((row > 45 and row < 50 and column > 250 and column < 275) and (s_l = 2 or s_l = 3 or s_l = 4 or s_l = 5 or s_l = 6 or s_l = 8 or s_l = 9)) then
rgb := "100";
else
-- rechter Punktestand - Seg A --
if ((row > 20 and row < 45 and column > 390 and column < 395) and (s_r = 0 or s_r = 1 or s_r = 2 or s_r = 3 or s_r = 4 or s_r = 7 or s_r = 8 or s_r = 9)) then
rgb := "001";
else
-- rechter Punktestand - Seg B --
if ((row > 50 and row < 75 and column > 390 and column < 395) and (s_r = 0 or s_r = 1 or s_r = 5 or s_r = 6 or s_r = 3 or s_r = 4 or s_r = 7 or s_r = 8 or s_r = 9)) then
rgb := "001";
else
-- rechter Punktestand - Seg C --
if ((row > 75 and row < 80 and column > 365 and column < 390) and (s_r = 0 or s_r = 2 or s_r = 3 or s_r = 5 or s_r = 6 or s_r = 8 or s_r = 9)) then
rgb := "001";
else
-- rechter Punktestand - Seg D --
if ((row > 50 and row < 75 and column > 360 and column < 365) and (s_r = 0 or s_r = 2 or s_r = 6 or s_r = 8)) then
rgb := "001";
else
-- rechter Punktestand - Seg E --
if ((row > 20 and row < 45 and column > 360 and column < 365) and (s_r = 0 or s_r = 4 or s_r = 5 or s_r = 6 or s_r = 8 or s_r = 9)) then
rgb := "001";
else
-- rechter Punktestand - Seg F --
if ((row > 15 and row < 20 and column > 365 and column < 390) and (s_r = 0 or s_r = 2 or s_r = 3 or s_r = 5 or s_r = 6 or s_r = 7 or s_r = 8 or s_r = 9)) then
rgb := "001";
else
-- rechter Punktestand - Seg G --
if ((row > 45 and row < 50 and column > 365 and column < 390) and (s_r = 2 or s_r = 3 or s_r = 4 or s_r = 5 or s_r = 6 or s_r = 8 or s_r = 9)) then
rgb := "001";
else
-- Ball (15x15 Pixel)
if (row > ball_pos_y and row < (ball_pos_y + 15) and column > ball_pos_x and column < (ball_pos_x + 15)) then
rgb := "111";
else
rgb := "000";
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
-- Ausgabe der rgb Werte
red <= rgb(2);
green <= rgb(1);
blue <= rgb(0);
end process;
end architecture;
Comments
The gadget spec URL could not be found