エンジニアいちねんせい

「エンジニアはブログを書け」という先人の教えにならってブログをはじめてみたよ

Tkinterでドラクエ風の戦闘を作ってみたよ

どうもこんにちは〜。今回はpythontkinterドラクエっぽい戦闘画面を作ったよの回です。
最近はpythonをぼちぼち触ってまして、まぁ何か作ってみようかなということでtkinterを使ったGUIドラクエ1っぽい戦闘画面を作ってみました。
画面はこんな感じ。
f:id:barayosi:20190424134602p:plain
画面左側に表示されているキャラクター。これがりゅうおうです。かわいいね。
最初はトレスしようかと思ったんですが怒られが発生したら嫌だなと思って自分で描きました。ドラゴンじゃなくてムラサキ色のカンガルーなので完全に大丈夫です。
プレイヤーは通常攻撃の[たたかう]、ベキラマ相当の[まほう]、ベホイミ相当の回復、絶対に逃げられない[にげる]コマンドを駆使してりゅうおうを倒す趣向です。DQ1の死闘がここにミニマムな形で再現されていますよこれは。
りゅうおうは絶対に先制で激しいほのお、火の息、鋭いツメ(通常攻撃)をローテで使ってくるようになってます。

遊び方

コードは以下の通り。テキストにコピペして[りゅうおう.py]で保存して、画像素材を[りゅうおう.jpg]で保存して任意の場所に置いて、ソースコード内の画像ファイルのパスを修正したのちにコマンドプロンプトから[python3 りゅうおう.py]で実行すれば遊べるよ!
※事前にpython3を使えるようにしておき、追加でPILのインストールも必要です。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import tkinter as tk
import PIL

#勇者のステータス
hp = 138
mxhp = 138
mp = 123
power = 135
dif = 92

#敵のステータス
en_hp = 350
en_pow = 127
en_dif = 154
en_act = 0
dmg = 0

root = tk.Tk()
from PIL import Image, ImageTk

#りゅうおうの画像
img1 = ImageTk.PhotoImage(file="/Users/ユーザー名/Desktop/りゅうおう.jpg")#画像を置いたパスに書き換えてね
root.title("りゅうおう")
root.geometry("470x380")

#画像のキャンバス
canvas = tk.Canvas(root,width=230,height=280,bg="white")
canvas.place(x=0,y=0)
canvas.create_image(120,150,image=img1,tag="illust")

#ボタンのフレーム
buttonFrame= tk.Frame(width=150,bg="white")
buttonFrame.place(x=250,y=0)

#メッセージのフレームを入れるキャンバス
msgcanvas = tk.Canvas(root,width=250,height=200,bg="white")
msgcanvas.place(x=220,y=130)

#メッセージのフレーム
msgFrame = tk.Frame(msgcanvas)

#スクロールバー
bar= tk.Scrollbar(root,orient=tk.VERTICAL)
bar.pack(side=tk.RIGHT,fill=tk.Y)
bar.config(command=msgcanvas.yview)
msgcanvas.config(yscrollcommand=bar.set)
msgcanvas.config(scrollregion=(0,0,5000,5000))
msgcanvas.create_window((0,0),window=msgFrame,anchor=tk.NW,width=msgcanvas.cget('width'))

#勇者のステータス表示
def update_label(event):
    strings=("勇者(HP:"+ str(round(hp)) + "/MP:" + str(round(mp))+")")
    var.set(strings)
var = tk.StringVar()
var.set("勇者(HP:"+ str(hp) + "/MP:" + str(mp)+")")
status = tk.Label(buttonFrame, textvariable=var,bg="white")
status.pack()

#各種ボタンのイベント
def tatakau():
    global hp,mp,power,dif,en_hp,en_pow,en_dif,en_act,dmg
    ryuoh()
    if hp <= 0:
        pass
    elif en_hp < 0:
        pass
    else:
        dmg = (power/2)-(en_dif/4)
        en_hp = en_hp - dmg
        if hp > mxhp:
            hp = mxhp
        if hp <= 0:
            result=tk.Label(msgFrame,text="勇者は倒れた!")
            result.pack()
        elif en_hp < 0:
            result=tk.Label(msgFrame,text="りゅうおうを倒した!")
            result.pack()
        else:
            result=tk.Label(msgFrame,text="勇者の攻撃!")
            result.pack()
            result=tk.Label(msgFrame,text="りゅうおうに"+str(round(dmg))+"のダメージ!")
            result.pack()
    strings=("勇者(HP:"+ str(round(hp)) + "/MP:" + str(round(mp))+")")
    var.set(strings)

def mahou():
    ryuoh()
    global hp,mp,power,dif,en_hp,en_pow,en_dif,en_act,dmg
    if hp <= 0:
        pass
    elif en_hp < 0:
        pass
    elif mp < 15:
        result=tk.Label(msgFrame,text="勇者は雷の呪文を唱えた!")
        result.pack()
        result=tk.Label(msgFrame,text="しかしMPが足りない!")
        result.pack()
    else:
        if hp <= 0:
            result=tk.Label(msgFrame,text="勇者は倒れた!")
            result.pack()
        elif en_hp < 0:
            result=tk.Label(msgFrame,text="りゅうおうを倒した!")
            result.pack()
        else:
            mp = mp - 15
            dmg  = 60
            en_hp = en_hp - dmg
            if hp > mxhp:
                hp = mxhp
            if mp < 0:
                mp = 0
            result=tk.Label(msgFrame,text="勇者は雷の呪文を唱えた!")
            result.pack()
            result=tk.Label(msgFrame,text="りゅうおうに"+str(round(dmg))+"のダメージ!",)
            result.pack()
    strings=("勇者(HP:"+ str(round(hp)) + "/MP:" + str(round(mp))+")")
    var.set(strings)

def kaifuku():
    ryuoh()
    global hp,mp,power,dif,en_hp,en_pow,en_dif,en_act,dmg
    if hp <= 0:
        pass
    elif en_hp < 0:
        pass
    elif mp < 10:
        result=tk.Label(msgFrame,text="勇者は癒しの呪文を唱えた!")
        result.pack()
        result=tk.Label(msgFrame,text="しかしMPが足りない!")
        result.pack()
    else:
        hp = hp + 80
        mp = mp - 10
        if hp > mxhp:
            hp = mxhp
        result=tk.Label(msgFrame,text="勇者は癒しの呪文を唱えた!")
        result.pack()
        result=tk.Label(msgFrame,text="勇者のHPが80回復!")
        result.pack()
    strings=("勇者(HP:"+ str(round(hp)) + "/MP:" + str(round(mp))+")")
    var.set(strings)

def nigeru():
    global hp,mp,power,dif,en_hp,en_pow,en_dif,en_act,dmg
    if hp <= 0:
        result=tk.Label(msgFrame,text="勇者は倒れた!")
        result.pack()
    elif en_hp < 0:
        result=tk.Label(msgFrame,text="りゅうおうを倒した!")
        result.pack()
    else:
        result=tk.Label(msgFrame,text="勇者は逃げ出した!")
        result.pack()
        result=tk.Label(msgFrame,text="しかし回り込まれてしまった!",)
        result.pack()
    ryuoh()
    strings=("勇者(HP:"+ str(round(hp)) + "/MP:" + str(round(mp))+")")
    var.set(strings)

#りゅうおうの行動
def ryuoh():
    global hp,mp,power,dif,en_hp,en_pow,en_dif,en_act,dmg
    if hp <= 0:
        result=tk.Label(msgFrame,text="勇者は倒れた!")
        result.pack()
    elif en_hp < 0:
        result=tk.Label(msgFrame,text="りゅうおうを倒した!")
        result.pack()
    elif en_act == 0:
        #激しいほのお(中ダメージ)
        dmg = 40
        hp = hp - dmg
        if hp <= 0:
            hp = 0
        en_act=en_act +1
        result=tk.Label(msgFrame,text="りゅうおうは激しい炎を吐いた!")
        result.pack()
        result=tk.Label(msgFrame,text="勇者に"+str(dmg)+"のダメージ!")
        result.pack()
    elif en_act == 1:
        #火の息(小ダメージ)
        dmg = 20
        hp = hp - dmg
        if hp <= 0:
            hp = 0
        en_act=en_act +1
        result=tk.Label(msgFrame,text="りゅうおうは火の息を吐いた!")
        result.pack()
        result=tk.Label(msgFrame,text="勇者に"+str(dmg)+"のダメージ!")
        result.pack()
    else:
        #鋭いツメ(通常攻撃・大ダメージ)
        dmg = (en_pow/2)-(dif/4)
        hp = hp - dmg
        if hp <= 0:
            hp = 0
        en_act = 0
        result=tk.Label(msgFrame,text="りゅうおうは鋭いツメで切りつけてきた!")
        result.pack()
        result=tk.Label(msgFrame,text="勇者に"+str(round(dmg))+"のダメージ!")
        result.pack()

#初期メッセージ
start = tk.Label(msgFrame,text="りゅうおうが現れた!")
start.pack()

#ボタン配置
Button1 = tk.Button(buttonFrame,text="たたかう",width=10,command=tatakau)
Button1.pack()
Button2 = tk.Button(buttonFrame,text="まほう",width=10,command=mahou)
Button2.pack()
Button3 = tk.Button(buttonFrame,text="かいふく",width=10,command=kaifuku)
Button3.pack()
Button4 = tk.Button(buttonFrame,text="にげる",width=10,command=nigeru)
Button4.pack()

root.mainloop()</span>

f:id:barayosi:20190424135041j:plain
りゅうおう.jpgで保存してね

かいせつ

はい。とりあえず中身の解説をしますと勇者とりゅうおうのステータスを定義した後にTkinterでフレームとボタンを配置して、PILで画像表示して、メッセージ用フレームをスクロールさせるスクロールバーを配置して、各コマンドのボタンを押した時の戦闘処理とメッセージをdefで書いて終わり!って感じです。
ダメージ処理はだいたい固定値なんですが[たたかう]だけはドラクエ風に攻撃/2-守備/4にしてるのが小さなこだわり。
あとは勝利or敗北時のメッセージを複数箇所に書いちゃってやたら行数が増えているのがチャームポイントですね。絶対もっといい書き方あるでしょ。。と思いつつまぁ動くし別にいいやということで完成させました。
また、勝利or敗北後に[にげる]ボタンを押すと勝利/敗北のメッセージが2回流れる不具合もあります。(直すのが面倒だった)

そんな感じでtkinterドラクエ風の戦闘作ったよの回でした。とりあえず動くもの作ってみよう、っていうのは勉強になりますね〜。次はDjangoで似たようなの作りたいかもしれない。
それではまた!シーユーネクスト!