監視カメラ
動かない
左右に回転
プレイヤーを検出する
New Scene > Camera
https://gyazo.com/bfe3e433abc84add6e2abfb4ef75f001
AnimationPlayerで、首振り
30° ~ -30° を3秒くらいで往復
1.5 sec止まる
Animation Tab > 新規アニメーション > 新規プロパティトラック > Kinematicbody2D.rotation_degree
Playerを検出する Part1
視野(FOV = Field Of View) のセットアップ
FOVは、カメラが発する light beam と一致させる
FOVは、NPC(カメラ)とともに回転する
Playerが視野にいるかチェックする。
見つかったら何かしらフィードバック
NPCが知る必要のあること
NPCがどの方向を向いているか
Playerがどの方向にいるか
PlayerがFOVのtolerance(範囲)にいるか
デフォルトの方向
右を向いた状態、すなわちVector2(1, 0)がデフォルト
https://gyazo.com/c62badf7bbfe27277913be6b1740e4e4
Vector2D.rotated(rad)
あるVector2Dをradだけ回転したベクトルを得る
つまり、NPCを回転させるのに使う
NPC->Playerの向きを計算する
ベクトルの減算で、向き(direction)を得る
大きさを正規化するならnormalize
https://gyazo.com/db2a979909ea20e9b8f0f7982f59a6cd
FOV Tolerance
視野の範囲は、向いている方向から
Negativeに20°
Positiveに20°
https://gyazo.com/ca14a33750ed70023a098746f478af7e
PlayerDetection.gd
CameraScene の、CameraBodyにつける(sceneツリーのルートじゃない)
Player Nodeを取得しておく
最初に変数として持っておけば、あとから位置をチェックできる
code: PlayerDetection.py
var Player : KinematicBody2D
func _ready():
Player = get_node("/root").find_node("Player", true, false)
get_node("/path") : Nodeを探して返す。NodePathを指定
絶対パス : / から始まる
相対パス : スクリプトがアタッチされているNodeの子供から探す
$がget_node("/path/to/current_node")のシンタックスシュガー
node.find_node("name", recursive, owend)
"name" : Node名。NodePathではない
recursive : 子要素、孫要素 ... まで探すかどうか
owend : 1つ下の要素までしか探さない
FOVにいるか検出
code: code.py
func _process(delta):
if Player_in_FOV():
$Torch.color = RED
else:
$Torch.color = WHITE
func Player_in_FOV():
var npc_facing_derection = Vector2(1, 0).rotated(global_rotation)
var direction_to_Player = (Player.position - global_position).normalized()
# vecA.angle_to(vecB) : vecAからvecBの角度を返す (-PIE ~ PIE、 時計回りが正の方向)
# PlayerがFOVの範囲にいるかどうかを返す
if abs(direction_to_Player.angle_to(npc_facing_derection)) < deg2rad(FOV_TOLERANCE):
return true
else:
return false
radianとdegreeの違いに注意
Playerが中に入っていたら、Light2Dのcolorを変更する。(デフォルトは白)
Part1まとめ
angle_to()の使い方
FOVに入っているかの検出
FOVに入ったら、スポットライトの色を変えて知らせる
Part2
次に、壁による視覚の遮蔽を実装していく
視覚遮断に必要なもの
Line of Sightの設定
何かが間に入ったら、見えなくなる
もしもPlayerが遠すぎたら、見えなくなる
PlayerがLOSにいるかどうかチェックする
bool
LOS(Line Of Sight)
LOS = 視線
World2D
https://gyazo.com/da6f792ba38e2e6f1a18c7e5b5aaa3a1
arbitrary = 任意の、恣意的な、わざと
World2Dクラスは、2D環境に関する全てのものを司る(サウンド、物理)
get_world_2D()関数で取得することで、RayCast的なことができる。
Ray-Cast
2D空間のある点 - ある点 を結ぶ線を引き、その間にあるCollisionを取得する。
World2D.direct_space_stateのメソッドとして
interserct_ray(vec_from, vec_to, [], collision_mask)
from : 開始点
to : 終了点
[] :
Player_in_LOS( )を実装
PlayerがCameraのLOS(視線)にいるかどうかを判定する
間に遮蔽物などがないか
検出できる距離にいるか
code: PlayerDetection.py
const MAX_DETECTION_RANGE = 640
func _process(delta):
if Player_in_FOV() and Player_in_LOS():
...
func Player_in_LOS():
# Player-NPC間に障害があるかの判定
var space = get_world_2d().direct_space_state
# obstacle = 障害
var LOS_obstacle = space.intersect_ray(
global_position,
Player.global_position,
collision_mask)
if not LOS_obstacle:
return false
# 視野の距離に入っているかの判定
var distance_to_player = Player.global_position.distance_to(self.global_position)
var is_player_in_range = distance_to_player < MAX_DETECTION_RANGE
if LOS_obstacle.collider == Player and is_player_in_range:
return true
else:
return false
vecA.distance_to(vecB) : 距離の大きさをfloatで返す
var space = get_world_2d().direct_space_state
space : すべての衝突、物理の情報を保存しているオブジェクト。
space.intersect_ray() : Raycastを作成して、線に入ったCollisionを検出する。
space.intersect_ray( )
https://docs.godotengine.org/ja/latest/_images/raycast_falsepositive.png
引数は、
1. from
2. to
3. [無視する物理オブジェクトのリスト]
RigitBody2D, KinematicBody2D, StaticBody2Dのこと
ここでは、監視カメラ自体の衝突判定を無視する(self)
4. 衝突マスク
衝突するレイヤーを指定する。
このスクリプト空間では、CameraBodyのKinematicBody2D のCollisionプロパティにアクセスできる
すなわち、self.collision_maskを指定すればいい
part2まとめ
World2Dの使い方
direct_space_state : RayCastを使って、任意の線を引いて、恣意的な衝突判定を取得できる
LOSを設定
条件に合うとき、カメラに見つからなくなる
RayCastで、PlayerとCameraの間の障害物を取得
最大検出距離を設定する