2020/11/17(火) [n年前の日記]
#1 [godot] Godot Engineでダメージエフェクトを作成
Godot Engine 3.2.3 x64 を使って3D表示の簡単なシューティングゲームっぽいものを作る。
今回は、プレイヤーや敵に弾が当たったことを分かりやすくするために簡単なダメージエフェクトをつけてみる。
今回は、プレイヤーや敵に弾が当たったことを分かりやすくするために簡単なダメージエフェクトをつけてみる。
◎ プレイヤーの点滅表示処理を追加。 :
やはりアクションゲームの類で自機がダメージを受けたら点滅するものだろう。って一体いつの時代の話ですか…昭和か…。何にせよ、とりあえず点滅表示処理を追加してみる。
プレイヤーのシーン、res://assets/Player.tscn を開いて、Timer を2つ追加。DmagaeTimer と BlinkTimer にリネーム。
DamageTimer には、ダメージを受けてから何秒間はダメージを受けないかを指定。
BlinkTimer には、表示と非表示を切り替える時間間隔を指定。
それぞれの timeoutシグナルにメソッドを接続してやる。
スクリプトファイル Player.gd に処理を追加。内容は以下。今回の処理に関係があるところだけ貼っておく。
_Player.gd
これで、プレイヤーに敵弾が当たってダメージを受けると一定時間点滅するようになった。
プレイヤーのシーン、res://assets/Player.tscn を開いて、Timer を2つ追加。DmagaeTimer と BlinkTimer にリネーム。
DamageTimer には、ダメージを受けてから何秒間はダメージを受けないかを指定。
- Wait Time を 2 に。
- One Shot を有効化。
BlinkTimer には、表示と非表示を切り替える時間間隔を指定。
- Wait Time を 0.05 に。
それぞれの timeoutシグナルにメソッドを接続してやる。
- DmagaeTimer や BlinkTimer を選んでから、ノードタブをクリック → シグナルをクリック。
- timeout を右クリックして「接続」。
- Player を選んで「接続」。
スクリプトファイル Player.gd に処理を追加。内容は以下。今回の処理に関係があるところだけ貼っておく。
_Player.gd
# ... var damaging = false func _ready(): # ... damage = 0 damaging = false # ... func _physics_process(delta): # ... if not damaging: if damage > 0: hp -= damage damage = 0 if hp <= 0: hp = 0 emit_signal("player_damaged") damaging = true $DamageTimer.start() $BlinkTimer.start() # ... func _on_DamageTimer_timeout(): $BlinkTimer.stop() damage = 0 damaging = false visible = true if hp <= 0: hp = 0 emit_signal("player_died") func _on_BlinkTimer_timeout(): visible = not visible
- ダメージ中を示すフラグ、damaging を用意。
- ダメージ中は、自分がダメージを受けたかどうかは判定しない。
- ダメージを受けたら、ダメージ中フラグを立ててから、$DamageTimer.start()、$BlinkTimer.start() を呼んで、2つの Timer を開始させる。
- BlinkTimer は指定した時間間隔で timeoutシグナルを何度も繰り返し発行する。そのたびに _on_BlinkTimer_timeout() が呼ばれる。このメソッドの中で、visibleプロパティを反転させて、表示と非表示を切り替えている。
- DmagaeTimer は指定時間が経過すると timeoutシグナルを1回だけ発行。_on_DamageTimer_timeout() が呼ばれる。BlinkTimerを停止させて、ダメージ中フラグを降ろして、表示を有効化。
これで、プレイヤーに敵弾が当たってダメージを受けると一定時間点滅するようになった。
◎ 画面フラッシュを追加。 :
プレイヤーに弾が当たると点滅するようになったけど、これではまだダメージを受けたことが伝わりにくいので、画面を軽くフラッシュさせてみる。
フラッシュさせるためのノードは、HUDシーンに追加する。res:/assets/Hud.tscn を開く。
ColorRectノードを追加。ColorRectノードは指定色で矩形を描画できる。
Flash にリネーム。
Flash (ColorRect) のプロパティを変更。
Timer を追加。FlashTimer にリネーム。この Timer を使って、一定時間が過ぎたらフラッシュ状態を終了させる。
FlashTimer のプロパティを変更。
FlashTimer の timeoutシグナルにメソッドを接続。
Flash の表示順を変更。ノード一覧の一番上に置くことで、HP表示より奥に表示されるようにする。
HUDシーンのスクリプト、res://scripts/Hud.gd を修正。内容は以下。
_Hud.gd
Mainシーンのスクリプト、res://scripts/Main.gd も修正。内容は以下。
_Main.gd
これで、プレイヤーに敵弾が当たってダメージを受けると、一瞬画面がフラッシュするようになった。
フラッシュさせるためのノードは、HUDシーンに追加する。res:/assets/Hud.tscn を開く。
ColorRectノードを追加。ColorRectノードは指定色で矩形を描画できる。
Flash にリネーム。
Flash (ColorRect) のプロパティを変更。
- Color を RGBA=(255, 255, 255, 200) に。
- Size x, y を (1280, 720) に。ゲームウインドウ全体を覆いつくすサイズを指定。
Timer を追加。FlashTimer にリネーム。この Timer を使って、一定時間が過ぎたらフラッシュ状態を終了させる。
FlashTimer のプロパティを変更。
- Wait Time を 0.125 に。0.125秒だけ Flash を表示させる。
- One Shot を有効化。
FlashTimer の timeoutシグナルにメソッドを接続。
- FlashTimer を選択してから、ノードタブをクリック → シグナルをクリック。
- timeout を右クリックして「接続」。
- Hud を選択して「接続」。
Flash の表示順を変更。ノード一覧の一番上に置くことで、HP表示より奥に表示されるようにする。
HUDシーンのスクリプト、res://scripts/Hud.gd を修正。内容は以下。
_Hud.gd
# ... func _ready(): $Flash.visible = false # ... func start_flash(): $Flash.visible = true $FlashTimer.start() func _on_FlashTimer_timeout(): $Flash.visible = false
- 初期化処理時に Flashノードを非表示にする。
- 画面フラッシュを開始するメソッド、start_flash() を追加。Flashノードの表示を有効化して、FlashTimer を開始している。
- FlashTimer は指定時間が経過すると timeout シグナルを発行して _on_FlashTimer_timeout() が呼ばれる。Flashノードを非表示にする。
Mainシーンのスクリプト、res://scripts/Main.gd も修正。内容は以下。
_Main.gd
# ... func damage_player(): # ... $Hud.start_flash() # ...
- プレイヤーがダメージを受けると Main.gd内の damage_player() が呼ばれる。その中で $Hud.start_flash() を呼んで Hud の画面フラッシュを開始させる。
これで、プレイヤーに敵弾が当たってダメージを受けると、一瞬画面がフラッシュするようになった。
◎ 敵のダメージエフェクトを追加。 :
敵のスクリプト、res://scripts/EnemyZako.gd を修正して、敵がダメージを受けた時のエフェクト(?)を追加する。
今回は、一定時間(0.25秒)、ランダムに位置がぶれる処理を追加してみた。
_EnemyZako.gd
これで、プレイヤーや敵がダメージを受けた時に、少しは見た目で分かりやすくなった…かどうかは怪しいけれど、とりあえず簡易の処理は追加できた。
次回はタイトル画面やゲームオーバー画面を作成してみる。
今回は、一定時間(0.25秒)、ランダムに位置がぶれる処理を追加してみた。
_EnemyZako.gd
# ... var damage_timer = 0 var rnd = RandomNumberGenerator.new() func _ready(): # ... damage = 0 damage_timer = 0 # ... func _physics_process(delta): # ... check_damage() damaging_animation(delta) # ... func check_damage(): if damage > 0: hp -= damage damage = 0 damage_timer = 0.25 if hp <= 0: hp = 0 emit_signal("enemy_died") emit_signal("enemy_damaged") func damaging_animation(delta): if damage_timer > 0: damage_timer -= delta var target_mesh = $EnemyZakoModel if damage_timer <= 0: damage_timer = 0 target_mesh.translation = Vector3() else: var r = 0.5 var x = rnd.randf_range(-r, r) var z = rnd.randf_range(-r, r) target_mesh.translation = Vector3(x, 0, z) # ... func _on_EnemyZako_area_entered(area): if is_queued_for_deletion(): return # print("playerbullet hit the enemy") if area.is_in_group("playerbullets"): damage = area.attack_point # ...
- var rnd = RandomNumberGenerator.new() で疑似乱数生成器を用意する。
- rnd.randf_range(-r, r) で、-r から r の範囲で乱数を生成。
- 自分(EnemyZako)の座標ではなく、その下にぶら下がってる EnemyZakoModel(表示モデルを担当)の位置をずらしている。
- 時間経過を、Timerノードを使わずにスクリプト内の処理で測っている。
これで、プレイヤーや敵がダメージを受けた時に、少しは見た目で分かりやすくなった…かどうかは怪しいけれど、とりあえず簡易の処理は追加できた。
次回はタイトル画面やゲームオーバー画面を作成してみる。
[ ツッコむ ]
#2 [godot] Godot Engineでタイトル画面その他を作成
Godot Engine 3.2.3 x64 を使って3D表示の簡単なシューティングゲームっぽいものを作る。
今回は、タイトル画面、ゲームオーバー画面、ステージクリア(You Win)画面を作成してみる。
今回は、タイトル画面、ゲームオーバー画面、ステージクリア(You Win)画面を作成してみる。
◎ タイトル画面を作成。 :
タイトル画面のシーンを作成。シーン → 新規シーン。
以下のようなノード構成を作成。
ルートノードを Title にリネーム。
res:// 直下に Title.tscn として保存。
以下のようなノード構成を作成。
ルートノードを Title にリネーム。
Title (Node2D) │ └─ CanvasLayer │ ├─ ColorRect │ ├─ Label │ └─ Button
res:// 直下に Title.tscn として保存。
◎ 各ノードをレイアウト。 :
以下のような見た目になるように、各ノードのプロパティを設定した。
- ColorRect で指定色の矩形を描画できる。青色にして、画面一杯に表示される size にした。
- Label で文字を描画できる。ゲームのタイトル「Simple Shoot'Em Up」を表示させた。使ったフォントは Vegur-Bold.otf。
- Button でマウスクリックに反応するボタンを置ける。「START」と表示させて下のほうに配置した。
◎ スクリプトを書く。 :
Title にスクリプトをアタッチして、res://scripts/Title.gd として保存。
ボタン(Button)を押した時に発行される pressed() シグナルにメソッドを接続する。
Title.gd の最後に、_on_Button_pressed() というメソッドが追記された。ボタンをマウスでクリックすると、このメソッドが呼ばれる状態になった。
スクリプトファイル Title.gd を修正する。内容は以下。
_Title.gd
これで、ボタンをマウスクリックすると Main.tscn にシーンが切り替わる状態になった。
ボタン(Button)を押した時に発行される pressed() シグナルにメソッドを接続する。
- Button を選択
- ノードタブをクリック → シグナルをクリック
- pressed() を右クリックして「接続」
- Title を選択して「接続」
Title.gd の最後に、_on_Button_pressed() というメソッドが追記された。ボタンをマウスでクリックすると、このメソッドが呼ばれる状態になった。
スクリプトファイル Title.gd を修正する。内容は以下。
_Title.gd
extends Node2D func _ready(): pass # Replace with function body. #func _process(delta): # pass func _on_Button_pressed(): get_tree().change_scene("res://Main.tscn")
- get_tree().change_scene("res://Main.tscn") で、res://Main.tscn にシーンを切り替える、という指定をしている。
これで、ボタンをマウスクリックすると Main.tscn にシーンが切り替わる状態になった。
◎ ボタンにショートカットキーを割り当て。 :
この状態では画面上のボタンをマウスでクリックしないと反応しないけど、できればキーボードを叩いた時も反応してほしい。Buttonノードにはショートカットキーを割り当てることもできるので、その設定をする。
Button を選択して、Shortcutプロパティを設定。
これで、スペースキーやEnterキーを叩いてもボタンが反応するようになった。
Button を選択して、Shortcutプロパティを設定。
- Shortcut の横の「空」をクリックして「新規 Shortcut」を選択。
- 作成された「Shortcut」をクリックするとその下にもShortcutの欄が出てくる。
- 横の「空」をクリックして「新規 InputEventAction」を選択。
- 作成された「InputEventAction」をクリックするとその下に Action 等が出てくる。
- Action に「ui_accept」と入力。
- ui_accept には、デフォルトでスペースキーやEnterキーが割り当てられている。プロジェクト設定で変更できる。
これで、スペースキーやEnterキーを叩いてもボタンが反応するようになった。
◎ メインシーンとして設定する。 :
ゲームを開始したら、Mainシーンではなく、この Titleシーンが真っ先に表示されてほしい。Title.tscn をメインシーンとして設定する。
ファイル一覧ウインドウで Title.tscn を右クリックして「メインシーンとして設定」を選択。
これで、このプロジェクトのメインシーンは Title.tscn になった。今後はF5キーを押すとメインシーン(Title.tscn) から実行される。
ファイル一覧ウインドウで Title.tscn を右クリックして「メインシーンとして設定」を選択。
これで、このプロジェクトのメインシーンは Title.tscn になった。今後はF5キーを押すとメインシーン(Title.tscn) から実行される。
◎ ゲームオーバー画面やステージクリア画面を作成。 :
Titleシーンを作った時と同じ手順で、ゲームオーバー画面とステージクリア画面のシーンを作成する。
それぞれ、ルートノードを、「GameOver」「YouWin」にリネームしておく。
ゲームオーバー画面にスクリプトをアタッチする。このスクリプトはステージクリア画面でも共用したいので、スクリプトファイル名は「GoToTitle.gd」にして保存する。
ステージクリア画面にも、ゲームオーバー画面と同じく GoToTitle.gd をアタッチ。Script → 「読込み」で GoToTitle.gd を選択してやる。もし操作を間違った時は、「クリア」を選べば「空」に戻る。
スクリプト GoToTitle.gd を修正。内容は以下。
_GoToTitle.gd
ゲームオーバー画面、ステージクリア画面の双方で、Button をマウスクリックした時に発行される pressed()シグナルにメソッドを接続してやる。
また、Button の Shortcut プロパティも設定して、キーボードを叩いてもボタンが反応するようにしておく。
それぞれ、ルートノードを、「GameOver」「YouWin」にリネームしておく。
ゲームオーバー画面にスクリプトをアタッチする。このスクリプトはステージクリア画面でも共用したいので、スクリプトファイル名は「GoToTitle.gd」にして保存する。
ステージクリア画面にも、ゲームオーバー画面と同じく GoToTitle.gd をアタッチ。Script → 「読込み」で GoToTitle.gd を選択してやる。もし操作を間違った時は、「クリア」を選べば「空」に戻る。
スクリプト GoToTitle.gd を修正。内容は以下。
_GoToTitle.gd
extends Node2D func _ready(): pass # Replace with function body. func _on_Button_pressed(): get_tree().change_scene("res://Title.tscn")
- get_tree().change_scene("res://Title.tscn") を呼ぶことで、res://Title.tscn にシーンを切り替える。
ゲームオーバー画面、ステージクリア画面の双方で、Button をマウスクリックした時に発行される pressed()シグナルにメソッドを接続してやる。
- GameOver、または YouWin を選択
- ノードタブをクリック → シグナルをクリック
- pressed() を右クリックして「接続」
- スクリプト GoToTitle.gd をアタッチしているノード(GameOver、YouWin)を選択
- メソッド名には _on_Button_pressed を入力して「接続」
また、Button の Shortcut プロパティも設定して、キーボードを叩いてもボタンが反応するようにしておく。
◎ ゲームオーバー画面にシーンを切り替える処理を追加。 :
Mainシーンのスクリプト Main.gd を修正。追加内容は以下。
_Main.gd
Player を選択して、プレイヤーが死んだ時に発行されるカスタムシグナル player_died() にメソッドを接続してやる。
Main を選択して、メソッド名に die_player を入力して「接続」。
これで、プレイヤーが死ぬと、Main.gd 内の die_player() が呼ばれて、ゲームオーバー画面に切り替わる状態になった。
同様に、敵にも設定をする。EnemyZako を選択して、敵が死んだ時に発行されるカスタムシグナル enemy_died() にメソッドを接続。
Main を選択して、メソッド名に die_enemy() を入力して「接続」。
これで、敵が死ぬと、Main.gd 内の die_enemy() が呼ばれて、ステージクリア画面に切り替わる状態になった。
_Main.gd
# ... func die_player(): get_tree().change_scene("res://GameOver.tscn") func die_enemy(): get_tree().change_scene("res://YouWin.tscn")
- ゲームオーバー画面にシーンを切り替えるメソッド、die_player() を追加。
- ステージクリア画面にシーンを切り替えるメソッド、die_enemy() を追加。
Player を選択して、プレイヤーが死んだ時に発行されるカスタムシグナル player_died() にメソッドを接続してやる。
- ノードタブをクリック → シグナルをクリック
- player_died() を右クリックして「接続」
Main を選択して、メソッド名に die_player を入力して「接続」。
これで、プレイヤーが死ぬと、Main.gd 内の die_player() が呼ばれて、ゲームオーバー画面に切り替わる状態になった。
同様に、敵にも設定をする。EnemyZako を選択して、敵が死んだ時に発行されるカスタムシグナル enemy_died() にメソッドを接続。
Main を選択して、メソッド名に die_enemy() を入力して「接続」。
これで、敵が死ぬと、Main.gd 内の die_enemy() が呼ばれて、ステージクリア画面に切り替わる状態になった。
◎ 動作確認。 :
F5キーを押して実行してみる。
プロジェクトのメインシーンとして Title.tscn を設定済みなので、F5キーを押せば最初にタイトル画面が表示されるはず。
タイトル画面 → ゲーム画面 → ゲームオーバー or ステージクリア → タイトル画面、とシーンが切り替わっていくようになった。
次回は…。今現在はプレイヤーが画面端を超えても平気で移動できてしまうので、そのあたりをどうにかしてみたい。
プロジェクトのメインシーンとして Title.tscn を設定済みなので、F5キーを押せば最初にタイトル画面が表示されるはず。
タイトル画面 → ゲーム画面 → ゲームオーバー or ステージクリア → タイトル画面、とシーンが切り替わっていくようになった。
次回は…。今現在はプレイヤーが画面端を超えても平気で移動できてしまうので、そのあたりをどうにかしてみたい。
[ ツッコむ ]
#3 [godot] Godot Engineでプレイヤーが画面端を超えないように補正
Godot Engine 3.2.3 x64 を使って3D表示の簡単なシューティングゲームっぽいものを作る。
今回は、プレイヤーキャラが画面端を超えて移動しないように補正する処理を追加してみる。
Player のスクリプトファイル、res://scripts/Player.gd を修正。追記した内容は以下。
_Player.gd
何をやっているのかというと…。
カメラから見えている範囲のx,z座標を求めるあたりで、なんだか計算が面倒臭いことになっている。
ただ、Godot Engine のことだから、もっと簡単に値を求める機能が既に用意されてそうな気もする。ググってもそのものズバリの情報が出てこなくて、こんな処理を書いてしまったけれど…。もっと良い方法・便利なメソッドがあったら誰か教えてください。みたいな。
おそらく結構ダサいことをしている気もするけれど、何にせよ、これでプレイヤーが画面外に出ない状態になった。
次回は、3DCGソフトの blender で作成した飛行機モデルデータ、敵モデルデータをインポートして、ゲーム画面の見栄えを改善したい。
今回は、プレイヤーキャラが画面端を超えて移動しないように補正する処理を追加してみる。
Player のスクリプトファイル、res://scripts/Player.gd を修正。追記した内容は以下。
_Player.gd
# ... export var padding = 1.7 var camera:Camera var disparea = Vector2() var btopleft = Vector3() var bbottomright = Vector3() func _ready(): # ... init_adjust_work() # ... func _physics_process(delta): # ... adjust_pos(padding) # ... # ... func init_adjust_work(): disparea = get_disp_area() camera = get_tree().root.get_node("Main/Camera") calc_move_area(camera, disparea.x, disparea.y) # get project display width and height func get_disp_area(): var w = ProjectSettings.get_setting("display/window/size/width") var h = ProjectSettings.get_setting("display/window/size/height") return Vector2(w, h) func adjust_pos(padding): if translation.z < btopleft.z + padding: translation.z = btopleft.z + padding if translation.z > bbottomright.z - padding: translation.z = bbottomright.z - padding var zd = btopleft.z - bbottomright.z var lz = translation.z - bbottomright.z var x = lz * (-btopleft.x - bbottomright.x) / zd + bbottomright.x if translation.x > x - padding: translation.x = x - padding if translation.x < -x + padding: translation.x = -x + padding func calc_move_area(camera:Camera, w, h): var cpos:Vector3 = camera.translation var crot:Vector3 = camera.rotation_degrees var fov = camera.fov var d = (h/2) / tan(deg2rad(fov/2)) var p1 = Vector3(-w/2, h/2, -d) var p2 = Vector3(w/2, -h/2, -d) var q1:Vector3 = _get_border(p1, -crot.x, -cpos.y, cpos.z) var q2:Vector3 = _get_border(p2, -crot.x, -cpos.y, cpos.z) btopleft = q1 bbottomright = q2 if false: print("topleft = (%f, %f, %f)" % [q1.x, q1.y, q1.z]) print("bottomright = (%f, %f, %f)" % [q2.x, q2.y, q2.z]) func _get_border(p:Vector3, rotx, y, cz): var v2:Vector2 = _get_rot_pos(p.z, p.y, rotx) var pr = Vector3(p.x, v2.y, v2.x) var x = y * pr.x / pr.y var z = y * pr.z / pr.y + cz return Vector3(x, 0, z) func _get_rot_pos(x, y, ang): var a = deg2rad(ang) var xr = x * cos(a) - y * sin(a) var yr = x * sin(a) + y * cos(a) return Vector2(xr, yr)
何をやっているのかというと…。
- カメラの情報と、プロジェクト設定内のゲームウインドウサイズを取得。(init_adjust_work(), get_disp_area())
- y = 0 の平面上でカメラから見えている範囲を計算。ゲームウインドウ左上の x,z 値と、右下の x,z値を得る。(calc_move_area(camera:Camera, w, h))
- プレイヤーの座標がゲームウインドウ左上・右上の x,z値を超えていたら、超えないような座標値に補正する。(adjust_pos(padding))
カメラから見えている範囲のx,z座標を求めるあたりで、なんだか計算が面倒臭いことになっている。
ただ、Godot Engine のことだから、もっと簡単に値を求める機能が既に用意されてそうな気もする。ググってもそのものズバリの情報が出てこなくて、こんな処理を書いてしまったけれど…。もっと良い方法・便利なメソッドがあったら誰か教えてください。みたいな。
おそらく結構ダサいことをしている気もするけれど、何にせよ、これでプレイヤーが画面外に出ない状態になった。
次回は、3DCGソフトの blender で作成した飛行機モデルデータ、敵モデルデータをインポートして、ゲーム画面の見栄えを改善したい。
◎ 関連ページ。 :
以前このあたりの処理で悩んでいた際に書き残したメモへのリンクも貼っておく。
_mieki256's diary - カメラ情報から自機の移動範囲を求めるソレを考えてるところ
_mieki256's diary - カメラ情報から自機の移動範囲を求めるソレがたぶんできた気がする
_mieki256's diary - カメラ情報から自機の移動範囲を求めるソレを考えてるところ
_mieki256's diary - カメラ情報から自機の移動範囲を求めるソレがたぶんできた気がする
◎ 余談。 :
Godot Engine には、3D空間内の座標が、カメラから見えている範囲の中にあるか、外にあるかを調べるメソッドが用意されているので、それを使って範囲外なら補正する処理を試したこともあったのだけど。
_mieki256's diary - 自機が画面外に行かないようにしたい
そのやり方だと、画面の端でプレイヤーを斜め移動させた時にガタガタした移動になっちゃって、このやり方ではダメだなと諦めた記憶が…。まあ、見た目は酷いけれど、画面外に出さない処理をそういうやり方でやれないこともない…と、一応メモしておきます。
他にも、画面の端に壁相当のオブジェクトを配置しておいて、その壁オブジェクトとプレイヤーでアタリ判定・衝突判定をして、画面外に出ないようにしてしまう、という方法もありそう。ただ、カメラの各プロパティをちょっと修正するたびに、壁オブジェクトの配置についても修正する手間が増えるであろう予感も…。
_mieki256's diary - 自機が画面外に行かないようにしたい
そのやり方だと、画面の端でプレイヤーを斜め移動させた時にガタガタした移動になっちゃって、このやり方ではダメだなと諦めた記憶が…。まあ、見た目は酷いけれど、画面外に出さない処理をそういうやり方でやれないこともない…と、一応メモしておきます。
他にも、画面の端に壁相当のオブジェクトを配置しておいて、その壁オブジェクトとプレイヤーでアタリ判定・衝突判定をして、画面外に出ないようにしてしまう、という方法もありそう。ただ、カメラの各プロパティをちょっと修正するたびに、壁オブジェクトの配置についても修正する手間が増えるであろう予感も…。
[ ツッコむ ]
以上、1 日分です。