Godot4 ビッグカツブロック崩し24 ボールを打ち返す位置で角度を変える

※この連載の全ての記事は、タグ「ビッグカツ」の検索一覧から探すことができます。
※この連載で作ったゲームは「BigBreakOut(ゲームの作り方の記事付き) | フリーゲーム投稿サイト GodotPlayer」でプレイできます。

昔から人気の駄菓子「ビッグカツ」フリー素材画像が公開されたので、無料・軽快な 2D / 3D 用のゲームエンジン Godot Engine 4 を使って、ビッグカツ画像を使ったブロック崩しを作成します。

「ビッグカツブロック崩し」作成の第24回では、ボールパドル衝突した位置によって、ボールの反射角度を変えるスクリプト例を紹介して、そのテストについても手順と結果を紹介します。

※ GodotEngine 4.3 を使用しています。.NET 版ではありません。
※スクリプトは自己責任でご使用ください。

前回の記事

前回は、ゲームオーバー時にボタンを押して選択できるリトライタイトルに戻る処理の実装例について紹介しました。

衝突した位置によってボールの移動方向を変化させる

今までは、パドルの上側のどの位置にあたっても同じ角度で跳ね返りました。
※角や側面にぶつかった場合は異なります。

この記事では、プレイヤーがよりボールの動きをコントロールできるように、パドルに衝突した位置によって下図のように、反射角度を変えるようにします。

Godot4 ビッグカツブロック崩し パドルに衝突する位置によって反射角度を変えます

左側に当たれば左上に、右側にあたれば右上に、中心に近ければ上方向に跳ね返るようにします。
真上に返ってしまうとゲームが簡単になるらしいので、中心にあたっても -90 度 ± 30 度の方向に傾かせるようにします。

パドルに衝突した位置によって反射角度を返すスクリプト

ball.gd の _physics_process イベント関数の定義を以下に置き換えます。

16, 17 行目で、ボールがパドルに接触した際に、反射角度を得る処理だけを変更しました。

func _physics_process(delta):
	# 一時停止している場合は、動かしません。
	if is_pause:
		return
	
	# 設定されている加速度で移動します。
	var collision: KinematicCollision2D = move_and_collide(velocity * delta)

	# 何かに衝突した場合
	if collision:
		# 衝突した Object を取得します。
		var collider:Object = collision.get_collider()
		# Paddle の場合
		if collider.name == "Paddle":
			#print("Paddle")
			var paddle:Paddle = collider
			velocity = paddle.get_bounce(velocity, collision.get_position())
			#velocity = velocity.bounce(collision.get_normal())		# 進行方向を変更
			SCUtil.AudioStreamOneShotPlay(self, audio_stream_hit)	# hit.wav 効果音を再生
		# Wall の場合
		elif collider.name.begins_with("Wall"):
			#print("Wall")
			velocity = velocity.bounce(collision.get_normal())	# 進行方向を変更
			SCUtil.AudioStreamOneShotPlay(self, audio_stream_hit)	# hit.wav 効果音を再生
		# Block の場合
		elif collider.name.begins_with("Block"):
			#print("Block")
			velocity = velocity.bounce(collision.get_normal())	# 進行方向を変更
			collider.queue_free()	# 衝突したブロックを消去します。
			SCUtil.AudioStreamOneShotPlay(self, audio_stream_break)	# break.wav 効果音を再生
		else:
			printerr("Ball が不明なものと衝突しました。")	# 不明なものとの接触はエラーメッセージを表示します。

	return

呼び出している Paddle.get_bounce 関数の実装は以下です。

_ready イベント関数では、後述する4つの変数が正しい関係にあるかを確認しています。
異なる場合はエラーメッセージを表示して警告します。

最初の4つの変数によって、左端、中心左寄り、中心右寄り、右端での反射角度を設定しています。

get_bounce 関数では、その4つの反射角度とぶつかった位置の割合により、その場所の反射角度を計算してベクトルに変換して返しています。

test_get_bounce 関数は、パドルの左端から右端までを10等分して、その場所から得られたベクトルをデバッグ用に出力する関数です。_ready イベント関数で呼び出してテストしました。test_get_bounce 関数は、テスト用なのでコピーする必要はありません

func _ready():
	#test_get_bound()
	
	# パドルの反射角度は、上方向(マイナス)になるように全てマイナスの値で設定されているか確認します。
	if not right_end_angle <= 0:
		printerr("right_end_angle <= 0  になるように設定してください。")
	if not right_start_angle <= 0:
		printerr("right_start_angle <= 0  になるように設定してください。")
	if not left_start_angle <= 0:
		printerr("left_start_angle <= 0  になるように設定してください。")
	if not left_end_angle <= 0:
		printerr("left_end_angle <= 0  になるように設定してください。")
	
	# パドルの、右端、中心(やや右)、中心(やや左)、左端にそれぞれの反射角度を設定する際に、
	# right_end_angle >= right_start_angle >= left_start_angle >= left_end_angle の関係であるか確認します。
	if not right_end_angle >= right_start_angle:
		printerr("right_end_angle >= right_start_angle になるように設定してください。")
	if not right_start_angle >= left_start_angle:
		printerr("right_start_angle >= left_start_angle になるように設定してください。")
	if not left_start_angle >= left_end_angle:
		printerr("left_start_angle >= left_end_angle になるように設定してください。")
	return

## パドルの右端にぶつかった場合の反射角度です。右側が0度、上方向はマイナスになります。
## right_end_angle >= right_start_angle >= left_start_angle >= left_end_angle の関係になるように設定してください。
@export var right_end_angle: float = -30.0
## パドルの中心からごくわずかに右寄りの場所にぶつかった場合の反射角度です。
## right_end_angle >= right_start_angle >= left_start_angle >= left_end_angle の関係になるように設定してください。
@export var right_start_angle: float = -60.0
## パドルの中心からごくわずかに左寄りの場所にぶつかった場合の反射角度です。
## right_end_angle >= right_start_angle >= left_start_angle >= left_end_angle の関係になるように設定してください。
@export var left_start_angle: float = -120.0
## パドルの左端にぶつかった場合の反射角度です。
## right_end_angle >= right_start_angle >= left_start_angle >= left_end_angle の関係になるように設定してください。
@export var left_end_angle: float = -150

## 衝突した位置によって設定された反射角度の単位ベクトルを返します。
## 入射角度は無視され、衝突した位置がパドルのどの位置かによって反射角度を決定します。
##
## 反射角度は以下の4か所に対応する4つのプロパティに設定します。その間は補間されます。
## LE       LS RS       RE
## 左端    中心    右端
## ┏─────────────────────┓
## ┃        Paddle       ┃
## ┗─────────────────────┛
## LE = left_end_angle, LS = left_start_angle
## RS = right_start_angle, RE = right_end_angle
## 完全に中心に衝突した場合は 50 % の確率で LS または RS の値を戻り値の単位ベクトルの反射角度にします。
#func get_bounce(velocity: Vector2, collision: KinematicCollision2D) -> Vector2:
func get_bounce(velocity: Vector2, position_hit: Vector2) -> Vector2:
	# 戻り値の単位ベクトルのもととなる反射角度
	var reflection_angle: float = 0
	
	# 衝突した位置とパドルの位置です。どちらもグローバル座標です。
	var pos_hit: Vector2 = position_hit #collision.get_position()
	var pos_paddle: Vector2 = global_position # center?
	
	# パドルの横幅と中心の座標を取得します。
	var shape: Shape2D = $CollisionShape2D.shape
	var shape_rect: Rect2 = shape.get_rect()
	var paddle_center: Vector2 = shape_rect.get_center() + pos_paddle
	var width_paddle_half = shape_rect.size.x / 2
	#print("paddle_center.x = ", str(paddle_center.x))
	
	# 衝突した位置が、パドル内の左右どちらかの端からどの程度離れているかを 0.0 ~ 1.0 の範囲で取得します。
	var distance_from_end: float = width_paddle_half - abs(pos_hit.x - paddle_center.x)
	var normalized_distance: float = clamp(distance_from_end / width_paddle_half, 0.0, 1.0)
	
	# 当たった場所がパドルの右側の場合
	if paddle_center.x < pos_hit.x:
		reflection_angle = (right_start_angle - right_end_angle) * normalized_distance + right_end_angle
	# 当たった場所がパドルの左側の場合
	elif paddle_center.x > pos_hit.x:
		reflection_angle = - (left_end_angle - left_start_angle) * normalized_distance + left_end_angle
	# 当たった場所がパドルの中心の場合
	else:
		if randf() < 0.5:
			reflection_angle = left_start_angle
		else:
			reflection_angle = right_start_angle

	# 得られた角度を単位ベクトルに変換します。
	var radian: float = deg_to_rad(reflection_angle)
	var reflection_vector: Vector2 = Vector2(cos(radian), sin(radian))
	#print("reflection_angle = ", str(reflection_angle), ", radian = ", str(radian))
	# 単位ベクトルに速度をかけたベクトルを返します。
	return reflection_vector * velocity.length()

## get_bound メンバ関数のテスト用の関数です。
## パドルの横幅を 10 等分した位置の反射角度の単位ベクトルを得た結果を出力します。
## あわせて get_bound メンバ関数でも、内部で得られた reflection_angle (反射角度)を print 出力して確認しました。
func test_get_bound():
	var vel: Vector2 = Vector2(50, 50)
	var paddle_width_half: float = $CollisionShape2D.shape.get_rect().size.x / 2
	for x in range(-paddle_width_half, paddle_width_half, paddle_width_half / 10):
		var pos_hit = Vector2(position.x + x, position.y)
		var ret: Vector2 = get_bounce(vel, pos_hit)
		print("☆ X = ", str(x), ", ret = ", str(ret))

テスト用にボールの初期角度を 45 度から 90 度(下方向)に変更

テストのために、ボールの初期角度を 45 度から 90 度(下方向)に一時的に変更して、パドルにぶつかる位置を想定しやすくしました。

## ボールの初期角度です。
#@export var INITIAL_ANGLE: float = PI / 4  # 45度
@export var INITIAL_ANGLE: float = PI / 2  # 90度

テスト

F6 キーで開いているステージのシーンを実行します。

今回のテストはパドルの左端から右端まで目安で5等分した位置にボールを当てて、それぞれボールの初期位置を調節して、パドルにあたる位置を変更して、反射角度を確認しました。
左側にぶつかれば左方向に、右側にぶつかれば右方向に、中心付近にぶつかれば上方向に反射することが確認できました。
※完全に左、右、上ではなく、プロパティで設定した -150 ~ -120 度、 -60 ~ -30 度の範囲内で反射します。

パドルの端にあたると、浅い反射角度になり、中心に近いと上方向に強めの反射角度になりました。

まとめ

「ビッグカツブロック崩し」作成の第24回では、ボールパドル衝突した位置によって、ボールの反射角度を変えるスクリプト例を紹介して、そのテストについても手順と結果を紹介しました。

参照サイト Thank You!

記事一覧 → Compota-Soft-Press

コメント

Ads Blocker Image Powered by Code Help Pro

お願い - Ads Blocker Detected

このサイトは広告を掲載して運営しています。

ポップアップを閉じて閲覧できますが、よろしければ

このサイト内の広告を非表示にする拡張機能をオフにしていただけませんか?

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

We have detected that you are using extensions to block ads. Please support us by disabling these ads blocker.

タイトルとURLをコピーしました