using UnityEngine;
namespace ServiceSim
{
///
/// 道の決め方(依頼 v9 の 2 章)。
/// MapPlanner … ロボットが地図(サイトの {名前}_map.json)の上で自分で道を引く。NavMesh は使わない
/// SiteRoutes … サイトの routes の点を順にたどる(v5b〜v8b と同じ。比べるために残す)
///
public enum PathMode
{
MapPlanner,
SiteRoutes
}
///
/// 配膳ロボットの数字。ロボットに関する数字はすべてこの .asset に置く。
///
[CreateAssetMenu(menuName = "ServiceSim/Robot Spec", fileName = "RobotSpec")]
public class RobotSpec : ScriptableObject
{
[Header("大きさ")]
[Tooltip("直径(m)")]
public float diameter = 0.5f;
[Tooltip("高さ(m)")]
public float height = 1.2f;
[Header("走り")]
[Tooltip("最高速度(m/秒)")]
public float maxSpeed = 0.6f;
[Tooltip("加速(m/秒^2)")]
public float acceleration = 1.0f;
[Tooltip("減速(m/秒^2)")]
public float deceleration = 1.0f;
[Tooltip("向きを変える速さ(度/秒)。仮の値には無かったので追加した")]
public float turnSpeedDegPerSec = 180f;
[Header("前のセンサー")]
[Tooltip("進む向きの前、どこまで見るか(m)")]
public float sensorRange = 0.6f;
[Tooltip("センサーの幅(m)。ロボットの直径に合わせる")]
public float sensorWidth = 0.5f;
[Tooltip("センサーの高さ(m)")]
public float sensorHeight = 1.2f;
[Header("止まる・動き出す")]
[Tooltip("前がこの秒数だけ空いたら動き出す(秒)")]
public float resumeAfterClearSec = 1.0f;
[Tooltip("止まってこの秒数たったら経路を引き直す(秒)")]
public float replanAfterStoppedSec = 10f;
[Tooltip("この秒数動けなかったら「動けなくなった」と記録する(秒)")]
public float stuckAfterStoppedSec = 60f;
[Tooltip("使っていない(v5 3章で、受け渡し口へ移すのをやめた)。" +
"動けなくなったらスタッフを呼び、ServiceSpec.rescueSec(サイトの rescue_s)動かしてもらう")]
public float stuckRecoveryWaitSec = 15f;
[Tooltip("1往復がこの秒数を超えたら、受け渡し口へ置き直して往復を打ち切る(秒)。無限に止まらないための保険")]
public float maxTripSec = 600f;
[Header("通路の譲り合い")]
[Tooltip("行き先の細い通路にもう1台のロボットがいたら、太い通路側の入口で待つ。場面2の撮影では切る")]
public bool aisleYielding = true;
[Header("台どうしの譲り合い(Unity だけの仮の値。サイトには無い)")]
[Tooltip("入口で順番を待つ上限(秒)。これを過ぎたら譲らずに進む。" +
"人がロボットを待つ上限(PeopleSpec.robotYieldMaxSec 20 秒)と同じ考え方")]
public float aisleWaitMaxSec = 20f;
[Tooltip("入口で待つとき、入口の点から通路の外へ下がる距離(m)。" +
"入口の点そのものに立つと、中の台が出られなくなる")]
public float aisleWaitSetbackM = 0.9f;
[Tooltip("入口で待つとき、中の台が出てくる道をふさがないよう横へずれる距離(m)")]
public float aisleWaitSideM = 0.6f;
[Tooltip("もう1台にふさがれてから、譲り始めるまでの秒数(秒)。" +
"すれ違うだけで譲り合わないよう、少し待ってから決める")]
public float robotYieldAfterSec = 2f;
[Tooltip("譲って下がったあと、相手を待つ上限(秒)。これを過ぎたら戻って進む")]
public float robotYieldMaxSec = 20f;
[Tooltip("相手が通り過ぎたとみなす、外周どうしの空き(m)")]
public float robotYieldClearM = 0.6f;
[Tooltip("下がる先が見つからないときに、その場から後ろへ下がる距離(m)")]
public float robotYieldBackM = 0.9f;
[Header("受け渡し口の前の通り方(Unity だけの決まり。サイトには無い)")]
[Tooltip("戻る台が、受け渡し口の前の点を通らず、その少し下を通って右の待機場所へ抜けるか。" +
"線が通れる床に乗らないときは、使わずに今までどおり受け渡し口の前へ戻る")]
public bool useReturnLine = true;
[Tooltip("戻りの線を、受け渡し口の前の点より何 m 下(−z)に通すか")]
public float passReturnOffsetM = 1.0f;
[Tooltip("戻りの線を、出ていく台の線(受け渡し口の前の点を通る線)から" +
"中心どうしで何 m 以上離すか")]
public float passReturnSeparationM = 1.0f;
[Tooltip("戻りの線を、ドリンクバーの並ぶ点から何 m 以上離すか")]
public float passReturnKeepAwayM = 0.6f;
[Tooltip("戻りの線が通れる床に乗っているかを調べる刻み(m)")]
public float passReturnCheckStepM = 0.05f;
[Header("同じ通路に2台を集めない(Unity だけの決まり。サイトには無い)")]
[Tooltip("料理を受け持つとき、ほかの台が向かっている・中にいる細い通路(店の形の aisles)の" +
"卓を後に回すか。後に回せる料理が無ければ、通路の入口ではなく待機場所で待つ。" +
"外すと、これまでどおり近い卓から順に受け持つ")]
public bool oneRobotPerAisle = true;
[Header("譲る順番と、道から横へどく(Unity だけの仮の値。サイトには無い)")]
[Tooltip("譲る順番の決まりを使うか。外すと、前のとおり互いに相手が動くのを待つ")]
public bool yieldOrderEnabled = true;
[Tooltip("譲る側が、相手の道(サイトの routes の線)から横へどく最小の距離(m)")]
public float yieldSideClearM = 0.6f;
[Tooltip("どく点を探す広さ(m)。相手の道から横へこの距離まで探す")]
public float yieldSideSearchM = 2.5f;
[Tooltip("どく点を探す刻み(m)")]
public float yieldSideStepM = 0.2f;
[Tooltip("どいて相手を待つ上限(秒)。過ぎたら元の道へ戻って進む")]
public float yieldSideWaitMaxSec = 30f;
[Tooltip("スタッフが譲るかを決めるとき、ロボットの「これから進む道」" +
"(サイトの routes の線の、ロボットの今の位置から先)を何 m 分見るか。" +
"この中に、横 yieldSideClearM+体の半径 以内で入っているときだけ譲る。" +
"ただのすれ違い(近くにいるだけ)では譲らない(v6b)")]
public float yieldPathAheadM = 2.0f;
[Header("角の止まりを数える範囲(Unity だけの仮の値。記録だけ)")]
[Tooltip("受け渡し口と客席をつなぐ角。x の下限(m)")]
public float cornerMinX = 10.5f;
[Tooltip("同じく x の上限(m)")]
public float cornerMaxX = 13.5f;
[Tooltip("同じく z の下限(m)")]
public float cornerMinZ = 3.5f;
[Tooltip("同じく z の上限(m)")]
public float cornerMaxZ = 5.8f;
[Header("卓での止まり")]
[Tooltip("卓に着いたら止まる秒数(秒)。客が料理を取る時間。「止まった」には数えない")]
public float tableDwellSec = 25f;
[Header("経路をたどる(仮の値には無かったので追加した)")]
[Tooltip("行き先にこれだけ近づいたら着いたとみなす(m)")]
public float arriveRadius = 0.25f;
[Tooltip("経路の角にこれだけ近づいたら次の角へ進む(m)")]
public float cornerReachDistance = 0.12f;
[Tooltip("これより速度が小さければ「止まっている」とみなす(m/秒)")]
public float stoppedSpeedEpsilon = 0.02f;
[Header("止まったときに測る左右の空き")]
[Tooltip("左右を見る高さ(m)。椅子(0.45 m)に当たる高さにする")]
public float clearanceProbeHeight = 0.3f;
[Tooltip("左右を見る距離(m)。これより遠いものは見ない")]
public float clearanceProbeRange = 3.0f;
[Tooltip("道が引けないとき、ふさいでいるものを探す半径(m)")]
public float pathBlockSearchRadius = 2.0f;
[Tooltip("NavMeshAgent の譲り合いの優先度。小さいほど譲らない。人より小さくして人がよけるようにする")]
public int avoidancePriority = 0;
[Tooltip("経路を引き直しても見つからないとき、次に試すまでの間(秒)")]
public float repathRetrySec = 1.0f;
[Header("途中までの経路から出る(B7)")]
[Tooltip("止まっているあいだの「道が引けていなかった秒数(no_path_s)」がこれを超えたら、" +
"repathRetrySec を待たずに強く引き直す(秒)")]
public float noPathForceReplanSec = 10f;
[Tooltip("強く引き直してもまだ途中までしか引けないとき、いちど通路の入口を目指して出る")]
public bool escapeToAisleEntranceOnPartialPath = true;
public float Radius { get { return diameter * 0.5f; } }
// ---- 譲る順番(1か所にまとめる)----
///
/// 譲る順番。**小さいほど先に通る**(譲られる側)。
/// ロボット(0)> スタッフ(1)> 客(2)
///
/// 立ち往生を助けに行くスタッフは、この順番の外(PersonRobotYield.ignoreRobot)。
/// その台には近づかないと仕事ができないので、今までどおりよけない。
///
public const int RankRobot = 0;
public const int RankStaff = 1;
public const int RankGuest = 2;
/// その人の順番
public static int RankOf(PersonKind kind)
{
return kind == PersonKind.Staff ? RankStaff : RankGuest;
}
/// その人はロボットに譲るか(順番が後ろなら譲る)
public static bool YieldsToRobot(PersonKind kind)
{
return RankOf(kind) > RankRobot;
}
/// 受け渡し口と客席をつなぐ角の中か(記録のため)
public bool InCorner(Vector3 p)
{
return p.x >= cornerMinX && p.x <= cornerMaxX &&
p.z >= cornerMinZ && p.z <= cornerMaxZ;
}
[Header("道順(v5b・サイトから受け取る)")]
[Tooltip("サイトの routes(曲がり角の点の並び)をたどるか。" +
"外すと、今までどおり行き先まで Unity の道探しで直接行く(比べるとき用)")]
public bool followSiteRoutes = true;
[Tooltip("道順の点に着いたとみなす距離(m)。最後の点(止まる位置)だけは arriveRadius を使う")]
public float waypointReachDistance = 0.25f;
[Tooltip("道順の最後の点(止まる位置)を、焼いた面へ寄せるときに探す半径(m)。" +
"サイトが通れる所に置いているので、寄せない(0.1 m 以下)。" +
"この半径で面に乗らなければ広げて引き直し、報告に回数を出す")]
public float routePointSnapM = 0.1f;
// ================= 依頼 v9:地図の上で自分で道を引く =================
[Header("道の決め方(依頼 v9 の 2 章)")]
[Tooltip("MapPlanner … 地図の上でロボットが自分で道を引く(新しい・既定)。" +
"SiteRoutes … サイトの routes の点をたどる(v8b までと同じ。比べるとき用)。" +
"戻りの線(useReturnLine)・横へどく点(yieldOrderEnabled)・角の箱(cornerMin/Max)は " +
"SiteRoutes のときだけ働く")]
public PathMode pathMode = PathMode.MapPlanner;
[Tooltip("通れる幅の道だけのマス(free_class 1)を通るときの重さの倍。" +
"狭い所は必要な所だけ通らせるため(目安 3 倍)")]
public float narrowCellCost = 3f;
[Tooltip("通路の真ん中寄りを走らせる強さ。1 歩の重さに " +
"centerWeight × max(0, preferClearM − clear) / preferClearM を足す(エイイチの決定)")]
public float centerWeight = 2f;
[Tooltip("障害物からこれだけ離れていれば、真ん中寄りの重さを足さない(m)。" +
"1.2 m の通路ならちょうど真ん中(エイイチの決定)")]
public float preferClearM = 0.6f;
[Tooltip("道を引き直すのは、1 台につきこの秒数に 1 回まで(秒)")]
public float replanMinIntervalSec = 0.5f;
[Tooltip("1 回の道探しで広げるマスの数の上限。超えたら引けないことにする。" +
"いつまでも探して回が止まらないようにするための保険")]
public int planMaxExpand = 200000;
[Tooltip("ロボットがいま立っている所が地図では通れないマス(0)のとき、" +
"近くの通れるマスを探す半径(m)。" +
"NavMesh は体の半径 0.25 m で焼いてあるのに free_class は道の幅で削ってあるので、" +
"通路の入口で待つ点・横へどいた点は 0 になる。地図は塗り直さず、出発点だけ寄せる")]
public float mapSnapM = 1.0f;
[Tooltip("曲がり角だけに縮めるとき、1 本の近道の長さの上限(m)。" +
"長すぎる近道を試すと道探しが重くなる")]
public float planSimplifyMaxM = 6f;
[Header("人や物で前がふさがったとき(依頼 v9 の 3 章)")]
[Tooltip("止まってこの秒数たっても前が空かなければ、回り込む道を探す(秒。エイイチの決定)。" +
"引けなければ待ち、この秒数ごとに探し直す")]
public float detourAfterSec = 3f;
[Tooltip("回り込む道を探すとき、通れないマスとして置く人・ロボットを探す半径(m)")]
public float detourNearM = 3f;
[Tooltip("回り込む道と人のあいだに足す余裕(m)。" +
"人の半径+ロボットの半径+この値 を通れないマスにする")]
public float detourPersonMarginM = 0.1f;
[Tooltip("回り込む道が、もとの残りの道のこの倍+detourExtraAddM より長ければ取らずに待つ")]
public float detourMaxFactor = 2f;
[Tooltip("同じく、足し算のぶん(m)")]
public float detourExtraAddM = 5f;
[Header("ロボットどうし(依頼 v9 の 4 章・エイイチの決定)")]
[Tooltip("相手の「先の道」を何 m 分見るか(m)")]
public float robotPathAheadM = 4f;
[Tooltip("優先が高い台の先の道から、2 台の半径の和+この値 以内のマスを重くする(m)")]
public float robotPathCostMarginM = 0.2f;
[Tooltip("重くする強さ(1 歩の重さに足す)。相手の道とかぶらない道があればそちらを選ばせる")]
public float robotPathCostAdd = 6f;
[Tooltip("よける場所は、相手の先の道から 2 台の半径の和+この値 以上離れていること(m)")]
public float robotYieldSeparationM = 0.1f;
[Tooltip("よける場所を探す広さ(m)。自分のまわりこの距離まで探す")]
public float robotYieldSpotSearchM = 2.5f;
[Tooltip("優先が高い台がこの距離まで来て、先の道がかぶるときに引き直す(m)")]
public float robotReplanNearM = 3f;
[Tooltip("2 台が向かい合ってこの秒数止まったら、優先が低い方がよける場所へ動く(秒)")]
public float headOnStuckSec = 10f;
[Tooltip("「向かい合っている」と見る、2 台の向きの内積の上限。" +
"−1 が真正面。−0.5 は 120 度より正面寄り(依頼に数字が無かったのでここで決めた)")]
public float headOnDotMax = -0.5f;
[Header("見せる(依頼 v9 の 6 章。測る回でも出してよい)")]
[Tooltip("いま引いている道を床の上に細い線で出すか")]
public bool showPathLine = true;
[Tooltip("線の太さ(m)")]
public float pathLineWidthM = 0.04f;
[Tooltip("線を床から浮かせる高さ(m)。床の塗りより上に出す")]
public float pathLineHeightM = 0.04f;
/// MapPlanner で走るか(依頼 v9)
public bool UseMapPlanner { get { return pathMode == PathMode.MapPlanner; } }
/// 道の引き方の数字を GridPlanner に渡す形にする
public GridPlanner.Settings PlannerSettings()
{
GridPlanner.Settings s = new GridPlanner.Settings();
s.narrowCellCost = narrowCellCost;
s.centerWeight = centerWeight;
s.preferClearM = preferClearM;
s.maxExpand = planMaxExpand;
s.snapM = mapSnapM;
s.simplifyMaxM = planSimplifyMaxM;
return s;
}
}
}