This commit is contained in:
aube.lee
2025-03-12 00:31:06 +09:00
1348 changed files with 2693 additions and 29710 deletions

View File

@@ -101,57 +101,9 @@ namespace TON
{
// 플레이어가 죽었을 때
SoundManager.instance.BgSoundPlay(null);
// 필요하다면 gameStarted = false; 등을 설정하여 한 번만 실행되게 함
}
}
private void SpawnBossMonster()
{
GameObject bossPrefab = GetBossPrefabForWave(currentWave);
if (spawnPoints.Length >= 2)
{
// 왼쪽 스폰 포인트
GameObject leftBoss = Instantiate(bossPrefab, spawnPoints[0].position, Quaternion.identity);
SetupBossComponents(leftBoss);
// 오른쪽 스폰 포인트
GameObject rightBoss = Instantiate(bossPrefab, spawnPoints[spawnPoints.Length - 1].position, Quaternion.identity);
SetupBossComponents(rightBoss);
}
else
{
Debug.LogError("스폰 포인트가 2개 이상 필요합니다.");
}
// // 랜덤한 스폰 포인트 선택
// int spawnPointIndex = Random.Range(0, spawnPoints.Length);
// GameObject bossPrefab = GetBossPrefabForWave(currentWave);
//
// GameObject boss = Instantiate(bossPrefab, spawnPoints[spawnPointIndex].position, Quaternion.identity);
// monsterPool.Add(boss);
// activeMonsters.Add(boss);
// 보스 웨이브에서는 자동으로 다음 웨이브로 넘어가지 않음
// 보스가 죽으면 Update에서 체크하여 다음 웨이브로 넘어감
}
private void SetupBossComponents(GameObject boss)
{
MonsterBase monsterBase = boss.GetComponent<MonsterBase>();
Attack attackComponent = boss.GetComponentInChildren<Attack>();
Eyesight eyesightComponent = boss.GetComponentInChildren<Eyesight>();
if (attackComponent != null)
attackComponent.SetMonsterBase(monsterBase);
if (eyesightComponent != null)
eyesightComponent.SetMonsterBase(monsterBase);
monsterPool.Add(boss);
activeMonsters.Add(boss);
}
private void StartNextWave()
{
StageManager.Singleton.SetWaveData(currentWave); // 웨이브 정보 전달.
@@ -177,7 +129,6 @@ namespace TON
if (currentWave > TOTAL_WAVES)
{
// Debug.Log("모든 웨이브 완료!");
return;
}
@@ -199,6 +150,43 @@ namespace TON
}
}
private void SpawnBossMonster()
{
GameObject bossPrefab = GetBossPrefabForWave(currentWave);
if (spawnPoints.Length >= 2)
{
// 왼쪽 스폰 포인트
GameObject leftBoss = Instantiate(bossPrefab, spawnPoints[0].position, Quaternion.identity);
SetupBossComponents(leftBoss);
// 오른쪽 스폰 포인트
GameObject rightBoss = Instantiate(bossPrefab, spawnPoints[spawnPoints.Length - 1].position, Quaternion.identity);
SetupBossComponents(rightBoss);
}
else
{
Debug.LogError("스폰 포인트가 2개 이상 필요합니다.");
}
}
private void SetupBossComponents(GameObject boss)
{
MonsterBase monsterBase = boss.GetComponent<MonsterBase>();
Attack attackComponent = boss.GetComponentInChildren<Attack>();
Eyesight eyesightComponent = boss.GetComponentInChildren<Eyesight>();
if (attackComponent != null)
attackComponent.SetMonsterBase(monsterBase);
if (eyesightComponent != null)
eyesightComponent.SetMonsterBase(monsterBase);
monsterPool.Add(boss);
activeMonsters.Add(boss);
}
private bool IsBossWave(int wave)
{
return wave == 3 || wave == 6 || wave == 9 || wave == 10;
@@ -277,7 +265,6 @@ namespace TON
}
}
}
// 일반 웨이브에서는 자동으로 다음 웨이브로 넘어가지 않음
// 몬스터가 모두 죽으면 Update에서 체크하여 다음 웨이브로 넘어감
}

View File

@@ -121,7 +121,6 @@ namespace TON
// duration 동안에는 항상 idle = 공격 쿨타운
if (_currentDuration < _duration)
return this;
// TODO : 데미지 받을 때
// 추적 범위에 들어왔을 때
if (_monsterBase.IsDetect)
@@ -186,7 +185,6 @@ namespace TON
{
_monsterBase = monsterBase;
_monsterBase.IsFinishAttack = false;
_lastAttackTime = -_attackDelayTime; // 처음 진입시 바로 공격하도록 설정
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b2481b4b29263a24ab59db8243a365bf
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,134 @@
using UnityEngine;
using UnityEngine.Events;
using System.Collections;
namespace TON
{
public class Roulette : MonoBehaviour
{
[SerializeField] private Transform piecePrefab; // 룰렛에 표시되는 정보 프리팹
[SerializeField] private Transform linePrefab; // 정보들을 구분하는 선 프리팹
[SerializeField] private Transform pieceParent; // 정보들이 배치되는 부모 Transform
[SerializeField] private Transform lineParent; // 선들이 배치되는 부모 Transform
[SerializeField] private RoulettePieceData[] roulettePieceData;
[SerializeField] private int spinDuration; // 회전 시간
[SerializeField] private Transform spinningRoulette; // 실제 회전하는 회전판 Transfrom
[SerializeField] private AnimationCurve spinningCurve; // 회전 속도 제어를 위한 그래프
private float pieceAngle; // 정보 하나가 배치되는 각도
private float halfPieceAngle; // 정보 하나가 배치되는 각도의 절반 크기
private float halfPieceAngleWithPaddings; // 선의 굴기를 고려한 padding이 포함된 절반 크기
private int accumulatedWeight; // 가중치 계산을 위한 변수
private bool isSpinning = false; // 현재 회전중인지
private int selectedIndex = 0; // 룰렛에서 선택된 아이템
private void Awake()
{
pieceAngle = 360 / roulettePieceData.Length;
halfPieceAngle = pieceAngle * 0.5f;
halfPieceAngleWithPaddings = halfPieceAngle - (halfPieceAngle * 0.25f);
SpawnPiecesAndLines();
CalculateWeightsAndIndices();
}
private void SpawnPiecesAndLines()
{
for (int i = 0; i < roulettePieceData.Length; ++i)
{
Transform piece = Instantiate(piecePrefab, pieceParent.position, Quaternion.identity, pieceParent);
// 생성한 룰렛 조각의 정보 설정(아이콘, 설명)
piece.GetComponent<RoulettePiece>().Setup(roulettePieceData[i]);
// 생성한 룰렛 조각 회전
piece.RotateAround(pieceParent.position, Vector3.back, (pieceAngle * i));
Transform line = Instantiate(linePrefab, lineParent.position, Quaternion.identity, lineParent);
// 생성한 선 회전 (룰렛 조각 사이를 구분하는 용도
line.RotateAround(lineParent.position, Vector3.back, (pieceAngle * i) + halfPieceAngle);
}
}
private void CalculateWeightsAndIndices()
{
for (int i = 0; i < roulettePieceData.Length; ++i)
{
roulettePieceData[i].index = i;
// 예외 처리, 혹시라도 chance값이 0 이하면 1로 설정
if (roulettePieceData[i].chance <= 0)
{
roulettePieceData[i].chance = 1;
}
accumulatedWeight += roulettePieceData[i].chance;
roulettePieceData[i].weight = accumulatedWeight;
Debug.Log($"({roulettePieceData[i].index}) {roulettePieceData[i].description}:{roulettePieceData[i].weight}");
}
}
private int GetRandomIndex()
{
int weight = Random.Range(0, accumulatedWeight);
for (int i = 0; i < roulettePieceData.Length; ++i)
{
if (roulettePieceData[i].weight > weight)
{
return i;
}
}
return 0;
}
public void Spin(UnityAction<RoulettePieceData> action = null)
{
if(isSpinning == true) return;
// 룰렛의 결과 값 선택
selectedIndex = GetRandomIndex();
// 선택된 결과의 중심 각도
float angle = pieceAngle * selectedIndex;
// 정확히 중심이 아닌 결과 값 범위 안의 임의의 각도 선택
float leftOffset = (angle - halfPieceAngleWithPaddings) % 360;
float rightOffset = (angle + halfPieceAngleWithPaddings) % 360;
float randomAngle = Random.Range(leftOffset, rightOffset);
// 목표 각도(targetAngle) = 결과 각도 + 360 * 회전 시간 * 회전 속도
int rotateSpeed = 2;
float targetangle = (randomAngle + 360 * spinDuration * rotateSpeed);
Debug.Log($"SelectedIndex:{selectedIndex}, angle:{angle}");
Debug.Log($"left/right/random:{leftOffset}/{rightOffset}/{randomAngle}");
Debug.Log($"targetAngle:{targetangle}");
isSpinning = true;
StartCoroutine(OnSpin(targetangle, action));
}
private IEnumerator OnSpin(float end, UnityAction<RoulettePieceData> action)
{
float current = 0;
float percent = 0;
while (percent < 1)
{
current = Time.deltaTime;
percent += current / spinDuration;
float z = Mathf.Lerp(0, end, spinningCurve.Evaluate(percent));
spinningRoulette.rotation = Quaternion.Euler(0, 0, z);
yield return null;
}
isSpinning = false;
if(action != null) action.Invoke(roulettePieceData[selectedIndex]);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9f22e98423cb35249a32007469a2c798
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace TON
{
public class RoulettePiece : MonoBehaviour
{
[SerializeField] private Image imageIcon;
[SerializeField] private TextMeshProUGUI textDescription;
public void Setup(RoulettePieceData pieceData)
{
imageIcon.sprite = pieceData.icon;
textDescription.text = pieceData.description;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ce906855e6e919547b21c723ee39fe0b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,22 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace TON
{
[System.Serializable]
public class RoulettePieceData
{
public Sprite icon; // 아이콘 이미지 파일
public string description; // 이름, 속성 등의 정보
// 3개의 아이템 등장 확률(chance)이 100, 60, 40이면
// 등장확률의 합은 200. 100/200 = 50%, 60/200 = 30%, 40/200 = 20%
[Range(1, 100)]
public int chance = 100; // 등장 확률
[HideInInspector] public int index; // 아이템 순번
[HideInInspector] public int weight; // 가중치
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 252fb732cea6501468e130b31963069d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace TON
{
public class RouletteSpin : MonoBehaviour
{
[SerializeField] private Roulette roulette;
[SerializeField] private Button buttonSpin;
private void Awake()
{
buttonSpin.onClick.AddListener(()=>
{
buttonSpin.interactable = false;
roulette.Spin(EndOfSpin);
});
}
private void EndOfSpin(RoulettePieceData selectedData)
{
buttonSpin.interactable = true;
Debug.Log($"{selectedData.index}:{selectedData.description}");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3f4d7c7eec813e4418f639e8a93b1070
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,7 +1,3 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
namespace TON
@@ -20,11 +16,8 @@ namespace TON
private PlayerDataManager playerDataManager;
// Start is called before the first frame update
void Start()
{
InitPopUpActive();
// 싱글톤으로 PlayerDataManager 접근
playerDataManager = PlayerDataManager.Singleton;
@@ -34,189 +27,65 @@ namespace TON
}
}
public void InitPopUpActive()
{
HeartPopUp.SetActive(false);
PositionPopUp.SetActive(false);
}
// Update is called once per frame
void Update()
{
}
public void OnClickLobbyButton()
{
Main.Singleton.ChangeScene(SceneType.Lobby);
}
public void OnClickHeartPopUpButton()
{
HeartPopUp.SetActive(true);
}
public void OnClickHeartCloseButton()
{
HeartPopUp.SetActive(false);
}
public void OnClickPositionPopUpButton()
// 포션 구매 메서드
private void BuyPotion(int price, string potionType, int quantity)
{
PositionPopUp.SetActive(true);
}
public void OnClickPositionCloseButton()
{
PositionPopUp.SetActive(false);
}
public void OnClickBuyHpPotion1Button()
{
if (playerDataManager.goldAmount >= hpPotionPrice1)
if (playerDataManager.goldAmount >= price)
{
playerDataManager.UseGold(hpPotionPrice1, (isSuccess) =>
playerDataManager.UseGold(price, (isSuccess) =>
{
if (isSuccess)
{
playerDataManager.userItem.hpPotion += 1;
Debug.Log($"HP 포션 구매 성공! 남은 골드: {playerDataManager.goldAmount}, HP 포션 수량: {playerDataManager.userItem.hpPotion}");
if (potionType == "hp")
{
playerDataManager.userItem.hpPotion += quantity;
}
else if (potionType == "mp")
{
playerDataManager.userItem.mpPotion += quantity;
}
UIManager.Singleton.UpdateCashData();
}
else
{
Debug.Log("골드가 부족하여 HP 포션 구매 실패!");
}
});
}
else
{
Debug.Log("골드가 부족합니다!");
}
}
// HP 포션 구매 버튼 클릭 시 호출
public void OnClickBuyHpPotion1Button()
{
BuyPotion(hpPotionPrice1, "hp", 1);
}
public void OnClickBuyHpPotion5Button()
{
if (playerDataManager.goldAmount >= hpPotionPrice5)
{
playerDataManager.UseGold(hpPotionPrice5, (isSuccess) =>
{
if (isSuccess)
{
playerDataManager.userItem.hpPotion += 5;
Debug.Log($"HP 포션 구매 성공! 남은 골드: {playerDataManager.goldAmount}, HP 포션 수량: {playerDataManager.userItem.hpPotion}");
UIManager.Singleton.UpdateCashData();
}
else
{
Debug.Log("골드가 부족하여 HP 포션 구매 실패!");
}
});
}
else
{
Debug.Log("골드가 부족합니다!");
}
BuyPotion(hpPotionPrice5, "hp", 5);
}
public void OnClickBuyHpPotion20Button()
{
if (playerDataManager.goldAmount >= hpPotionPrice20)
{
playerDataManager.UseGold(hpPotionPrice20, (isSuccess) =>
{
if (isSuccess)
{
playerDataManager.userItem.hpPotion += 20;
Debug.Log($"HP 포션 구매 성공! 남은 골드: {playerDataManager.goldAmount}, HP 포션 수량: {playerDataManager.userItem.hpPotion}");
UIManager.Singleton.UpdateCashData();
}
else
{
Debug.Log("골드가 부족하여 HP 포션 구매 실패!");
}
});
}
else
{
Debug.Log("골드가 부족합니다!");
}
BuyPotion(hpPotionPrice20, "hp", 20);
}
// MP 포션 구매 버튼 클릭 시 호출
public void OnClickBuyMpPotion1Button()
{
if (playerDataManager.goldAmount >= mpPotionPrice1)
{
playerDataManager.UseGold(mpPotionPrice1, (isSuccess) =>
{
if (isSuccess)
{
playerDataManager.userItem.mpPotion += 1;
Debug.Log($"MP 포션 구매 성공! 남은 골드: {playerDataManager.goldAmount}, MP 포션 수량: {playerDataManager.userItem.mpPotion}");
UIManager.Singleton.UpdateCashData();
}
else
{
Debug.Log("골드가 부족하여 MP 포션 구매 실패!");
}
});
}
else
{
Debug.Log("골드가 부족합니다!");
}
BuyPotion(mpPotionPrice1, "mp", 1);
}
// MP 포션 구매 버튼 클릭 시 호출
public void OnClickBuyMpPotion5Button()
{
if (playerDataManager.goldAmount >= mpPotionPrice5)
{
playerDataManager.UseGold(mpPotionPrice5, (isSuccess) =>
{
if (isSuccess)
{
playerDataManager.userItem.mpPotion += 5;
Debug.Log($"MP 포션 구매 성공! 남은 골드: {playerDataManager.goldAmount}, MP 포션 수량: {playerDataManager.userItem.mpPotion}");
UIManager.Singleton.UpdateCashData();
}
else
{
Debug.Log("골드가 부족하여 MP 포션 구매 실패!");
}
});
}
else
{
Debug.Log("골드가 부족합니다!");
}
BuyPotion(mpPotionPrice5, "mp", 5);
}
// MP 포션 구매 버튼 클릭 시 호출
public void OnClickBuyMpPotion20Button()
{
if (playerDataManager.goldAmount >= mpPotionPrice20)
{
playerDataManager.UseGold(mpPotionPrice20, (isSuccess) =>
{
if (isSuccess)
{
playerDataManager.userItem.mpPotion += 20;
Debug.Log($"MP 포션 구매 성공! 남은 골드: {playerDataManager.goldAmount}, MP 포션 수량: {playerDataManager.userItem.mpPotion}");
UIManager.Singleton.UpdateCashData();
}
else
{
Debug.Log("골드가 부족하여 MP 포션 구매 실패!");
}
});
}
else
{
Debug.Log("골드가 부족합니다!");
}
BuyPotion(mpPotionPrice20, "mp", 20);
}
}
}