[初心者向け]Pythonista3のsceneでゲーム作成〜当たり判定〜
こんにちは。
普段プログラミングを趣味として自分なりに楽しんでやっています。
その中で学んだ事などを備忘録として残し、僕の思いも少し付け足してnoteにまとめています。
今回はゲームで良くある当たり判定のやり方を書いています。
今回は分かりやすくする為に2つの四角形を描画して、その2つが当たっているかどうか判定する処理を実装してみたいと思います。
四角形が終わったら丸の当たり判定もやってみましょう!
まず四角形2つ作成
とりあえずsceneのdrawメソッドをオーバーライドして、四角形を2つ描画します。
そして、片一方の四角形を画面タッチで移動させられるようにします。
#①
from scene import *
#②
class MyScene(Scene):
#③
def setup(self):
self.background_color = 'black'
self.w1 = 100
self.h1 = 100
self.x1 = 100
self.y1 = 100
self.color1 = 1.0, 1.0, 0.0, 0.7
self.w2 = 100
self.h2 = 100
self.x2 = self.size.width/2 - self.w2 / 2
self.y2 = self.size.height /2 - self.h2 / 2
self.color2 = 0.0, 1.0, 0.0, 0.7
#④
def draw(self):
fill(self.color1)
self.rect1 = rect(self.x1, self.y1, self.w1, self.h1)
fill(self.color2)
self.rect2 = rect(self.x2, self.y2, self.w2, self.h2)
#⑤
def update(self):
pass
#⑥
def touch_moved(self, touch):
self.x1 = touch.location.x
self.y1 = touch.location.y
#⑦
run(MyScene())
#①
sceneモジュールをインポートしています。
#②
Sceneクラスを継承してMySceneクラスを定義しています。
#③
sceneのsetupメソッドで初期化処理を書いています。
self.background_color = 'black' で背景色を黒色にしています。
・self.w1とself.h1で1つ目の四角形のサイズとして数値100を割り当てています。
・self.x1とself.y1で1つ目の四角形の座標として数値100を割り当てています。
・self.color1にRGBA形式で色の数値を割り当てています。
self.w2とself.h2は四角形を画面の真ん中に配置したいので、画面サイズ割る2から四角形のサイズの半分を引いた値を割り当てています。
#④
drawメソッドをオーバーライドして、四角形を画面に描画しています。
fill()は、図形を塗り潰す時の色を指定するメソッドです。
fill(色1)を使うと、その後の図形の色が全て(色1)で描画されます。その後にfill(色2)をしたらそこから後に作った図形が(色2)で描画されます。
このself.colorで引数の4つ目に0.7を指定しているのは『アルファ』といって透過率を指定できるので四角形が重なっているかどうかを分かりやすくする為にわざと少し透過させています。
rect()で座標とサイズを指定して画面に描画しています。
#⑤
とりあえずupdateメソッドはpassで置いておきます。
#⑥
touch_movedメソッドをオーバーライドして引数にはtouchを指定しています。(このtouchは、画面がタッチされてtouch_movedが呼び出された時の画面上の座標を取得して保持しています。
また、touch_movedメソッドは画面をタッチして指が画面上で動いている間呼ばれ続けます。
そして、self.x1とself.y1にそれぞれtouch.locationで取得したx座標とy座標を割り当てています。
これをすることによってtouch_movedが呼ばれるごとに四角形1の座標が変更されるので指の動きに合わせて四角形1も動くようになります。
#⑦
画面描画を開始する為にrun(MyScene())で実行しています。
これで四角形2つが画面に描画されて、あなたが画面をタッチして指を移動させると黄色の四角があなたの指の移動に合わせて動くはずです!
一旦右上の▷ボタンを押して実行してみましょう!
四角形が2つ表示されて、あなたが画面をタッチして指を移動させると動くとおもいます!
次にいきましょう!
四角形2つの当たり判定の方法
当たり判定は、ゲームエンジンなどによってはモノ同士が重なっているか見るメソッドがあったりして、なんやかんや色々やり方があるみたいなんですけど今回は一般的?なやり方を見ていきたいと思います!
今回の四角形2つで考えると、座標とサイズを使って当たり判定を実装する事ができます。
「四角形1のx座標(左側)」が「四角形2のx座標+四角形2の幅(右側)」よりも小さい場合というのを一旦作ってみましょう。
まず、モノ同士が当たっているかどうか表示させるためにラベルを追加します、setupメソッド内に以下のコードを追加してください↓
#①
self.label = LabelNode('.....', position=(50,self.size.height-50))
self.label.color = 'red'
self.add_child(self.label)
#①
ラベルノードをインスタンス化してself.labelに割り当てています。
その後ラベルの文字色を赤にしています。
最後にself.add_childで描画領域に追加しています。
そして先ほど言った座標とサイズを使った当たり判定を実装していきます。updateのところのpassを消して以下のコードを書いてください↓
if self.x1 < self.x2 + self.w2:
self.label.text = 'Hit!!!'
else:
self.label.text = 'No Hit'
《もしself.x1の値がself.x2とself.w2を足した値よりも小さかったら》
[ラベルのテキストをHit!!!に変更する]
《そうでなければ》
[ラベルのテキストをNo Hitに変更する]
これで四角形1が四角形2よりも右側に存在する時は、当たっていないよ!という判定ができます。
これで一度右上の▷ボタンを押して実行してみましょう!
始めは「Hit!!!」が左上のラベルに表示されているはずです、そしてタッチして指を動かして四角形1が四角形2よりも右側に行くと「No Hit」と表示されるはずです!
こんな感じで表示が変わるはずです!
右側からの当たり判定は、これで大丈夫ですが四角形同士が交差する場所以外でも「Hit!!!」と表示されてしまうと思います。
なので次は2つの四角形が交差しているときに「Hit!!!」と表示されるようにしていこうと思います。
さっきのif文の条件を以下のコードのようにしてください↓
if (self.x1 < self.x2 + self.w2
and
self.x1 + self.w1 > self.x2
and
self.y1 < self.y2 + self.h2
and
self.y1 + self.h1 > self.y2):
if文の条件式は丸括弧で囲うと改行しても大丈夫なので今回見やすくする為に丸括弧で括って書いています。
条件式のイメージはこんな感じです↓
こうする事で四角形1が四角形2の領域に重なるとラベルのテキストが『Hit!!!』に変わると思います。
領域の外にある場合は『No Hit』と表示されていると思います。
実際に右上の▷ボタンを押して実行してみましょう!
こんな感じで、四角形1を移動させて四角形2と重ねると『Hit!!!』と表示されたと思います!
重なっていなければ『No Hit』と表示されましたね!
あと、ついでに丸同士の当たり判定も見ておきましょう。
丸2つの当たり判定
四角形は座標とサイズを使えば当たり判定を実装できましたが、丸は角がないので座標とサイズだけでみてしまうと丸が重なっていない部分でも当たっていることになってしまいます。
とりあえず描画するモノを四角形から丸に変更しましょう。
あと、丸の判定にmathモジュールを使うので先にインポートしておきましょう↓
import math
次にdrawメソッド内に書いてあるrectメソッドをellipseに変更しましょう↓
def draw(self):
fill(self.color1)
#①
self.circle = ellipse(self.x1, self.y1, self.w1, self.h1)
fill(self.color2)
#②
self.circle2 = ellipse(self.x2, self.y2, self.w2, self.h2)
#①
元々self.rectだった部分をself.circleに変更。
rect()だったのをellipse()に変更しています。
#②
元々self.rect2だった部分をself.circle2に変更。
rect()だったのをellipse()に変更しています。
次にupdateメソッドを変更します↓
def update(self):
#①
x = (self.x2 + self.w2 / 2) - (self.x1 + self.w1 / 2)
#②
y = (self.y2 + self.h2 / 2) - (self.y1 + self.h1 / 2)
#③
distance = math.sqrt(x**2 + y**2)
#④
if distance < self.w1/2 + self.w2/2:
self.label.text = 'Hit!!!'
else:
self.label.text = 'No Hit'
#①
(丸2のx座標から丸2の幅の半分を足した値)から(丸1のx座標から丸1の幅の半分を足した値)を変数xに割り当てています。
これで丸1と丸2の間のx方向の距離が求まります。
#②
(丸2のy座標から丸2の高さの半分を足した値)から(丸1のy座標から丸1の高さの半分を足した値)を変数yに割り当てています。
これで丸1と丸2の間のy方向の距離が求まります。
#③
(xの二乗+yの二乗)した値をmarh.sqrt()に渡して平方根を求めてその値を変数distanceに割り当てています。
これが丸1の中心から丸2の中心までの距離になります
#④
《distanceに割り当てられている値が丸1と丸2の半径を足した値よりも小さかったら》
[ラベルのテキストをHit!!!にする]
《そうでなければ》
[ラベルのテキストをNo Hitにする]
今回は幅で半径を表していますが、半径が必要なだけなので高さの半分でも大丈夫です。
このコードのイメージが以下のような感じです↓
中心からの距離という事で少し長いコードになりましたが、正直言って必要なのは丸同士の距離だけなので変数xとyのところは以下のコードでも大丈夫です↓
x = self.x2 - self.x1
y = self.y2 - self.y1
どちらでも大丈夫なので、コードが書けたら右上の▷ボタンを押して実行してみましょう!
これで2つの丸の当たり判定が実装できましたね!
まとめ的なやつ
今回は四角形の当たり判定と丸の当たり判定を説明しました!
四角形は座標とサイズを使って判定をしました。
丸はお互いの中心点間の距離を計算して、丸の半径を使って判定をしました。
今回は一般的?な方法でしたが、判定方法は他にも色々あるみたいなので、あなたの作成するプログラムに合わせて1番良い方法を考えて実装すると良いと思います!
最後まで読んでいただきありがとうございました!
では、またお会いしましょう!