この記事は、岩手県八幡平市のプログラミング教室「アクセルキャンプ」の公開教材です。
アクセルキャンプ(フリースペースプラウド)のリンク
教材の作成依頼等も承っております。ご意見等は、リンク先の問い合わせ欄からお願いします。
教材の転用・利用等は自由です
最近リッキーの気になってるもの
皆さんポケモンGOとかやったことありますか?先生は最初にインストールだけして、ちょっと動かしただけなんですか、最初にやったときは、「すげ!ポケモンいる!カビゴンがうちにいる!」みたいになりました笑
でも、あれって携帯通してしか見れないので、しばらくやってるうちに「なんかなー、惜しいよなー」って思った人いません?先生はそうでした。そこで、ついに最近こういうのが出てきたんですよ!!
https://rental.kikito.docomo.ne.jp/portal/articles/5015/
ついにメガネでARできるようになったわけですよ!ドキがムネムネしませんか??ヤバくない??
まだ対応してるアプリが少なかったりするのが何店ですが、そらそうですよ!未来のガジェットだもん!
先生のサイフポイントはもうゼロよ!という状態なのですが、どうする??買っちゃう?
サンタさんに聞いてみます。
なんちゃってRPGゲーム、とりあえずここまでとしましょう
今まで作ってきたRPGゲームは、まだまだゲームとしては単純すぎますが、とりあえず今回は「ゲームってこんな感じで動いてるんだぜ!」というのを理解するのが目的なので、授業で扱うのはこんかいまでとします。
「こんな機能をつけたい!」「改造したい!」というのがあったら、リッキーに相談してください!
まずは最終のコードを見てみよう!
今までは自分でコードを書くのがメインでしたが、人が書いたコードを読むのもものすごく勉強になります!
import pygame
from pygame.locals import *
import sys
import time
import random
# 効果音
def sound_effect(sound):
se = pygame.mixer.Sound(sound)
se.play()
# 半角文字を全角にし、DragonQuestFC.tffに対応させる
def dq_font(number):
return str(number).translate(str.maketrans({chr(0x0021 + i): chr(0xFF01 + i) for i in range(94)}))
def main():
pygame.init() # Pygameの初期化
screen = pygame.display.set_mode((800, 600)) # 400 x 300の大きさの画面を作る
pygame.display.set_caption("いけめんがつくったげえむ") # 画面上部に表示するタイトルを設定
# 選択肢ボックスの引数
rectX_select = 20 # 長方形左上のx座標
rectY_select = 400 # 長方形左上のx座標
rectW_select = 800 - (rectX_select * 2) # 長方形の幅
rectH_select = 600 - (rectY_select + 20) # 長方形の高さ
# 選択ボックス内テキスト
message = "りっきーが あらわれた!"
# HPボックスの引数
rectX_slymeHP = 550
rectY_slymeHP = 20
rectX_playerHP = 600
rectY_playerHP = 200
rectY_slymeHP = 20
rectW_HP = 180
rectH_HP = 100
slyme_image = pygame.image.load("image/slyme.png") # スライムの画像
slymeX = 200 # スライムのx座標
font = pygame.font.Font("font/DragonQuestFC.ttf", 30)
# 色
white = (255, 255, 255)
yellow = (255, 255, 0)
red = (255, 0, 0)
# 効果音
pygame.mixer.init()
run = False # 逃げる
slyme_HP = 530000 # スライムのHP
player_HP = 10000
action_number = 0 # 選択肢の番号
damage = 0
damageY = 0 # ダメージ量表示のY座標
time_start = time.time()
while (1):
time_end = time.time()
if slyme_HP > 0 and time_end - time_start > 10.0:
player_damage = random.randint(8000, 10000)
player_HP -= player_damage
sound_effect("sound/battle.mp3")
if player_HP <= 0:
player_HP = 0
sound_effect("sound/gameover.mp3")
message = "スライム:でなおしてきな!"
else:
message = "リッキーの こうげき プレイヤーに " + dq_font(player_damage) + "のダメージ"
time_start = time.time()
# リスト:
colors = [white, white, white]
colors[action_number] = yellow
screen.fill((0, 0, 0)) # 画面を黒色に塗りつぶし
screen.blit(slyme_image, (slymeX, 0)) # スライムの画像表示
# HPテキスト表示の枠を表示
pygame.draw.rect(screen, white, Rect(rectX_slymeHP, rectY_slymeHP, rectW_HP, rectH_HP), 10)
pygame.draw.rect(screen, white, Rect(rectX_playerHP, rectY_playerHP, rectW_HP, rectH_HP), 10)
# スライムのHP表示
slymeHP_text = font.render("HP:" + dq_font(slyme_HP), True, white)
slymeHP_rect = slymeHP_text.get_rect(center=(640, 70))
screen.blit(slymeHP_text, slymeHP_rect)
# プレイヤーのHP表示
playerHP_text = font.render("HP:" + dq_font(player_HP), True, white)
playerHP_rect = playerHP_text.get_rect(center=(690, 250))
screen.blit(playerHP_text, playerHP_rect)
# attackのアニメーション表示
text_damage = font.render(dq_font(damage), True, red)
damage_rect = slymeHP_text.get_rect(center=(400, damageY))
if damageY > 50:
screen.blit(text_damage, damage_rect)
damageY -= 1
# 選択しのウィンドウを表示
pygame.draw.rect(screen, white, Rect(rectX_select, rectY_select, rectW_select, rectH_select), 10)
message_text = font.render(message, True, (255, 255, 255))
battle_text = font.render("たたかう", True, colors[0])
spell_text = font.render("じゅもん", True, colors[1])
run_text = font.render("にげる", True, colors[2])
screen.blit(message_text, (40, 420))
screen.blit(battle_text, (45, 450))
screen.blit(spell_text, (45, 480))
screen.blit(run_text, (45, 510))
pygame.display.update() # 画面を更新
# HP0のときの終了処理
if message in ("スライム:でなおしてきな!", "リッキーをたおした! プレイヤーのしょうり!!"):
time.sleep(10)
sys.exit()
if run == True:
slymeX -= 2
if slymeX < -400:
pygame.quit()
# イベント処理
for event in pygame.event.get():
if event.type == QUIT: # 閉じるボタンが押されたら終了
pygame.quit() # Pygameの終了(画面閉じられる)
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_DOWN:
if action_number == 2:
action_number = 0
else:
action_number += 1
sound_effect("sound/select_se.mp3")
elif event.key == K_UP:
if action_number == 0:
action_number = 2
else:
action_number -= 1
sound_effect("sound/select_se.mp3")
# エンターキーが押されたとき
elif event.key == K_RETURN:
# 「にげる」のBGM
if action_number == 2:
sound_effect("sound/run.mp3")
run = True
continue
# 「たたかう」のBGM
elif action_number == 0:
sound_effect("sound/battle.mp3")
# hit_pointの計算
damage = random.randint(10000, 30000)
# 「じゅもん」のBGM
elif action_number == 1:
sound_effect("sound/spell.mp3")
# hit_pointの計算
damage = random.randint(30000, 50000)
slyme_HP -= damage
damageY = 200
if slyme_HP <= 0:
slyme_HP = 0
message = "リッキーをたおした! プレイヤーのしょうり!!"
sound_effect("sound/victory.mp3")
else:
message = "リッキーに " + dq_font(damage) + "の ダメージ"
if __name__ == "__main__":
main()
改めて見てみると、200行くらいのコードになってますね!ここまでよく頑張りました!一生懸命書いたコードがちゃんと動くのを見るのは、気持ちいいですよね!また、うまくいかない原因がわかって解決するのは、また違ったうれしさがありますよね!
前回のコードと見比べてみましょう!いくつかの変更点があります。
まずは、新機能ではない、前回のコード動きを変更しないコードの変更(リファクタリング)から。
①dq_font 関数
リッキーがコードを書きながら気づいたのですが、dragonquestFC.ttfは半角の文字に対応していませんでした。対応してるフォントであればこれは必要ない関数ですが、今回は関数を作ることで対応しました。「ググルのめんどくさいからコード書いた」って言えばちょっとかっこいいかな?笑
def dq_font(number):
return str(number).translate(str.maketrans({chr(0x0021 + i): chr(0xFF01 + i) for i in range(94)}))
辞書型という、まだあんまり勉強してないpythonの文法を使っているので、気になる人は調べているのもいいかもしれません。いろいろ書いてますが、やってることは「半角文字を全角にする」です。
②HP表示
前回はprint文でログを出力下だけでしたが、今回はしっかりと画面上に表示するようにしました。
前回はスライムのHPだけ表示してましたが、プレイヤー側にダメージを与える仕組みにしたので(後述)プレイヤー側のHPも表示しています。
# HPテキスト表示の枠を表示
pygame.draw.rect(screen, white, Rect(rectX_slymeHP, rectY_slymeHP, rectW_HP, rectH_HP), 10)
pygame.draw.rect(screen, white, Rect(rectX_playerHP, rectY_playerHP, rectW_HP, rectH_HP), 10)
# スライムのHP表示
slymeHP_text = font.render("HP:" + dq_font(slyme_HP), True, white)
slymeHP_rect = slymeHP_text.get_rect(center=(640, 70))
screen.blit(slymeHP_text, slymeHP_rect)
# プレイヤーのHP表示
playerHP_text = font.render("HP:" + dq_font(player_HP), True, white)
playerHP_rect = playerHP_text.get_rect(center=(690, 250))
screen.blit(playerHP_text, playerHP_rect)
まず同じ大きさの四角を2つ作り、その中にそれぞれのtextをblitする。
↑の文章で「なるほど!」とうなずける人は、だいぶpygameがわかってきた証拠です。ダメージが発生するたびに、ここに表示される数が減っていきます。
③スライムによる反撃機能の作成
リッキー(スライム)だって黙ってやられるわけではありません。めんどくさがりのリッキーでも、たまには反撃します。具体的には10秒に一回。
time_end = time.time()
if slyme_HP > 0 and time_end - time_start > 10.0:
player_damage = random.randint(8000, 10000)
player_HP -= player_damage
sound_effect("sound/battle.mp3")
if player_HP <= 0:
player_HP = 0
sound_effect("sound/gameover.mp3")
message = "スライム:でなおしてきな!"
else:
message = "リッキーの こうげき プレイヤーに " + dq_font(player_damage) + "のダメージ"
time_start = time.time()
time_start time_endという2つの変数で時間を管理します。time モジュールの使い方はいろいろあり、細かくは説明しませんが、time.time()を実行したときに、その時の時間を設定します。time_endーtime_startを引き算した結果=経過した時間が10秒以上だったとき、スライムは攻撃し、その後time_startをリセットして次の10秒に備えます。
また、そのときにプレイヤーのHPが0以下になったときに、ゲームオーバー用の音楽を流します。
④ゲームオーバー、勝利の演出追加
if message in ("スライム:でなおしてきな!", "リッキーをたおした! プレイヤーのしょうり!!"):
time.sleep(10)
sys.exit()
このif文だけで、終了したあとに操作しても、HP表示が変化したり、音がなったりしないように制御しています。
気をつけるべきポイント、と言うか、リッキーが気をつけずにミスってしまったことは、これをpygame.update()のあとに書くことです。こうしないと、終了はするけど、HP表示が変化しないで終わってしまうという、なんとも歯切れの悪い感じになってしまいます。
今回はぶっちゃけ細かい変更はそれ意外にも結構してるので、アクセルキャンプに参加せずにこの記事だけで勉強してる人(一部いるみたいです!ありがとう!)は、「??」ってなるところも多いと思いますが、そこはご勘弁を。
RPGっぽいなにかの制作、どうだった?
最初に言った通り、今回はゲームを作ったと言うより、ゲームの1シーンをそれっぽく作ってみた感じになりますが、どうだったでしょうか。
普段勉強していることがこういう感じで生かされているんだな、という視点を持つと、またプログラミングの勉強に対する見方が変わってきませんか?
アレンジしまくろう!
せっかく作ったのにここで終わるのはもったいない!って思ってるそこのキミ!まずはアイディアだしからしていって、できそうなところから挑戦してみよう!
・オープニング画面などの挿入
・スライムの次の敵キャラの追加
・行動の選択肢の追加
などなど
ではまた来週もPygameで面白いの作っていく予定なので、お楽しみに!