This prototype extends the one described in the previous section to make a single player game that includes a 'paddle' drawn on left-hand edge of the screen. The position of the paddle is determined by a potentiometer (ADC1) fitted to the evaluation board that provides a voltage input to the Analog-Digital (A-D) Converter.
helloPong_c2v0
, and within this, a new project. Configure the RTE to include board support software components for the Graphic LCD (API) and A/D Converter (API). Alternatively, clone the folder helloBounce_c2v0
, from the previous recipe and modify the RTE. Use Resolve to automatically load any missing libraries.helloBounce.c
and helloBounce.h
from the previous recipe, rename them helloPong.c
and helloPong.h
, and include these in your project. Change the #include
in helloPong.c
, and replace helloBounce.h
with helloPong.h
. Build the program and test it as before.#include "Board_ADC.h"
and call ADC_Initialize()
in main()
.update_ball()
, and move the code concerned with updating the ball's position and collision detection into the body of the function. This tidies up the superloop and makes the main function much easier to read.helloPong.c
to hold the position of the ball, paddle, and information about the Game.#define wait_delay HAL_Delay #define WIDTH GLCD_WIDTH #define HEIGHT GLCD_HEIGHT #define CHAR_H GLCD_Font_16x24.height /* Character Height (in pixels) */ #define CHAR_W GLCD_Font_16x24.width /* Character Width (in pixels) */ #define BAR_W 6 /* Bar Width (in pixels) */ #define BAR_H 24 /* Bar Height (in pixels) */ #define T_LONG 1000 /* Long delay */ #define T_SHORT 5 /* Short delay */ typedef struct { int dirn; int x; int y; } BallInfo; typedef struct { int x; int y; } PaddleInfo; typedef struct { unsigned int num_ticks; BallInfo ball; PaddleInfo p1; } GameInfo; /* Function Prototypes */ void game_Initialize(void); void update_ball (void); void update_player (void); void check_collision (void);
pong.c
:GameInfo thisGame;
game_Initialize()
. This function initializes the values of the global variables./*------------------------------------------------ * game_Init() * Initialize some game parameters. *-------------------------------------------------*/ void game_Initialize(void) init_pstn.dirn = 1; init_pstn.x = WIDTH-CHAR_W)/2; init_pstn.y = (HEIGHT-CHAR_H)/2; thisGame.ball = init_pstn; thisGame.p1.x = 0; thisGame.p1.y = 0; thisGame.num_ticks = T_SHORT; }
check_collision()
, and copy the code concerned with collision detection into this function. Modify the function check_collision()
to check for collisions between the ball and the paddle as well as collisions between the ball and screen edge./*-------------------------------------------------- * check_collision(void) * check for contact between ball and screen * edges/bat and change direction accordingly *---------------------------------------------------*/ void check_collision(void) { /* check collision with RH vertical screen edge OR P1 paddle */ if ((thisGame.ball.x == BAR_W) || thisGame.ball.x == (WIDTH-CHAR_W)) { switch (thisGame.ball.dirn) { case 0: thisGame.ball.dirn = (thisGame.ball.dirn+4)%8; break; case 1: thisGame.ball.dirn = (thisGame.ball.dirn+2)%8; break; case 3: if ( (thisGame.ball.y >= thisGame.p1.y-CHAR_H) && (thisGame.ball.y <= (thisGame.p1.y+BAR_H)) ) thisGame.ball.dirn = (thisGame.ball.dirn+6)%8; else /* empty statement */ break; case 4: if ( (thisGame.ball.y >= thisGame.p1.y-CHAR_H) && (thisGame.ball.y <= (thisGame.p1.y+BAR_H)) ) thisGame.ball.dirn = (thisGame.ball.dirn+4)%8; else /* empty statement */; break; case 5: if ( (thisGame.ball.y >= thisGame.p1.y-CHAR_H) && (thisGame.ball.y <= (thisGame.p1.y+BAR_H)) ) thisGame.ball.dirn = (thisGame.ball.dirn+2)%8; else /* empty statement */; break; case 7: thisGame.ball.dirn = (thisGame.ball.dirn+6)%8; break; } } /* check collision with horizontal screen edge */ if ((thisGame.ball.y < 0) || thisGame.ball.y > (HEIGHT-CHAR_H)) { switch (thisGame.ball.dirn) { case 1: thisGame.ball.dirn = (thisGame.ball.dirn+6)%8; thisGame.ball.y++; break; case 2: thisGame.ball.dirn = (thisGame.ball.dirn+4)%8; thisGame.ball.y++; break; case 3: thisGame.ball.dirn = (thisGame.ball.dirn+2)%8; thisGame.ball.y++; break; case 5: thisGame.ball.dirn = (thisGame.ball.dirn+6)%8; thisGame.ball.y--; break; case 6: thisGame.ball.dirn = (thisGame.ball.dirn+4)%8; thisGame.ball.y--; break; case 7: thisGame.ball.dirn = (thisGame.ball.dirn+2)%8; thisGame.ball.y--; break; } } }
update_ball()
:/* reset position */ if (thisGame.ball.x<BAR_W) { wait_delay (T_LONG); /* Erase Ball */ GLCD_DrawChar( thisGame.ball.x, thisGame.ball.y, ' '); thisGame.ball = init_pstn; }
GLCD_customFont_16x24
in the file GLCD_customFont.c
, and add this to the project.#include "Board_GLCD.h" static const uint8_t customFont_16x24_h[] = { /* PONG PADDLE */ 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x3F, }; GLCD_FONT GLCD_customFont_16x24 = { 16, ///< Character width 24, ///< Character height 0, ///< Character offset 1, ///< Character count customFont_16x24_h ///< Characters bitmaps };
update_player()
by adding the following code fragment:/*--------------------------------------------- * update_player(unsigned int *) * Read the ADC and draw the player 1's paddle *----------------------------------------------*/ void update_player(void) { int adcValue; static int lastValue = 0; ADC_StartConversion(); adcValue = ADC_GetValue (); adcValue = (adcValue >> 4) * (HEIGHT-BAR_H)/256; /* Erase Paddle */ GLCD_DrawChar (0, lastValue, ' '); /* Draw Paddle */ GLCD_SetFont (&GLCD_customFont_16x24); GLCD_DrawChar (0, adcValue, 0x00 ); GLCD_SetFont (&GLCD_Font_16x24); lastValue = adcValue; thisGame.p1.y = adcValue; }
helloPong.h
, and include this in pong.c
with a #include
preprocessor directive./*-------------------------------------------------- * Recipe: helloPong_c1v0 * Name: helloPong.h * Purpose: pong function prototypes and defs *-------------------------------------------------- * * Modification History * 06.02.14 Created * 09.12.15 Updated (uVision5.17 + DFP2.6.0) * * Dr Mark Fisher, CMP, UEA, Norwich, UK *--------------------------------------------------*/ #ifndef _PONG_H #define _PONG_H #define wait_delay HAL_Delay #define WIDTH GLCD_WIDTH #define HEIGHT GLCD_HEIGHT #define CHAR_H GLCD_Font_16x24.height /* Character Height (in pixels) */ #define CHAR_W GLCD_Font_16x24.width /* Character Width (in pixels) */ #define BAR_W 6 /* Bar Width (in pixels) */ #define BAR_H 24 /* Bar Height (in pixels) */ #define T_LONG 1000 /* Long delay */ #define T_SHORT 5 /* Short delay */ typedef struct { int dirn; int x; int y; } BallInfo; typedef struct { int x; int y; } PaddleInfo; typedef struct { unsigned int num_ticks; BallInfo ball; PaddleInfo p1; } GameInfo; /* Function Prototypes */ void game_Initialize(void); void update_ball (void); void update_player (void); void check_collision (void); #endif /* _PONG_H */
game_Initialize()
, update_ball()
, update_player()
, and check_collision()
can be moved to a file called pong_utils.c
, which shares the header pong.h
.The data structures defined within pong.h
define three new compound data types which build on the primitive types such as char, integer, and so on, which are part of the language. A global variable thisGame
stores all the data used in the application. The main file helloPong.c
is shown in step 6. New functions game_Initialize()
, update_ball()
, update_player()
, and check_collision()
have been defined within the file pong_utils.c
(and delay has also been moved) to declutter main and improve the readability of the code. The function prototypes are shown in step 9.
The function game_Initialize( )
writes the initial values to the global structs, gameInfo
and init_pstn()
. The function update_player()
(step 10) reads the A-D converter, and draws the paddle. Since the paddle may move in large increments, we must explicitly erase the paddle, and redraw it in a new position. The static
qualifier is used to ensure that the variable lastValue
persists after the function has terminated (that is, it behaves rather like a global variable, although its scope is local to the function). It is important to understand the scoping rules for variables. Variables declared within a function (so-called automatic variables) can only be changed by assignments within the function. But variables declared outside a function have global scope, and can be accessed by any function declared within the same file. The variable gameInfo
is a global variable and can be accessed by any function declared in helloPong.c
, and because of the extern declaration, by any function declared in pong_utils.c
.
The functions named check_collision()
and update_ball()
are similar to those described in the previous section but with some important additions. When the ball moves in directions 3, 4, or 5, we need to check for a collision with the paddle; modifications necessary to achieve this are shown in step 8. If the ball fails to make contact with the paddle, then a clause in update_ball()
holds the ball in its current position for a few seconds, and then restarts the game (see step 9).
The paddle itself can be drawn by declaring our own 'paddle' character bitmap in file GLCD_customFont.c
, and by using GLCD_DrawChar()
to render it to the screen. The code for checking collisions needs to be extended to include collisions between the ball and the inner vertical edge of the paddle. These can only occur when the ball direction is from right to left (that is, direction codes 3, 4 and 5). We'll need variables to represent the position of the paddle (as we do in case of the ball). As we now have quite a few variables, it's a good opportunity to introduce a data structure that can be used to group them together. The C struct provides us with a mechanism for achieving this. Information about the ball are declared in a struct called ballInfo
. The information associated with the paddle is declared in paddleInfo
and that about the game in gameInfo
, within helloPong.h
3.138.37.20