using System.Collections.Generic;
using UnityEngine;
namespace ServiceSim
{
///
/// 譲る側が「相手の道から横へどく」ための形の計算(2026-10-23 エイイチと決めた)。
///
/// 前は、ロボットも人も相手が動くのを待つ作りだった。そのため
/// 受け渡し口と客席をつなぐ角で向かい合って止まり、溜まっていた
/// (前の2回(1311・1536)で、ロボットが止まった時間の約3分の1がこの角)。
/// そこで譲る順番(RobotSpec の RankRobot / RankStaff / RankGuest)を決め、
/// 譲る側は **その場で下がるのではなく、相手の道から横へどく**。
///
/// ここは **形の計算だけ**。通れる床かどうかは呼ぶ側が確かめる
/// (人とロボットで見る NavMesh の区画が違うため)。
/// 距離の値は RobotSpec の1か所に置く。
///
public static class YieldSide
{
///
/// 線 a→b に p をおろした足。線の外側に出たら端で止める
///
public static Vector3 ProjectOnSegment(Vector3 a, Vector3 b, Vector3 p)
{
a.y = 0f; b.y = 0f; p.y = 0f;
Vector3 ab = b - a;
float len2 = ab.sqrMagnitude;
if (len2 < 0.000001f) return a;
float t = Mathf.Clamp01(Vector3.Dot(p - a, ab) / len2);
return a + ab * t;
}
/// 線 a→b から p までの横の距離(m)
public static float LateralDistance(Vector3 a, Vector3 b, Vector3 p)
{
Vector3 foot = ProjectOnSegment(a, b, p);
p.y = 0f;
return Vector3.Distance(foot, p);
}
///
/// 折れ線(points)から p までの横の距離(m)。いちばん近い区間で測る。
/// 点が 2 つ未満なら float.MaxValue(道が分からない=上に乗っていないとみなす)。
/// 「これから進む道の上にいるか」を見るのに使う(v6b)
///
public static float LateralDistanceToPath(List points, Vector3 p)
{
if (points == null || points.Count < 2) return float.MaxValue;
float best = float.MaxValue;
for (int i = 1; i < points.Count; i++)
{
float d = LateralDistance(points[i - 1], points[i], p);
if (d < best) best = d;
}
return best;
}
///
/// 相手の道 a→b から横へ clearM 以上どいた点の候補を、近い順に作る。
///
/// ・足(from をおろした点)から、線に直角の向きへ clearM から searchM まで stepM きざみ
/// ・いま自分がいる側を先に試す(よけいに道を横切らないため)
/// ・呼ぶ側が、先頭から順に「通れる床か」を確かめて、最初に通ったものを使う
///
/// 線の長さが 0 のときは何も入れない(呼ぶ側が別の手に回る)。
///
public static void Candidates(Vector3 a, Vector3 b, Vector3 from,
float clearM, float searchM, float stepM,
List into)
{
if (into == null) return;
into.Clear();
a.y = 0f; b.y = 0f; from.y = 0f;
Vector3 ab = b - a;
ab.y = 0f;
if (ab.sqrMagnitude < 0.000001f) return;
Vector3 dir = ab.normalized;
Vector3 left = new Vector3(-dir.z, 0f, dir.x); // 線に直角(左)
Vector3 foot = ProjectOnSegment(a, b, from);
// いまいる側を先に試す
float sideSign = Vector3.Dot(from - foot, left) >= 0f ? 1f : -1f;
float step = Mathf.Max(0.05f, stepM);
float start = Mathf.Max(0.01f, clearM);
float end = Mathf.Max(start, searchM);
for (float d = start; d <= end + 0.0001f; d += step)
{
into.Add(foot + left * (sideSign * d));
into.Add(foot - left * (sideSign * d));
}
}
///
/// その点が、相手の道 a→b から clearM 以上横に離れているか。
/// 候補を NavMesh へ寄せたあと、寄せたぶんで近づいてしまっていないかを確かめる
///
public static bool FarEnough(Vector3 a, Vector3 b, Vector3 p, float clearM)
{
return LateralDistance(a, b, p) >= clearM - 0.0001f;
}
}
}