
一つ前のページではゲームクリアを設定する方法について学習しました。
今回は 追跡してくる敵を実装 し、いよいよゲームを完成させましょう。
Chapter1:pygame入門|画面を表示しよう
Chapter2:簡単なノベルゲームを作ろう
Chapter3:簡単なアクションゲームを作ろう
・Chapter3-1:衝突判定に基本を理解しよう
・Chapter3-2:画面に敵を配置しよう
・Chapter3-3:ゲームオーバーを設定しよう
・Chapter3-4:ゲームクリアを設定しよう
・Chapter3-5:追跡してくる敵を実装しよう ◁今回はここ
Chapter4:ブロック崩しを作ろう
Chapter5:シューティングゲームを作ろう
Chapter6:pygameのお役立ち情報
ここまでのレッスンでプレイヤーが操作する猫を自由に動かし、犬との衝突によるゲームオーバーや、ゴールに到達したときのゲームクリアといった基本的なゲームの流れを作ってきました。
今回は仕上げとして、「プレイヤーを追いかけてくる敵キャラクター(幽霊)」を新しく追加します。
この幽霊は、プレイヤーの動きを自動的に追跡してくる特徴的な敵です。
プレイヤーがどの方向に動いても、それを追いかけてくる敵がいることで、ゲームに緊張感が生まれ、より本格的なアクションゲームらしさが加わります。
新たに登場するこの敵は、簡単な「追跡アルゴリズム」によって実装します。アルゴリズムといっても難しいものではなく、座標を比較して少しずつ近づけるというシンプルな仕組みです。
今回のレッスンではその考え方を学びながら、ゲームへの実装方法を順を追って確認していきましょう。
追跡アルゴリズムの基本を理解しよう
これまでのゲームでは、敵はあらかじめ配置された位置にじっとしており、プレイヤーが移動してぶつからなければゲームは進行していきました。
しかし現実のアクションゲームでは、プレイヤーの動きに反応する敵キャラクターが登場することが多く、そうした敵はゲームの緊張感や面白さを大きく引き上げてくれます。
今回新たに追加する「幽霊」は、そのような動きのある敵キャラクターの第一歩です。
この幽霊はプレイヤーの動きに関係なく、常にプレイヤーの現在位置に向かって移動してきます。
こうした動きを実現するには、プレイヤーと敵の位置関係を計算し、敵をプレイヤーの方向へ少しずつ移動させるという処理が必要です。
この仕組みは「追跡アルゴリズム」と呼ばれ、ゲームAIの中でも基本的な考え方になります。
幽霊のような追跡型の敵を加えることで、プレイヤーに「動き続けなければ捕まってしまう」という新たなプレッシャーが生まれ、単純な回避だけではなく戦略的な操作も求められるようになります。
今回の実装を通じて、よりプレイしごたえのあるゲームづくりを体験していきましょう。
敵キャラ(幽霊)の画像と初期位置を設定
ゲームに新しいキャラクターを追加するには、まずそのキャラクターの「画像」と「初期位置」の設定を行う必要があります。
幽霊も同様で、ゲームに登場させるためには画像ファイルを読み込み、ゲーム画面上のどこに表示するかを指定します。
画像読み込みと編集方法
まずは、幽霊の画像ファイル yuurei.png
を読み込みます。
さらにプレイヤーが左右に動くのに合わせて、幽霊の画像も左右反転できるように画像を2パターン用意しておきましょう。
このコードでは、pygame.image.load()
で画像を読み込み、pygame.transform.scale()
で画像サイズを統一しています。
さらに、pygame.transform.flip()
で左右反転した画像を作ることで、移動方向に応じて見た目を変えることができるようになります。
初期位置の座標指定
続いて、幽霊がゲーム開始時にどこに現れるかを指定します。
これは他のキャラクターと同じようにpygame.Rect
を使って定義します。
このコードにより、幽霊はゲーム開始時に画面の左下(x=50, y=550)に現れます。
プレイヤーである猫は左上に出現するため、幽霊はそこからプレイヤーに向かってじわじわと近づいてくることになります。
プレイヤーを追いかける処理を実装
幽霊の画像と初期位置の設定が完了したら、次はプレイヤーを追いかけてくる動きを実装していきます。
このような処理を「追跡アルゴリズム」と呼びますが、今回は難しい計算は行わず、プレイヤーの位置と幽霊の位置を比較して少しずつ近づける というシンプルな方法を使います。
敵の移動方向を計算するロジック
まず、現在のプレイヤーの座標(neko_rect.x
, neko_rect.y
)と幽霊の座標(yuurei_rect.x
, yuurei_rect.y
)を比べて、どちらの方向に動けば近づけるかを判断します。
このようにして、幽霊は毎フレーム1ピクセルずつプレイヤーの方向に向かって移動するようになります。
追跡スピードは「1」としてありますが、より速くしたい場合はこの数値を2や3に変更すれば対応できます。
方向に応じて画像を切り替える方法
幽霊の画像も、プレイヤーの右側にいるときは左向き画像、左側にいるときは右向き画像を表示するようにします。
この処理をgamestage()
関数の中に追加することで、幽霊がそれを追いかけてくる動きが実現します。
プレイヤーと敵の接触を判定しよう
幽霊がプレイヤーを追いかけてくる動きが完成したら、次に必要なのは「幽霊がプレイヤーにぶつかったかどうか」を判断する処理です。
これにより、プレイヤーが幽霊につかまった場合にゲームオーバーとすることができます。
衝突判定とは
衝突判定とは2つのオブジェクト(ここでは猫と幽霊)が同じ位置に重なっているかを調べる処理のことです。
Pygameではオブジェクトの位置と大きさを表すRect
(矩形)に対して、colliderect()
という便利な関数が用意されています。
この関数はあるRectが別のRectと重なっているかどうかを調べるもので、結果はTrue
またはFalse
で返ってきます。
このコードはneko_rect
とyuurei_rect
が重なったときに変数page
を2
に変更します。
page
の値が2
になると、mainループ
でgameover()
関数が呼ばれるようになっており、自動的にゲームオーバー画面へと切り替わります。
幽霊の初期位置をリセットする
幽霊の動きと衝突判定が完成したことで、ゲームプレイ中にプレイヤーを追いかける敵としてしっかり機能するようになりました。
しかし、ゲームオーバー後にリセットボタンを押して再スタートしたときに、幽霊の位置がそのままになっていると、正しくゲームが始まりません。
そのため、他のキャラクター(猫や犬)と同じように、幽霊の初期位置もゲーム開始時の座標に戻す処理が必要です。
これを行うのが、ゲームの状態を初期化するreset()
関数です。
これでリセット時に幽霊もゲーム開始時の場所に戻るようになり、プレイヤーと同じタイミングで再スタートすることができます。
このようにプレイヤーだけでなく、ゲーム内のすべてのキャラクターや状態を初期化することは、ゲームの安定した動作にとって非常に重要です。
まとめ
Chapter3では簡単なアクションゲームを作りながら、実践的なゲームづくりの基本を一つ一つ積み重ねてきました。
以下の5つの記事を通して、プレイヤーの操作、敵キャラクターの設置、衝突によるゲームの状態遷移、ゴールの設定、そして動く敵の実装といった重要な技術を学びました。
- 3-1 衝突判定の基本を理解しよう:プレイヤーと壁の衝突処理の方法
- 3-2 画面に敵を表示しよう:敵キャラクター(犬)を表示し、画面上に配置
- 3-3 ゲームオーバーを設定しよう:敵との接触でゲームオーバーになる仕組み
- 3-4 ゲームクリアを設定しよう:ゴールに到達するとゲームクリアになる処理
- 3-5 追跡してくる敵を実装しよう:追いかけてくる動的な敵を追加し、よりゲーム性を高めた
これらを通して、アクションゲームに必要な基本要素を一通り自分の手で作ることができるようになったはずです。
どの機能も一見シンプルに見えますが、組み合わせることでしっかりと遊べるゲームが完成しました。
ゲームづくりは創造力と工夫の連続です。今回学んだことを土台にして、さらに新しいアイデアを加えてみてください。後述の「チャレンジ!さらに面白いゲームに改造しよう!」に挑戦するのもお勧めです^^
ここまで学習を進めてきたあなたは、すでに立派なゲームクリエイターの仲間入りです。
ぜひ自分だけのオリジナルゲームづくりにチャレンジしてみてください。お疲れさまでした!
- Chapter3-5の完成コード
-
今回の記事での完成するコード全体は↓↓の通りです。
必要な方は開いて確認しましょう。
# 初期化(ゲームの準備をする) import pygame, sys, random pygame.init() screen = pygame.display.set_mode((800,600)) ## 背景画像の設定 haikei_img = pygame.image.load("images/shibafu.png") haikei_img = pygame.transform.scale(haikei_img, (800, 600)) ## プレイヤーの猫の設定 neko_imgR = pygame.image.load("images/neko.png") neko_imgR = pygame.transform.scale(neko_imgR, (50,50)) neko_imgL = pygame.transform.flip(neko_imgR, True, False) neko_rect = pygame.Rect(50,50,50,50) # 猫のRect ## 敵の犬の設定 enemy_img = pygame.image.load("images/inu.png") enemy_img = pygame.transform.scale(enemy_img,(50,50)) enemys = [] # 複数の犬のRectを格納する空のリスト for i in range(13): wx = 100 + i * 50 wy = random.randint(20,550) enemys.append(pygame.Rect(wx,wy,50,50)) # 犬のRect ## 敵の幽霊の設定 yuurei_imgR = pygame.image.load("images/yuurei.png") yuurei_imgR = pygame.transform.scale(yuurei_imgR,(50,50)) yuurei_imgL = pygame.transform.flip(yuurei_imgR,True, False) yuurei_rect = pygame.Rect(50,550,50,50) # 幽霊のRect ## ゴールの設定 sakana_img = pygame.image.load("images/sakana.png") sakana_img = pygame.transform.scale(sakana_img,(100,100)) sakana_rect = pygame.Rect(680,480,100,100) ## 壁の設定 walls = [pygame.Rect(0,0,800,20), pygame.Rect(0,0,20,600), pygame.Rect(780,0,20,600), pygame.Rect(0,580,800,20)] ## リセットボタンの設定 replay_img = pygame.image.load("images/replaybtn.png") ## メインループ内で使う変数 rightFlag = True # 猫の向き pushFlag = False # リセットボタンが押されたか page = 1 # 表示するページ(1:ゲーム画面、2:ゲームオーバー画面、3、ゲームクリア画面) # ここから関数の定義 ## ゲームステージ def gamestage(): global rightFlag global page # 画面の初期化 screen.blit(haikei_img, (0,0)) vx = 0 vy = 0 #ユーザー入力 key = pygame.key.get_pressed() if key[pygame.K_RIGHT]: # 右キーが押されたら右へ移動 vx = 4 rightFlag = True if key[pygame.K_LEFT]: # 左キーが押されたら左へ移動 vx = -4 rightFlag = False if key[pygame.K_UP]: # 上キーが押されたら上へ移動 vy = -4 if key[pygame.K_DOWN]: # 下キーが押されたら下へ移動 vy = 4 ## 猫の処理 neko_rect.x += vx neko_rect.y += vy if neko_rect.collidelist(walls) != -1: # 壁と衝突したらそれ以上動かない neko_rect.x -= vx neko_rect.y -= vy if rightFlag: # 猫の向きの指定 screen.blit(neko_imgR, neko_rect) else: screen.blit(neko_imgL, neko_rect) ## 犬の処理 for enemy in enemys: # enemysリストの全要素(敵のRect) screen.blit(enemy_img, enemy) if neko_rect.collidelist(enemys) != -1: # 猫と犬との衝突判定 page = 2 ## 幽霊の処理 y_vx = 0 # 幽霊の移動量 y_vy = 0 if yuurei_rect.x < neko_rect.x: # ネコと幽霊の位置関係から移動方向を指定 y_vx = 1 else: y_vx = -1 if yuurei_rect.y > neko_rect.y: y_vy = 1 else: y_vy = -1 yuurei_rect.x += y_vx yuurei_rect.y -= y_vy if y_vx > 0: # 幽霊の向きを指定 screen.blit(yuurei_imgR,yuurei_rect) else: screen.blit(yuurei_imgL,yuurei_rect) if neko_rect.colliderect(yuurei_rect): # 猫と幽霊との衝突判定 page = 2 ## ゴールの処理 screen.blit(sakana_img,sakana_rect) if neko_rect.colliderect(sakana_rect): # 猫と魚の山との衝突判定 page = 3 ## 壁の処理 for wall in walls: # wallsリストの全要素(壁のRect) pygame.draw.rect(screen, pygame.Color("DARKGREEN"),wall) ## ジャンプ関数(ボタンが押されたらnewpageへジャンプする) def button_to_jump(btn, newpage): global page, pushFlag #ユーザー入力 mdown = pygame.mouse.get_pressed() (mx, my) = pygame.mouse.get_pos() if mdown[0]: if btn.collidepoint(mx, my) and pushFlag == False: page = newpage pushFlag = True else: pushFlag = False ## リセット関数(リセットボタンが押されたらゲームをリセットする) def reset(): # 猫を再配置 neko_rect.x = 50 neko_rect.y = 50 # 犬を再配置 for i in range(13): enemys[i].x = 100 + i * 50 enemys[i].y = random.randint(20,550) # 幽霊を再配置 yuurei_rect.x = 50 yuurei_rect.y = 550 ## ゲームオーバー関数 def gameover(): reset() # リセット関数の呼び出し screen.fill(pygame.Color("NAVY")) font = pygame.font.Font(None, 150) text = font.render("GAMEOVER", True, pygame.Color("RED")) screen.blit(text, (100,200)) btn1 = screen.blit(replay_img,(320,480)) button_to_jump(btn1, 1) # ジャンプ関数の呼び出し ## ゲームクリア関数 def gameclear(): reset() # リセット関数の呼び出し screen.fill(pygame.Color("GOLD")) font = pygame.font.Font(None, 150) text = font.render("GAMECLEAR", True, pygame.Color("RED")) screen.blit(text,(60,200)) btn1 = screen.blit(replay_img,(320,480)) button_to_jump(btn1,1) # ジャンプ関数の呼び出し # メインループ while True: if page == 1: gamestage() elif page == 2: gameover() elif page == 3: gameclear() #画面表示 pygame.display.update() pygame.time.Clock().tick(60) # 終了処理 for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit()
チャレンジ!さらに面白いゲームに改造しよう!
アクションゲームの製作講座はここで終了ですが、このゲームはまだまだ面白くできる余地があるとおもいませんか?
以下のアイディアをベースにして、あなたの手でより面白いゲームへ作り変えてみて下さい。
- 犬の敵に動きをつける
- 壁を作って迷路にし、犬を特定の場所に配置+動かす
- 特定のRectに触れると別のRectの位置へワープする
- 複数のステージを作り、クリア後に次のステージへ移動する
FAQ|敵の追跡アルゴリズム【pygame】
- Q1. 敵キャラがプレイヤーを追いかけるにはどんなロジックが必要?
-
プレイヤーと敵の座標を比較し、方向ごとに速度を加算・減算することで追跡の動きを作ることができます。
- Q2. 敵の動きが滑らかにならない場合の対処法は?
-
フレームごとの移動量が多すぎたり、座標の更新が整数単位で行われているとカクつきが出やすくなります。微調整を行いましょう。
- Q3. 敵の向きに応じて画像を切り替える方法は?
-
左右の移動方向を判定して、pygameの
transform.flip()
などで画像を反転させることで、向きを変える表現が可能です。
質問用コンタクトフォーム
この記事を書くにあたりAIを活用しています。
人間の目による確認も行っていますが、もし間違い等ありましたらご指摘頂けると大変助かります。