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);
}
}
}