OpenCV

ブロック崩しを半日で作る3

3回目の記事になりました。(前回の記事はこちら)
今回は、ボールとバーを動かす(2,3)&ゲームオーバーになる(7)ところまで実装しましょう。

さて、前回までにゲームをはじめる最初の画面を作成しましたね。ここからはどのようにゲームを開始していくかについて考えていきたいところですが、1回目の記事では、それをボールが動き出した時点と考えていました。
しかし、考え直してみると、「じゃあ、ボールが動き出すタイミングはいつにしたらええねん」と思いませんか?(僕は実際、このような思考のプロセスをたどりました。)

「プログラムを開始してから数秒後に動き出す」とかでも、いいのはいいのですが、やっぱり自分のタイミングで開始したいところではあります。何かいい手はありますでしょうか。

. . .

…僕が考えたのは、単純に「バーを動かし始めたらボールも落ちてくる」というものでした。これなら自分がキーを押したタイミングで好きにゲームを開始できます。

幸い、OpenCVにはキーボードから入力されたキーを受け取るwaitKeyという機能があります。
前回の記事では、単純に画面に表示した画像を任意の時間表示し続けるだけの機能しか使いませんでしたが、キーボードから入力を受け取るのが本来の役割だったわけです。ですので、これを使えば、簡単にバーの操作の実装ができるということになります。

まずは前回のinitialize(wallpaper, bar, ball, block)のあとに次のコードを挿入してみてください。

while True:
	k = cv2.waitKey(5) # waiting input

	move_bar(k)

ここではwaitKeyを使って、kにキーボードからの入力したキーを代入しています。ちなみに5という引数はこの行が実行されている5ミリ秒間のうちに入力されたキーを代入するということです。そして、それがwhile True:の中にあるということはつまり、kは5ミリ秒間隔で入力されたキーを更新していくということです。

次に、このkの値をもとにバーを動かすためのmove_barという関数を定義していきましょう(3)。

def move_bar(k):
    global bar_x
    if k==102 and bar_x>5: #input 'F'
        wallpaper[600:600+bar.shape[0], bar_x:bar_x+bar.shape[1]] = wallpaper_ori[600:600+bar.shape[0], bar_x:bar_x+bar.shape[1]]
        bar_x = bar_x-10
        wallpaper[600:600+bar.shape[0], bar_x:bar_x+bar.shape[1]] = bar
    elif k==106 and bar_x<420: #input 'J'
        wallpaper[600:600+bar.shape[0], bar_x:bar_x+bar.shape[1]] = wallpaper_ori[600:600+bar.shape[0], bar_x:bar_x+bar.shape[1]]
        bar_x = bar_x+10
        wallpaper[600:600+bar.shape[0], bar_x:bar_x+bar.shape[1]] = bar
    cv2.imshow('Breakout', wallpaper)

move_barの中ではまずグローバル変数からバーの位置を取得しています。
もし、これをローカルの変数としてしまうと、関数を処理し終われば、またもとの数字に戻ってしまいます。これでは、結局全然バーが動かないことになってしまいますので、グローバル変数として定義し、この関数の処理が終わった後も
そのままの値で次の処理につなげる形にしておかないといけないわけです。

さて、その次のif文ではASCIIコードをもとにキーが’F’`であるかどうか&バーが左端にないかどうかを判定し、それを満たしていればバーを左に少し(今回のプログラムなら10ピクセル分)ずらすという操作を行っています。
そして、elifで同様の内容を今度は右にずらすときのケースも記述し、最後に更新されたゲーム画面をimshowで表示しています。

それでは、この調子でボールを動かすmove_ballも書いていきましょう(2)。

def move_ball(): #game over is also set here.
     global ball_x, ball_y, vector_x, vector_y
     wallpaper[ball_y:ball_y+ball.shape[0], ball_x:ball_x+ball.shape[1]] = wallpaper_ori[ball_y:ball_y+ball.shape[0], ball_x:ball_x+ball.shape[1]]
     ball_x = ball_x + vector_x
     ball_y = ball_y + vector_y
     wallpaper[ball_y:ball_y+ball.shape[0], ball_x:ball_x+ball.shape[1]] = ball
     cv2.imshow('Breakout', wallpaper)

if ball_y == 620:
    print("GAME OVER")
    exit()

ここではxの移動間隔、言ってみればボールのスピードをvector_x, vector_yとしてグローバル変数から受け取っています。
また、ここでボールがもしバーよりも下に落ちてしまった場合、ゲームオーバーとしてゲームを終了させる機能を付けています。ゲームオーバーは簡単に、画面に”GAME OVER”という文字を出力して、exit()を使うことでプログラム自体を終了させています(7)。

同じロジックを使って、ゲーム中、プレイヤーが任意のタイミングでゲームを終了できるように、以下のコードをmainにあるwhile文の中に埋め込みます。

if k==101: # input 'e'
	print('Exit')
	exit()

では、これまで書いてきたコードを確認してみましょう。
次の通りになっていればOKです。

import os
import cv2

def initialize(wallpaper, bar, ball, block):
    global ball_x, ball_y, bar_x, bar_y, vector_x, vector_y, block_x, block_y
    ball_x = 246
    ball_y = 500
    bar_x = 214
    vector_x = 0
    vector_y = 3

    for i in range(20, 406, 77):
        for j in range(5, 100, 17):
            wallpaper[j:j+block.shape[0], i:i+block.shape[1]] = block

    wallpaper[ball_y:ball_y+ball.shape[0], ball_x:ball_x+ball.shape[1]] = ball
    wallpaper[600:600+bar.shape[0], bar_x:bar_x+bar.shape[1]] = bar

    cv2.imshow('Breakout', wallpaper)
    cv2.waitKey(0)

def move_bar(k):
    global bar_x
    if k==102 and bar_x>5: #input 'F'
        wallpaper[600:600+bar.shape[0], bar_x:bar_x+bar.shape[1]] = wallpaper_ori[600:600+bar.shape[0], bar_x:bar_x+bar.shape[1]]
        bar_x = bar_x-10
        wallpaper[600:600+bar.shape[0], bar_x:bar_x+bar.shape[1]] = bar
    elif k==106 and bar_x<420: #input 'J'
        wallpaper[600:600+bar.shape[0], bar_x:bar_x+bar.shape[1]] = wallpaper_ori[600:600+bar.shape[0], bar_x:bar_x+bar.shape[1]]
        bar_x = bar_x+10
        wallpaper[600:600+bar.shape[0], bar_x:bar_x+bar.shape[1]] = bar
    cv2.imshow('Breakout', wallpaper)

def move_ball(): #game over is also set here.
    global ball_x, ball_y, vector_xs, vector_y
    wallpaper[ball_y:ball_y+ball.shape[0], ball_x:ball_x+ball.shape[1]] = wallpaper_ori[ball_y:ball_y+ball.shape[0], ball_x:ball_x+ball.shape[1]]
    ball_x = ball_x + vector_x
    ball_y = ball_y + vector_y
    wallpaper[ball_y:ball_y+ball.shape[0], ball_x:ball_x+ball.shape[1]] = ball
    cv2.imshow('Breakout', wallpaper)

    if ball_y == 620:
        print("GAME OVER")
        exit()

#main
PWD = os.getcwd() + "/"
global wallpaper_ori, ball_x, ball_y, bar_x, bar_y, vector_x, vector_y

wallpaper = cv2.imread(PWD + "images/osaka.jpg")
wallpaper = cv2.resize(wallpaper, (500, 650))
wallpaper_ori = cv2.resize(wallpaper, (500, 650))
bar = cv2.imread(PWD + "images/bar.png")
bar = cv2.resize(bar, (72, 12))
block = cv2.imread(PWD + "images/block.png")
block = cv2.resize(block, (72, 12))
ball = cv2.imread(PWD + "images/ball.png")
ball = cv2.resize(ball, (8, 8))

initialize(wallpaper, bar, ball, block)

while True:
    k = cv2.waitKey(5) # waiting input

    move_bar(k)

    ## end game
    if k==101: # input 'e'
        print('Exit')
        exit()

    move_ball()

これを実行すると、こんな感じになります。

正直、ゲームとしてはまだ全く機能していませんね笑。
残念ながら、今回はここまでですが、次回で十分楽しめるレベルにまで
持っていきます。あとひと踏ん張りです。がんばりましょう!

関連記事

  1. OpenCV

    ブロック崩しを半日で作る1

    はじめにPythonの学習を始めてから2週間~1か月程度の方…

  2. OpenCV

    ブロック崩しを半日で作る2

    ブロック崩しを半日で作る2です。まずはおさらいとして、前回考えた実装…

  3. OpenCV

    ブロック崩しを半日で作る4

    今回がこのシリーズ最後の記事となります。(前回の記事はこちら)…

コメント

  1. この記事へのコメントはありません。

  1. この記事へのトラックバックはありません。

カテゴリー

最近の記事

おすすめ記事

  1. OpenCV

    ブロック崩しを半日で作る3
  2. その他

    Panasonic Singaporeでのインターンの思い出3
  3. Environment

    CUDAインストール時の注意点
  4. Computer Vision

    研究に役立つ?リンク集
  5. その他

    個人的にPowerPointでよく使うショートカット
PAGE TOP