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()
これを実行すると、こんな感じになります。
正直、ゲームとしてはまだ全く機能していませんね笑。
残念ながら、今回はここまでですが、次回で十分楽しめるレベルにまで
持っていきます。あとひと踏ん張りです。がんばりましょう!
この記事へのコメントはありません。