Note

Hello, welcome to the SunFounder Raspberry Pi & Arduino & ESP32 Enthusiasts Community on Facebook! Dive deeper into Raspberry Pi, Arduino, and ESP32 with fellow enthusiasts.

Why Join?

  • Expert Support: Solve post-sale issues and technical challenges with help from our community and team.

  • Learn & Share: Exchange tips and tutorials to enhance your skills.

  • Exclusive Previews: Get early access to new product announcements and sneak peeks.

  • Special Discounts: Enjoy exclusive discounts on our newest products.

  • Festive Promotions and Giveaways: Take part in giveaways and holiday promotions.

👉 Ready to explore and create with us? Click [here] and join today!

GAME - Pong

This is a simple Pong game designed using an OLED display and an Arduino board. In the Pong game, players compete against the computer, controlling a vertical paddle to bounce back a bouncing ball. The goal is to prevent the ball from passing your paddle’s edge, or else the opponent scores.

The game mechanics can be divided into the following parts:

  1. Ball Movement - The ball moves along its current direction at a set speed. Whenever the ball collides with a paddle, its speed increases, making the game more challenging.

  2. Paddle Movement - Used to block the ball’s movement, the paddle can move up or down. Players control their own paddle using buttons, while the computer’s paddle automatically follows the ball’s position.

  3. Scoring - Whenever the ball goes beyond the left or right edge of the screen, the corresponding player or CPU scores.

Required Components

In this project, we need the following components.

It’s definitely convenient to buy a whole kit, here’s the link:

Name

ITEMS IN THIS KIT

LINK

Elite Explorer Kit

300+

Elite Explorer Kit

You can also buy them separately from the links below.

COMPONENT INTRODUCTION

PURCHASE LINK

Arduino Uno R4 WiFi

-

Breadboard

BUY

Jumper Wires

BUY

Resistor

BUY

OLED Display Module

BUY

Button

BUY

Power Supply Module

-

Wiring

Note

To protect the Power Supply Module’s battery, please fully charge it before using it for the first time.

../_images/12_pong_bb.png

Schematic

../_images/12_pong_schematic.png

Code

Note

  • You can open the file 12_pong_oled.ino under the path of elite-explorer-kit-main\fun_project\12_pong_oled directly.

  • Or copy this code into Arduino IDE.

Note

To install the library, use the Arduino Library Manager and search for “Adafruit SSD1306” and “Adafruit GFX” and install them.

12_pong_oled.ino
  1/*
  2  This code creates a basic Pong game using an Arduino Uno, an OLED display, 
  3  and buttons. Players use the buttons to control a vertical paddle and bounce 
  4  back a moving ball to prevent it from passing their paddle. Scoring happens 
  5  when the ball goes off the screen edges.
  6
  7  Board: Arduino Uno R4 
  8  Component: OLED Display Module and Button
  9  Library: https://github.com/adafruit/Adafruit_SSD1306 (Adafruit SSD1306 by Adafruit)  
 10           https://github.com/adafruit/Adafruit-GFX-Library (Adafruit GFX Library by Adafruit) 
 11*/
 12
 13
 14#include <SPI.h>
 15#include <Wire.h>
 16#include <Adafruit_GFX.h>
 17#include <Adafruit_SSD1306.h>
 18
 19#define UP_BUTTON 3
 20#define DOWN_BUTTON 2
 21
 22#define SCREEN_WIDTH 128  // OLED display width, in pixels
 23#define SCREEN_HEIGHT 64  // OLED display height, in pixels
 24
 25// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
 26#define OLED_RESET 4  // Reset pin # (or -1 if sharing Arduino reset pin)
 27Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
 28
 29// ball set
 30const unsigned long BALL_RATE = 16;
 31int ball_x = 64, ball_y = 32;
 32int ball_speed = 1; 
 33int8_t ball_dir_x = 1, ball_dir_y = 1;
 34
 35//flash rate
 36unsigned long ball_update;
 37unsigned long paddle_update;
 38
 39// paddle set
 40const unsigned long PADDLE_RATE = 33;
 41const uint8_t PADDLE_HEIGHT = 16;
 42const uint8_t CPU_X = 12;
 43int8_t cpu_y = 16;
 44const uint8_t PLAYER_X = 115;
 45int8_t player_y = 16;
 46int paddle_speed = 3;
 47
 48// score
 49uint8_t player_score = 0;
 50uint8_t cpu_score = 0;
 51
 52
 53void setup() {
 54  Serial.begin(115200);
 55
 56  randomSeed(analogRead(A0));
 57  ball_dir_x = random(0, 2) * 2 - 1;
 58  ball_dir_y = random(0, 2) * 2 - 1;
 59
 60  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
 61  display.display();
 62
 63  pinMode(UP_BUTTON, INPUT);
 64  pinMode(DOWN_BUTTON, INPUT);
 65
 66  unsigned long start = millis();
 67
 68  display.clearDisplay();
 69  drawCourt();
 70
 71  while (millis() - start < 2000)
 72    ;
 73
 74  displayScore();
 75  display.display();
 76
 77  ball_update = millis();
 78  paddle_update = ball_update;
 79}
 80
 81void loop() {
 82  bool update = false;
 83  unsigned long time = millis();
 84
 85  static bool up_state = false;
 86  static bool down_state = false;
 87
 88  /* check if the button pressed */
 89  up_state |= (digitalRead(UP_BUTTON) == LOW);
 90  down_state |= (digitalRead(DOWN_BUTTON) == LOW);
 91
 92  /* refresh the ball */
 93  if (time > ball_update) {
 94    int new_x = ball_x + ball_dir_x * ball_speed;
 95    int8_t new_y = ball_y + ball_dir_y * ball_speed;
 96
 97    // Check if it hits the horizontal walls.
 98    if (new_y <= 0 || new_y >= SCREEN_HEIGHT - 1) {
 99      ball_dir_y = -ball_dir_y;
100      new_y += ball_dir_y + ball_dir_y * ball_speed;
101      displayScore();
102    }
103
104    // Check if it hits the CPU paddle
105    if (crossesCpuPaddle(ball_x, new_x, ball_y)) {
106      ball_dir_x = -ball_dir_x;
107      new_x = CPU_X + 1;// move the ball's position to the left edge of the paddle
108      ball_speed++;     // speeds up
109    }
110
111    // Check if it hits the player paddle
112    if (crossesPlayerPaddle(ball_x, new_x, ball_y)) {
113      ball_dir_x = -ball_dir_x;
114      new_x = PLAYER_X - 1; // move the ball's position to the right edge of the paddle
115      ball_speed++;         // speeds up
116    }
117
118    // Check if it hits the vertical walls
119    if (new_x <= 0 || new_x >= SCREEN_WIDTH - 1) {
120      if (new_x <= 1) {
121        player_score++;
122      }
123      if (new_x >= 126) {
124        cpu_score++;
125      }
126      /* reset ball */
127      displayScore();
128      ball_speed = 1;  // reset speed
129      new_x = 64; // reset position
130      // new_y = 32;
131      ball_dir_x = (ball_dir_x > 0) ? -1 : 1;     // reset direction
132      ball_dir_y = (random(0, 2) == 0) ? 1 : -1;  // reset direction
133    }
134
135    display.drawPixel(ball_x, ball_y, BLACK);
136    display.drawPixel(new_x, new_y, WHITE);
137    ball_x = new_x;
138    ball_y = new_y;
139
140    ball_update += BALL_RATE; // next refresh time
141    update = true;
142  }
143
144  /* refresh paddles */
145  if (time > paddle_update) {
146    paddle_update += PADDLE_RATE; // next refresh time
147
148    // CPU paddle
149    display.drawFastVLine(CPU_X, cpu_y, PADDLE_HEIGHT, BLACK); //clear paddle
150    const uint8_t half_paddle = PADDLE_HEIGHT >> 1;
151    if (cpu_y + half_paddle > ball_y) {
152      cpu_y -= paddle_speed;
153    }
154    if (cpu_y + half_paddle < ball_y) {
155      cpu_y += paddle_speed;
156    }
157    // constraint position
158    if (cpu_y < 1) cpu_y = 1;
159    if (cpu_y + PADDLE_HEIGHT > 63) cpu_y = 63 - PADDLE_HEIGHT;
160    display.drawFastVLine(CPU_X, cpu_y, PADDLE_HEIGHT, WHITE); //show paddle
161
162    // Player paddle
163    display.drawFastVLine(PLAYER_X, player_y, PADDLE_HEIGHT, BLACK); //clear paddle
164    if (up_state) {
165      player_y -= paddle_speed;
166    }
167    if (down_state) {
168      player_y += paddle_speed;
169    }
170    up_state = down_state = false;
171    // constraint position
172    if (player_y < 1) player_y = 1;
173    if (player_y + PADDLE_HEIGHT > 63) player_y = 63 - PADDLE_HEIGHT;
174    display.drawFastVLine(PLAYER_X, player_y, PADDLE_HEIGHT, WHITE); //show paddle
175    update = true;
176  }
177
178  if (update)
179    display.display();
180}
181
182bool crossesPlayerPaddle(uint8_t old_x, uint8_t new_x, uint8_t ball_y) {
183  return old_x < PLAYER_X && new_x >= PLAYER_X && ball_y >= player_y && ball_y <= player_y + PADDLE_HEIGHT;
184}
185
186bool crossesCpuPaddle(uint8_t old_x, uint8_t new_x, uint8_t ball_y) {
187  return old_x > CPU_X && new_x <= CPU_X && ball_y >= cpu_y && ball_y <= cpu_y + PADDLE_HEIGHT;
188}
189
190void drawCourt() {
191  display.drawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, WHITE);
192}
193
194void displayScore() {
195  display.fillRect(SCREEN_WIDTH / 2 - 20, 10, 60, 10, BLACK);// clear
196  
197  display.setCursor(SCREEN_WIDTH / 2 - 20, 10);  
198  display.setTextSize(1);
199  display.setTextColor(WHITE);
200
201  display.print(cpu_score);
202  display.print(" - ");
203  display.print(player_score);
204}

How it works?

The program structure can be divided into the following five parts:

  1. Import Necessary Libraries - Used to control the OLED screen and read button inputs.

  2. Define Constants and Global Variables:

    Definitions for OLED screen width and height. Definitions for buttons and OLED reset pins. Position, speed, size, and direction of the ball and paddles. Scores for player and CPU.

  3. Initialization:

    Initialize serial communication, the OLED screen, and display the initial interface. Set buttons as inputs and connect pull-up resistors. Draw the playing field.

  4. Main Loop:

    Read button states. Move the ball based on the set refresh rate. Detect collisions between the ball and paddles or walls, adjusting the ball’s direction and speed accordingly. Update the screen with scores based on scoring events. Refresh paddle positions.

  5. Additional Functions:

    crossesPlayerPaddle and crossesCpuPaddle - Used to detect whether the ball collides with the player’s or CPU’s paddle.

    drawCourt - Draws the playing field on the OLED screen.

    displayScore - Displays the player’s and CPU’s scores on the screen.