Two games: Pong and Driver

I made both of these games in one free evening. I spent circa 2 hours writing each of them, so it should be relatively simple to understand how they work. I will try to describe them briefly now.

The first game is a typical Pong. There is one paddle and one ball bouncing off the walls. To made it simple, the ball can bounce off only at right angle. In the program I created three tasks. The first one, main one, counts points and changes difficulty of the game. Every 2 seconds the paddle becomes shorter or the ball starts to move faster. Player gets one point every 2 seconds so the overal score depends on the time of playing.

The second task makes the ball bounce and checks if it bounces off the paddle or not. If the ball is on the edge of the screen it changes direction thus bounces. The last task draws the paddle and handles its movement according to buttons being pressed.

There is one interesting piece of code. LineOut(0, PADDLE_HEIGHT, 99, PADDLE_HEIGHT, DRAW_OPT_CLEAR); LineOut(x1, PADDLE_HEIGHT, x2, PADDLE_HEIGHT); It draws a horizontal line in WHITE erasing the paddle and then draws the paddle in a new position. It is done that way for two reasons. First of all erasing the whole screen is very time-consuming and makes the display flicker. Furthermore the ball would be erased too. Drawing in white is a very neat trick making the whole program work faster and better.

Now, the second game - The Driver. Rules are simple: keep the dot-car inside the track. Once again I used three tasks. In the first one I handle drawing the road on the screen. The road, precisely center of the road, is stored in an array being a cyclic buffer. You may have noticed that in every step of the game only one new segment is generated and 63 stay the same. I used that knowledge to greatly speed up execution time of the program. Let me give an example. In one moment I read segments of the road in order: 13 to 63 and then 0 to 12, where 13 is the beginning. Now in the next step I generate one new segment and store it on the 13-th position. New road should be read as follows: 14 to 63 and then 0 to 13. You can see now, that the beginning of the road moves cyclically from 0 to 63, and I don't need to change position of any old segments in the array. This is huge improvement.

The second task controls the position of the dot-car. You can move it using arrow-shaped buttons on the NXT-brick. The last task constantly checks the car's position and if it's outside the track it ends the game.

Both programs are extremally simple in theory. My implementation may not be simple but I don't work on new projects because they are simple. Simple is boring. I know I might have made these games more interesting and fun. For example the track in The Driver might have been generated more wisely (less randomly and more smoothly). In The Pong the ball might have bounced off at angles different than 90 degrees or there might have been more paddles controlled by a player. Maybe I will improve it someday.

Feel free to download the code from below and play my games on yours NXT brick!

Video


Gallery

3D Gallery

To watch pictures in this gallery you need special red-cyan anaglyph glasses. They are to buy in many Internet stores for less than 1$.


No images.

Program code

//author: Krzysztof Kapusta, All rights reserved // PONG - The Game #define PADDLE_HEIGHT 4 #define BALL_SIZE 2 int PADDLE_PERIOD = 20; int PADDLE_WIDTH = 15; int PADDLE_X = 50; int BALL_X = 50; int BALL_Y = 32; int BALL_PERIOD = 100; bool BALL_GOING_LEFT = true; bool BALL_GOING_UP = true; int SCORE = 0; safecall void drawPaddle() { int x1, x2; x1 = PADDLE_X - PADDLE_WIDTH/2; if (x1 < 0) x1 = 0; x2 = PADDLE_X + PADDLE_WIDTH/2; if (x2 > 99) x2 = 99; LineOut(0, PADDLE_HEIGHT, 99, PADDLE_HEIGHT, DRAW_OPT_CLEAR); LineOut(x1, PADDLE_HEIGHT, x2, PADDLE_HEIGHT); } task main(); task paddle() { drawPaddle(); while(true) { while (!ButtonPressed(BTNLEFT, false) && !ButtonPressed(BTNRIGHT, false)); if (ButtonPressed(BTNLEFT, false)) { while (ButtonPressed(BTNLEFT, false)) { if (PADDLE_X > 0) PADDLE_X--; drawPaddle(); Wait(PADDLE_PERIOD); } } else if (ButtonPressed(BTNRIGHT, false)) { while (ButtonPressed(BTNRIGHT, false)) { if (PADDLE_X < 99) PADDLE_X++; drawPaddle(); Wait(PADDLE_PERIOD); } } } } task ball() { int left, bottom; while(true) { left = BALL_X - BALL_SIZE/2 + (BALL_SIZE%2 == 0 ? 1 : 0); bottom = BALL_Y - BALL_SIZE/2 + (BALL_SIZE%2 == 0 ? 1 : 0); RectOut(left, bottom, BALL_SIZE, BALL_SIZE, DRAW_OPT_CLEAR); if (BALL_GOING_LEFT) { if (left > 0) BALL_X--; else { BALL_GOING_LEFT = false; BALL_X++; } } else { if (left+BALL_SIZE-1 < 99) BALL_X++; else { BALL_GOING_LEFT = true; BALL_X--; } } if (BALL_GOING_UP) { if (bottom+BALL_SIZE-1 < 63) BALL_Y++; else { BALL_GOING_UP = false; BALL_Y--; } } else { if (bottom > PADDLE_HEIGHT) BALL_Y--; else { if (left+BALL_SIZE-1 < PADDLE_X + PADDLE_WIDTH/2 + 1 && left > PADDLE_X - PADDLE_WIDTH/2 - 1) { BALL_GOING_UP = true; BALL_Y++; drawPaddle(); } else { StopTask(paddle); StopTask(main); ClearScreen(); TextOut(25, LCD_LINE4, "GAME OVER"); TextOut(15, LCD_LINE5, FormatNum("Score: %d", SCORE)); Wait(5000); StopAllTasks(); } } } RectOut(BALL_X, BALL_Y, 2, 2); Wait(BALL_PERIOD); } } task main() { StartTask(paddle); StartTask(ball); while(true) { for (int i = 0; i<5; i++) { Wait(SEC_2); SCORE++; if (BALL_PERIOD >= 10) BALL_PERIOD -= 5; if (PADDLE_PERIOD > BALL_PERIOD+2) PADDLE_PERIOD = BALL_PERIOD+2; } Wait(SEC_2); SCORE++; if (PADDLE_WIDTH > 5) PADDLE_WIDTH -= 2; } }

//author: Krzysztof Kapusta, All rights reserved // Driver - The Game #define CAR_SIZE 3 #define CAR_HEIGHT 8 int CAR_PERIOD = 80; int ROAD_WIDTH = 9; int CAR_X = 50; int ROAD_CENTER[]; int ROAD_TAB_POS = 0; int ROAD_PERIOD = 40; int prev_x = 50; safecall void drawCar() { int left; left = prev_x - CAR_SIZE/2 + (CAR_SIZE % 2 == 0 ? 1 : 0); RectOut(left, CAR_HEIGHT, CAR_SIZE - 1, CAR_SIZE - 1, DRAW_OPT_CLEAR | DRAW_OPT_FILL_SHAPE); left = CAR_X - CAR_SIZE/2 + (CAR_SIZE % 2 == 0 ? 1 : 0); RectOut(left, CAR_HEIGHT, CAR_SIZE - 1, CAR_SIZE - 1, DRAW_OPT_FILL_SHAPE); prev_x = CAR_X; } void drawRoad() { int pos = ROAD_TAB_POS; for (int i = 0; i<64; i++) { LineOut(0, i, 99, i, DRAW_OPT_CLEAR); PointOut(ROAD_CENTER[pos] - ROAD_WIDTH/2, i); PointOut(ROAD_CENTER[pos] + (ROAD_WIDTH-1)/2, i); if (i == CAR_HEIGHT + CAR_SIZE - 1) drawCar(); pos = (pos + 1) % 64; } } task generateRoad() { int newval; while (true) { newval = ROAD_CENTER[ROAD_TAB_POS-2 < 0 ? ROAD_TAB_POS+62 : ROAD_TAB_POS-2] + (Random(5)-2)/2; if (newval < 0) newval = 0; else if (newval > 99) newval = 99; ROAD_CENTER[ROAD_TAB_POS-1 < 0 ? 63 : ROAD_TAB_POS-1] = newval; ROAD_TAB_POS = (ROAD_TAB_POS+1)%64; Wait(ROAD_PERIOD); } } task car() { drawCar(); while(true) { while (!ButtonPressed(BTNLEFT, false) && !ButtonPressed(BTNRIGHT, false)); if (ButtonPressed(BTNLEFT, false)) { while (ButtonPressed(BTNLEFT, false)) { if (CAR_X > 0) CAR_X--; drawCar(); Wait(CAR_PERIOD); } } else if (ButtonPressed(BTNRIGHT, false)) { while (ButtonPressed(BTNRIGHT, false)) { if (CAR_X < 99) CAR_X++; drawCar(); Wait(CAR_PERIOD); } } } } task road() { ArrayInit(ROAD_CENTER, 50, 64); while (true) { drawRoad(); if (CAR_X - CAR_SIZE/2 + (CAR_SIZE % 2 == 0 ? 1 : 0) < ROAD_CENTER[(ROAD_TAB_POS+CAR_HEIGHT)%64] - ROAD_WIDTH/2 || CAR_X + CAR_SIZE/2 > ROAD_CENTER[(ROAD_TAB_POS+CAR_HEIGHT)%64] + (ROAD_WIDTH-1)/2) { StopAllTasks(); } } } task main() { StartTask(generateRoad); StartTask(car); StartTask(road); }

Show/hide all the code

Comments

to jeeeeeeeeeeeeeeeeeeeeeeeeeeest suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuper

Posted by: kuba | 2013-11-06

OOO nareszcie działa trzeba było dać NXT... :P Ale wielkie THX za to :)

Posted by: RedstonePL | 2013-01-16

A w BrixCC jak to włączam to co mam zaznaczyć? Bo reszte chyba ogarniam :)

Posted by: RedstonePL | 2013-01-16

bricxcc.sourceforge.net/test_releases/test_release20121101.zip Wystarczy poszukać

Posted by: Admin | 2013-01-16

A gdzie dokładniej można pobrać enhanced?

Posted by: RedstonePL | 2013-01-16

See all comments

Post your comment