TIL(2024,07,22)최종 프로젝트 1차구현 하나씩 만들기

김보근·2024년 7월 22일

Unity

목록 보기
45/113
post-thumbnail

Today I Learned (TIL)

작업 내용 및 트러블슈팅
오늘은 유니티 프로젝트에서 터치 생명력 생산량과 업그레이드 비용 계산 방식을 개선하고, 레벨 업 시 식물 오브젝트를 순차적으로 활성화하는 작업을 진행했습니다. 또한, 업그레이드 비용과 생명력 생산량을 공식에 따라 계산하도록 수정했습니다.

작업 내용

TouchData 클래스 수정:

터치 생명력 생산량과 업그레이드 비용이 주어진 공식에 따라 계산되도록 수정했습니다.
UpgradeTouchGeneration 메서드를 통해 각 레벨 업마다 생명력 생산량과 업그레이드 비용이 적절히 증가하도록 설정했습니다.

RootBase 클래스 수정:

레벨 업 시 미리 배치된 식물 오브젝트를 순차적으로 활성화하도록 변경했습니다.
1레벨에서는 첫 번째 오브젝트가 활성화되고, 이후 25레벨마다 다음 오브젝트가 활성화되도록 로직을 구현했습니다.

트러블슈팅 :
문제 1: rootLevel이 25일 때 다음 오브젝트가 활성화되지 않는 문제
원인: ActivateNextPlantObject 메서드에서 rootLevel이 25일 때 인덱스를 계산하는 로직에 오류가 있었습니다.
해결: ActivateNextPlantObject 메서드를 수정하여 rootLevel이 1일 때와 25의 배수일 때 적절히 오브젝트가 활성화되도록 수정했습니다.

TouchData 클래스

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)} 생명력";
    }
}

RootBase 클래스

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 클래스의 기능을 개선하였습니다. 터치 생명력 생산량과 업그레이드 비용을 주어진 공식에 맞춰 계산하도록 수정하고, 레벨 업 시 미리 배치된 식물 오브젝트를 순차적으로 활성화하도록 로직을 구현하였습니다. 이를 통해 게임의 진행과 성장을 더욱 직관적으로 만들 수 있었습니다.

profile
게임개발자꿈나무

0개의 댓글