はじめに
前回の記事にて、pythonでゲーム開発してみましたが、お試しで作ったものでしたので、実行と共にいきなりゲームスタートしていたり汗、プレイヤーも左右に移動するのみで単調な動きでした。
そのため今回は、よりゲーム感をでるように機能を追加して改修してみました!
実施環境
macOS Monterey 12.2.1(M1チップ)
python 3.10
pygame 2.1.2(pygameドキュメントはこちら)
追加対応
今回は、下記仕様を追加して改修しました。
- スタート画面追加
STARTボタン:ゲーム開始
QUITボタン:ゲーム終了 - スペースキー+左右キーでジャンプの動作を追加
- 唐辛子ヒットした時のアクションを追加
- ゲームオーバー画面にリトライボタンと終了ボタンを追加
RETRYボタン:ゲーム再開始
QUITボタン:ゲーム終了
スタート画面を用意して、よりゲーム感を出してみます。
game_init()関数で、スタート画面の設定を行なっています。
ボタン設定のところで
STARTボタン:ボタン押下でゲームスタート(main呼び出し)
QUITボタン:ボタン押下でゲーム終了(game_quit呼び出し)
のように設定しています。
def game_init():
global game_start
while game_start:
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_quit()
surface.blit(start_img, (0, 0))
display_text(GAME_TITLE, 100, white_color, (SURFACE_WIDTH/2), (SURFACE_HEIGHT / 8)*4.5)
display_button("START", 15, black_color, (SURFACE_WIDTH / 4), (SURFACE_HEIGHT/4)*3, 100, 50, button_white, button_blue, main)
display_button("QUIT", 15, black_color, (SURFACE_WIDTH / 4)*3-80, (SURFACE_HEIGHT / 4)*3, 100, 50, button_white, button_blue, game_quit)
pygame.display.update()
clock.tick(30)
ジャンプの動作は、左右キーのアクションのmove_player()関数にスペースキーが押されていたらプレイヤー位置の高さを足す制御を入れました。
プレイヤー描画後元に戻して、着地な動作に見せています。重力計算とかはまだ。。。;;
ジャンプ用のドット絵も追加して、ジャンプ感出しました。
# プレイヤーの移動
def move_player(key):
global px, is_jump, last_key, flg_turn, p_width, p_height, jump
# 「⇦」キー押下の動き
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
# NEW スペースキーはジャンプ
if key[pygame.K_SPACE] ==1:
# spaceはジャンプ
p_height = PLAYER_HEIGHT + 15o
jump = 1
画面はこのようなイメージになりました。
プレイヤーの描画は、各アクションの情報を保持し、アクションに沿ったイメージ画像に切り替えています。
if dmg_effect > 0:
# ダメージ受けた場合は画面揺らす
bx = random.randint(-60, 30)
by = random.randint(-10, 20)
# 背景
surface.blit(img_dmg_bg, [bx, by])
dmg_effect = 0
elif jump > 0:
# 背景
surface.blit(img_bg, [bx, by])
surface.blit(jump_player, [px-p_width/2, PLAYER_Y-p_height/2])
# jump後元に戻す
p_height = PLAYER_HEIGHT
jump = 0
else:
# 背景
surface.blit(img_bg, [bx, by])
surface.blit(img_player, [px-p_width/2, PLAYER_Y-p_height/2])
実際に遊んでみよう!
それではコードを実行してゲームで遊んでみましょう!
今回は動画も撮ってみましたー
え待って!プレイヤーのジャンプアクション追加したらすごい可愛くなりますね!!!動いてる感が出ました!
各画面キャプチャは下記になります。
スタート画面あるとゲーム感出ますね!
STARTボタンを押して、ゲーム開始します!
ジャンプ!
唐辛子ヒット!ヒィィィィィ!
激辛唐辛子なので火を吹いちゃってます><
ゲームオーバー!
更新後のソースはこちらになります。
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
GAME_TITLE = "まじうまドーナツ"
# 各種初期値を設定
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
jump = 0
# 画像読み込み
# アイコン
icon = pygame.image.load('img/icon_beaver.png')
# 背景
img_bg = pygame.image.load('img/img_bg.png')
img_dmg_bg = pygame.image.load('img/img_dmg_bg.png')
# プレイヤー
img_player = pygame.image.load('img/beaver.png')
jump_player = pygame.image.load('img/jump.png')
dmg_player = pygame.image.load('img/dmg_player.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')
# スタートアップ
start_img = pygame.image.load('img/start.png')
# ボタン
button_white = pygame.image.load('img/white_button.png')
button_blue = pygame.image.load('img/blue_button.png')
game_start = True
# ウィンドウを作成
pygame.init()
surface = pygame.display.set_mode((SURFACE_WIDTH, SURFACE_HEIGHT))
black_color = (0, 0, 0)
white_color = (255, 255, 255)
green_color = (60, 186, 84)
clock = pygame.time.Clock()
def game_init():
global game_start
while game_start:
for event in pygame.event.get():
if event.type == pygame.QUIT:
game_quit()
surface.blit(start_img, (0, 0))
display_text(GAME_TITLE, 100, white_color, (SURFACE_WIDTH/2), (SURFACE_HEIGHT / 8)*4.5)
display_button("START", 15, black_color, (SURFACE_WIDTH / 4), (SURFACE_HEIGHT/4)*3, 100, 50, button_white, button_blue, main)
display_button("QUIT", 15, black_color, (SURFACE_WIDTH / 4)*3-80, (SURFACE_HEIGHT / 4)*3, 100, 50, button_white, button_blue, game_quit)
pygame.display.update()
clock.tick(30)
def display_button(msg, size, msg_color, x, y, w, h, ic, ac, action = None):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x+w > mouse[0] > x and y+h > mouse[1] > y:
surface.blit(ac, (x, y))
if click[0] == 1 and action != None:
action()
else:
surface.blit(ic, (x, y))
display_text(msg, size, msg_color, (x+(w/2)), (y+(h/2)))
def display_text(text, size, color, center_x, center_y):
textFont = pygame.font.SysFont("arialunicode", size)
TextSurf = textFont.render(text, True, color)
TextRect = TextSurf.get_rect()
TextRect.center = (center_x, center_y)
surface.blit(TextSurf, TextRect)
def game_quit():
pygame.quit()
sys.exit()
# プレイヤーの移動
def move_player(key):
global px, is_jump, last_key, flg_turn, p_width, p_height, jump
# 「⇦」キー押下の動き
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
if key[pygame.K_SPACE] ==1:
# spaceはジャンプ
p_height = PLAYER_HEIGHT + 15o
jump = 1
# アイテムの作成
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
# ゲームオーバーからのリトライ
def retry():
global step, flg_turn, stuffed
step = STEP_READY
flg_turn = False
last_key = pygame.K_RIGHT
# main関数
def main():
global step, timer, stuffed, px, is_jump
global item_num, img_player, jump_player, dmg_player, flg_turn, dmg_effect
global p_width, p_height, jump
pygame.display.set_caption(GAME_TITLE)
pygame.display.set_icon(icon)
#surface = pygame.display.set_mode((SURFACE_WIDTH, SURFACE_HEIGHT))
# ループ
while True:
timer += 1
# イベントごとの処理
for event in pygame.event.get():
# 閉じるボタン押下された時はゲーム終了
if event.type == pygame.QUIT:
game_quit()
# プレイ開始時
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')
jump_player = pygame.image.load('img/jump.png')
img_player = pygame.image.load('img/dmg_player.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)
# 各種描画
bx = 0
by = 0
if dmg_effect > 0:
# ダメージ受けた場合は画面揺らす
bx = random.randint(-60, 30)
by = random.randint(-10, 20)
# 背景
surface.blit(img_dmg_bg, [bx, by])
dmg_effect = 0
elif jump > 0:
# 背景
surface.blit(img_bg, [bx, by])
surface.blit(jump_player, [px-p_width/2, PLAYER_Y-p_height/2])
# jump後元に戻す
p_height = PLAYER_HEIGHT
jump = 0
else:
# 背景
surface.blit(img_bg, [bx, by])
surface.blit(img_player, [px-p_width/2, PLAYER_Y-p_height/2])
if flg_turn == True:
img_player = pygame.transform.flip(img_player, True, False)
jump_player = pygame.transform.flip(jump_player, True, False)
flg_turn = False
# アイテム描画
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])
display_button("RETRY", 15, black_color, (SURFACE_WIDTH / 4), (SURFACE_HEIGHT/4)*3, 100, 50, button_white, button_blue, retry)
display_button("QUIT", 15, black_color, (SURFACE_WIDTH / 4)*3-80, (SURFACE_HEIGHT / 4)*3, 100, 50, button_white, button_blue, game_quit)
stuffed = 0
# 満腹メータ
surface.fill((250, 237, 240), (50, 30, STUFFED_MAX, 40))
stuffed_r = 60
stuffed_g = 186
stuffed_b = 84
if stuffed < STUFFED_MAX/5:
# メーターが残り1/3になったら赤くする
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)
game_init()
game_quit()
おわりに
今回スタート画面やプレイヤーの動きを追加することでよりゲーム感が増して実装していて楽しかったですね。今度は別のゲームも作ってみたくなりました。