using System.Collections.Generic; using UnityEngine; namespace ServiceSim { /// 席の種類 public enum SeatKind { WallBooth, // 壁際ブース(両側ベンチ) WallTable, // 壁沿いの卓(壁側ベンチ+通路側に椅子) IslandTable, // 島の卓(両側に椅子) Counter, // カウンター席(今回は使わないが置ける) LargeTable // 大人数席(今回は使わないが置ける) } /// 場所の種類 public enum PlaceKind { Kitchen, // 厨房(通れない) Pantry, // パントリー(水・おしぼり・カトラリー) FoodPass, // 料理の受け渡し口 DishReturn, // 下げ膳の返却口 Entrance, // 入口 Vestibule, // 風除室 Waiting, // 待合 Register, // レジ Toilet, // トイレ DrinkBar, // ドリンクバー Office, // 事務所・更衣室 RobotStandby, // ロボットの待機場所(受け渡し口の前をふさがないため)B2 DrinkBarStand,// ドリンクバーの前に立つ点(機械1台に1つ)B4 DrinkBarQueue,// ドリンクバーの並ぶ点 B4 RegisterQueue,// レジの前に並ぶ点(会計に立つ人が1人ずつ並ぶ) StaffStandby, // 手の空いたスタッフが待つ点(1人に1つ。太い通路の通り道をふさがない) RegisterBack // レジの内側(スタッフだけが入れる。太い通路からだけ入れる) } /// 箱の見た目(StoreBuilder がどの prefab を使うかを決める) public enum BoxStyle { Floor, Wall, Divider, Table, Bench, Chair, Counter, AisleMark, /// /// ベンチの座面の見た目(高さ 0.45 m)。当たりを持たないので /// NavMesh にも、前のセンサーにも、左右の空きの測りにも効かない。見た目だけ。 /// BenchSeat, /// /// ベンチの背もたれの見た目(机と反対側のふちに薄く・高さ 1.1 m)。当たりなし。 /// BenchBack, /// /// ベンチの当たりの箱(大きさ・位置・高さはベンチのまま、**見た目を持たない**)。 /// NavMesh と前のセンサーはこの箱だけを見る。見た目は BenchSeat と BenchBack が受け持つ。 /// BenchBody } /// XZ 平面の長方形(y は持たない) public struct PlanRect { public float xMin, xMax, zMin, zMax; public PlanRect(float xMin, float zMin, float xMax, float zMax) { this.xMin = Mathf.Min(xMin, xMax); this.xMax = Mathf.Max(xMin, xMax); this.zMin = Mathf.Min(zMin, zMax); this.zMax = Mathf.Max(zMin, zMax); } public static PlanRect FromSize(float xMin, float zMin, float sizeX, float sizeZ) { return new PlanRect(xMin, zMin, xMin + sizeX, zMin + sizeZ); } public static PlanRect FromCenter(Vector3 center, float sizeX, float sizeZ) { return new PlanRect(center.x - sizeX * 0.5f, center.z - sizeZ * 0.5f, center.x + sizeX * 0.5f, center.z + sizeZ * 0.5f); } public float SizeX { get { return xMax - xMin; } } public float SizeZ { get { return zMax - zMin; } } public float CenterX { get { return (xMin + xMax) * 0.5f; } } public float CenterZ { get { return (zMin + zMax) * 0.5f; } } public Vector3 Center { get { return new Vector3(CenterX, 0f, CenterZ); } } public bool IsEmpty { get { return SizeX <= 0.0005f || SizeZ <= 0.0005f; } } public bool Contains(Vector3 p) { return p.x >= xMin && p.x <= xMax && p.z >= zMin && p.z <= zMax; } public PlanRect Expand(float m) { return new PlanRect(xMin - m, zMin - m, xMax + m, zMax + m); } public Bounds ToBounds(float yMin, float yMax) { Bounds b = new Bounds(); b.SetMinMax(new Vector3(xMin, yMin, zMin), new Vector3(xMax, yMax, zMax)); return b; } public override string ToString() { return "x " + xMin.ToString("0.##") + ".." + xMax.ToString("0.##") + " / z " + zMin.ToString("0.##") + ".." + zMax.ToString("0.##"); } } /// 置く箱1つ public class PlanBox { public string name; public BoxStyle style; public PlanRect rect; public float height; /// 下の面の高さ(m)。ふつうは 0 public float baseY; public PlanBox(string name, BoxStyle style, PlanRect rect, float height) { this.name = name; this.style = style; this.rect = rect; this.height = height; this.baseY = 0f; } } /// 椅子1脚(引けるものと引けないもの) public class PlanChair { public PlanRect rect; public float height; /// 引けるか。ブースのベンチは false public bool pullable; /// 引く向き(通路の側へ)。XZ の単位ベクトル public Vector3 pullDirection; } /// /// 座る場所1つぶんの「人が立つ点」。 /// 通路からここまで人が歩けるかを Build Store が確かめる(席に入れない卓を見つけるため)。 /// public struct PlanSeatSpot { /// どの席か(報告に出す。例「壁側」「通路側」「南ベンチ」) public string name; /// その席に着くために人が立つ点 public Vector3 point; public PlanSeatSpot(string name, Vector3 point) { this.name = name; this.point = point; } } /// 卓1つ public class PlanSeat { public int tableId; public SeatKind kind; public PlanRect tableRect; public float tableHeight; /// 何人かけの卓か(2人席・4人席)。組の座らせ方に使う public int seatCount = 4; /// ロボットが料理を置くために止まる点(通路の上) public Vector3 deliveryPoint; /// この卓に行くときに使う細い通路の番号(譲り合いの区画に使う) public int aisleIndex = -1; public readonly List chairs = new List(); /// 席ごとの「人が立つ点」(Build Store の席への入りやすさの確かめに使う) public readonly List spots = new List(); /// /// 人が実際に座る位置(座面・椅子の真ん中)。seatCount 個ある。 /// 座っている客の形を出す場所に使う(見た目だけ。当たりも NavMesh も持たない)。 /// public readonly List seatPoints = new List(); } /// 通路1本(譲り合いの区画と、入る前に待つ点) public class PlanAisle { public string name; public PlanRect rect; /// 細い通路か(幅を切り替える対象か) public bool narrow; // v5b: 「通りにくい通路」の印(hardToPass)はやめた。 // 狭い道を使うかどうかは、サイトの routes の use(normal / narrow)が正。 /// この通路に入る前に待つ点 public Vector3 entrance; public Bounds Zone(float yMin, float yMax) { return rect.ToBounds(yMin, yMax); } } /// 場所1つ(厨房・パントリー・受け渡し口など) public class PlanPlace { public PlaceKind kind; public string name; public PlanRect rect; public float height; /// 人が立つ・ロボットが止まる点 public Vector3 point; /// 通れない箱として置くか public bool solid; } /// /// 店の「部品をどこに置くか」の一覧。 /// StoreLayout(の種類ごとの実装)がこれを作り、StoreBuilder がこれだけを見て並べる。 /// StoreBuilder は格子や帯の組み方を知らない。 /// public class StorePlan { /// 床の全体の広がり(カメラの画角とベイク範囲に使う) public PlanRect floor; /// /// 実際に床を敷く長方形。建物の外に床が出ないように分けて持つ。 /// 空なら floor を1枚だけ敷く。 /// public readonly List floorPieces = new List(); public float wallHeight = 2.4f; public float wallThickness = 0.15f; public float floorThickness = 0.2f; /// この plan を作ったときの細い通路の幅(m) public float aisleWidth; /// /// 人がロボットをよけるときに下がれる場所(通路の外)。 /// 壁ぞいの卓と卓のすき間など。店の形が入れる。 /// public readonly List refuges = new List(); /// /// ロボットの道順(v5b)。サイトが決めた曲がり角の点の並び。 /// 無ければ null(そのときは今までどおり Unity の道探しで行き先まで行く)。 /// public RouteBook routes; public readonly List boxes = new List(); public readonly List seats = new List(); public readonly List aisles = new List(); public readonly List places = new List(); // ---- 組み立てを助ける小さな道具 ---- /// 床を1枚足す。floor の広がりも合わせて広げる public void AddFloor(PlanRect rect) { if (rect.IsEmpty) return; if (floorPieces.Count == 0) floor = rect; else { floor = new PlanRect( Mathf.Min(floor.xMin, rect.xMin), Mathf.Min(floor.zMin, rect.zMin), Mathf.Max(floor.xMax, rect.xMax), Mathf.Max(floor.zMax, rect.zMax)); } floorPieces.Add(rect); } public PlanBox AddBox(string name, BoxStyle style, PlanRect rect, float height) { if (rect.IsEmpty || height <= 0.0005f) return null; PlanBox b = new PlanBox(name, style, rect, height); boxes.Add(b); return b; } public PlanAisle AddAisle(string name, PlanRect rect, bool narrow, Vector3 entrance) { PlanAisle a = new PlanAisle(); a.name = name; a.rect = rect; a.narrow = narrow; a.entrance = entrance; aisles.Add(a); return a; } public PlanPlace AddPlace(PlaceKind kind, string name, PlanRect rect, float height, Vector3 point, bool solid) { PlanPlace p = new PlanPlace(); p.kind = kind; p.name = name; p.rect = rect; p.height = height; p.point = point; p.solid = solid; places.Add(p); return p; } /// 場所の点を引く。無ければ fallback public Vector3 PointOf(PlaceKind kind, Vector3 fallback) { for (int i = 0; i < places.Count; i++) if (places[i].kind == kind) return places[i].point; return fallback; } /// /// 同じ種類の場所の点をすべて集める(出てきた順)。 /// ロボットの待機場所やドリンクバーの立つ点のように、1種類に点がいくつもあるときに使う。 /// public List PointsOf(PlaceKind kind) { List list = new List(); for (int i = 0; i < places.Count; i++) if (places[i].kind == kind) list.Add(places[i].point); return list; } public bool HasPlace(PlaceKind kind) { for (int i = 0; i < places.Count; i++) if (places[i].kind == kind) return true; return false; } public PlanRect RectOf(PlaceKind kind) { for (int i = 0; i < places.Count; i++) if (places[i].kind == kind) return places[i].rect; return new PlanRect(0f, 0f, 0f, 0f); } public PlanAisle AisleAt(int index) { if (index < 0 || index >= aisles.Count) return null; return aisles[index]; } /// 細い通路の数 public int NarrowAisleCount { get { int n = 0; for (int i = 0; i < aisles.Count; i++) if (aisles[i].narrow) n++; return n; } } public Bounds NavBounds(float padding) { PlanRect r = floor.Expand(padding); return r.ToBounds(-1f, wallHeight + 1f); } } }