【初心者向け】Pythonでタイピングゲームを簡単に作ってみた(tkinter)

【初心者向け】Pythonでタイピングゲームを簡単に作ってみた(tkinter) プログラミング

Pythonのtkinterでタイピングゲームを作成してみた

Pythonの基礎構文(繰り返し・条件分岐)やクラス、関数を使用して内部処理を実装し、UI部分はtkinterでデスクトップで動くタイピングゲームを作成したので、コードの解説をしていきたいと思います。

完成イメージ

ゲームの流れとしては

  • ユーザーが「ゲーム開始」ボタンをクリック
  • タイマーが開始され、問題が表示される
  • ユーザーが入力し、正解すると次の問題に進む
  • 制限時間が終了すると、ゲームオーバーとなりスコアが表示される

ファイル構成

main.py
typeBrain.py
ui.py
data.py
score.txt
  • main.py: これはプログラムのメインファイルです。ゲームの実行やその他のモジュールの呼び出しを行う中心的な役割を果たします。
  • typeBrain.py: このファイルはゲームのロジック部分を担当しています。タイピングの正誤判定、スコア計算、時間管理などの機能を実装しています。
  • ui.py: ユーザーインターフェース(UI)に関する処理を行うファイルです。
  • data.py: タイピングゲームの問題文が含まれています。
  • score.txt: プレイヤーのスコアを保存するためのテキストファイルです。

メイン処理(main.py)

main.pyの役割としては、必要なモジュールを集約し、オブジェクトを初期化するものとなります。

from data import typing_data
from typeBrain import TypeBrain
from ui import Ui

typing=TypeBrain(typing_data)
ui=Ui(typing)

typeBrainの引数には、問題文typing_data(data.py)を渡しており、uiの引数には、typingを渡しており、TypeBrainで作成したオブジェクトを参照できるようにしています。

内部処理(typeBrain.py)

このTypeBrainクラスは、タイピングゲームのコアロジックを実装しています。主な機能は以下の通りです

class TypeBrain:
    def __init__(self,typing_data):
        self.question_data=typing_data
        self.score=0
        self.question_num=0
        self.time=60
        self.current_question=None
        
    
    def next_question(self):
        self.current_question=self.question_data[self.question_num]
        self.question_num+=1
        return self.current_question #次の質問を返す
    

    def check_answer(self,entry):
        if self.current_question["question"]==entry:
            self.score+=1
            return True
        else:
            return False
    
    def update_score(self,high_score):
        if self.score>high_score:
            with open("Day82~Portfolio/Day85_typing_game/score.txt",mode="w")as file:
                file.write(str(self.score))           
  • 初期化 (__init__):問題データ(引数)、スコア、問題番号、制限時間、現在の問題を初期化、制限時間は60秒に設定しています。
  • 次の問題を取得 (next_question):問題リストから次の問題を取得し、問題番号を1つ進めます。現在の問題を返します。
  • 回答をチェック (check_answer):ユーザーの入力と現在の問題を比較します。正解の場合、スコアを1増やしてTrueを返します。不正解の場合、Falseを返します。
  • スコアの更新 (update_score):現在のスコアがハイスコアを超えた場合、新しいハイスコアをファイルに書き込みます。

ユーザーが入力するたびにcheck_answerメソッドが呼び出され、正誤判定とスコア計算が行われます。次の問題に進む際にnext_questionメソッドが呼び出されます。ゲーム終了時にupdate_scoreメソッドが呼び出され、必要に応じてハイスコアが更新されます。

UI実装(ui.py)

このコードは、タイピングゲームのユーザーインターフェース(UI)を実装しています。Tkinterを使用してGUIを作成しています。引数で渡ってきたTypeBrainのオブジェクトを参照することができます

import tkinter as tk
from tkinter import messagebox
from tkinter import font as tkfont

class Ui:
    def __init__(self, typing):
        self.typing = typing
        self.window = tk.Tk()
        self.window.title("タイピングゲーム")
        self.window.config(padx=20, pady=20, bg="#F0F0F0")
        self.window.geometry("600x400")  # ウィンドウサイズを設定

        # フォントの設定
        self.title_font = tkfont.Font(family="Helvetica", size=16, weight="bold")
        self.normal_font = tkfont.Font(family="Helvetica", size=12)
        self.large_font = tkfont.Font(family="Helvetica", size=14)

        # フレームの作成
        self.header_frame = tk.Frame(self.window, bg="#F0F0F0")
        self.header_frame.pack(fill=tk.X, pady=(0, 20))

        self.content_frame = tk.Frame(self.window, bg="#F0F0F0")
        self.content_frame.pack(expand=True, fill=tk.BOTH)

        # ヘッダー部分(スコア、タイマー)
        self.time_label = tk.Label(self.header_frame, text="60", font=self.title_font, bg="#F0F0F0", fg="#333333")
        self.time_label.pack(side=tk.LEFT, padx=(0, 20))

        self.score_label = tk.Label(self.header_frame, text=f"スコア: {self.typing.score}", font=self.normal_font, bg="#F0F0F0", fg="#333333")
        self.score_label.pack(side=tk.LEFT, padx=(0, 20))

        self.high_score = self.load_high_score()
        self.high_score_label = tk.Label(self.header_frame, text=f"ハイスコア: {self.high_score}", font=self.normal_font, bg="#F0F0F0", fg="#333333")
        self.high_score_label.pack(side=tk.LEFT)

        # 問題表示部分
        self.question_jpn_label = tk.Label(self.content_frame, text="", font=self.large_font, bg="#F0F0F0", fg="#333333")
        self.question_jpn_label.pack(pady=(0, 10))

        self.question_romaji_label = tk.Label(self.content_frame, text="", font=self.normal_font, bg="#F0F0F0", fg="#666666")
        self.question_romaji_label.pack(pady=(0, 20))

        # 入力欄
        self.type_entry = tk.Entry(self.content_frame, width=40, font=self.normal_font)
        self.type_entry.pack(pady=(0, 20))
        self.type_entry.bind("<KeyRelease>", self.keypress)

        # スタートボタン
        self.start_button = tk.Button(self.content_frame, text="ゲーム開始", command=self.start_game, font=self.normal_font, bg="#4CAF50", fg="white", activebackground="#45a049")
        self.start_button.pack()

        self.next_question_view()
        self.window.mainloop()

    def load_high_score(self):
        try:
            with open("Day82~Portfolio/Day85_typing_game/score.txt", mode="r") as file:
                return int(file.read().strip() or 0)
        except (FileNotFoundError, ValueError):
            return 0

    def start_game(self):
        self.start_button.pack_forget()  # スタートボタンを隠す
        self.type_entry.focus_set()
        self.count_down()
        # ここでゲーム開始のロジックを実装

    def next_question_view(self):
        self.current_quesiton=self.typing.next_question()
        self.question_jpn_label.config(text=self.current_quesiton["question"])
        
    def keypress(self,e):
        entry=self.type_entry.get()
        self.feedback(self.typing.check_answer(entry))
    
    def feedback(self,is_result):
        if is_result: #回答と入力が一致した場合は次の問題へ進む
            self.score_label.config(text=f"スコア:{self.typing.score}")
            self.next_question_view()
            self.type_entry.delete(0,tk.END)

    def count_down(self):
        if self.typing.time<0:
            self.typing.update_score(self.high_score)
            messagebox.showinfo("メッセージ", f"ゲーム終了です。スコアは {self.typing.score} です。")
            self.window.destroy()
            return
        
        self.time_label.config(text=self.typing.time)
        self.timer=self.window.after(1000,self.count_down)
        self.typing.time-=1 

  • ウィンドウの設定(__init__):self.window=tk.TK()でTkinterのオブジェクトを作成してタイトル、サイズ、背景色などを設定しています。
  • UI要素の作成(__init__):
  • ヘッダー部分(__init__):時間、スコア、ハイスコアを表示。スコアは、typingオブジェクト内にある変数の値を取得しており、ハイスコアはscore.txtから取得して表示しています。
  • コンテンツ部分(__init__):問題、入力欄、スタートボタンを配置。入力欄はself.type_entry.bind(“<KeyRelease>”, self.keypress)で、キーボード入力があった都度、判定処理が実行されるようにしています。スタートボタンがクリックされると command=self.start_gameの処理でゲーム開始の処理が実行されます。
  • start_game():ゲーム開始時の処理。スタートボタンを非表示として、入力欄にフォーカスを当てます。その後、self.count_down()を実行します。
  • next_question_view()次の問題を表示
  • keypress():キー入力時の処理。キーボード入力の都度、入力欄の値を取得し、feedbackメソッドを呼び出します。引数には、check_answer(entry)の正誤の戻り値を渡します。
  • feedback():回答のフィードバック処理。正解していた場合は、スコア、問題番号、入力欄の更新を行います。
  • count_down():タイマー機能。self.window.after(1000,self.count_down)1秒ごとにカウントダウンを実行します。タイマーが0秒以下になると、スコア更新の実行を行いウィンドウを終了します。
  • load_high_score():ファイルからハイスコアを読み込み。テキストファイルからスコアを読み込み、int型に変換しています。

データ保存(data.py、score.txt)

このdata.pyファイルは、タイピングゲームで使用される問題データを定義しています。

typing_dataという名前のリストが定義されています。リストの各要素は辞書形式で、”question”キーに対応する値として問題文が格納されています。

typing_data = [
    {
        "question": "The quick brown fox jumps over the lazy dog.",
    },
    {
        "question": "To be or not to be, that is the question.",
    },
    {
        "question": "All that glitters is not gold.",
    }
]

score.txtは、空のファイルを用意しておく必要があります。

タイトルとURLをコピーしました