using System.Collections.Generic; using UnityEngine; namespace ServiceSim { /// /// 1卓ぶんの椅子の出し入れ。座っている人は人として置かないので、 /// この script が「その卓の椅子」だけを持つ。 /// /// 引くきっかけは人の動きに合わせる(B6)。GroupManager が次のときに呼ぶ。 /// 座るとき/ドリンクバーへ立つとき/トイレへ立つとき/会計で立つとき/退店するとき/戻って座り直すとき /// 食事中に何もなく引く分は、別の長い間隔(spec.chairPullIdleMeanIntervalSec)で少しだけ起こす。 /// /// この script は椅子の出し入れだけを扱う。いつ引くかは GroupManager が決め、 /// 椅子1脚の状態は Chair が持つ。数字は PeopleSpec の .asset に置く。 /// public class SeatedGuest : MonoBehaviour { /// 引いたきっかけ(記録に出す) public const string ReasonSit = "座る"; public const string ReasonDrinkBar = "ドリンクバー"; public const string ReasonToilet = "トイレ"; public const string ReasonPay = "会計"; public const string ReasonLeave = "退店"; public const string ReasonSitBack = "戻って座る"; public const string ReasonIdle = "食事中"; [Header("設定")] public PeopleSpec spec; [Header("いまの様子(読むだけ)")] [SerializeField] int _pulledNow; [SerializeField] float _nextIdlePullIn; [SerializeField] int _pulls; readonly List _aisleChairs = new List(); /// _aisleChairs と同じ並び。0 より大きければ、その秒数だけ引いたまま readonly List _holdLeft = new List(); System.Random _rng; ServiceOps _ops; int _tableIndex = -1; // ---- 座っている客の形(見た目だけ。当たり・PersonBody・NavMeshAgent を持たない)---- GameObject _bodyPrefab; readonly List _seatPoints = new List(); readonly List _bodies = new List(); int _shown; bool _warnedNoBody; // 出せないことを1回だけ知らせる /// いま出している座った形の数(読むだけ) public int SeatedShown { get { return _shown; } } /// この卓の席の数(座った形を出せる上限) public int SeatPointCount { get { return _seatPoints.Count; } } /// いま1脚でも引いているか public bool IsPulling { get { return _pulledNow > 0; } } /// この卓で引いた回数 public int Pulls { get { return _pulls; } } /// 引ける椅子の数 public int AisleChairCount { get { return _aisleChairs.Count; } } /// この卓の引ける椅子と、乱数の種を渡す public void Configure(PeopleSpec peopleSpec, IList aisleChairs, int seed, ServiceOps ops, int tableIndex) { Configure(peopleSpec, aisleChairs, seed, ops, tableIndex, null, null); } /// 席の位置と、座った形の prefab も渡す public void Configure(PeopleSpec peopleSpec, IList aisleChairs, int seed, ServiceOps ops, int tableIndex, GameObject seatedBodyPrefab, IList seatPoints) { _ops = ops; _tableIndex = tableIndex; spec = peopleSpec; _bodyPrefab = seatedBodyPrefab; _seatPoints.Clear(); if (seatPoints != null) for (int i = 0; i < seatPoints.Count; i++) _seatPoints.Add(seatPoints[i]); ClearBodies(); _aisleChairs.Clear(); _holdLeft.Clear(); if (aisleChairs != null) { for (int i = 0; i < aisleChairs.Count; i++) { if (aisleChairs[i] == null) continue; _aisleChairs.Add(aisleChairs[i]); _holdLeft.Add(0f); } } _rng = new System.Random(seed); _pulledNow = 0; _pulls = 0; _nextIdlePullIn = NextIdleInterval(); } /// /// 人が動いたので、その人の席の椅子を引く。 /// seatOrder はその卓の何人目か(同じ人がいつも同じ椅子を引くようにする)。 /// public void PullFor(string reason, int seatOrder) { if (spec == null || _aisleChairs.Count == 0) return; int i = Mathf.Abs(seatOrder) % _aisleChairs.Count; Pull(i, reason); } void Pull(int i, string reason) { if (i < 0 || i >= _aisleChairs.Count) return; Chair c = _aisleChairs[i]; if (c == null) return; // すでに引いてあるなら、引いたままの時間だけ延ばす(二重に数えない) if (_holdLeft[i] > 0f) { _holdLeft[i] = Mathf.Max(_holdLeft[i], spec.chairPullHoldSec); return; } c.Pull(spec.chairPullDistance); _holdLeft[i] = spec.chairPullHoldSec; _pulledNow++; _pulls++; if (_ops != null) _ops.AddChairPull(reason, TableIdOf()); } int TableIdOf() { if (_ops == null || _tableIndex < 0 || _tableIndex >= _ops.Tables.Count) return 0; return _ops.Tables[_tableIndex].tableId; } float NextIdleInterval() { if (spec == null || spec.chairPullIdleMeanIntervalSec <= 0f) return float.MaxValue; double u = _rng.NextDouble(); if (u <= 1e-9) u = 1e-9; return (float)(-spec.chairPullIdleMeanIntervalSec * System.Math.Log(u)); } void Update() { if (spec == null || _rng == null) return; float dt = Time.deltaTime; // 引いたままの時間が切れた椅子を戻す for (int i = 0; i < _holdLeft.Count; i++) { if (_holdLeft[i] <= 0f) continue; _holdLeft[i] -= dt; if (_holdLeft[i] > 0f) continue; _holdLeft[i] = 0f; if (_aisleChairs[i] != null) _aisleChairs[i].Release(); _pulledNow = Mathf.Max(0, _pulledNow - 1); } // 食事中に何もなく引く分(少しだけ) if (_nextIdlePullIn == float.MaxValue) return; _nextIdlePullIn -= dt; if (_nextIdlePullIn > 0f) return; _nextIdlePullIn = NextIdleInterval(); if (!Occupied()) return; if (_aisleChairs.Count == 0) return; Pull(_rng.Next(_aisleChairs.Count), ReasonIdle); } /// この卓に組が座っているか bool Occupied() { if (_ops == null || _tableIndex < 0) return true; // 分からないときは引く if (_tableIndex >= _ops.Tables.Count) return true; return _ops.Tables[_tableIndex].groupId >= 0; } // ---------------- 座っている客の形(見た目だけ)---------------- /// /// 座っている客の形を count 人ぶん出す(0 なら全部消す)。 /// 形は席の位置に置くだけ。当たり判定も PersonBody も NavMeshAgent も持たないので、 /// NavMesh・前のセンサー・人の数の記録・物理にはいっさい効かない。 /// public void ShowSeated(int count) { // 出せないときは黙って戻らない。 // 10/21 の分が2日間だれにも気づかれなかったのは、ここが黙って戻っていたため if (_bodyPrefab == null || _seatPoints.Count == 0) { if (count > 0 && !_warnedNoBody) { _warnedNoBody = true; Debug.LogWarning("[ServiceSim] 座っている客の形を出せません(卓 " + _tableIndex + ")。" + (_bodyPrefab == null ? "形の prefab が入っていません(PeopleSpawner.seatedBodyPrefab)。" + "ServiceSim/座っている客の形を用意する を押してください" : "この卓に座る位置(seat_points)がありません")); } _shown = 0; return; } int want = Mathf.Clamp(count, 0, _seatPoints.Count); if (want == _shown) return; EnsureBodies(); for (int i = 0; i < _bodies.Count; i++) { if (_bodies[i] == null) continue; bool on = i < want; if (_bodies[i].activeSelf != on) _bodies[i].SetActive(on); } _shown = want; } void EnsureBodies() { if (_bodies.Count == _seatPoints.Count) return; ClearBodies(); for (int i = 0; i < _seatPoints.Count; i++) { GameObject go = Instantiate(_bodyPrefab, transform); go.name = "SeatedBody" + (i + 1).ToString("00"); go.transform.position = _seatPoints[i]; go.SetActive(false); _bodies.Add(go); } } void ClearBodies() { for (int i = 0; i < _bodies.Count; i++) { if (_bodies[i] == null) continue; if (Application.isPlaying) Destroy(_bodies[i]); else DestroyImmediate(_bodies[i]); } _bodies.Clear(); _shown = 0; } void OnDisable() { ShowSeated(0); for (int i = 0; i < _holdLeft.Count; i++) { _holdLeft[i] = 0f; if (_aisleChairs[i] != null) _aisleChairs[i].Release(); } _pulledNow = 0; } } }