Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

MENU

MediaPipeでじゃんけんゲームを作成

先日MediaPipeで手の形状を検出してみました。
elsammit-beginnerblg.hatenablog.com

最後に手の形状を検出してじゃんけんの判定を行ってみました。

これを見てふと、、、
せっかくなのでCPUと対戦出来るじゃんけんゲーム作ったら面白いかな??
と思い立ち、作成することに。
最初はとりあえず、OpenCVのimshowでの表示を用いてじゃんけんゲームを作成してみました。




■できたアプリ

作成できたアプリはこちら!!
音声も自分で用意したものを流しております。
youtu.be

■コードはgithubにて公開中

コードはこちらに格納しております。
よろしければご覧ください。
https://github.com/elsammit/rockpaperscissors-usingmediapipe

こちらのコードですが、
git cloneで取ってきたソースコードにて、

python JankenMain.py

を実行すればOKです。
なお、MediaPipeライブラリのインストールが必要になります。
インストール手順は前回のブログにまとめておりますのでこちらを参考に。
elsammit-beginnerblg.hatenablog.com

■コードに関して

こちらのコードですが、クラスとして下記定義しております。
・handsクラス:MediaPipeライブラリを用いて検出したじゃんけんの手
・Pertnerクラス:CPUの出す手
・Judgeクラス:CPUの手とユーザの手の結果を元に勝ち負け判定
・Onseiクラス:「じゃんけん。ぽん。」音声出力

handsクラスで実施している内容は前回のブログで記載済みになりますので詳細は割愛します。
また、Onseiクラスに関してもメソッドコールで音声出力しているのみになります。
このため、今回はPartnerクラス内で実施している内容について以下でまとめていきたいと思います。

■CPUが出す手の処理

CPU側の処理は下記コードの通りになります。

import cv2
import threading
import JankenOnsei 
import random
import time
import Define

m_Define = Define.Define()
m_Onsei = JankenOnsei.Onsei()

class Pertner:
    looretCnt = 1000
    filePath = m_Define.HANDSHAPE_ROCK_IMG
    result_img = cv2.imread(m_Define.HANDSHAPE_ROCK_IMG)
    StartFlg = False
    JankenResult = False
    rsp = ""
    FinishFlg = False
    testFlg = False

    def StartOnseiThread(self):
        thread1 = threading.Thread(target=m_Onsei.jankenOnsei)
        thread1.start()

    def GetHandsMat(self, cnt):
        if cnt % 10 <= 2:
            self.filePath = m_Define.HANDSHAPE_ROCK_IMG
            self.rsp = m_Define.HANDSHAPE_ROCK
        elif cnt % 10 <= 5:
            self.filePath = m_Define.HANDSHAPE_SCISSORS_IMG
            self.rsp = m_Define.HANDSHAPE_SCISSOR
        elif cnt % 10 <= 9:
            self.filePath = m_Define.HANDSHAPE_PAPPERS_IMG
            self.rsp = m_Define.HANDSHAPE_PAPPER

    def PartnerHands(self):
        if m_Onsei.onseiStatus == 0:
            self.GetHandsMat(self.looretCnt)
            if self.looretCnt > 1000:
                self.looretCnt = 0
        elif m_Onsei.onseiStatus == 1:
            x = random.randint(0,5)
            self.GetHandsMat(x)
            self.JankenResult = True
            m_Onsei.onseiStatus += 1
        elif m_Onsei.onseiStatus == 2:
            self.JankenResult = False
        elif m_Onsei.onseiStatus == 3:
            m_Onsei.onseiStatus = 0
            self.StartOnseiThread()
            
            self.JankenResult = False
            self.looretCnt = 0
            time.sleep(0.1)

        resultImage = cv2.imread(self.filePath)

        return resultImage
    
    def PartnerLoop(self):
        m_Onsei.onseiStatus = 3
        while True:
            self.looretCnt += 1
            self.result_img = self.PartnerHands()
            self.StartFlg = True

            time.sleep(0.01)

            if self.FinishFlg == True:
                break

CPU側の処理としては、大きく分けると
 ・じゃんけんの掛け声の間は数百ミリ秒単位でグー、チョキ、パーをローリング
 ・ぽんの掛け声でランダムに処理を切り替える
の2つになります。

こちらの2つの処理の切り替えですが、
音声と同期をとるために、m_Onsei.onseiStatusにて管理しております。
m_Onsei.onseiStatusですが、
m_Onsei.onseiStatus = 0 :じゃんけんの掛け声中。
m_Onsei.onseiStatus = 1 :ぽんを言う直前。CPUの手を決定し表示。
m_Onsei.onseiStatus = 2 :ぽんを言った後。CPUとの勝敗を表示している間の待ち。
m_Onsei.onseiStatus = 3 :次のじゃんけんを行うための初期化処理。

音声ですが、本クラス内でOnseiインスタンスを生成させ、別スレッドで音声出力を実行しております。

じゃんけんの掛け声中のグー、チョキ、パーのルーレットですが、
looretCntの数値により切り替えております。
(こちらは別の方法の方がいいかも?)

こちらの切り替えを実施しているのが、
PartnerHands関数になります。
そして、PartnerHands関数を定期的にコールしているのが、
PartnerLoop関数。
PartnerLoop関数内では10ms間隔でループを実行。

PartnerLoop関数ですが、
このじゃんけんゲームのmain関数にあたる、
JankenMain.py内でmainとは別スレッドでコールしております。

デバッグ用ですがCPU側のみを出力させることも出来るようになっております。
こちらのコードを実行するとCPU側だけの処理が動くようになります。

python PartnerHands.py

実際に実行するとこちらのようになります。
※実際には音声も出力されています。
f:id:Elsammit:20220116105735g:plain

■最後に

とりあえずのレベルですがじゃんけんゲーム動くレベルまで持っていくことが出来ました!!
ちょっと気になる点もあるので、時間を見て修正していきたいと思います!!
また最新版が出来たらご連絡したいと思います。