미션 생성기
This commit is contained in:
@@ -32,5 +32,12 @@ public bool PayMoney(int cost)
|
||||
//예산 부족
|
||||
return false;
|
||||
}
|
||||
|
||||
// 미션 생성 시점에 예산 덮어쓰기
|
||||
public void SetBudget(int budget)
|
||||
{
|
||||
_budget = Mathf.Max(0, budget);
|
||||
OnBudgetChanged?.Invoke(_budget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,5 +11,13 @@ public class ShoppingOrderEntry
|
||||
|
||||
public ProductGroup ProductGroup => _productGroup;
|
||||
public int RequiredQuantity => _requiredQuantity;
|
||||
|
||||
public ShoppingOrderEntry() { }
|
||||
|
||||
public ShoppingOrderEntry(ProductGroup productGroup, int requiredQuantity)
|
||||
{
|
||||
_productGroup = productGroup;
|
||||
_requiredQuantity = requiredQuantity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,5 +10,12 @@ public class ShoppingOrderList : ScriptableObject
|
||||
|
||||
public IReadOnlyList<ShoppingOrderEntry> Entries => _entries;
|
||||
public int Count => _entries.Count;
|
||||
|
||||
// 런타임 생성용 (MissionGenerator 등에서 사용)
|
||||
public void SetEntries(IEnumerable<ShoppingOrderEntry> entries)
|
||||
{
|
||||
_entries.Clear();
|
||||
_entries.AddRange(entries);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
118
Assets/02_Scripts/Shopping/MissionGenerator.cs
Normal file
118
Assets/02_Scripts/Shopping/MissionGenerator.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using VRShopping.Items;
|
||||
using VRShopping.Player;
|
||||
using VRShopping.UI;
|
||||
|
||||
namespace VRShopping.Shopping
|
||||
{
|
||||
// 씬 로드 시 현재 씬의 상품 재고를 스캔해서 랜덤 쇼핑 미션과 예산을 생성한다.
|
||||
// - 미션 항목 수: [_minMissionCount, _maxMissionCount] 범위에서 랜덤 (씬 보유 그룹 수로 자동 클램프)
|
||||
// - 항목당 수량: [1, _maxQuantityPerEntry] 범위에서 랜덤 (그룹별 재고 수로 자동 클램프)
|
||||
// - 예산: 그룹별 최저가 기준 최적 비용 × (1 + Random[_budgetMarginMin, _budgetMarginMax])
|
||||
public class MissionGenerator : MonoBehaviour, ISceneInitializable
|
||||
{
|
||||
[Header("Mission Size")]
|
||||
[SerializeField, Min(1)] private int _minMissionCount = 10;
|
||||
[SerializeField, Min(1)] private int _maxMissionCount = 15;
|
||||
|
||||
[Header("Quantity Per Entry")]
|
||||
[SerializeField, Min(1)] private int _maxQuantityPerEntry = 5;
|
||||
|
||||
[Header("Budget Margin (over optimal cost)")]
|
||||
[SerializeField, Range(0f, 1f)] private float _budgetMarginMin = 0.20f;
|
||||
[SerializeField, Range(0f, 1f)] private float _budgetMarginMax = 0.30f;
|
||||
|
||||
[Header("References")]
|
||||
[SerializeField] private ShoppingOrderView _orderView;
|
||||
[SerializeField] private PlayerWallet _wallet;
|
||||
|
||||
public void OnSceneLoaded()
|
||||
{
|
||||
Generate();
|
||||
}
|
||||
|
||||
public void Generate()
|
||||
{
|
||||
if (_orderView == null || _wallet == null)
|
||||
{
|
||||
Debug.LogWarning("[MissionGenerator] 참조 누락 (orderView/wallet)");
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 씬의 모든 상품을 ProductGroup별로 집계 (None과 ItemData 누락은 제외)
|
||||
var stockByGroup = new Dictionary<ProductGroup, List<ItemData>>();
|
||||
var allItems = Object.FindObjectsByType<ItemInstance>(FindObjectsSortMode.None);
|
||||
foreach (var inst in allItems)
|
||||
{
|
||||
var data = inst.ItemDataInfo;
|
||||
if (data == null) continue;
|
||||
if (data.ProductGroup == ProductGroup.None) continue;
|
||||
|
||||
if (!stockByGroup.TryGetValue(data.ProductGroup, out var list))
|
||||
{
|
||||
list = new List<ItemData>();
|
||||
stockByGroup[data.ProductGroup] = list;
|
||||
}
|
||||
list.Add(data);
|
||||
}
|
||||
|
||||
if (stockByGroup.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("[MissionGenerator] 씬에 유효한 상품이 없습니다");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 사용 가능한 그룹 풀에서 미션 개수만큼 무작위 선택
|
||||
var groupPool = new List<ProductGroup>(stockByGroup.Keys);
|
||||
Shuffle(groupPool);
|
||||
|
||||
int desiredCount = Random.Range(_minMissionCount, _maxMissionCount + 1);
|
||||
int missionCount = Mathf.Min(desiredCount, groupPool.Count);
|
||||
|
||||
// 3. 각 그룹별로 (재고로 클램프된) 수량 부여 + 최저가 기준 최적 비용 누적
|
||||
var entries = new List<ShoppingOrderEntry>(missionCount);
|
||||
int optimalCost = 0;
|
||||
|
||||
for (int i = 0; i < missionCount; i++)
|
||||
{
|
||||
var group = groupPool[i];
|
||||
var stock = stockByGroup[group];
|
||||
|
||||
int maxQty = Mathf.Min(_maxQuantityPerEntry, stock.Count);
|
||||
int qty = Random.Range(1, maxQty + 1);
|
||||
|
||||
int cheapestPrice = int.MaxValue;
|
||||
foreach (var data in stock)
|
||||
{
|
||||
if (data.FinalPrice < cheapestPrice) cheapestPrice = data.FinalPrice;
|
||||
}
|
||||
|
||||
entries.Add(new ShoppingOrderEntry(group, qty));
|
||||
optimalCost += cheapestPrice * qty;
|
||||
}
|
||||
|
||||
// 4. 런타임 ShoppingOrderList 생성 후 뷰에 주입
|
||||
var runtimeList = ScriptableObject.CreateInstance<ShoppingOrderList>();
|
||||
runtimeList.name = "ShoppingOrderList (Runtime)";
|
||||
runtimeList.SetEntries(entries);
|
||||
_orderView.SetOrderList(runtimeList);
|
||||
|
||||
// 5. 예산 책정 (최적 비용 + 20~30% 마진)
|
||||
float margin = Random.Range(_budgetMarginMin, _budgetMarginMax);
|
||||
int budget = Mathf.CeilToInt(optimalCost * (1f + margin));
|
||||
_wallet.SetBudget(budget);
|
||||
|
||||
Debug.Log($"[MissionGenerator] 미션 {missionCount}개 / 최적가 {optimalCost:N0} / 예산 {budget:N0} (+{margin * 100f:F0}%)");
|
||||
}
|
||||
|
||||
private static void Shuffle<T>(IList<T> list)
|
||||
{
|
||||
for (int i = list.Count - 1; i > 0; i--)
|
||||
{
|
||||
int j = Random.Range(0, i + 1);
|
||||
(list[i], list[j]) = (list[j], list[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/02_Scripts/Shopping/MissionGenerator.cs.meta
Normal file
2
Assets/02_Scripts/Shopping/MissionGenerator.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: beba3b48f8e8647409d75cef1950a419
|
||||
@@ -20,6 +20,13 @@ private void Start()
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
// 런타임에 미션 목록 교체 (MissionGenerator에서 호출)
|
||||
public void SetOrderList(ShoppingOrderList orderList)
|
||||
{
|
||||
_orderList = orderList;
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
public void Rebuild()
|
||||
{
|
||||
if (_orderList == null || _rowPrefab == null || _rowContainer == null) return;
|
||||
|
||||
Reference in New Issue
Block a user