์ค๋์ Unity์์ ๋ฃฐ๋ ํ์์ ๋ฝ๊ธฐ ์์คํ
์ ์ง์ ๊ตฌํํด๋ดค๋ค.
์์ดํ
๋ฐ์ดํฐ๋ ScriptableObject๋ก ๊ด๋ฆฌํ๊ณ , ๋ฃฐ๋ ์นธ์ UI๋ก ์๋ ์์ฑ๋๋๋ก ํ๋ค.
๋ฃฐ๋ ์ํ ์์ ์์ดํ ์ ๋ณด๊ฐ UI๋ก ํ์๋๊ณ
๋ฒํผ์ ๋๋ฅด๋ฉด ๋ฃฐ๋ ์ด ํ์ ํ๋ฉด์ ์์ดํ ์ ๋๋ค์ผ๋ก ๋ฝ๊ณ
๊ฐ ์์ดํ ์ ์ง์ ํ ํ๋ฅ ์ ๋ฐ๋ผ ๋ฝํ๋๋ก ๊ตฌ์ฑ
์์ดํ
์ ๋ณด๋ GachaItemData๋ผ๋ ScriptableObject๋ก ๋ง๋ค๊ณ ,
์ด ์์ ์ด๋ฆ, ํ๋ฅ , ์์ด์ฝ, ์ค๋ช
์ ์ ์ฅํ๋ค.
[CreateAssetMenu(fileName = "NewGachaItem", menuName = "Gacha/Item")]
public class GachaItemData : ScriptableObject
{
public string itemName;
public float probability;
public Sprite icon;
[TextArea] public string description;
}
๊ทธ๋ฆฌ๊ณ GachaDatabase ๋ผ๋ ScriptableObject์์
์ด ์์ดํ
๋ค์ ๋ฐฐ์ด๋ก ๋ชจ์์ ๊ด๋ฆฌํ๋ค.
GachaWheelUI ์คํฌ๋ฆฝํธ์์๋ ์ํ ์์ ๊ฐ ์์ดํ
์ ๋ณด๋ฅผ ๋ด์ UI(Sector)๋ฅผ ์๋์ผ๋ก ์์ฑํ๋ค.
void CreateWheelSectors()
{
int itemCount = gachaDatabase.items.Length;
float segmentAngle = 360f / itemCount;
for (int i = 0; i < itemCount; i++)
{
var item = gachaDatabase.items[i];
GameObject sector = Instantiate(sectorPrefab, wheelParent);
RectTransform rt = sector.GetComponent<RectTransform>();
rt.anchorMin = rt.anchorMax = new Vector2(0.5f, 0.5f);
rt.pivot = new Vector2(0.5f, 0f); // ์ค์ฌ ์๋์ชฝ
rt.localPosition = Vector3.zero;
rt.localScale = Vector3.one;
float angle = -segmentAngle * i - (segmentAngle / 2f);
rt.localRotation = Quaternion.Euler(0, 0, angle);
// UI ์ค์
rt.Find("Icon").GetComponent<Image>().sprite = item.icon;
rt.Find("NameText").GetComponent<TextMeshProUGUI>().text = item.itemName;
rt.Find("ProbabilityText").GetComponent<TextMeshProUGUI>().text = $"{item.probability}%";
rt.Find("DescriptionText").GetComponent<TextMeshProUGUI>().text = item.description;
}
}
์ฌ๊ธฐ์ ์ค์ํ ๊ฑด RectTransform์ Pivot๊ณผ Anchor ์ค์ ์ด๋ค.
(0.5, 0)๋ก ์ค์ ํ๋ฉด ์ค์ฌ ๊ธฐ์ค์ผ๋ก ๋ฐ๊นฅ์ชฝ์ผ๋ก ํผ์ณ์ง๋ฏ ๋ฐฐ์น๋๋ค.
๋ฃฐ๋ ํ์ ์ GachaWheel ์คํฌ๋ฆฝํธ์์ ์ฒ๋ฆฌํ๋ค.
๋ฒํผ์ ๋๋ฅด๋ฉด ํ๋ฅ ์ ๋ฐ๋ผ ์์ดํ
์ ํ๋ ์ ํํ๊ณ ,
ํด๋น ์นธ ๋ฒ์์ ๋ง์ถฐ ๋ฃฐ๋ ์ ๋๋ฆฐ๋ค.
IEnumerator Spin()
{
isSpinning = true;
GachaItemData selectedItem = GetRandomItem();
int itemCount = gachaDatabase.items.Length;
int index = System.Array.IndexOf(gachaDatabase.items, selectedItem);
float segmentAngle = 360f / itemCount;
float minAngle = index * segmentAngle;
float maxAngle = minAngle + segmentAngle;
float targetAngle = Random.Range(minAngle, maxAngle);
float startAngle = wheel.eulerAngles.z;
float endAngle = 360f * Random.Range(5, 7) + targetAngle;
float duration = 4f;
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float t = elapsed / duration;
float eased = Mathf.SmoothStep(0, 1, t);
float angle = Mathf.Lerp(startAngle, endAngle, eased);
wheel.eulerAngles = new Vector3(0, 0, angle);
yield return null;
}
Debug.Log($"ํ๋ ์์ดํ
: {selectedItem.itemName}");
isSpinning = false;
}
์ด๋ ๋ฃฐ๋ ์ 5~7๋ฐํด + ํด๋น ์์ดํ
๊ฐ๋ ๋งํผ ํ์ ํด์
์๊ฐ์ ์ผ๋ก ๊ทธ๋ด๋ฏํ๊ฒ ๋์๊ฐ๋ค.
SmoothStep์ ์จ์ ํ์ ์ด ์์ฐ์ค๋ฝ๊ฒ ๊ฐ์๋๋ฉฐ ๋ฉ์ถ๋ค.
ํ๋ฅ ์ probability ํ๋์ ๋ฐ๋ผ ๊ฐ์ค์น ๋ฐฉ์์ผ๋ก ๊ตฌํํ๋ค.
GachaItemData GetRandomItem()
{
var items = gachaDatabase.items;
float total = items.Sum(i => i.probability);
float rand = Random.Range(0, total);
float sum = 0;
foreach (var item in items)
{
sum += item.probability;
if (rand <= sum)
return item;
}
return items.Last(); // fallback
}
RectTransform์์ Pivot, Anchor, Rotation์ด UI ๋ฐฐ์น์ ๋งค์ฐ ์ค์ํ๋ค
๋ฃฐ๋ ์ฒ๋ผ ํ์ ๊ธฐ๋ฐ UI๋ Sector ์ค์ฌ์ด ์ ํํ ๋ฐ๋๊ณผ ๋ง์์ผ ์๊ฐ์ ์ผ๋ก ์ด์ํ์ง ์์
ScriptableObject๋ฅผ ํ์ฉํ๋ฉด ์์ดํ
์ถ๊ฐ/์์ ์ด ๋งค์ฐ ์ ์ฐํด์ง๋ค
ํ๋ฅ ํ ๋ฝ๊ธฐ ์์คํ ์ ๋ง๋ค ๋๋ ๋จ์ํ ๋๋ค์ด ์๋ ํ๋ฅ ๊ฐ์ค์น ๋ก์ง์ด ํ์ํ๋ค
์์ดํ ๋ฑ๊ธ์ ๋ฐ๋ผ ์๊ฐ์ ํจ๊ณผ ๋ค๋ฅด๊ฒ ์ฃผ๊ธฐ (์ํฝ์ ๋ฒ์ฉ ๋ฒ์ฉ โจ)
๋ฝ๊ธฐ ๊ฒฐ๊ณผ ํ์ ์ฐฝ ์ถ๊ฐ (Get!)
์ฌ๋ฌ ๊ฐ ๋์์ ๋ฝ๋ 10์ฐ ๋ฝ๊ธฐ ์์คํ
๋ฃฐ๋ ์ ๋๋ฉ์ด์ ์ฐ์ถ ๋ ์์ฐ์ค๋ฝ๊ฒ (์ฌ์ด๋, ๋จ๋ฆผ ๋ฑ)
์ด๋ฒ ์์
์ ํตํด UI ๋ฐฐ์น์ ์ ๋๋ฉ์ด์
, ScriptableObject์ ์ ์ฉํจ์ ๋ค์ ํ๋ฒ ์ฒด๊ฐํ๋ค.
๋ฃฐ๋ UI๋ฅผ ์ง์ ๊ตฌ์ฑํ๋ฉด์ ๊ฐ๋ ๊ณ์ฐ๊ณผ UI ์ ๋ ฌ์ด ์ผ๋ง๋ ์ค์ํ์ง๋ ์๊ฒ ๋๋ค.
