본문 바로가기
개발일지/게임개발

Project_DT - 카드 database 구축

by 라이티아 2025. 10. 1.
    // 드래그 종료 처리
    public void OnEndDrag(PointerEventData eventData)
    {
        //Debug.Log("OnEndDrag");
        CardArrowLineMaker.Instance.SetIsDragging(false);

        CardArrowLineMaker.Instance.ActiveLineDrawer(false);

        // shoot ray to camera space
        Camera cam = Camera.main;
        Ray ray = cam.ScreenPointToRay(eventData.position);

        RaycastHit2D hit = Physics2D.GetRayIntersection(ray, Mathf.Infinity);
        if (hit.collider.gameObject.tag == "Enemy")
        {
            Debug.Log("Hit to enemy");
            OnUsingCard.Invoke(this, hit.collider.gameObject.GetComponent<Character>());
            //Destroy(gameObject); // todo : des는 어쩔 수 없다고 생각해도, 무덤으로 가는걸 여기서 처리하는게 나은가??
        }
    }
    /// <summary>
    /// 카드 효과 호출 함수
    /// </summary>
    /// <param name="target"></param>
    /// <param name="cardID"></param>
    public void UsingCard(Card card, Character target)
    {
        Debug.Log($"target:{target} / card : {card.CardID}");
        // forTest
        BattleManager.Instance.ApplyDamage(target, 5);
        Debug.Log("적에게 5 데미지를 주었습니다");

        card.ActiveCard(); // 카드 사용 처리
        _graveyardDeck.SetCardToTop(card.CardID);
    }

현재 드래그가 끝나면 카드 효과를 처리하도록 되어 있다

 

임시로 데미지를 넣도록 처리를 해두었는데 이를 각 가드마다 id를 통해 처리하도록 하려 한다

 

기본 골자는 유니티의 scritableobject를 사용한다

https://docs.unity3d.com/kr/2021.3/Manual/class-ScriptableObject.html

 

ScriptableObject - Unity 매뉴얼

ScriptableObject는 클래스 인스턴스와는 별도로 대량의 데이터를 저장하는 데 사용할 수 있는 데이터 컨테이너입니다. ScriptableObject의 주요 사용 사례 중 하나는 값의 사본이 생성되는 것을 방지하

docs.unity3d.com

 

현재 구상해둔 형태는

 

구글 시트에서 정의를 한 것을 기반으로 자동으로 .asset들로 만들고 이를 데이터베이스 오브젝트에서 저장 후 딕셔너리로 저장하고 이를 카드에서 호출해서 사용하는 방식으로 처리하려 한다

 

일단 구글 시트에 카드에 사용될 것 같은 것들을 정리하여 준다

 

이를 파싱해온 뒤 사용한다

using UnityEngine;

[CreateAssetMenu(menuName = "Cards/CardDefinition")]
public class CardSpec : ScriptableObject
{
    public int id;
    public string cardName;
    public int cost;
    public string type;
    public string instruction;
    public string[] targeting;
    public string rarity;
    public string discardPolicy;
}

이에 맞게 스크립터블 오브젝트를 작성을 해준다

 

좀 좋지 않은 부분이 많지만, 나중에 enum define을 통해 정리해주면 될 것 같다

 

 

    [MenuItem("CustomFunc/Card Generator/Generate Attack Cards")]
    public static void GenerateAttackCards()
    {
        string path = "Assets/GoogleSheetsCSV/Card.csv"; // CSV 경로
        if (!File.Exists(path))
        {
            Debug.LogError("CSV 파일이 존재하지 않습니다.");
            return;
        }

따로 작동시키기 위해서 menuitem을 사용한다

경로를 지정하고 만약 없을시 실행 자체를 무효로 한다

 

        string[] lines = File.ReadAllLines(path);
        for (int i = 1; i < lines.Length; i++) // 헤더 스킵
        {
            string[] parts = lines[i].Split(',');

            string id = parts[0].Trim('"');
            string name = parts[1].Trim('"');
            int cost = int.Parse(parts[2].Trim('"'));
            string type = parts[3].Trim('"');
            string instruction = parts[4].Trim('"');

            if (type != "attack") continue;
            else if (type != "attack") ;
            else if (type != "attack") ;

            // CardSpec.asset 생성
            var cardSpec = ScriptableObject.CreateInstance<CardSpec>();
            cardSpec.id = int.Parse(id);
            cardSpec.cardName = name;
            cardSpec.cost = cost;

            string assetPath = $"Assets/Cards/Card_{id}.asset";
            AssetDatabase.CreateAsset(cardSpec, assetPath);
            Debug.Log($"CardSpec 생성: {assetPath}");
        }

csv를 가져온 뒤, 1차원 배열 string에 넣어서 관리한다

for문으로 해당 배열을 순회한다

 

순회시 각 부분들을 , 단위로 분해해서 다시 parts단위로 분해 후 좌우의 "를 제거해 준다

그리고 이를 지역 변수로 저장한다

 

그후 나중에 type에 따라 처리하도록 하고 현재는 카드와 1대1 대응하는 asset을 만들어 준다

해당 값을 넣어서 처리 후 AssetDatabase를 통해 파일로 해당 asset을 저장한다

https://docs.unity3d.com/kr/2018.4/Manual/AssetDatabase.html

 

AssetDatabase - Unity 매뉴얼

AssetDatabase는 프로젝트에 포함된 에셋에 접근하게 해주는 API입니다. 특히 에셋을 찾고 로드하며 생성, 삭제, 수정하는 메서드를 제공합니다. Unity 에디터는 내부적으로 AssetDatabase를 사용해 에셋

docs.unity3d.com

        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        Debug.Log("카드 생성 완료");

모든 반복문을 넘기고 저장처리 한 뒤, 갱신을 해준다

 

바로 실행했는데 오류가 나온다

 

path를 찾지 못해서 나온 오류이다

            string assetPath = $"Assets/CardAssets/Card_{id}.asset";
            AssetDatabase.CreateAsset(cardSpec, assetPath);
            Debug.Log($"CardSpec 생성: {assetPath}");

적당히 빈 path를 만들어 처리한다

 

무언가 무언가 많이 비어있지만 일단 들어간다

 

이제 비어있는 부분들을 땜빵하자

 

string[] lines = File.ReadAllLines(path);
        for (int i = 1; i < lines.Length; i++) // 헤더 스킵
        {
            string[] parts = lines[i].Split(',');
            foreach (string word in parts)
                Debug.Log(word);
            string id = parts[0].Trim('"');
            string name = parts[1].Trim('"');
            int cost = int.Parse(parts[2].Trim('"'));

일단 나누는 부분에서 로그를 찍어 확인한다

 

잘 나오는 것을 확인할 수 있다

 

            // CardSpec.asset 생성
            var cardSpec = ScriptableObject.CreateInstance<CardSpec>();
            cardSpec.id = int.Parse(id);
            cardSpec.cardName = name;
            cardSpec.cost = cost;
            cardSpec.type = type;
            cardSpec.instruction = instruction;

그냥 밑 부분이 비어있어서 생긴 문제였다

 

채워주자

 

			string[] parts = lines[i].Split(',');
            // foreach (string word in parts)
            //     Debug.Log(word);
            string id = parts[0].Trim('"');
            string name = parts[1].Trim('"');
            string cost = parts[2].Trim('"');
            string type = parts[3].Trim('"');
            string instruction = parts[4].Trim('"');
            string[] targeting = parts[5].Trim('"').Split('/');
            string rarity = parts[6].Trim('"');
            string discardPolicy = parts[7].Trim('"');

            if (type != "attack") continue;
            else if (type != "defence") continue;
            else if (type != "effect") continue;

            // CardSpec.asset 생성
            var cardSpec = ScriptableObject.CreateInstance<CardSpec>();
            cardSpec.id = int.Parse(id);
            cardSpec.cardName = name;
            cardSpec.cost = int.Parse(cost);
            cardSpec.type = type;
            cardSpec.instruction = instruction;
            cardSpec.targeting = targeting;
            cardSpec.rarity = rarity;
            cardSpec.discardPolicy = discardPolicy;


            string assetPath = $"Assets/CardAssets/Card_{id}.asset";
            AssetDatabase.CreateAsset(cardSpec, assetPath);
            Debug.Log($"CardSpec 생성: {assetPath}");

빈 부분을 모두 채워넣었다

 

뭔가 딱 봐도  반복이 너무 많은게 보인다

 

일단은 실행한다

 

??

            string assetPath = $"Assets/CardAssets/Card_{id}.asset";
            AssetDatabase.CreateAsset(cardSpec, assetPath);
            Debug.Log($"CardSpec 생성: {assetPath}");

해당 부분이 출력이 안되면서 생성도 안된다

 

            // if (type == "attack") continue;
            // else if (type == "defence") continue;
            // else if (type == "effect") continue;

나중에 자세히 처리하려 했던 부분이 continue 처리해서 그런 것 이다

 

실행 시 잘 처리 되는것을 확인할 수 있다