말감로그

[Unity] Valley - 식물 성장 코드 분리 및 확장성 개선 본문

TIL

[Unity] Valley - 식물 성장 코드 분리 및 확장성 개선

habbn 2024. 10. 24. 23:00
728x90

 

 

ItemData ScriptableObject를 만들어서 아이템을 확장한 것과 같이 PlantData ScriptableObject를 만들어 식물 또한 데이터 관리와 확정성을 개선해주었다.

 


마찬가지로 Inventory 클래스와 Item.cs에 PlantData를 추가해주고 초기화한다.

public class Inventory
{
    [System.Serializable]
    public class Slot
    {
        public string itemName;
        public Sprite icon;
        public int count;
        public int maxAllowed;
        public PlantData plantData;

        public Slot()
        {
            itemName = "";
            count = 0;
            maxAllowed = 99;
            plantData = null;
        }
        
        :
[RequireComponent(typeof(Rigidbody2D))]
public class Item : MonoBehaviour
{
    public ItemData itemData;
    public PlantData plantData;

    public bool canInteract;

    [HideInInspector] public Rigidbody2D rigid;

    void Awake()
    {
        rigid = GetComponent<Rigidbody2D>();
        canInteract = true;
    }
}

 

 

그리고 기존엔 Player.cs와 TileManager.cs 에서 식물 성장 코드를 작성하였는데 의존성을 위해 PlantGrowthManager.cs 를 만들어 코드를 분리해주었다.

그리고 해당 PlantData를 가져와 그 식물에 맞는 Prefab, Growth Stages Tiles, Growth Times를 설정하도록 각 타일의 좌표와 그 위치에 심어진 PlantData를 담는 딕셔너리를 생성였다.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;

public class PlantGrowthManager : MonoBehaviour
{
    private Dictionary<Vector3Int, PlantData> plantDataDict = new Dictionary<Vector3Int, PlantData>(); //PlantData 딕셔너리
    private Dictionary<Vector3Int, int> plantGrowthDays = new Dictionary<Vector3Int, int>(); // 씨앗 심은 날 저장
    private Dictionary<Vector3Int, int> currentGrowthStages = new Dictionary<Vector3Int, int>(); // 현재 성장 단계 저장

    private TileManager tileManager;
    private TimeManager timeManager;

    private void Start() 
    {
        tileManager = GameManager.instance.tileManager;
        timeManager = GameManager.instance.timeManager;

        if (timeManager != null)
        {
            timeManager.OnDayEnd += OnDayEnd;
        }
    }
    
    void OnDestroy()
    {
        if (timeManager != null)
        {
            timeManager.OnDayEnd -= OnDayEnd;
        }
    }

    public void PlantSeed(Vector3Int position, PlantData plantData)
    {
        if (tileManager.DoesTileExist(position) && tileManager.GetTileName(position) == "Plowed")
        {
            tileManager.SetTileState(position,"Seeded");
            tileManager.seedMap.SetTile(position, tileManager.plantedTile);

            plantDataDict[position] = plantData;
            plantGrowthDays[position] = 0;
            currentGrowthStages[position] = 0;
        }
    }
    
    IEnumerator GrowPlant(Vector3Int position)
    {
        if (!tileManager.DoesTileExist(position) || tileManager.GetTileState(position) == "Grown")
        {
            yield break;
        }

        PlantData plantData = plantDataDict[position];
        int currentStage = currentGrowthStages[position];
        int daysSincePlanted = plantGrowthDays[position];
        
        // 각 성장 단계별로 경과된 시간이 맞는지 확인
        if (currentStage < plantData.growthStagesTiles.Length && daysSincePlanted >= plantData.growthTimes[currentStage])
        {
            tileManager.seedMap.SetTile(position, plantData.growthStagesTiles[currentStage]);
            plantData.growthStagesTiles[currentStage].colliderType = Tile.ColliderType.Sprite;

            currentGrowthStages[position] = currentStage + 1;  // 성장 단계 1 증가

            // 모든 성장 단계를 완료했으면 "Grown" 상태로 변경
            if (currentGrowthStages[position] >=  plantData.growthStagesTiles.Length)
            {
                tileManager.SetTileState(position,"Grown");
            }
            else
            {
                tileManager.SetTileState(position,"Growing");
            }
        }
        yield return null;
    }

    public void HarvestPlant(Vector3Int position)
    {
        PlantData plantData = GetPlantData(position);

        if(plantData != null)
        {
            Vector3 spawnPosition = tileManager.interactableMap.GetCellCenterWorld(position);
            GameObject plant = Instantiate(plantData.plantPrefab, spawnPosition, Quaternion.identity);
            Debug.Log("Spawning plant prefab: " + plantData.plantName + " at " + spawnPosition);

            Rigidbody2D rb = plant.GetComponent<Rigidbody2D>();
            if (rb != null)
            {
                StartCoroutine(FloatAndLand(plant));
            }
        }
        RemovePlantData(position);
    }

    private IEnumerator FloatAndLand(GameObject plant)
    {
        float floatDuration = 0.5f;
        float landDuration = 0.5f;
        float smoothTime = 0.2f; // 부드럽게 이동할 시간
        Vector2 velocity = Vector2.zero; // 속도를 관리하기 위한 변수

        Vector2 initialPosition = plant.transform.position;
        Vector2 floatTargetPosition = initialPosition + new Vector2(0, 0.5f); // 살짝 위로 떠오를 목표 지점

        float elapsedTime = 0;

        Item interactable = plant.GetComponent<Item>();
        if (interactable != null)
            interactable.canInteract = false;

        // 위로 부드럽게 떠오르는 애니메이션
        while (elapsedTime < floatDuration)
        {
            plant.transform.position = Vector2.SmoothDamp(plant.transform.position, floatTargetPosition, ref velocity, smoothTime);
            elapsedTime += Time.deltaTime;
            yield return null; // 다음 프레임을 기다림
        }

        // 정확한 위치로 설정
        plant.transform.position = floatTargetPosition;

        // 약간 대기
        yield return new WaitForSeconds(0.1f);

        // 착지할 때 다시 속도 초기화
        velocity = Vector2.zero;
        elapsedTime = 0;

        // 아래로 부드럽게 내려오는 애니메이션
        while (elapsedTime < landDuration)
        {
            plant.transform.position = Vector2.SmoothDamp(plant.transform.position, initialPosition, ref velocity, smoothTime);
            elapsedTime += Time.deltaTime;
            yield return null; // 다음 프레임을 기다림
        }

        // 마지막으로 정확한 착지 위치로 설정
        plant.transform.position = initialPosition;

        interactable.canInteract = true;
    }

    void OnDayEnd()
    {
        // wateredTiles의 키를 미리 복사하여 List에 저장
        List<Vector3Int> wateredTilesKey = tileManager.GetWateredTilesKeys();

        foreach (var position in wateredTilesKey)
        {
            if (tileManager.GetWateringTile(position) && plantGrowthDays.ContainsKey(position))
            {
                plantGrowthDays[position]++;

                StartCoroutine(GrowPlant(position));
                tileManager.SetWateringTile(position, false);
            }

            TileBase tile = tileManager.interactableMap.GetTile(position);
            if (tile != null)
                tileManager.interactableMap.SetColor(position, Color.white);
        }
    }

    public PlantData GetPlantData(Vector3Int position)
    {
        if(plantDataDict.ContainsKey(position))
            return plantDataDict[position];
        return null;
    }

    public void RemovePlantData(Vector3Int position)
    {
        plantDataDict.Remove(position);
        currentGrowthStages.Remove(position);
        plantGrowthDays.Remove(position);
    }
}

 

728x90