using UnityEngine; namespace ServiceSim { /// /// ロボットの前に人・ロボット・椅子があるかを見る。 /// 見るだけで、止まる・動き出すの判断はしない(それは ServingRobotController)。 /// 止まった原因を分けられるように、いちばん近いものの種類も返す。 /// public class FrontSensor : MonoBehaviour { public struct Reading { /// 前に何かあるか public bool blocked; /// いちばん近いものまでの、ロボットの外周からの距離(m)。何もなければ sensorRange public float nearestDistance; public bool sawRobot; public bool sawPerson; public bool sawChair; /// いちばん近いものが人か public bool nearestIsPerson; /// いちばん近いものが人のときの種類 public PersonKind nearestPersonKind; /// いちばん近いものが人のときの速さ(m/秒) public float nearestPersonSpeed; /// いちばん近いものが人で、立ち止まっているか public bool nearestPersonStanding; /// いちばん近いものがロボットか public bool nearestIsRobot; /// いちばん近いものが椅子か public bool nearestIsChair; /// いちばん近いものの名前(cause_detail に使う) public string nearestName; } [Header("設定")] public RobotSpec spec; [Tooltip("自分のロボット。自分を見ないようにするために使う")] public ServingRobotController owner; const int MaxHits = 32; readonly Collider[] _hits = new Collider[MaxHits]; /// /// この回で、前のセンサーの箱に入った当たりのいちばん多かった数(依頼 v11 の 5 章)。 /// MaxHits(32)に届いていなければ切り捨てが起きていないので、 /// 「物理の返る順番」はずれの元から外せる。ExperimentRunner が回し始めに 0 に戻す /// public static int MaxSeen; /// 切り捨てが起きた回数(0 のはず) public static int Overflows; Reading _last; public Reading Last { get { return _last; } } /// いまの前の様子を見る public Reading Sense() { Reading r = new Reading(); if (spec == null) { _last = r; return r; } r.nearestDistance = spec.sensorRange; r.nearestName = ""; float radius = spec.Radius; Vector3 fwd = transform.forward; Vector3 eye = transform.position + Vector3.up * (spec.sensorHeight * 0.5f); Vector3 center = eye + fwd * (radius + spec.sensorRange * 0.5f); Vector3 half = new Vector3(spec.sensorWidth * 0.5f, spec.sensorHeight * 0.5f, spec.sensorRange * 0.5f); int n = Physics.OverlapBoxNonAlloc(center, half, _hits, transform.rotation, ~0, QueryTriggerInteraction.Ignore); // 依頼 v11 の 5 章:当たりが MaxHits を超えると切り捨てになり、 // どれが落ちるかは物理の返す順番で決まる(=順番に頼る所)。 // 実際に超えているかを数えて summary.json に出す。 // 超えていなければ「物理の返る順番」はずれの元から外せる if (n > MaxSeen) MaxSeen = n; if (n > MaxHits) { n = MaxHits; Overflows++; } for (int i = 0; i < n; i++) { Collider col = _hits[i]; if (col == null) continue; PersonBody person = col.GetComponentInParent(); ServingRobotController other = null; Chair chair = null; if (person == null) { other = col.GetComponentInParent(); if (other != null && owner != null && other == owner) continue; // 自分は見ない if (other == null) chair = col.GetComponentInParent(); } if (person == null && other == null && chair == null) continue; // 床・壁・卓は見ない r.blocked = true; if (person != null) r.sawPerson = true; if (other != null) r.sawRobot = true; if (chair != null) r.sawChair = true; // ロボットの外周からの距離 Vector3 closest = col.ClosestPoint(eye); Vector3 flat = closest - transform.position; flat.y = 0f; float d = Mathf.Max(0f, Vector3.Dot(flat, fwd) - radius); if (d >= r.nearestDistance) continue; r.nearestDistance = d; r.nearestIsPerson = person != null; r.nearestIsRobot = other != null; r.nearestIsChair = chair != null; if (person != null) { r.nearestPersonKind = person.kind; r.nearestPersonSpeed = person.Speed; r.nearestPersonStanding = person.IsStanding; r.nearestName = person.name; } else if (other != null) r.nearestName = other.name; else if (chair != null) r.nearestName = chair.transform.parent != null ? chair.transform.parent.name + "/" + chair.name : chair.name; } _last = r; return r; } void OnDrawGizmosSelected() { if (spec == null) return; Vector3 fwd = transform.forward; Vector3 center = transform.position + Vector3.up * (spec.sensorHeight * 0.5f) + fwd * (spec.Radius + spec.sensorRange * 0.5f); Gizmos.color = _last.blocked ? new Color(1f, 0.3f, 0.2f, 0.6f) : new Color(0.3f, 1f, 0.4f, 0.4f); Matrix4x4 old = Gizmos.matrix; Gizmos.matrix = Matrix4x4.TRS(center, transform.rotation, Vector3.one); Gizmos.DrawWireCube(Vector3.zero, new Vector3(spec.sensorWidth, spec.sensorHeight, spec.sensorRange)); Gizmos.matrix = old; } } }