diff --git a/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerAnimator.cs b/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerAnimator.cs new file mode 100644 index 00000000..dc746743 --- /dev/null +++ b/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerAnimator.cs @@ -0,0 +1,129 @@ +using System; +using UniRx; +using UnityEngine; + +public class CustomerAnimator : MonoBehaviour +{ + public static readonly int WalkFront = Animator.StringToHash("WalkFront"); + public static readonly int WalkSide = Animator.StringToHash("WalkSide"); + public static readonly int WalkBack = Animator.StringToHash("WalkBack"); + public static readonly int StayBack = Animator.StringToHash("StayBack"); + public static readonly int WalkFrontEat = Animator.StringToHash("WalkFrontEat"); + public static readonly int WalkSideEat = Animator.StringToHash("WalkSideEat"); + + private static readonly int Complain = Animator.StringToHash("ComplainTrigger"); + + [SerializeField] private Animator animator; + [SerializeField] private CustomerDirection customerDirection; + [SerializeField] private Animator orderAnimator; + [SerializeField] private GameObject tapReaction; + [SerializeField] private GameObject orderPopup; + [SerializeField] private Transform leftPopcornTarget; + [SerializeField] private Transform rightPopcornTarget; + [SerializeField] private Transform frontPopcornTarget; + [SerializeField] private Transform wantFlavorSpriteTarget; + [SerializeField] private SpriteRenderer leftPopcorn; + [SerializeField] private SpriteRenderer rightPopcorn; + [SerializeField] private SpriteRenderer frontPopcorn; + [SerializeField] private SpriteRenderer wantFlavorSprite; + [SerializeField] private Sprite defaultSprite; + [SerializeField] private Sprite caramelSprite; + + private readonly ReactiveProperty triggerName = new ReactiveProperty(); + + private void Start() + { + triggerName.AddTo(this); + triggerName.Subscribe(x => + { + animator.SetTrigger(x); + }).AddTo(this); + } + + public void SetTrigger(CustomerMovingType movingType) + { + switch (movingType) + { + case CustomerMovingType.WalkSide: + triggerName.Value = WalkSide; + break; + case CustomerMovingType.WalkSideEat: + triggerName.Value = WalkSideEat; + break; + case CustomerMovingType.WalkCenter: + triggerName.Value = WalkSide; + break; + case CustomerMovingType.StayBackOrder: + triggerName.Value = StayBack; + break; + case CustomerMovingType.StayBack: + triggerName.Value = StayBack; + break; + case CustomerMovingType.WalkBack: + triggerName.Value = WalkBack; + break; + case CustomerMovingType.WalkBackHalf: + triggerName.Value = WalkBack; + break; + case CustomerMovingType.WalkFront: + triggerName.Value = WalkFront; + break; + case CustomerMovingType.WalkFrontEat: + triggerName.Value = WalkFrontEat; + break; + default: + throw new ArgumentOutOfRangeException(nameof(movingType), movingType, null); + } + + } + + public void SetSide(bool isDefaultSide) + { + if (isDefaultSide) + { + customerDirection.SetDefaultSide(); + } + else + { + customerDirection.SetFlipSide(); + } + } + + public void SetWantFlavor(ProductStockData stockData) + { + // if (stockData.FlavorId == 2) + // { + // leftPopcorn.sprite = caramelSprite; + // rightPopcorn.sprite = caramelSprite; + // frontPopcorn.sprite = caramelSprite; + // wantFlavorSprite.sprite = caramelSprite; + // } + // else + // { + // leftPopcorn.sprite = defaultSprite; + // rightPopcorn.sprite = defaultSprite; + // frontPopcorn.sprite = defaultSprite; + // wantFlavorSprite.sprite = defaultSprite; + // } + orderPopup.SetActive(true); + } + + public void ShowComplain() + { + orderAnimator.SetTrigger(Complain); + this.CallWaitForSeconds(1.5f, () => + { + orderPopup.SetActive(false); + }); + } + + public void HideOrderPopup() + { + orderPopup.SetActive(false); + } + + public void ShowTapReaction() + { + tapReaction.SetActive(true); + } +} \ No newline at end of file diff --git a/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerAnimator.cs.meta b/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerAnimator.cs.meta new file mode 100644 index 00000000..e741e6b5 --- /dev/null +++ b/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerAnimator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 72fc7821a0f8481495274ac394c7b620 +timeCreated: 1633316259 \ No newline at end of file diff --git a/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerData.cs b/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerData.cs new file mode 100644 index 00000000..59d8d208 --- /dev/null +++ b/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerData.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using UnityEngine; + +public class CustomerData : MonoBehaviour +{ + [SerializeField] private List normalCustomerList; + [SerializeField] private List specialCustomerList; + + public CustomerAnimator ChooseNormalPrefab() + { + return normalCustomerList.RandomChoose(); + } + + public CustomerAnimator ChooseSpecialPrefab() + { + return specialCustomerList.RandomChoose(); + } +} diff --git a/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerData.cs.meta b/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerData.cs.meta new file mode 100644 index 00000000..6be9fac5 --- /dev/null +++ b/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerData.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: eee40f4ea08e42fab5bf3ea8810f972a +timeCreated: 1633065710 \ No newline at end of file diff --git a/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerFlow.cs b/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerFlow.cs new file mode 100644 index 00000000..f5434088 --- /dev/null +++ b/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerFlow.cs @@ -0,0 +1,66 @@ +using System; +using System.Linq; +using UniRx; +using UnityEngine; + +public class CustomerFlow : MonoBehaviour +{ + private IObservable customerObservable; + private IObservable walkerObservable; + private IObservable adWalkerObservable; + private readonly Subject adStartObservable = new Subject(); + private static readonly float checkHeartInterval = 1f; + // 歩行者の出現間隔 + private static readonly float walkerInterval = 60f / 6; + + public IObservable Flow => walkerObservable.Merge(customerObservable, adWalkerObservable); + + private void Awake() + { + adStartObservable.AddTo(this); + + var shopLevelList = SpreadsheetDataManager.Instance.GetBaseDataList(Const.ShopLevelDataSheet); + // 10分間期待値を来客の間隔に変換 + var intervalList = shopLevelList.Select(shopLevel => (heart: shopLevel.heart, interval: 60f * 10 / shopLevel.customer)); + + // 1秒間隔でハートを確認 + var changeCustomerFlowObservable = Observable.Interval(TimeSpan.FromSeconds(checkHeartInterval)) + .Select(_ => GameDataManager.GameData.Heart) + .DistinctUntilChanged() + .Select(heart => intervalList.Last(x => x.heart <= heart).interval); + + // お客さん出現タイマー + customerObservable = changeCustomerFlowObservable + .DistinctUntilChanged() + .Do(x => Debug.Log($"changeInterval:{x}")) + .Select(customerInterval => Observable.Interval(TimeSpan.FromSeconds(customerInterval)) + .Do(_ => Debug.Log($"customer:{GetHashCode()}")) + .Select(_ => true)) + .Switch(); + + // 歩行者出現頻度、立ち止まり確率も設定(歩行者タイマー1分間に6人 + walkerObservable = Observable.Interval(TimeSpan.FromSeconds(walkerInterval)) + .Do(l => Debug.Log($"walker:{GetHashCode()}")) + .Select(x => false); + + // 宣伝時、タップすると60秒だけ稼働するストリーム + adWalkerObservable = adStartObservable + .Delay(TimeSpan.FromSeconds(2f)) + .Select(x => Observable.Interval(TimeSpan.FromSeconds(3f)) + .Take(TimeSpan.FromSeconds(60f)) + .Do(_ => Debug.Log($"adWalker:{GetHashCode()}")) + .Select(_ => false)) + .Switch(); +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (UsayaStorageManager.LoadOrDefault(UsayaStorageFilename.Settings_Data, "DebugManyWalker", false)) + { + Observable.Timer(TimeSpan.FromSeconds(1f), TimeSpan.FromSeconds(70f)).Subscribe(_ => { adStartObservable.OnNext(default); }).AddTo(this); + } +#endif + } + + public void StartAdWalker() + { + adStartObservable.OnNext(Unit.Default); + } +} diff --git a/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerFlow.cs.meta b/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerFlow.cs.meta new file mode 100644 index 00000000..89953014 --- /dev/null +++ b/popcorn/Assets/MyGame/Scenes/marketing/Scripts/CustomerFlow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 772e0346311a433fb714573f207813d2 +timeCreated: 1633323544 \ No newline at end of file diff --git a/popcorn/Assets/MyGame/Scenes/marketing/Scripts/MarketManager.cs b/popcorn/Assets/MyGame/Scenes/marketing/Scripts/MarketManager.cs new file mode 100644 index 00000000..0bfdf08f --- /dev/null +++ b/popcorn/Assets/MyGame/Scenes/marketing/Scripts/MarketManager.cs @@ -0,0 +1,226 @@ +using System; +using System.Linq; +using TMPro; +using UniRx; +using UniRx.Triggers; +using UnityEngine; + +public class MarketManager : MonoBehaviour +{ + [SerializeField] private GameObject closeSign; + [SerializeField] private ShopStockView stockView; + [SerializeField] private MarketCartView cartView; + [SerializeField] private BrotherBlueView blueView; + [SerializeField] private BrotherPinkView pinkView; + [SerializeField] private Transform coinPrefab; + [SerializeField] private Transform rootTransform; + private Market market; + + private void Start() + { + var gameData = GameDataManager.GameData; + CoinManager.Instance.ChangeCoin(gameData.Coin); + HeartMeter.Instance.Initialize(); + HeartMeter.Instance.SetHeart(gameData.Heart); + + market = Market.Instance; + stockView.SetStock(gameData.StorageTanks); + var startStocks = market.DisplayFlavors + .Select((flavor, index) => (flavor, index)) + .Where(data => market.ShuffledOrder.Contains(data.index)) + .Select(data => (data.index, new ProductStockData + { + FlavorId = data.flavor, + Rarity = ProductRarity.Normal, + Stock = 1 + })) + .ToList(); + cartView.SetStock(startStocks, false); + pinkView.Initialize(); + + // Customerの各アニメーション設定 + foreach (var controller in market.CustomerControllerList) + { + GenerateCustomer(controller); + } + market.CustomerControllerList.ObserveAdd().Subscribe(x => + { + GenerateCustomer(x.Value); + }).AddTo(this); + + // 販売 + market.SellObservable.Subscribe(coin => + { + // コイン獲得エフェクト + CoinEffect(coin); + CoinManager.Instance.AddCoinWithEffect(coin, () => { }); + + blueView.SellAction(); + }).AddTo(this); + + market.SellOrderSubject.Subscribe(x => + { + // 販売アニメーション + foreach (var order in x.orders) + { + cartView.SellStock(order); + }; + + // 時間を開けて補充アニメーション + this.CallWaitForSeconds(1f, () => + { + stockView.SetStock(gameData.StorageTanks); + if (gameData.ShopStock.Count == Market.ShopStockCount) + { + // 補充したフレーバーのスキンを設定 + foreach (var order in x.orders) + { + cartView.Refill(order, market.DisplayFlavors[order]); + } + } + else if (x.isReorder) + { +#if UNITY_EDITOR + Debug.Log($"bb setStockFlag {market.DisplayFlavors.Count} {market.ShuffledOrder.Count}"); +#endif + // 陳列表示更新(陳列13=650ms,7=350ms) + var stocks = market.DisplayFlavors + .Select((flavor, index) => (flavor, index)) + .Where(data => market.ShuffledOrder.Contains(data.index)) + .Select(data => (data.index, new ProductStockData + { + FlavorId = data.flavor, + Rarity = ProductRarity.Normal, + Stock = 1 + })) + .ToList(); + cartView.SetStock(stocks, true); + } + }); + }).AddTo(this); + + // 開閉店 + CompositeDisposable shopStateCompositeDisposable = new CompositeDisposable(); + shopStateCompositeDisposable.AddTo(this); + market.CurrentShopState.Subscribe(state => + { + shopStateCompositeDisposable.Clear(); + switch (state) + { + case ShopState.Open: + pinkView.SetNormal(); + closeSign.SetActive(false); + break; + case ShopState.Busy: + break; + case ShopState.Close: + if (market.CustomerList.Count(x => x.State.Value == CustomerState.Order) == 0) + { + pinkView.SetSleepy(); + closeSign.SetActive(true); + } + else + { + // お客さんがいなくなったあと、閉店の看板を出す + Observable.CombineLatest( + market.CustomerList + .Select(x => x.State) + .Where(x => x.Value == CustomerState.Order)) + .FirstOrDefault(states => states.Count(x => x == CustomerState.Order) == 0) + .Delay(TimeSpan.FromSeconds(2f)) + .Subscribe(_ => + { + pinkView.SetSleepy(); + closeSign.SetActive(true); + }).AddTo(shopStateCompositeDisposable); + } + break; + default: + throw new ArgumentOutOfRangeException(nameof(state), state, null); + } + }).AddTo(this); + + // お客さんが少なくなったら弟が走る + market.CustomerList.ObserveCountChanged(true).AsUnitObservable() + .Merge(Observable.Interval(TimeSpan.FromSeconds(1f)).AsUnitObservable()) + .Where(_ => market.CurrentShopState.Value != ShopState.Close) + .Subscribe(_ => + { + var count = market.CustomerList.Count(c => c.State.Value == CustomerState.Wait || c.State.Value == CustomerState.Order); + if (count < pinkView.FewerBorder) + { + pinkView.SetWalk(); + } + }).AddTo(this); + } + + private void CoinEffect(int count) + { + var effect = Instantiate(coinPrefab, Vector3.zero, Quaternion.identity, rootTransform); + effect.GetComponentInChildren().text = count.ToString(); + Destroy(effect.gameObject, 1f); + } + + private void GenerateCustomer(CustomerController controller) + { + var customerObject = new GameObject(); + customerObject.transform.localPosition = controller.transform.localPosition; + // controllerが破棄のタイミングでこちらも破棄 + controller.OnDestroyAsObservable().Subscribe(_ => + { + Destroy(customerObject); + }).AddTo(customerObject); + // customer位置同期 + this.UpdateAsObservable().Subscribe(_ => + { + customerObject.transform.localPosition = controller.transform.localPosition; + }).AddTo(customerObject); + + var customerAnimator = Instantiate(controller.CustomerPrefab, customerObject.transform); + controller.CurrentMovingType.Subscribe(x => + { + customerAnimator.SetTrigger(x); + }).AddTo(customerAnimator); + controller.IsDefaultSide.Subscribe(x => + { + customerAnimator.SetSide(x); + }).AddTo(customerAnimator); + controller.WantFlavor + .SkipLatestValueOnSubscribe() + .Subscribe(data => + { + customerAnimator.SetWantFlavor(data); + }).AddTo(customerAnimator); + controller.IsComplain + .SkipLatestValueOnSubscribe() + .Subscribe(active => + { + if (active) + { + customerAnimator.ShowComplain(); + } + }).AddTo(customerAnimator); + controller.Purchased.Subscribe(_ => + { + customerAnimator.HideOrderPopup(); + var heartAnimation = customerAnimator.GetComponent(); + heartAnimation.GetHeart(HeartMeter.Instance.transform, () => + { + HeartMeter.Instance.AddHeart(1); + }); + }).AddTo(customerAnimator); + var eventTrigger = customerAnimator.gameObject.AddComponent(); + eventTrigger.OnPointerClickAsObservable() + .TakeWhile(_ => market.CurrentShopState.Value != ShopState.Close) + .Take(1) + .Subscribe(_ => + { + controller.Tapped(); + customerAnimator.ShowTapReaction(); + if (customerAnimator.TryGetComponent(typeof(Collider2D), out var target)) + { + Destroy(target); + } + }).AddTo(customerAnimator); + } +} \ No newline at end of file diff --git a/popcorn/Assets/MyGame/Scenes/marketing/Scripts/MarketManager.cs.meta b/popcorn/Assets/MyGame/Scenes/marketing/Scripts/MarketManager.cs.meta new file mode 100644 index 00000000..4c3b1b0f --- /dev/null +++ b/popcorn/Assets/MyGame/Scenes/marketing/Scripts/MarketManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2acf7c5ed16d4e3890013ca7519535d5 +timeCreated: 1633320855 \ No newline at end of file