バックグラウンド対応のためMarketのModelView分離
This commit is contained in:
parent
eb8d5cf151
commit
3a0ab744b5
|
|
@ -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<int> triggerName = new ReactiveProperty<int>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 72fc7821a0f8481495274ac394c7b620
|
||||
timeCreated: 1633316259
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class CustomerData : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private List<CustomerAnimator> normalCustomerList;
|
||||
[SerializeField] private List<CustomerAnimator> specialCustomerList;
|
||||
|
||||
public CustomerAnimator ChooseNormalPrefab()
|
||||
{
|
||||
return normalCustomerList.RandomChoose();
|
||||
}
|
||||
|
||||
public CustomerAnimator ChooseSpecialPrefab()
|
||||
{
|
||||
return specialCustomerList.RandomChoose();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: eee40f4ea08e42fab5bf3ea8810f972a
|
||||
timeCreated: 1633065710
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
public class CustomerFlow : MonoBehaviour
|
||||
{
|
||||
private IObservable<bool> customerObservable;
|
||||
private IObservable<bool> walkerObservable;
|
||||
private IObservable<bool> adWalkerObservable;
|
||||
private readonly Subject<Unit> adStartObservable = new Subject<Unit>();
|
||||
private static readonly float checkHeartInterval = 1f;
|
||||
// 歩行者の出現間隔
|
||||
private static readonly float walkerInterval = 60f / 6;
|
||||
|
||||
public IObservable<bool> Flow => walkerObservable.Merge(customerObservable, adWalkerObservable);
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
adStartObservable.AddTo(this);
|
||||
|
||||
var shopLevelList = SpreadsheetDataManager.Instance.GetBaseDataList<ShopLevelData>(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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 772e0346311a433fb714573f207813d2
|
||||
timeCreated: 1633323544
|
||||
|
|
@ -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<TextMeshProUGUI>().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>();
|
||||
heartAnimation.GetHeart(HeartMeter.Instance.transform, () =>
|
||||
{
|
||||
HeartMeter.Instance.AddHeart(1);
|
||||
});
|
||||
}).AddTo(customerAnimator);
|
||||
var eventTrigger = customerAnimator.gameObject.AddComponent<ObservableEventTrigger>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2acf7c5ed16d4e3890013ca7519535d5
|
||||
timeCreated: 1633320855
|
||||
Loading…
Reference in New Issue