ブロック崩しを半日で作る2です。
まずはおさらいとして、前回考えた実装に必要な機能についてみてみましょう。
まず最初に必要なのは、ゲームの初期画面ですよね。背景を表示して、その中にボールやバー、ブロックを正しい場所に表示すること(1)です。
その次に必要なのは何でしょう。もちろん、ゲームを開始する機能になるのですが、これはボールが動き出す(2)タイミングからですよね。
ボールが動きだせば、プレイヤーとして、ボールが落ちそうな位置を予測して、バーを動かさないといけません(3)。
ボールとバーが接触すると、ボールはバー上を反射して逆方向に進行し(4)、壁ともぶつかりながら(5)、ブロックにぶつかることでドンドン消していきます(6)。
その間、途中でボールが画面一番下まで落ちてしまったらゲームオーバー(7)ですし、全部のブロックを消せば、ゲームクリア(8)となります。
これを簡単にまとめると、以下のようになります。
ゲームの初期化(1)
ボールを動かす(2)
バーを動かす(3)
ボールとバーの接触(4)
ボールと壁の接触(5)
ボールとブロックの接触(6)
ゲームオーバー(7)
ゲームクリア(8)
それでは、できる限りこの流れに沿って実装していきたいと思います。
プログラムを書いていく前に、OpenCVをPC上にインストールする必要があります。
これはコマンドプロンプトもしくはターミナル上でpip install opencv-python
というコマンドを入力することで可能なので、先にやっておきましょう。
1.ゲームの初期化
まずは画像を読み込んでいく必要があります。作業ディレクトリ上にこのようなimageフォルダを作成し、その中に前回用意した画像を入れておいてください。
さて、画像の読み込みには前回紹介したOpenCVを使います。
変数 = cv2.imread(画像のパス)
これで変数に画像が読み込まれます。
また、今回は画像のサイズを調整するので
変数 = cv2.resize(画像, 修正後の画像のサイズ)
といったかたちでresizeの機能を使います。
実際にやってみるとこうなります。
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))
ちなみにimportでは実行に必要なライブラリやパッケージを読み込んでいます。
読み込んだライブラリなどはos.getcwd()やcv2.imread()といった形で利用しています。
なお、PWDという変数にははosライブラリの機能を利用して現在のディレクトリの位置(パス)を取得し、代入しています。
さて、実はこれだけを書いてプログラムを走らせたところで何も起きません。
というのも、imreadはプログラム上に画像を読み込むのであって、
それを画面上に出力するわけではないからです。
画面上に出力するにはimshowとwaitKeyという機能を使います。
簡単に言うと、imshowは画像を表示して、waitKeyはどれだけの長さ表示するかという役割を持っています。(厳密には違いますが、ここでは簡略化して言っています。次回の記事でもう1回触れます。)
とりあえず、背景を出力してみましょう。以下のコードを上のコードに付け足して実行してみてください。
cv2.imshow('Breakout', wallpaper) cv2.waitKey(0)
このように背景が映し出されれば成功です
このまま背景以外のものも表示していきましょう。
ここで1点、注意しないといけないことがあります。それは、ほかの3要素はすべて背景の画像の上に表示するということです。このことが意味するのは背景画像のピクセルを部分的に書き換える必要があるということです。
多分、説明だけ聞いてもピンとこないかと思いますので、具体例を見てみましょう。
global ball_x, ball_y
ball_x = 246
ball_y = 500
wallpaper[ball_y:ball_y+ball.shape[0], ball_x:ball_x+ball.shape[1]] = ball
これはボールを表示する例です。ここではまず、ball_x, ball_yでボールを表示する位置を設定しています。ここでは最初から正しい数値を設定していますが、自分ではじめてやる場合には微調整しながら決定していくのが普通です。
そして、次の行でwallpaperのピクセル(画素)の一部をballのピクセルに変更しています。これを一般的な表現に直すとこのようになります。
変更される画像[変更するy座標:変更するy座標+変更する画像のy軸方向の長さ,
変更するx座標:変更するx座標+変更する画像のx軸方向の長さ] = 変更する画像
では、このやり方に基づいて、すべての要素を表示してみましょう。
以下のコードをball = cv2.resize(ball, (8, 8))
とcv2.imshow('Breakout', wallpaper)
の間に挿入してみてください。
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
ブロックに関しては、複数個表示する必要があるため、for文を使用していますが、やっていることは同じです。なお、appendはブロックの位置をblock_x, block_yの各配列に収納する役割を持っています。(のちのち、必要になっていきます。)
実行してこのような画面が出れば大丈夫です!
最後にここまでの初期化の内容を関数として定義しておきましょう。
このように機能ごとに関数をわけることでデバッグがしやすくなったり、プログラム全体に対して、新しい機能を拡張しやすくなるといったメリットがあります。(オブジェクト指向)
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) #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)
今回は画面の初期設定まで進めることができました。
次回は、ボールを動かす機能を実装していきたいと思います。
この記事へのコメントはありません。