PONGuino – Arduino & S65 Shield – 2 Player MiniGame
I wanted a playable game to show the possibilities of the Arduino plattform in workshops. Since I already have a S65-Shield built in a box with two joysticks, I wanted to use that MirrorBot hardware for presentation.
The most funny code I could find for the S65 Shield was a Pong game by
This mod uses one axis of two joysticks – or two potis connected to input pins to move the two bats of the two players up and down.
The AI and encoder codes were removed and replaced by a similar check:
If the joystick potis are moved up or down, they decrease or increase the y position value of the matching bat.
If you have a s65 shield and would like to run this code, you could connect two potis to input pins (e.g. 0,1) and try those. Thes bats ‘should’ move, if you move the pots out of the center position.
I included a veeery simple level system:
After each round a level int variable gets increased. This level var decreases a delay() (starting at 10 ms) in the main loop -that way, so the game gets faster, the more rounds you play ( 5 times).
🙂
….
This game is minimal, but already fun to play.
At least a six year old friend of mine often asks to play it – again and again 😉
It’s interesting to see, how fast this micro system works. Very fast animations are not possible, because there is no double buffering for the LCD, but everything runs just fast enough to keep you (or a six year old) concentrated on the screen.
.
..
.
.
more s65 shields projects here: MirrorBot
..
here is the modified 2 player PONGuino code
– feel free to use it & we hope someone shares another extension!
.
.
/* PONGuino Mini Game Arduino Pong using S65 Shield - Mod by tk@synoptx.net 1.2010 original: http://www.codetorment.com/2009/11/11/arduino-pong-using-s65-shield/ mod: http://lab.synoptx.net/2010/02/12/ponguino-arduino-s65-shield-2-player-minigame/ += - 2 player with 2 joysticks (2x 1 y axis) or pots - super simple levels (decreasing delay) - intro & score screens Just a trial to get to know the s65shield library, but nice gameplay ;) ThanX to codetorment for the code! licensed under a Creative Commons Attribution- Noncommercial-Share Alike 3.0 Unported License BY NC SA */ #include <S65Display.h> #include <RotaryEncoder.h> #include <string.h> #define WIDTH 176 #define HEIGHT 132 #define BAT_WIDTH 5 #define BAT_HEIGHT 30 #define BAT_INIT_X 5 #define BAT_INIT_Y 56 #define BALL 5 #define BAT_STEP 1 #define AI_STEP 2 #define MAX_TURNS 2 // hw pins: const byte pinJoyLeftY = 1; // analog in 1 - LEFT joystick Y axis const byte pinJoyRightY = 0; // analog in 0 - RIGHT joystick Y axis float ball_x = WIDTH / 2; float ball_x_prev = ball_x; float ball_y = HEIGHT / 2; float ball_y_prev = ball_y; float ball_dir = 0; float bat_1_dir = 0; int bat1_x = BAT_INIT_X; int bat1_y = BAT_INIT_Y; int bat1_y_prev = bat1_y; int bat2_x = WIDTH - BAT_INIT_X - BAT_WIDTH; int bat2_y = BAT_INIT_Y; int bat2_y_prev = bat2_y; int bat2_step = AI_STEP; int p1_score = 0; int p2_score = 0; int ball_angle = 0; // angle between ball path and horizontal axis int ball_angle_sign = 0; // sign of ball_angle : up = +1, down = -1, horizontal = 0 boolean score = false; boolean gameover = false; int level = 1; char buffer[256]; // string buffer S65Display lcd; RotaryEncoder encoder; // Encoder must be serviced regularly. ISR(TIMER2_OVF_vect) { TCNT2 -= 250; //1000 Hz encoder.service(); } void setup() { lcd.init(2); encoder.init(); // More encoder stuff //init Timer2 TCCR2B = (1<<CS22); //clk=F_CPU/64 TCNT2 = 0x00; TIMSK2 |= (1<<TOIE2); //enable overflow interupt score = false; gameover = false; lcd.clear(0); initTurn(); startGame(); //debug: Serial.begin(19200); //Serial.print("Ready"); } void loop(){ if(checkPress() ){ //&& !gameover if a player scored wait for rotary press initTurn(); } if( gameover ){ // if a player scored wait for rotary press p1_score = 0; p2_score = 0; level++; drawGameover(); gameover = false; return; } if( score && !gameover ){ lcd.drawText(20, 40, "Score!", 3, RGB(255,255,0), RGB(0,0,0)); lcd.drawText(10, 120, "press knob to go on", 1, RGB(255,255,0), RGB(0,0,0)); } while(!score && !gameover){ // run game: checkControls(); // updateAI(); updateFields(); drawScreen(); if(level < 5) { delay(10-(level*2)); } } } // initialize fields for the beginning of game/round void initTurn(void){ lcd.clear(0); ball_x = WIDTH / 2; ball_x_prev = ball_x; ball_y = HEIGHT / 2; ball_y_prev = ball_y; ball_dir = 0; bat1_x = BAT_INIT_X; bat1_y = BAT_INIT_Y; bat1_y_prev = bat1_y; bat2_x = WIDTH - BAT_INIT_X - BAT_WIDTH; bat2_y = BAT_INIT_Y; bat2_y_prev = bat2_y; ball_angle = 0; // angle between ball path and horizontal axis ball_angle_sign = 0; // up = +1, down = -1, horizontal = 0 score = false; ball_dir = 1; ball_angle = 45; } // start the game and wait for press on rotary encoder void startGame(void){ // draw intro screen & wait lcd.drawText(15, 2, "PONG", 3, RGB(255,150,0), RGB(100,0,0)); lcd.drawText(60, 37, "uino", 3, RGB(255,150,0), RGB(100,0,0)); sprintf(buffer, "Level %i", level); lcd.drawText(32, 82, buffer, 2, RGB(255,255,0), RGB(0,0,0)); lcd.drawText(32, 112, "Click to start", 1, RGB(255,255,255), RGB(0,0,0)); while(!checkPress()){ delay(1); }; lcd.clear(0); } // checks if rotary encoder was pressed boolean checkPress(void){ int8_t press; press = encoder.sw(); if (SW_PRESSED == press || SW_PRESSEDLONG == press) { return true; } else{ return false; } } void checkControls(void){ // pause btn & call input checks if(checkPress()){ // pause lcd.drawText(45, 60, "Game paused", 1, RGB(255,255,255), RGB(0,0,0)); while(!checkPress()){ // wait until pressed } lcd.clear(0); } checkRotation(); } void checkRotation(void){ moveBat(checkInput(1), 1 ); moveBat(checkInput(2), 2 ); } int checkInput(int batid) { // read joy values of 1 input int step = 0; int valJ = 0; if( batid == 1 ) { valJ = analogRead(pinJoyLeftY); } else { valJ = analogRead(pinJoyRightY); } if( valJ > 600 ) { // up if( valJ > 800 ) { // fast up step = 2; } else { step = 1; } } else if( valJ < 400 ) { // down if( valJ < 200 ) { step = -2; } else { step = -1; } } return step; } void moveBat(int rot, int batid ){ // move one of the bats int y_val = 1; if(batid == 1) { // switch bat y_val = bat1_y; } else { y_val = bat2_y; } if(!score && !gameover){ if(rot == 1){ // clockwise rotation, move bat up if(y_val - BAT_STEP >= 0){ // only move up when there is enough space left y_val -= 1; } } else if(rot == 2){ if(y_val - 2 >= 0){ y_val -= 2; } } else if(rot == -1){ // anti-clockwise rotation, move bat down if((y_val + BAT_HEIGHT + 1) < HEIGHT){ y_val += 1; } } else if(rot == -2 ){ if((y_val + BAT_HEIGHT + 2 ) < HEIGHT){ y_val += 2; } } if(batid == 1) { // switch bat bat1_y = y_val; } else { bat2_y = y_val; } } } void updateFields(void){ // top or bottom of screen reached, bounce ball back if(ball_y <= 0 || ball_y + BALL >= HEIGHT){ if(ball_angle_sign == -1){ // ball was going down ball_angle_sign = 1; // ball is now going up } else{ // ball was going up ball_angle_sign = -1; // ball is now going down } } // check if ball has is in reach of bat1 (horizontal), for some reason checking for equality doens't seem to work here ? if((ball_x <= 12 && ball_x >= 11) && (ball_dir == 1)){ // ball is in reach of bat1(vertical) if((ball_y + BALL) >= bat1_y && ball_y <= (bat1_y + BAT_HEIGHT)){ ball_dir = 0; // ball hits bat1, change direction } } // check if ball has is in reach of bat2 (horizontal) if((ball_x >= 160 && ball_x <= 162) && (ball_dir == 0)){ // ball is in reach of bat2 (vertical) if((ball_y + BALL) >= bat2_y && ball_y <= (bat2_y + BAT_HEIGHT)){ ball_dir = 1; // ball hits bat2, change direction } } if( ball_x > 0 && ball_x + BALL < WIDTH){ // ball is not near left or right edge if(ball_dir == 0){ // ball is moving to the right ball_x += cos(ball_angle); // cosine is always positive in 1st and 4th quadrant if(ball_angle_sign == -1){ // check sign of angle ball_y -= sin(ball_angle); // negative if angle in 4th quadrant } else{ ball_y += sin(ball_angle); // positive if angle in 1th quadrant } } else { // ball is moving to the left ball_x -= cos(ball_angle); // cosine is always negative in 2nd and 3rd quadrant if(ball_angle_sign == -1){ ball_y -= sin(ball_angle); // sine is always negative in 3rd quadrant } else{ ball_y += sin(ball_angle); // sine is always positive in 2nd quadrant } } } else{ // ball hits left or right edge if(!score && !gameover){ if(ball_dir == 0){ // ball was going to the right if(p1_score < MAX_TURNS){ p1_score++; // player 1 scores }else{ gameover = true; } }else{ // ball was going to the left if(p2_score < MAX_TURNS){ p2_score++; // player 2 scores } else{ gameover = true; } } score = true; } } } void drawScreen(void){ // draw ball lcd.fillRect( ball_x_prev, ball_y_prev, ball_x_prev + BALL, ball_y_prev + BALL, RGB(0,0,0)); lcd.fillRect( ball_x, ball_y, ball_x + BALL, ball_y + BALL, RGB(255,255,255)); ball_x_prev = ball_x; ball_y_prev = ball_y; // draw left bat lcd.fillRect( bat1_x, bat1_y_prev, bat1_x + BAT_WIDTH, bat1_y_prev + BAT_HEIGHT, RGB(0,0,0)); lcd.fillRect( bat1_x, bat1_y, bat1_x + BAT_WIDTH, bat1_y + BAT_HEIGHT, RGB(255,255,0)); bat1_y_prev = bat1_y; // draw right bat lcd.fillRect( bat2_x, bat2_y_prev, bat2_x + BAT_WIDTH, bat2_y_prev + BAT_HEIGHT, RGB(0,0,0)); lcd.fillRect( bat2_x, bat2_y, bat2_x + BAT_WIDTH, bat2_y + BAT_HEIGHT, RGB(255,0,0)); bat2_y_prev = bat2_y; drawScore(); } void drawScore(void){ // ..of 2 players + level char sc[16]; sprintf(sc, "%i", p1_score); lcd.drawText(88-23, 5, sc, 2, RGB(255,150,0), RGB(0,0,0)); lcd.drawLine( 88, 14, 88, 118, RGB(100,100,100) ); sprintf(sc, "%i", p2_score); lcd.drawText(88+10, 5, sc, 2, RGB(255,0,0), RGB(0,0,0)); sprintf(sc, "lvl: %i", level); lcd.drawText(60, 120, sc, 1, RGB(100,100,100), RGB(0,0,0)); } void drawGameover(void){ lcd.clear(0); lcd.drawText(5, 20, "Level Over", 2, RGB(255,0,0), RGB(0,0,0)); lcd.drawText(3, 80, "press knob for next level", 1, RGB(255,255,255), RGB(0,0,0)); lcd.drawText(3, 110, "press knob long to restart", 1, RGB(255,255,255), RGB(0,0,0)); }
[…] This post was mentioned on Twitter by SynOptX. SynOptX said: Many visitors at the Arduino+S65shield — PONGuino game mod http://is.gd/c0PGt since it is linked in some forums 🙂 […]
[…] player PONGuino – [Link] Filed in Arduino | 1 views No Comments […]
[…] response to the Arduino pong post from a few days ago, SynOptx shared their 2-player PONGuino game. Rather than using a television for a display, they opted to go with an LCD display mounted on an […]
[…] response to the Arduino pong post from a few days ago, SynOptx shared their 2-player PONGuino game. Rather than using a television for a display, they opted to go with an LCD display mounted on an […]
[…] response to the Arduino pong post from a few days ago, SynOptx shared their 2-player PONGuino game. Rather than using a television for a display, they opted to go with an LCD display mounted on an […]
[…] response to the Arduino pong post from a few days ago, SynOptx shared their 2-player PONGuino game. Rather than using a television for a display, they opted to go with an LCD display mounted on an […]
[…] response to the Arduino pong post from a few days ago, SynOptx shared their 2-player PONGuino game. Rather than using a television for a display, they opted to go with an LCD display mounted on an […]
I have read your thoughtregarding the Arduino. You definitely have put a considerable amount of effort into writing it. 🙂
Right now, I run a website that sells Arduino motherboards and their related accessories. They are all imported from overseas.
Finally, customers in Malaysia can own an Arduino. If you have a Malaysian friend, do inform them of this site.
Thank you very much. I will remember your help. Keep in touch.
response to the Arduino pong post from a few days ago, SynOptx shared their 2-player PONGuino game. Rather than using a television for a display, they opted to go with an LCD display mounted on an [