Arduino連携×Pygameのジャンプゲーム解説
ゲームやプログラムの世界観を交えながら、飽きずに読めるよう意識し、表やコード、対話文なども盛り込みました。やや物語調の部分も含まれていますが、コードの詳細な解説や Arduino・Pygame のポイントなども丁寧に説明しています。どうぞお楽しみください。
はじめに
「プログラミングは味気ない」なんて思っている人は、まだ本当のプログラミングの魅力を知らないのかもしれません。
数行のコードが、ゲームの世界を作り出し、電子回路の信号がキャラクターを飛躍させ、障害物を飛び越える――そんな体験を可能にするのが、今回ご紹介する Arduino連携×Pygameのジャンプゲーム です。
この記事では、以下のコードを題材に、プログラムの構成、実装のポイント、さらには Arduino から送られる「TRIGGER」信号がどのようにゲーム内のジャンプアクションにつながるのかを解説します。また、コードの解説だけでなく、ゲームを作る楽しさや、ちょっとしたデザインの工夫、そしてゲームを学習素材として活用する魅力などにも触れてみます。
最後までお読みいただくことで、「なるほど、こういう発想でゲームと電子工作をつなげるんだ!」と感心していただければ幸いです。
【2025年版】Arduinoのコードの書き方|基礎から応用まで全てがわかる初心者の手引き
【徹底解説】Arduinoとブレッドボードで学ぶ!電子工作におけるボタン回路の基礎から応用まで
シナリオの導入
小さな教室での対話
学生「先生、ゲーム作りとArduinoってどうつなげたら面白いんでしょうか? ゲームはソフトウェア、Arduinoはハードウェアってイメージがあって、なかなか一緒にやるって発想がなかったんです」
先生「そうだね。Arduinoのセンサーから送られるデータを、ゲーム側で受け取ってアクションに変換するんだ。たとえばボタンを押したらジャンプするとか、加速度センサーを傾けたらキャラクターが動くとか、いろんな可能性があるんだよ」
学生「へえ、なんだか面白そう! ゲームエンジンとか使うんですか?」
先生「有名なUnityやUnreal Engineでもできるけれど、今回は Python のライブラリ『Pygame』を使って、より軽量に、そしてプログラミングの基礎を理解しやすい形でやってみよう。Arduinoとのシリアル通信もPythonなら簡単に扱えるよ」
学生「なるほど。じゃあ、早速見てみたいです!」
そんな対話のあと、学生の目の前に示されたのが、今回の記事のメインとなる Pygame と Arduino の連携サンプルコード です。
コード全体
まずは何より、全体のコードを眺めてみましょう。以下のコードが、今回の主役です。長いコードですが、実は大きく分けて「シリアル通信の設定」「Pygame の初期設定」「プレイヤーや障害物のクラス」「メインループ」という構成になっています。
【完全攻略】圧センサーを用いた電子工作の基礎編|圧センサーの種類、配線、読み取り値など徹底解説
import serial
import pygame
import sys
import random
# --- シリアル通信の設定 ---
SERIAL_PORT = 'COM3' # 環境に合わせて変更してください
BAUD_RATE = 9600
try:
ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
except Exception as e:
print(f"シリアルポート {SERIAL_PORT} に接続できませんでした: {e}")
sys.exit()
# --- Pygameの初期設定 ---
pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Mario Jump Game")
clock = pygame.time.Clock()
# 背景画像の読み込み(なければ白背景)
try:
background = pygame.image.load("background.jpg").convert()
background = pygame.transform.scale(background, (WIDTH, HEIGHT))
except Exception as e:
background = None
# キャラクター画像の読み込み(mario.pngを用意してください)
try:
mario_img = pygame.image.load("mario.png").convert_alpha()
mario_img = pygame.transform.scale(mario_img, (50, 50))
except Exception as e:
mario_img = None
# カラー定義
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
BLACK = (0, 0, 0)
# ゲーム設定
GRAVITY = 0.5
JUMP_VELOCITY = -10 # 上向きの初速度
GROUND_Y = HEIGHT - 100 # 地面のY座標
# プレイヤークラス
class Player:
def __init__(self):
self.x = 100
self.y = GROUND_Y - 50 # プレイヤーの高さは50px
self.width = 50
self.height = 50
self.vy = 0
self.jumping = False
def jump(self):
if not self.jumping:
self.vy = JUMP_VELOCITY
self.jumping = True
def update(self):
self.vy += GRAVITY
self.y += self.vy
if self.y >= GROUND_Y - self.height:
self.y = GROUND_Y - self.height
self.vy = 0
self.jumping = False
def draw(self, surface):
if mario_img:
surface.blit(mario_img, (self.x, self.y))
else:
pygame.draw.rect(surface, (0, 128, 255), (self.x, self.y, self.width, self.height))
# 障害物クラス
class Obstacle:
def __init__(self):
self.width = random.randint(20, 40)
self.height = random.randint(40, 80)
self.x = WIDTH
self.y = GROUND_Y - self.height
self.speed = random.uniform(5, 8)
def update(self):
self.x -= self.speed
def draw(self, surface):
pygame.draw.rect(surface, (255, 0, 0), (self.x, self.y, self.width, self.height))
def off_screen(self):
return self.x + self.width < 0 def check_collision(player, obstacle): p_rect = pygame.Rect(player.x, player.y, player.width, player.height) o_rect = pygame.Rect(obstacle.x, obstacle.y, obstacle.width, obstacle.height) return p_rect.colliderect(o_rect) # ゲーム用変数(リセット可能な状態にするためグローバル変数として管理) def reset_game(): global player, obstacles, obstacle_timer, game_over player = Player() obstacles = [] obstacle_timer = 0 game_over = False reset_game() # 初期状態をセット def game_loop(): global obstacle_timer, game_over while True: # イベント処理 for event in pygame.event.get(): if event.type == pygame.QUIT: ser.close() pygame.quit() sys.exit() # ゲームオーバー時に「R」キーでリセット if event.type == pygame.KEYDOWN and game_over: if event.key == pygame.K_r: reset_game() # Arduinoからのシリアルデータを監視し、「TRIGGER」受信でジャンプ(ゲームオーバーでなければ) if ser.in_waiting and not game_over: try: line = ser.readline().decode('utf-8').strip() except UnicodeDecodeError: line = "" if line == "TRIGGER": player.jump() # ゲーム更新(Game Overでなければ) if not game_over: player.update() for obs in obstacles: obs.update() # 画面外に出た障害物は削除 obstacles[:] = [obs for obs in obstacles if not obs.off_screen()] # 定期的に障害物を生成(約1.5秒ごと) obstacle_timer += 1 if obstacle_timer > 90:
obstacles.append(Obstacle())
obstacle_timer = 0
# 衝突判定
for obs in obstacles:
if check_collision(player, obs):
game_over = True
# 描画処理
if background:
screen.blit(background, (0, 0))
else:
screen.fill(WHITE)
# 地面の描画
pygame.draw.rect(screen, GREEN, (0, GROUND_Y, WIDTH, HEIGHT - GROUND_Y))
player.draw(screen)
for obs in obstacles:
obs.draw(screen)
# ゲームオーバー表示
if game_over:
font = pygame.font.SysFont(None, 72)
text = font.render("GAME OVER", True, (255, 0, 0))
screen.blit(text, (WIDTH//2 - text.get_width()//2, HEIGHT//2 - text.get_height()//2))
# ゲーム再スタートのメッセージ
small_font = pygame.font.SysFont(None, 36)
restart_text = small_font.render("Press R to Restart", True, (255, 255, 255))
screen.blit(restart_text, (WIDTH//2 - restart_text.get_width()//2, HEIGHT//2 + text.get_height()))
pygame.display.flip()
clock.tick(60)
if __name__ == "__main__":
game_loop()
今回の記事では、このコードを 「どのような構成になっているのか」「各部分がどんな役割を果たしているのか」 などを丁寧に紐解いていきます。さらに、Arduino から送られる「TRIGGER」というシリアル通信メッセージが、どのタイミングでどのように使われているのかも解説します。
コードの構成を俯瞰する
まずは大きな流れを把握するために、各セクションを一覧できるよう表にまとめましょう。
セクション | 概要 | 該当部分 |
---|---|---|
シリアル通信の設定 | Arduinoと通信するためのシリアルポートとボーレートの設定、接続の例外処理など | SERIAL_PORT , BAUD_RATE など、 ser = serial.Serial(...) の部分 |
Pygameの初期設定 | Pygame の起動、画面サイズの設定、画面タイトルの設定、背景画像・キャラクター画像の読み込みなど | pygame.init() , screen = pygame.display.set_mode(...) , pygame.display.set_caption(...) |
ゲーム全体で使用する定数 | 色定義(WHITE, GREEN, BLACK)、重力、ジャンプ速度、地面のY座標など | WHITE = (255, 255, 255) , GRAVITY = 0.5 など |
プレイヤークラス (Player) | キャラクターの位置や速度、ジャンプや描画ロジックをまとめたクラス | class Player: ... |
障害物クラス (Obstacle) | 障害物の位置や速度、描画、画面外に出たかどうかのチェックなどをまとめたクラス | class Obstacle: ... |
衝突判定関数 | Player と Obstacle の衝突をチェックするシンプルな関数 | check_collision(player, obstacle) |
ゲームの状態管理とリセット | グローバル変数にプレイヤーや障害物リスト、タイマー、ゲームオーバーフラグなどを管理し、リセット操作 | reset_game() |
メインループ (game_loop) | ゲームのメイン処理。イベント処理、シリアル受信、更新処理、描画処理、ゲームオーバー時の表示など | def game_loop(): ... |
エントリポイント | if __name__ == "__main__": で game_loop() を呼び出し、ゲームを開始 | 最後の if __name__ == "__main__": game_loop() の部分 |
このようにまとめると、最初にシリアル通信を確立し、次に Pygame のセットアップを済ませたら、あとはクラス定義や関数定義があり、最後に game_loop()
というメインループが実行されている構造になっていることがわかります。
シリアル通信の設定
Arduino と通信するために、まず pyserial
ライブラリが使われています。pyserial
は Python でシリアルポート(COM ポートなど)を操作するための定番ライブラリです。
SERIAL_PORT = 'COM3' # 環境に合わせて変更してください
BAUD_RATE = 9600
try:
ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
except Exception as e:
print(f"シリアルポート {SERIAL_PORT} に接続できませんでした: {e}")
sys.exit()
SERIAL_PORT = 'COM3'
Windows 環境の場合、Arduino が接続されたポートが COM3 ならこれで OK です。Mac や Linux では/dev/tty...
のパスになりますし、使用環境に合わせて修正が必要です。BAUD_RATE = 9600
Arduino 側で設定したシリアル通信速度に合わせてください。標準的には 9600 や 115200 などがよく使われます。ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
ここで実際のシリアルポートのオブジェクトser
を生成しています。timeout=1
は読み取り時のタイムアウトを 1 秒に設定しているという意味です。- 例外処理
接続できなかった場合はエラーを表示し、sys.exit()
でプログラムを終了します。
Pygameの初期設定
続いて、Pygame 側の設定を見てみます。
pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Mario Jump Game")
clock = pygame.time.Clock()
pygame.init()
Pygame の各モジュールを初期化します。WIDTH, HEIGHT = 800, 600
ウィンドウサイズを幅 800px、高さ 600px に指定しています。screen = pygame.display.set_mode((WIDTH, HEIGHT))
ウィンドウを作成し、描画先のサーフェスをscreen
という変数で保持します。pygame.display.set_caption("Mario Jump Game")
ウィンドウのタイトルを設定します。clock = pygame.time.Clock()
フレームレートの制御に使うClock
オブジェクトを生成しています。
背景画像とキャラクター画像の読み込み
try:
background = pygame.image.load("background.jpg").convert()
background = pygame.transform.scale(background, (WIDTH, HEIGHT))
except Exception as e:
background = None
try:
mario_img = pygame.image.load("mario.png").convert_alpha()
mario_img = pygame.transform.scale(mario_img, (50, 50))
except Exception as e:
mario_img = None
- 背景画像 (
background.jpg
) やキャラクター画像 (mario.png
) が用意されていれば読み込み、読み込みに失敗すればNone
を設定しています。 - 背景画像はウィンドウサイズに合わせてリサイズし、キャラクター画像は 50×50 px にリサイズしています。
- もし画像がない場合は、後の描画処理でデフォルトの白背景や四角形描画が行われるようになっています。
カラー定義
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
BLACK = (0, 0, 0)
単純な色の定義です。今回は背景や地面の描画などに使われています。
ゲーム設定の定数
GRAVITY = 0.5
JUMP_VELOCITY = -10
GROUND_Y = HEIGHT - 100
GRAVITY = 0.5
キャラクターにかかる重力加速度です。数値が大きいほど早く落下します。JUMP_VELOCITY = -10
ジャンプ開始時に与える上向きの速度です。負の値にすることで、上方向に移動します。GROUND_Y = HEIGHT - 100
地面の高さを設定しています。ウィンドウ下端よりも 100px 上ということです。
プレイヤークラス (Player)
ここからがゲームオブジェクトの本体です。プレイヤーキャラクターを表現するクラスですね。
class Player:
def __init__(self):
self.x = 100
self.y = GROUND_Y - 50 # プレイヤーの高さは50px
self.width = 50
self.height = 50
self.vy = 0
self.jumping = False
def jump(self):
if not self.jumping:
self.vy = JUMP_VELOCITY
self.jumping = True
def update(self):
self.vy += GRAVITY
self.y += self.vy
if self.y >= GROUND_Y - self.height:
self.y = GROUND_Y - self.height
self.vy = 0
self.jumping = False
def draw(self, surface):
if mario_img:
surface.blit(mario_img, (self.x, self.y))
else:
pygame.draw.rect(surface, (0, 128, 255), (self.x, self.y, self.width, self.height))
プロパティ
self.x, self.y
プレイヤーの座標。初期値は x=100、y=地面の高さより 50px 分上でスタートします。self.width, self.height
幅・高さともに 50px。self.vy
垂直方向の速度(velocity on the y-axis)。初期値は 0。self.jumping
現在ジャンプ中かどうかを判定するフラグ。
メソッド
jump()
ジャンプフラグがFalse
(つまり地面にいる状態)のときに、vy
にJUMP_VELOCITY
(-10)をセットし、jumping
をTrue
にします。update()
毎フレーム呼ばれ、垂直速度にGRAVITY
を加算してself.y
を更新します。地面より下に行かないように、もしself.y
が地面よりも低くなったら地面上に固定し、速度を 0 にしてjumping
をFalse
に戻しています。draw()
キャラクター画像mario_img
があれば貼り付け、なければ水色の四角形を描画します。
障害物クラス (Obstacle)
プレイヤーが飛び越えるべき障害物を表現するクラスです。
class Obstacle:
def __init__(self):
self.width = random.randint(20, 40)
self.height = random.randint(40, 80)
self.x = WIDTH
self.y = GROUND_Y - self.height
self.speed = random.uniform(5, 8)
def update(self):
self.x -= self.speed
def draw(self, surface):
pygame.draw.rect(surface, (255, 0, 0), (self.x, self.y, self.width, self.height))
def off_screen(self):
return self.x + self.width < 0
width, height
ランダムな幅 (20~40 px) と高さ (40~80 px) を与えています。障害物の大きさが毎回変わるので、ゲームとしてのバリエーションが生まれます。x, y
画面の右端 (x = WIDTH
) に初期位置をセットし、高さに合わせてy = GROUND_Y - self.height
にしています。speed
障害物の移動速度。5~8 の間でランダムにしています。update()
毎フレーム、speed
分だけ左に移動します。draw()
赤い長方形として障害物を描画しています。off_screen()
画面の左端から消えたかどうかを判定し、もし消えていれば True を返します。
衝突判定関数
def check_collision(player, obstacle):
p_rect = pygame.Rect(player.x, player.y, player.width, player.height)
o_rect = pygame.Rect(obstacle.x, obstacle.y, obstacle.width, obstacle.height)
return p_rect.colliderect(o_rect)
ここではシンプルに 矩形同士の衝突判定 を行っています。Pygame の Rect
オブジェクトが提供する colliderect()
メソッドによって、プレイヤーと障害物が重なったかどうかを確認します。
ゲーム状態管理とリセット処理
def reset_game():
global player, obstacles, obstacle_timer, game_over
player = Player()
obstacles = []
obstacle_timer = 0
game_over = False
グローバル変数として player
, obstacles
, obstacle_timer
, game_over
を再設定し、ゲームを最初の状態に戻します。ゲームオーバーの状態から再スタートするときに呼び出されます。
メインループ (game_loop)
ここがゲーム全体の心臓部です。イベント処理、シリアル受信、更新・描画処理、ゲームオーバーの演出がまとめられています。
def game_loop():
global obstacle_timer, game_over
while True:
# イベント処理
for event in pygame.event.get():
if event.type == pygame.QUIT:
ser.close()
pygame.quit()
sys.exit()
# ゲームオーバー時に「R」キーでリセット
if event.type == pygame.KEYDOWN and game_over:
if event.key == pygame.K_r:
reset_game()
# Arduinoからのシリアルデータを監視し、「TRIGGER」受信でジャンプ(ゲームオーバーでなければ)
if ser.in_waiting and not game_over:
try:
line = ser.readline().decode('utf-8').strip()
except UnicodeDecodeError:
line = ""
if line == "TRIGGER":
player.jump()
# ゲーム更新(Game Overでなければ)
if not game_over:
player.update()
for obs in obstacles:
obs.update()
# 画面外に出た障害物は削除
obstacles[:] = [obs for obs in obstacles if not obs.off_screen()]
# 定期的に障害物を生成(約1.5秒ごと)
obstacle_timer += 1
if obstacle_timer > 90:
obstacles.append(Obstacle())
obstacle_timer = 0
# 衝突判定
for obs in obstacles:
if check_collision(player, obs):
game_over = True
# 描画処理
if background:
screen.blit(background, (0, 0))
else:
screen.fill(WHITE)
# 地面の描画
pygame.draw.rect(screen, GREEN, (0, GROUND_Y, WIDTH, HEIGHT - GROUND_Y))
player.draw(screen)
for obs in obstacles:
obs.draw(screen)
# ゲームオーバー表示
if game_over:
font = pygame.font.SysFont(None, 72)
text = font.render("GAME OVER", True, (255, 0, 0))
screen.blit(text, (WIDTH//2 - text.get_width()//2, HEIGHT//2 - text.get_height()//2))
# ゲーム再スタートのメッセージ
small_font = pygame.font.SysFont(None, 36)
restart_text = small_font.render("Press R to Restart", True, (255, 255, 255))
screen.blit(restart_text, (WIDTH//2 - restart_text.get_width()//2, HEIGHT//2 + text.get_height()))
pygame.display.flip()
clock.tick(60)
主なポイント
- イベントループ
pygame.event.get()
を通してウィンドウを閉じる操作やキーボード操作を監視し、終了処理やリスタート操作を行っています。 - シリアル通信からのジャンプトリガー
if ser.in_waiting and not game_over:
のブロックで Arduino からのメッセージを読み込み、"TRIGGER"
が受信されたらplayer.jump()
を呼び出しています。ここが Arduino×Pygame 連携の肝となる部分です。 - ゲームロジック
- プレイヤーと障害物の
update()
呼び出し - 画面外に出た障害物の削除
- 約1.5秒(=90フレーム)ごとに障害物を生成(フレームレートが 60 の場合)
- 衝突判定で
game_over = True
- プレイヤーと障害物の
- 描画処理
背景や地面、プレイヤー、障害物を描画していきます。 - ゲームオーバー演出
GAME OVER
の文字を画面中央に表示し、Press R to Restart
で再スタートを促します。 - フレームレート制御
clock.tick(60)
によって 1 秒間に最大 60 フレームの更新に制御しています。
Arduino からの「TRIGGER」信号とは?
コード中でポイントとなるのが、if line == "TRIGGER": player.jump()
という一行。ここで、「TRIGGER」という文字列を受け取ったときだけ、プレイヤーがジャンプします。
Arduino 側ではどうなっているかというと、例えばこんなスケッチを書くことが考えられます。
// Arduinoの例(ボタンを押したら"TRIGGER"を送信するだけのコード)
int buttonPin = 2;
int buttonState = 0;
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
Serial.begin(9600);
}
void loop() {
buttonState = digitalRead(buttonPin);
// ボタンが押された(LOW)なら
if (buttonState == LOW) {
Serial.println("TRIGGER");
delay(200); // チャタリング対策 & 連続ジャンプ防止
}
}
- ボタンが押されたら
Serial.println("TRIGGER");
を送信。 - Python 側の
ser.readline()
でこれを受け取り、文字列を比較して一致すればジャンプ。
もちろん、圧力センサーや加速度センサーを使って「ある閾値を超えたら TRIGGER を送る」といった応用もできます。ゲームの操作デバイスは自由自在です。
ゲームのカスタマイズポイント
今回のサンプルは、あくまで「Arduino からのシリアル信号でジャンプさせる」という機能を示したシンプルなゲームです。実際には、以下のような改造ポイントが考えられます。
- 障害物の種類を増やす
小さいけど高速に動く障害物、大きいけどゆっくり動く障害物などを作ってみる。
画像を用意して描画することで、見た目を豊かにする。 - 背景を動かす
パララックススクロールを導入したり、背景に動きを持たせてより本格的なランゲームに近づける。 - スコアシステム
障害物を飛び越えた数や経過時間に応じてスコアを表示したり、ハイスコアを保存したりする。 - BGMや効果音の追加
Pygame にはオーディオの再生機能もあるため、ジャンプ時やゲームオーバー時に音を鳴らすと、よりゲームらしい演出になる。 - Arduino 側の工夫
ボタン以外にも、加速度センサーでデバイスを振ったらジャンプするなど。
VR コントローラ風に腕を振ったらジャンプさせるアイデアも面白い。
エラーやトラブルシューティング
「なんか動かないぞ?」というときによくある落とし穴と、その対策例を挙げてみます。
- シリアルポートが違う
Windows なら「デバイスマネージャー」を確認して COM ポートを調べる。
Mac や Linux ならls /dev/tty.*
などで該当ポートを探す。 - ボーレート不一致
Arduino 側Serial.begin(9600);
と Python 側BAUD_RATE = 9600
が一致しているか確認する。 - 文字コード問題
Python 側でdecode('utf-8')
しているが、Arduino IDE で何らかの原因で Shift-JIS 文字やバイナリデータを送っているとUnicodeDecodeError
が出ることがある。その場合は変換を変えるか、生データで扱う。 - Arduino からの送信が速すぎる
無限ループで頻繁にSerial.println("TRIGGER")
を送ると、ゲームがジャンプしまくる。必要に応じてデバウンスやインターバルを入れる。 - Pygame のイベントループを抜けている
Pygame はイベントループをきちんと回さないとウィンドウが固まる、もしくは OS が「応答なし」とみなす場合がある。
game_loop()
内で必ずpygame.event.get()
を呼んでいること、clock.tick(60)
で回していることを確認する。
発展的な話題:学習教材としての価値
プログラミング初学者への利点
- 視覚的なフィードバックが得られる
ゲームを動かしながらコードを変更してみると、画面にすぐ結果が反映されます。文字列処理やアルゴリズムの学習だけでは味わえないダイナミックな反応が楽しめるため、学習意欲が向上します。 - オブジェクト指向の理解
「プレイヤーはこういう属性を持ち、障害物はこういう属性を持ち…」というようにクラス設計を学べます。 - イベント駆動型プログラミング
シリアルからの信号やキーボード・マウス操作をイベントとして扱うことで、リアルタイム処理の基礎を学習できます。
Arduino 学習へのメリット
- モチベーションの向上
LED 点滅やセンサー値の取得だけでは飽きやすい学習者も、「自分の操作がゲーム内のキャラクターを動かす」という体験には強い興味を持ちやすいです。 - リアルな制御理論への応用
本格的にはセンサーを使い、アナログ値を読み取った上でゲーム側で処理をするといった流れで、制御理論・フィードバック制御などにも発展可能です。
対話形式:先生と学生の掛け合い
学生「ゲームのプログラムって意外とシンプルなんですね。たくさんの要素が入ってるイメージだったので驚きです」
先生「確かに大規模なゲームはもっと複雑なエンジンを用いるけど、今回のようにシンプルなランゲーム程度なら、Pygame とクラス設計だけで十分形になるんだよ」
学生「Arduino を使う利点って何でしょう? 普通にキーボードでジャンプでもいい気がするんですが…」
先生「そこがポイントなんだ。Arduino を使えば『現実の行動がゲームに反映される』仕組みをいくらでも作れる。たとえばジャンプボタン付きの自作コントローラを作ったり、センサーで傾きを検知してキャラクターが飛ぶ仕組みにしたり、想像の余地は無限大さ」
学生「なるほど、ゲーム制作と電子工作が結びつくと、こんなに楽しいことになるんですね!」
まとめ
ここまで見てきたように、Arduino と Pygame を連携させたジャンプゲームは、コード自体はコンパクトながらも、学習のエッセンスが詰まっています。
- シリアル通信を使って Arduino からの信号を受け取り
- Pygame で 2D 描画とイベントループを回し
- クラス設計によってプレイヤーと障害物を管理し
- 重力やジャンプ、衝突判定など、ゲームならではの処理を行う
ゲームが動く様子を見るのはワクワクしますし、ソフトウェアとハードウェアを連携させるワクワク感はさらに格別です。センサーやボタンを工夫するだけで、操作方法を自由に設計できるのも大きな魅力です。
もし本記事を参考にして、より高度なゲームやコントローラを作ってみたくなった方は、ぜひ以下のステップに挑戦してみてください。
- スコアを導入して、障害物をどれだけよけたか計算し、画面に表示してみる。
- 音楽や効果音を追加して、ジャンプ時やゲームオーバー時にサウンドを鳴らす。
- 背景をパララックススクロールさせて、本格的なランニングゲームに近づける。
- 別のセンサーを利用して、意外な操作方法でジャンプをトリガーさせる。
- ゲーム難易度の調整として障害物の生成速度や速度を時間によって変化させる。
ゲーム制作の過程には、プログラミングの基礎から応用までの学びが散りばめられています。さらにArduinoと組み合わせることで、「物理的な操作」と「画面内のアクション」が結びつき、想像力を大いに刺激する体験となるでしょう。
学生「先生、これもっと発展させて、加速度センサーで『床ドン!』みたいなアクションをしたらジャンプする仕組みにしたいんですけど、どう思います?」
先生「いいね。加速度センサーの値が一定以上になったら Arduino から ‘TRIGGER’ を送るようにすれば、地面を強く叩いた衝撃でキャラクターが跳ねるんじゃないかな!」
学生「おお、それやります! ゲームというよりスポーツみたいになってきましたね(笑)」
先生「そうなんだよ。こうやってコードとハードウェアをつなげると、遊び方が一気に広がるんだ。ぜひ実験してみて!」
おわりに
本記事では、Arduino からの「TRIGGER」信号を受け取って Pygame で描画されるプレイヤーをジャンプさせる、一連のゲームの流れを紹介しました。
プログラミングの世界には、数字やアルゴリズムのみならず、こうした 「実世界とリンクするインタラクティブな遊び」 がいくらでも生み出せる面白さがあります。学習やプロトタイピングにも最適で、ほんの少しのコード改変であなたのアイデアをどんどん形にできます。
最後に、対話形式のワンシーンをもう一度。
学生「先生、これもっと発展させて、加速度センサーで『床ドン!』みたいなアクションをしたらジャンプする仕組みにしたいんですけど、どう思います?」
先生「いいね。加速度センサーの値が一定以上になったら Arduino から ‘TRIGGER’ を送るようにすれば、地面を強く叩いた衝撃でキャラクターが跳ねるんじゃないかな!」
学生「おお、それやります! ゲームというよりスポーツみたいになってきましたね(笑)」
先生「そうなんだよ。こうやってコードとハードウェアをつなげると、遊び方が一気に広がるんだ。ぜひ実験してみて!」
プログラミングが、ほんの少しの発想でエンターテインメントの可能性を無限に広げる――そんな希望を抱きつつ、この記事を締めくくらせていただきます。ぜひ、ご自身のアイデアを Arduino×Pygame で実現してみてください。きっと、最初に感じていた“ゲーム作り”のハードルが、ずっと低く感じられるはずです。
今こそ、「プログラミングは味気ない」というイメージを打ち破りましょう。Arduino と Pygame を掛け合わせた、魅力的で創造的な世界が、あなたのアイデアを待っています。
コメント