注釈

こんにちは、SunFounderのRaspberry Pi & Arduino & ESP32愛好家コミュニティへようこそ!Facebook上でRaspberry Pi、Arduino、ESP32についてもっと深く掘り下げ、他の愛好家と交流しましょう。

参加する理由は?

  • エキスパートサポート:コミュニティやチームの助けを借りて、販売後の問題や技術的な課題を解決します。

  • 学び&共有:ヒントやチュートリアルを交換してスキルを向上させましょう。

  • 独占的なプレビュー:新製品の発表や先行プレビューに早期アクセスしましょう。

  • 特別割引:最新製品の独占割引をお楽しみください。

  • 祭りのプロモーションとギフト:ギフトや祝日のプロモーションに参加しましょう。

👉 私たちと一緒に探索し、創造する準備はできていますか?[ ここ]をクリックして今すぐ参加しましょう!

ゲーム - ポン

これは、OLEDディスプレイとArduinoボードを使用して設計されたシンプルなポンゲームです。 ポンゲームでは、プレイヤーはコンピュータと対戦し、垂直なパドルを操作して跳ね返るボールを跳ね返します。 目標は、ボールを自分のパドルの端を越えさせないようにすることで、さもなければ相手に得点されます。

ゲームのメカニズムは以下のパーツに分けられます:

  1. ボールの動き - ボールは現在の方向に沿って設定された速度で動きます。ボールがパドルに衝突するたびに、その速度が増加し、ゲームがより難しくなります。

  2. パドルの動き - ボールの動きをブロックするために使用されるパドルは、上または下に動かすことができます。プレイヤーはボタンを使用して自分のパドルを操作し、コンピュータのパドルは自動的にボールの位置に従います。

  3. スコアリング - ボールが画面の左または右端を越えるたびに、対応するプレイヤーまたはCPUが得点します。

必要なコンポーネント

このプロジェクトでは、以下のコンポーネントが必要です。

一式を購入するのが便利です。こちらがリンクです:

名称

このキットのアイテム数

リンク

Elite Explorer Kit

300+

Elite Explorer Kit

以下のリンクから個別に購入することもできます。

コンポーネント紹介

購入リンク

Arduino Uno R4 WiFi

-

ブレッドボード

購入

ジャンパーワイヤー

購入

抵抗器

購入

OLEDディスプレイモジュール

購入

ボタン

購入

電源モジュール

-

配線図

../_images/12_pong_bb.png

回路図

../_images/12_pong_schematic.png

コード

注釈

  • ファイル 12_pong_oled.ino は、パス elite-explorer-kit-main\fun_project\12_pong_oled で直接開けます。

  • または、このコードをArduino IDEにコピーしてください。

注釈

ライブラリをインストールするには、Arduinoライブラリマネージャーで 「Adafruit SSD1306」「Adafruit GFX」 を検索し、インストールしてください。

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}

どのように動作するのか?

プログラムの構造は以下の5つの部分に分けられます:

  1. 必要なライブラリのインポート - OLEDスクリーンの制御とボタン入力の読み取りに使用されます。

  2. 定数とグローバル変数の定義:

    OLEDスクリーンの幅と高さの定義。 ボタンとOLEDリセットピンの定義。 ボールとパドルの位置、速度、サイズ、方向。 プレイヤーとCPUのスコア。

  3. 初期化:

    シリアル通信、OLEDスクリーンを初期化し、初期インターフェースを表示します。 ボタンを入力として設定し、プルアップ抵抗器を接続します。 プレイングフィールドを描きます。

  4. メインループ:

    ボタンの状態を読み取ります。 設定されたリフレッシュレートに基づいてボールを動かします。 ボールとパドルまたは壁との衝突を検出し、ボールの方向と速度をそれに応じて調整します。 得点イベントに基づいてスクリーン上のスコアを更新します。 パドルの位置をリフレッシュします。

  5. 追加の関数:

    crossesPlayerPaddlecrossesCpuPaddle - ボールがプレイヤーのパドルまたはCPUのパドルと衝突するかどうかを検出するために使用されます。

    drawCourt - OLEDスクリーン上にプレイングフィールドを描きます。

    displayScore - 画面上にプレイヤーとCPUのスコアを表示します。