
작업 내용 및 트러블슈팅
오늘은 유니티 프로젝트에서 터치 생명력 생산량과 업그레이드 비용 계산 방식을 개선하고, 레벨 업 시 식물 오브젝트를 순차적으로 활성화하는 작업을 진행했습니다. 또한, 업그레이드 비용과 생명력 생산량을 공식에 따라 계산하도록 수정했습니다.
작업 내용
TouchData 클래스 수정:
터치 생명력 생산량과 업그레이드 비용이 주어진 공식에 따라 계산되도록 수정했습니다.
UpgradeTouchGeneration 메서드를 통해 각 레벨 업마다 생명력 생산량과 업그레이드 비용이 적절히 증가하도록 설정했습니다.
RootBase 클래스 수정:
레벨 업 시 미리 배치된 식물 오브젝트를 순차적으로 활성화하도록 변경했습니다.
1레벨에서는 첫 번째 오브젝트가 활성화되고, 이후 25레벨마다 다음 오브젝트가 활성화되도록 로직을 구현했습니다.
트러블슈팅 :
문제 1: rootLevel이 25일 때 다음 오브젝트가 활성화되지 않는 문제
원인: ActivateNextPlantObject 메서드에서 rootLevel이 25일 때 인덱스를 계산하는 로직에 오류가 있었습니다.
해결: ActivateNextPlantObject 메서드를 수정하여 rootLevel이 1일 때와 25의 배수일 때 적절히 오브젝트가 활성화되도록 수정했습니다.
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using TMPro;
using UnityEngine;
public class TouchData : MonoBehaviour
{
public int touchIncreaseLevel = 1;
public BigInteger touchIncreaseAmount = 50; // 1레벨 터치 생명력 생산량 초기값
public BigInteger upgradeLifeCost = 1000; // 1레벨 업그레이드 비용 초기값
public TextMeshProUGUI touchLevelText;
public TextMeshProUGUI touchIncreaseText;
public TextMeshProUGUI upgradelifeCostText;
public void UpgradeTouchGeneration()
{
// 터치 생명력 생산량 공식 적용
if (touchIncreaseLevel % 25 == 0)
{
touchIncreaseAmount *= 2; // 25레벨마다 두 배로 증가
}
else
{
touchIncreaseAmount = touchIncreaseAmount * 104 / 100; // n레벨 터치 생명력 생산량 공식 적용
}
// 업그레이드 비용 공식 적용
upgradeLifeCost = upgradeLifeCost * 120 / 100; // n레벨 업그레이드 비용 공식 적용
touchIncreaseLevel++; // 레벨 증가
UpdateUI();
UIManager.Instance.tree.UpdateTreeMeshes(touchIncreaseLevel); // 나무 모습 업데이트
}
public void ApplyIncreaseRate(BigInteger rate)
{
touchIncreaseAmount *= 1 + rate;
UpdateUI();
}
public void UpdateUI()
{
UpdateTouchUI(touchIncreaseLevel, touchIncreaseAmount, upgradeLifeCost);
}
public void UpdateTouchUI(int touchIncreaseLevel, BigInteger touchIncreaseAmount, BigInteger upgradelifeCost)
{
touchLevelText.text = $"외로운 나무 레벨: {BigIntegerUtils.FormatBigInteger(touchIncreaseLevel)}";
touchIncreaseText.text = $"현재 터치당 얻는 생명력: {BigIntegerUtils.FormatBigInteger(touchIncreaseAmount)}";
upgradelifeCostText.text = $"강화 비용: {BigIntegerUtils.FormatBigInteger(upgradelifeCost)} 생명력";
}
}
using System.Collections;
using System.Numerics;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public interface IRoot
{
void ApplyIncreaseRate(BigInteger rate);
BigInteger GetTotalLifeGeneration();
void Unlock();
void ApplyTemporaryBoost(BigInteger multiplier, float duration); // 임시 부스트 메서드 추가
}
public class RootBase : MonoBehaviour, IRoot
{
public int rootLevel = 0; // 초기 레벨을 0으로 설정
public BigInteger baseLifeGeneration = 1; // 기본 생명력 생성량
public BigInteger initialUpgradeCost = 20; // 초기 레벨업 비용
public BigInteger unlockCost = 0; // 해금 비용
public BigInteger upgradeLifeCost;
public float generationInterval = 1f;
public TextMeshProUGUI rootLevelText;
public TextMeshProUGUI generationRateText; // 생산률을 나타내는 텍스트 추가
public TextMeshProUGUI rootUpgradeCostText;
public Image lockImage; // 해금 이미지
public TextMeshProUGUI lockText; // 해금 텍스트
public bool isUnlocked = false; // 잠금 상태를 나타내는 변수 추가
public GameObject[] plantObjects; // 미리 배치된 식물 오브젝트 배열 추가
private float timer;
public int unlockThreshold = 5; // 잠금 해제에 필요한 터치 레벨
public GameObject objectPrefab;
public delegate void LifeGenerated(BigInteger amount);
protected event LifeGenerated OnLifeGenerated;
public event System.Action OnGenerationRateChanged;
protected CameraTransition cameraTransition; // CameraTransition 참조 추가
private BigInteger currentMultiplier; // 현재 적용 중인 배수
private Coroutine boostCoroutine; // 부스트 코루틴 참조 변수
protected virtual void Start()
{
OnLifeGenerated -= LifeManager.Instance.IncreaseWater;
OnLifeGenerated += LifeManager.Instance.IncreaseWater;
OnGenerationRateChanged += UpdateUI; // 이벤트 핸들러 추가
OnGenerationRateChanged?.Invoke(); // 초기화 시 이벤트 트리거
UpdateUI();
cameraTransition = FindObjectOfType<CameraTransition>(); // CameraTransition 컴포넌트 참조 초기화
currentMultiplier = 1;
}
protected virtual void Update()
{
CheckUnlockCondition(); // 업데이트 시 잠금 해제 조건 확인
if (isUnlocked && rootLevel > 0)
{
timer += Time.deltaTime;
if (timer >= generationInterval)
{
GenerateLife();
timer = 0f;
}
}
}
protected virtual void GenerateLife()
{
if (!isUnlocked || rootLevel == 0) return; // 잠금 해제된 경우에만 생명력 생성
BigInteger generatedLife = GetTotalLifeGeneration(); // currentMultiplier는 이미 GetTotalLifeGeneration에 반영됨
InvokeLifeGenerated(generatedLife);
}
protected void InvokeLifeGenerated(BigInteger amount)
{
OnLifeGenerated?.Invoke(amount);
}
public BigInteger CalculateUpgradeCost()
{
if (rootLevel == 0)
{
return unlockCost;
}
else
{
return unlockCost * BigInteger.Pow(120, rootLevel) / BigInteger.Pow(100, rootLevel); // 1.2^rootLevel
}
}
public virtual void UpgradeLifeGeneration()
{
if (!isUnlocked) return; // 잠금 해제된 경우에만 업그레이드 가능
rootLevel++;
// 첫 번째 레벨에서 첫 번째 오브젝트 활성화
if (rootLevel == 1)
{
ActivateNextPlantObject();
}
// 25레벨마다 다음 오브젝트 활성화
if (rootLevel > 1 && rootLevel % 25 == 0)
{
baseLifeGeneration *= 2; // 25레벨마다 기본 생명력 생성량 두 배 증가
ActivateNextPlantObject(); // 25레벨마다 다음 식물 오브젝트 활성화
}
upgradeLifeCost = CalculateUpgradeCost();
OnGenerationRateChanged?.Invoke();
UpdateUI();
}
private void ActivateNextPlantObject()
{
if (plantObjects == null || plantObjects.Length == 0) return;
// 첫 번째 레벨일 때 첫 번째 오브젝트 활성화
if (rootLevel == 1)
{
plantObjects[0].SetActive(true);
}
else if (rootLevel > 1 && rootLevel % 25 == 0)
{
int plantIndex = rootLevel / 25; // 현재 레벨에 해당하는 인덱스 계산
if (plantIndex >= 0 && plantIndex < plantObjects.Length)
{
plantObjects[plantIndex].SetActive(true); // 해당 인덱스의 식물 오브젝트 활성화
}
}
}
public virtual void UpdateUI()
{
UpdateRootLevelUI(rootLevel, upgradeLifeCost);
UpdateGenerationRateUI(GetTotalLifeGeneration()); // 생산률 업데이트 추가
UpdateUnlockUI(); // 잠금 해제 UI 업데이트 추가
}
public virtual void ApplyIncreaseRate(BigInteger rate)
{
if (!isUnlocked) return; // 잠금 해제된 경우에만 적용 가능
baseLifeGeneration = baseLifeGeneration * (1 + rate);
OnGenerationRateChanged?.Invoke();
UpdateUI();
}
public virtual void UpdateRootLevelUI(int rootLevel, BigInteger upgradeCost)
{
if (rootLevelText != null)
{
rootLevelText.text = isUnlocked ? $"뿌리 레벨: {rootLevel}" : $"뿌리 레벨: 0";
}
if (rootUpgradeCostText != null)
{
rootUpgradeCostText.text = $"강화 비용: {BigIntegerUtils.FormatBigInteger(upgradeCost)} 물";
}
}
public virtual void UpdateGenerationRateUI(BigInteger generationRate)
{
if (generationRateText != null)
{
generationRateText.text = $"생산률: {BigIntegerUtils.FormatBigInteger(generationRate)} 물/초";
if (isUnlocked && rootLevel == 0)
{
BigInteger levelOneGenerationRate = baseLifeGeneration * BigInteger.Pow(103, 0) / BigInteger.Pow(100, 0); // 1.03^0 / 1.00^0
generationRateText.text = $"생산률: {BigIntegerUtils.FormatBigInteger(generationRate)} 물/초 \n1레벨 업그레이드시 자동생산: {BigIntegerUtils.FormatBigInteger(levelOneGenerationRate)} 물/초";
}
if (!isUnlocked && rootLevel == 0)
{
BigInteger levelOneGenerationRate = baseLifeGeneration * BigInteger.Pow(103, 0) / BigInteger.Pow(100, 0); // 1.03^0 / 1.00^0
generationRateText.text = $"생산률: {BigIntegerUtils.FormatBigInteger(generationRate)} 물/초 \n1레벨 업그레이드시 자동생산: {BigIntegerUtils.FormatBigInteger(levelOneGenerationRate)} 물/초";
}
}
}
public virtual void UpdateUnlockUI()
{
if (!isUnlocked)
{
if (lockText != null)
{
lockText.text = $"잠금 해제 조건: 세계수 레벨 {unlockThreshold}\n식물 해금 시 배치 가능 동물 수 + 5";
}
if (lockImage != null)
{
lockImage.gameObject.SetActive(true);
}
}
else
{
if (lockText != null)
{
lockText.gameObject.SetActive(false);
}
if (lockImage != null)
{
lockImage.gameObject.SetActive(false);
}
}
}
public virtual BigInteger GetTotalLifeGeneration()
{
if (!isUnlocked || rootLevel == 0) return 0; // 잠금 해제 전이나 레벨이 0일 때는 0
BigInteger baseGeneration = baseLifeGeneration * BigInteger.Pow(103, rootLevel - 1) / BigInteger.Pow(100, rootLevel - 1); // 1.03^rootLevel-1
BigInteger totalGeneration = baseGeneration * currentMultiplier; // currentMultiplier를 곱하여 반환
return totalGeneration;
}
public void Unlock()
{
isUnlocked = true;
upgradeLifeCost = CalculateUpgradeCost(); // 업그레이드 비용 업데이트
OnGenerationRateChanged?.Invoke(); // 잠금 해제 시 이벤트 트리거
DataManager.Instance.animalGenerateData.AddMaxAnimalCount();
UpdateUI();
Debug.Log("Unlocked successfully.");
}
private void CheckUnlockCondition()
{
// 잠금 해제 조건 확인 로직
if (!isUnlocked && DataManager.Instance.touchData != null
&& DataManager.Instance.touchData.touchIncreaseLevel >= unlockThreshold)
{
Unlock(); // 잠금 해제 조건 만족 시 Unlock 호출
}
}
public void ApplyTemporaryBoost(BigInteger multiplier, float duration)
{
if (boostCoroutine != null)
{
StopCoroutine(boostCoroutine);
}
boostCoroutine = StartCoroutine(TemporaryBoost(multiplier, duration));
}
private IEnumerator TemporaryBoost(BigInteger multiplier, float duration)
{
currentMultiplier = multiplier;
OnGenerationRateChanged?.Invoke(); // 부스트 시작 시 생산률 업데이트 이벤트 호출
UpdateUI(); // 부스트 시작 시 UI 업데이트
yield return new WaitForSeconds(duration);
currentMultiplier = 1; // 부스트가 끝나면 배수를 초기값으로 되돌림
OnGenerationRateChanged?.Invoke(); // 생산률 업데이트 이벤트 호출
UpdateUI(); // 부스트가 끝난 후 UI 업데이트
}
}
오늘 작업을 통해 TouchData 클래스와 RootBase 클래스의 기능을 개선하였습니다. 터치 생명력 생산량과 업그레이드 비용을 주어진 공식에 맞춰 계산하도록 수정하고, 레벨 업 시 미리 배치된 식물 오브젝트를 순차적으로 활성화하도록 로직을 구현하였습니다. 이를 통해 게임의 진행과 성장을 더욱 직관적으로 만들 수 있었습니다.