はじめに
pythonでデータ分析など学習しているのですが、ちょっと息抜きに違うことをやってみたくなりました><
そんな時に、pygameというライブラリを使用してオリジナルゲームが作成できることを知り、これだ!と思った次第です。
pythonでもゲームって作れるんですね〜! (^ワ^)
それでは、サンプルコードを交えながら、オリジナルゲームを作成してみます!
実施環境
macOS Monterey 12.2.1(M1チップ)
python 3.10
pygame 2.1.2(pygameドキュメントはこちら)
ゲーム仕様内容
まずどんなゲーム作成するか決めないと始まらないですね><
ということで、下記のようなゲームを作ってみます。
- 右、左キーでプレイヤーを動かして、上から降ってくるアイテムをゲットする
- アイテムは、まじうまドーナツと激辛唐辛子の2種類
- メータは時間経過とともに減っていく
- まじうまドーナツをゲットするとメータが復活
- 激辛唐辛子をゲットとメータが減ると同時にプレイヤー巨大化
- メータが「0」になったらゲームオーバー
- 一定時間経ったら降ってくるアイテムが増えていき難易度がアップ
- 閉じるボタン押されたらゲーム終了
ゲーム用にプレイヤー、アイテムのビット画像を自作で作ってみました(^o^)
環境構築
まずは、環境作ってpygameをインストールします。
- python仮想環境構築
python -m venv beaver_game
- 仮想環境アクティブ化
source bin/activate
- 仮想環境にて、pygameをインストール
% pip install pygame % pip list Package Version ---------- ------- pip 21.2.4 pygame 2.1.2 setuptools 58.1.0
これで環境ができました。それでは実際にpygameを使ってプログラミングしてみます。
pygame設定
<pygame基本設定>
pygameの基本設定処理は下記のようになっています。
- paygame初期化
paygame.init() - 画像ファイルを読み込み
pygame.image.load(‘画像パス’); - pygame終了
pygame.quit() - ゲームタイトル設定
pygame.display.set_caption(ゲームタイトル) - ゲームアイコン設定
pygame.display.set_icon(読み込んだアイコン画像オブジェクト) - タイマー管理用オブジェクト生成
pygame.time.Clock() - ゲーム画面初期化しディスプレイオブジェクト生成
pygame.display.set_mode((長さ, 高さ))
set_modeで作成したディスプレイオブジェクトが基盤となります。このオブジェクトにプレイヤーやアイテムを描画していきます。
<Game Main処理>
- ゲーム状態として
ゲーム開始状態(STEP_READY)
ゲームプレイ中(STEP_PLAY)
ゲームオーバ(STEP_GAMEOVER)
としています。
ゲームオーバーすると最初のSTEP_READYの状態になるようにしています。 - ゲーム終了判定
main関数で無限ループとして、閉じるボタン押下のイベントを取得すると終了するようにしています。閉じるボタンが押下された時、paygameを終了させます。同時に、pythonプログラムも修正させます。
# 閉じるボタン押下された時はゲーム終了 if event.type == pygame.QUIT: pygame.quit() sys.exit()
- アイテムが当たったかの判定
プレイヤー、アイテム自体の中心位置の距離と、プレイヤーとアイテムが現在いる場所のX座標、Y座標の絶対値を比較し、プレイヤーとアイテムが当たっているかを判定しています。文章だとわかりづらいですが式になるとこのようになります。
abs(プレイヤーX座標 – アイテムX座標) <= プレイヤー長さ/2 + アイテム長さ/2
※高さとY座標も上記と同じように計算します。
どちらも条件満たしていたら”アイテムがヒットした!”状態となります。
if (abs(x1-x2) <= +p_width/2+ITEM_WIDTH/2 and abs(y1-y2) <= p_height/2+ITEM_HEIGHT/2): return True return False
<Sample Code>
上記をベースにしたサンプルコードはこちらになります。
import logging import pygame import random import sys # 画面サイズ SURFACE_WIDTH = 800 SURFACE_HEIGHT = 550 # STEP STEP_READY = 0 STEP_PLAY = 1 STEP_GAMEOVER = 2 # アイテム設定 ITEM_TYPE_NUM = 2 ITEM_WIDTH = 50 ITEM_HEIGHT = 72 ITEM_MAX = 60 # ビーバーちゃん設定 PLAYER_WIDTH = 50 PLAYER_HEIGHT = 72 PLAYER_Y = 520 # 満腹メータMAX STUFFED_MAX = 200 # 各種初期値を設定 step = STEP_READY timer = 0 is_jump = 0 p_width = PLAYER_WIDTH p_height = PLAYER_HEIGHT stuffed = STUFFED_MAX item_hit = [False] * ITEM_MAX item_x = [0] * ITEM_MAX item_y = [0] * ITEM_MAX item_type = [''] * ITEM_MAX item_num = 10 flg_turn = False last_key = pygame.K_RIGHT dmg_effect = 0 # 画像読み込み # アイコン icon = pygame.image.load('img/icon_beaver.png') # 背景 img_bg = pygame.image.load('img/img_bg.png') # プレイヤー img_player = pygame.image.load('img/beaver.png') # アイテム(ドーナツ) img_donuts = pygame.image.load('img/donuts.png') # アイテム(唐辛子) img_red_hot = pygame.image.load('img/redhot.png') # ゲームオーバー txt_gameover = pygame.image.load('img/txt_gameover.png') # プレイヤーの移動 def move_player(key): global px, is_jump, last_key, flg_turn, p_width, p_height # 「⇦」キー押下の動き if key[pygame.K_LEFT] == 1: px -= 10 if px < 50+p_width/2: px = 50+p_width/2 if last_key == pygame.K_RIGHT: flg_turn = True last_key = pygame.K_LEFT # 「→」キー押下した時の動き elif key[pygame.K_RIGHT] == 1: px += 10 if px > SURFACE_WIDTH-50-p_width/2: px = SURFACE_WIDTH-50-p_width/2 if last_key == pygame.K_LEFT: flg_turn = True last_key = pygame.K_RIGHT # アイテムの作成 def locate_item(): # アイテム数MAX値まで繰り返し for i in range(ITEM_MAX): # ランダムでドーナツか唐辛子どっちかを設定する item_x[i] = random.randint(50, SURFACE_WIDTH-50-ITEM_WIDTH/2) item_y[i] = random.randint(-500, 0) if i % ITEM_TYPE_NUM == 0: # ドーナツ item_type[i] = 'd' else: # 激辛唐辛子 item_type[i] = 'r' # アイテムの落下と当たり判定 def move_item(surface): for i in range(item_num): item_y[i] += 6 + i / 5 if item_y[i] > SURFACE_HEIGHT: item_hit[i] = False item_x[i] = random.randint(50, SURFACE_WIDTH-50-ITEM_WIDTH/2) item_y[i] = random.randint(-500, 0) if item_hit[i] == False: # プレイヤーとアイテムの座標を見て当たったか判定 if is_item_hit(px, PLAYER_Y, item_x[i], item_y[i]) == True: item_hit[i] = True hit_item(item_type[i], surface) # アイテムを描画 def draw_item(surface): for i in range(item_num): if item_hit[i] == False and item_type[i] == 'd': surface.blit( img_donuts, [item_x[i]-ITEM_WIDTH/2, item_y[i]-ITEM_HEIGHT/2]) elif item_hit[i] == False and item_type[i] == 'r': surface.blit( img_red_hot, [item_x[i]-ITEM_WIDTH/2, item_y[i]-ITEM_HEIGHT/2]) # アイテムに当たったときの処理 def hit_item(category, surface): global stuffed, dmg_effect # ドーナツゲットの時は満腹メータプラス if category == 'd': stuffed += 10 if stuffed > STUFFED_MAX: stuffed = STUFFED_MAX # 激辛唐辛子の場合は満腹メータ激減り elif category == 'r': stuffed -= 20 if stuffed < 0: stuffed = 0 dmg_effect = 1 # 当たり判定 def is_item_hit(x1, y1, x2, y2): if (abs(x1-x2) <= +p_width/2+ITEM_WIDTH/2 and abs(y1-y2) <= p_height/2+ITEM_HEIGHT/2): return True return False # main関数 def main(): global step, timer, stuffed, px, is_jump global item_num, img_player, flg_turn, dmg_effect global p_width, p_height # ウィンドウを作成 pygame.init() pygame.display.set_caption('くいしんぼうビーバーちゃんのドーナツいっぱい食べたい!') pygame.display.set_icon(icon) clock = pygame.time.Clock() surface = pygame.display.set_mode((SURFACE_WIDTH, SURFACE_HEIGHT)) # ループ while True: timer += 1 # イベントごとの処理 for event in pygame.event.get(): # 閉じるボタン押下された時はゲーム終了 if event.type == pygame.QUIT: pygame.quit() sys.exit() # プレイ開始時 if step == STEP_READY: # プレイモードへ step = STEP_PLAY # 満腹メータMAX stuffed = STUFFED_MAX flg_turn = False # ビーバーちゃんスタンバイ px = SURFACE_WIDTH / 2 p_width = PLAYER_WIDTH p_height = PLAYER_HEIGHT img_player = pygame.image.load('img/beaver.png') # 最初は10個からスタートし、だんだん増えてくる item_num = 10 # アイテム落下 locate_item() # プレイモード elif step == STEP_PLAY: if stuffed <= 0: step = STEP_GAMEOVER timer = 0 if item_num != ITEM_MAX and timer % 15 == 0: item_num += 10 # 時間経過でも徐々に満腹メータ減る stuffed -= 0.5 # キャラクター、アイテム移動 move_player(pygame.key.get_pressed()) move_item(surface) # ゲームオーバー elif step == STEP_GAMEOVER: if timer == 50: step = STEP_READY timer = 0 # 各種描画 bx = 0 by = 0 if dmg_effect > 0: # ダメージ受けた場合は画面揺らす bx = random.randint(-60, 20) by = random.randint(-40, 10) dmg_effect = 0 # ダメージ受けたらビーバーちゃん巨大化 p_width = p_width*1.2 p_height = p_height*1.2 img_player = pygame.transform.scale(img_player, (p_width,p_height)) # 背景 surface.blit(img_bg, [bx, by]) if flg_turn == True: img_player = pygame.transform.flip(img_player, True, False) flg_turn = False # ビーバーちゃん描画 surface.blit(img_player, [px-p_width/2, PLAYER_Y-p_height/2]) # アイテム描画 draw_item(surface) # ゲームオーバー if step == STEP_GAMEOVER: logging.info(stuffed) # ゲームオーバーの文言のサブ画面を作ってメーン画面へかぶせる sub_surface = pygame.Surface((SURFACE_WIDTH, SURFACE_HEIGHT), pygame.SRCALPHA) sub_surface.fill((0, 0, 0, 100)) surface.blit(sub_surface, [0, 0]) surface.blit(txt_gameover, [100, 220]) stuffed = 0 # 満腹メータ surface.fill((250, 237, 240), (50, 30, STUFFED_MAX, 40)) stuffed_r = 100 stuffed_g = 100 stuffed_b = 255 if stuffed < STUFFED_MAX/5: # メーターが残り1/5になったら赤くする stuffed_r = 255 stuffed_g = 100 stuffed_b = 128 surface.fill((stuffed_r, stuffed_g, stuffed_b), (50, 30, stuffed, 40)) # ゲーム画面更新 pygame.display.update() clock.tick(10) # main実行 main()
実際に遊んでみよう!
それでは上記で作成したコードを実行してみますー(^ワ^)
実行するとこのようにゲーム画面が立ち上がりゲーム開始します!
おわりに
いかがでしたでしょうか?今回はpygameを使ってオリジナルゲームを作成してみました。今回作ったコードから色々派生させて面白いゲームに育てていくのも通かもしれませんね。
このゲームのドーナツや唐辛子の素材はどこにあるのでしょうか??
コメントありがとうございます!
まじうまドーナツや唐辛子は、自作したドット絵になります。
“ドット絵 作成” で検索したブラウザ上でドット絵作成できるツールを使って自作しました。
この作成した画像を、ソースがある直下にimgフォルダを作成して格納し、
pygame.image.load(‘img/beaver.png’)
のように読み込ませています。