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

[Unity]Project_DT - 카드 기능 수정

by 라이티아 2025. 11. 3.

현재 수정해야 하는 부분이 크게 2가지가 보인다

 

1. 카드 처리를 적에게만 하고 있다. 즉, 자신에게 처리해야 하는경우가 없다

2. 처리를 이상하게 하고 있다. damage 스크립터블을 여러개 만들어서 사용하는데, 이렇게가 아니라 spec에서 값을 가져와서 사용해야 한다

 

일단 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는 어쩔 수 없다고 생각해도, 무덤으로 가는걸 여기서 처리하는게 나은가??
        }
    }
        if (_cardSpec.targeting[0] == "Enemy" && hit.collider.gameObject.tag == "Enemy")
        {
            if (_cardSpec.targeting[1] == "single")
            {
                Debug.Log("Hit to enemy");
                OnUsingCard.Invoke(this, hit.collider.gameObject.GetComponent<Character>());
                //Destroy(gameObject); // todo : des는 어쩔 수 없다고 생각해도, 무덤으로 가는걸 여기서 처리하는게 나은가??
            }
            else if (_cardSpec.targeting[1] == "all")
            {
                //다중 공격 구현
            }
        }
        else if (_cardSpec.targeting[0] == "Player" && hit.collider.gameObject.tag == "Player")
        {
            OnUsingCard.Invoke(this, hit.collider.gameObject.GetComponent<Character>());
        }

우선 카드의 사용 조건을 좀 더 걸어서 정확한 처리를 하도록 유도한다

 

수정후 타겟팅이 잡히지 않는 문제가 발생했다

 

대소문제를 지키지 않아 생긴 문제이다

        if (_cardSpec.targeting[0] == "enemy" && hit.collider.gameObject.tag == "Enemy")
        {
            if (_cardSpec.targeting[1] == "single")
            {
                Debug.Log("Hit to enemy");
                OnUsingCard.Invoke(this, hit.collider.gameObject.GetComponent<Character>());
                //Destroy(gameObject); // todo : des는 어쩔 수 없다고 생각해도, 무덤으로 가는걸 여기서 처리하는게 나은가??
            }
            else if (_cardSpec.targeting[1] == "all")
            {
                //다중 공격 구현
            }
        }
        else if (_cardSpec.targeting[0] == "player" && hit.collider.gameObject == null)
        {
            OnUsingCard.Invoke(this, hit.collider.gameObject.GetComponent<Character>());
        }

 

맞춰서 수정해준 뒤
플레이어 사용의 경우 현재 UI로는 플레이어 오브젝트가 없기에 허공에 사용하는 것으로 처리한다

처리가 잘 되는 것을 확인할 수 있다

 

이제 다음 문제를 해결한다

 

지금 카드 처리시 이상하게 스크립터블을 사용하고 있다

이를 1개의 스크립터블을 가져와서 수치는 spec의 것을 사용하도록 처리하려 한다

        if (_cardSpec.targeting[0] == "enemy" && hit.collider.gameObject.tag == "Enemy")
        {
            if (_cardSpec.targeting[1] == "single")
            {
                Debug.Log("Hit to enemy");
                OnUsingCard.Invoke(this, hit.collider.gameObject.GetComponent<Character>());
                //Destroy(gameObject); // todo : des는 어쩔 수 없다고 생각해도, 무덤으로 가는걸 여기서 처리하는게 나은가??
            }
            else if (_cardSpec.targeting[1] == "all")
            {
                //다중 공격 구현
            }
        }

이때 card에서 invoke를 하고

    public void UsingCard(Card card, Character target)
    {
        Debug.Log($"target:{target} / card : {card.CardID}");

        foreach (var cardeffect in card.cardEffects)
        {
            cardeffect.Execute(target, card);
        }

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

카드 매니저에서 사용 효과를 처리한다

using UnityEngine;

public abstract class CardEffect : ScriptableObject
{

    // public abstract void Execute(Player source, Character target, Card card);
    
    /// <summary>
    /// 카드 효과 처리 함수
    /// </summary>
    /// <param name="source"></param>
    /// <param name="target"></param>
    /// <param name="card"></param>
    public abstract void Execute(Character target, Card card);
}

이를 호출하며

using UnityEngine;

[CreateAssetMenu(menuName = "Cards/Effects/DamageEffect")]
public class DealDamageEffect : CardEffect
{
    [SerializeField] private int damageAmount;
    public int DamageAmount
    {
        get => damageAmount;
        set => damageAmount = value;
    }

    public override void Execute(Character target, Card card)
    {
        BattleManager.Instance.ApplyDamage(target, damageAmount);
    }
}

상속받은 스크립트가 처리한다

 

이때 세부적으로 문제가 카드에서 받은 값을 그대로 처리하게 되어 있기에 이 값을 spec에서 가져와서 처리하도록 수정이 필요하다

 

    public abstract void Execute(Character target, Card card);

핵심은 여기서 가중치를 받아서 처리를 하는게 맞다는 것이다

using UnityEngine;

[CreateAssetMenu(menuName = "Cards/Effects/DamageEffect")]
public class DealDamageEffect : CardEffect
{
    // [SerializeField] private int damageAmount;
    // public int DamageAmount
    // {
    //     get => damageAmount;
    //     set => damageAmount = value;
    // }

    public override void Execute(Character target, Card card, int i)
    {
        // BattleManager.Instance.ApplyDamage(target, damageAmount);
        BattleManager.Instance.ApplyDamage(target, card.CardSpec.effectAmount[i]);
    }
}

처리가 될때 이렇게 되는것이 맞는 것이다

 

using UnityEngine;

public abstract class CardEffect : ScriptableObject
{

    // public abstract void Execute(Player source, Character target, Card card);
    
    /// <summary>
    /// 카드 효과 처리 함수
    /// </summary>
    /// <param name="source"></param>
    /// <param name="target"></param>
    /// <param name="card"></param>
    public abstract void Execute(Character target, Card card, int effectAmount, int effectHoldingTime);
}

핵심은 effect를 받는것 자체를 뒤엎는것이기에 수정을 가해준다

 

    public void UsingCard(Card card, Character target)
    {
        Debug.Log($"target:{target} / card : {card.CardID}");

        // foreach (var cardeffect in card.cardEffects)
        // {
        //     cardeffect.Execute(target, card);
        //     // StartCoroutine 로 지연처리
        // }

        for (int i = 0; i < card.cardEffects.Count; i++)
        {
            card.cardEffects[i].Execute(target, card, card.CardSpec.effectAmount[i], card.CardSpec.effectHoldingTime[i]);
        }

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

카드 사용시도 직접 spec에서 가져오도록 처리한다

 

이제 스크립터블을 가져오는 매커니즘도 수정해야 한다

 

    public void Init(Canvas canvas, int id)
    {
        _canvas = canvas;
        _cardID = id;
        transform.GetChild(0).GetComponent<TextMeshProUGUI>().text = _cardID.ToString();

        _cardSpec = CardDatabase.Instance.Get(_cardID);
        if (_cardSpec == null)
        {
            Debug.LogError($"CardSpec을 찾을 수 없습니다: {_cardID}");
        }
        _costText.text = _cardSpec.cost.ToString();
        _instructionText.text = _cardSpec.instruction.ToString();
        _nameText.text = _cardSpec.cardName.ToString();
        _idText.text = _cardSpec.id.ToString();

        for (int i = 0; i < _cardSpec.effect.Length; i++)
        {
            Debug.Log(i + "번째 effect add");
            _cardEffects.Add(CardDatabase.Instance.GetCardEffect(
                _cardSpec.effect[i], _cardSpec.effectAmount[i], _cardSpec.effectHoldingTime[i]));
        }
        // foreach (var effect in _cardEffects)
        // {
        //     Debug.Log($"Card에 등록된 Effect: {effect.name}");
        // }
        if (_cardEffects[0] == null)
        {
            Debug.Log($"{CardID} : 뭔가 잘못되었다");
        }
    }

해당 부분에서 카드 세팅을 하는데, 이를 현재 만든 매커니즘으로 맞춰서 수정해주어야 한다

 

    /// <summary>
    /// 카드 생성시 effect list를 카드에 넣기 위한 함수
    /// </summary>
    /// <param name="effectName"></param>
    /// <param name="amount"></param>
    /// <param name="holdingTime"></param>
    /// <returns></returns>
    public CardEffect GetCardEffect(string effectName)
    {
        if (effectName == "Damage")
        {
            return cardDamageEffects;
        }
        return null;
    }

데이터베이스에서 가져올때 이름만 확인하고 가져오도록 하고

        for (int i = 0; i < _cardSpec.effect.Length; i++)
        {
            Debug.Log(i + "번째 effect add");
            _cardEffects.Add(CardDatabase.Instance.GetCardEffect(_cardSpec.effect[i]));
        }

이에 맞춰서 생성한다

 

이제 effect에 대해서 하나만 있으면 처리가 가능하다

 

 

추가로 가드에 대한 기능을 구현한다

using UnityEngine;

[CreateAssetMenu(menuName = "Cards/Effects/DefenceEffect")]
public class DefenceEffect : CardEffect
{
    public override void Execute(Character target, Card card, int effectAmount, int effectHoldingTime)
    {
        BattleManager.Instance.ApplyShield(target, effectAmount);
    }
}
        if (_cardSpec.targeting[0] == "player")
        {
            OnUsingCard.Invoke(this, GameObject.FindWithTag("Player").GetComponent<Character>());
        }

그에 맞춰, 사용도 수정을 해준다

 

방어도가 반영 되는 것을 확인할 수 있다