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

Project_DT - 카드 사용 구현

by 라이티아 2025. 9. 30.
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;

public class Card : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler
{
    [Header("Card ID")]
    [SerializeField]
    private int _cardID = -1; // 초기값, -1인 상태로 사용되면 예외처리 핸들링
    // public int CardID => _cardID;

    private Canvas _canvas;

    /// <summary>
    /// 카드의 여러 값을 생싱시 세팅
    /// </summary>
    /// <param name="id"></param>
    public void Init(Canvas canvas, int id)
    {
        _canvas = canvas;
        _cardID = id;
        transform.GetChild(0).GetComponent<TextMeshProUGUI>().text = _cardID.ToString();
    }

    public int GetCardID()
    {
        return _cardID;
    }

    // 드래그 시작 처리
    public void OnBeginDrag(PointerEventData eventData)
    {
        Debug.Log("OnBeginDrag");


        CardArrowLineMaker.Instance.ActiveLineDrawer(true);
        CardArrowLineMaker.Instance.SetIsDragging(true);

        var cam = Camera.main;
        float depth = 0f - cam.transform.position.z;
        Vector3 sp = new Vector3(eventData.position.x, eventData.position.y, depth);
        Vector3 start = cam.ScreenToWorldPoint(sp);
        start.z = 0f;
        CardArrowLineMaker.Instance.SetStartPoint(start);
        
    }

    // 드래그 중 처리
    public void OnDrag(PointerEventData eventData)
    {
        Debug.Log("OnDrag");

        var cam = Camera.main;
        float depth = 0f - cam.transform.position.z; // 목표 z=0
        Vector3 sp = new Vector3(eventData.position.x, eventData.position.y, depth);
        Vector3 end = cam.ScreenToWorldPoint(sp);
        end.z = 0f;
        CardArrowLineMaker.Instance.SetEndPoint(end);
    }

    // 드래그 종료 처리
    public void OnEndDrag(PointerEventData eventData)
    {
        Debug.Log("OnEndDrag");
        CardArrowLineMaker.Instance.SetIsDragging(false);

        CardArrowLineMaker.Instance.ActiveLineDrawer(false);
    }
}

현재 카드 기능을 드래그해서 GUI에 표현하는 것 까지 해두었다

 

이제 카드를 적에게 드래그해서 놓는 순간을 구현한다

 

핵심은 end시 ray를 발사해서 적을 찾고, 그 적에게 어떤 행동을 한다는 매커니즘이다

 

    // 드래그 종료 처리
    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");
        }
    }

드래그를 끝내는 순간 레이를 발사해서 적 태그를 감지 후 처리한다

 

레이어로 처리해도 되는데 일단은 편한게 태그라서 태그로 처리했다

 

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

 

using UnityEngine;

public class CardManager : MonoBehaviour
{
    [Header("card area")]
    [SerializeField]
    private Transform _handArea;

    [Header("card prefab")]
    [SerializeField]
    private GameObject _card;

    [Header("draw Deck")]
    [SerializeField]
    private Deck _drawDeck;

    [Header("player object")]
    [SerializeField]
    private Player _player;


    [Header("select card")]
    [SerializeField]
    private Card _selectCard;

    [SerializeField]
    private Canvas _canvas;          // 최상위 Canvas

    void Start()
    {
        _player.OnDrawCard += InstanceCard;
    }

    /// <summary>
    /// 카드 드로우시 생성
    /// </summary>
    /// <param name="cardID"></param>
    public void InstanceCard(int cardID)
    {
        GameObject card = Instantiate(_card, _handArea);
        card.GetComponent<Card>().Init(_canvas, cardID);
    }

    /// <summary>
    /// 카드 효과 호출 함수
    /// </summary>
    /// <param name="target"></param>
    /// <param name="cardID"></param>
    public void UsedCard(Character target, int cardID)
    {
        Debug.Log($"target:{target} / card : {cardID}");
    }
}

이제 이 카드 매니저의 usedcard 함수와 연동시키면 된다

 

    public event System.Action OnUsingCard;
    
    public void UsedCard(Character target, int cardID)
    {
        Debug.Log($"target:{target} / card : {cardID}");
        OnUsingCard.Invoke();
    }

이때 델리게이트를 사용해서 연결하려 한다

맞는 방법은 모르겠지만 이번 프로젝트에서 델리게이트를 적극적으로 사용해볼 것 같다

 

....

 

반대로 해야한다

카드가 사용을 호출하면 이를 delegate로 cardmanager가 받아서 카드 효과를 사용한다

 

    // if using card, than call this delegate
    public event System.Action<int, Character> OnUsingCard;
    
        // 드래그 종료 처리
    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(_cardID, hit.collider.gameObject.GetComponent<Character>());
        }
    }

카드쪽에서 드래그를 끝낼시 검사하고 이를 델리게이트로 넘겨준다

    /// <summary>
    /// 카드 드로우시 생성
    /// </summary>
    /// <param name="cardID"></param>
    public void InstanceCard(int cardID)
    {
        GameObject card = Instantiate(_card, _handArea);
        card.GetComponent<Card>().Init(_canvas, cardID);

        card.GetComponent<Card>().OnUsingCard += UsingCard;
    }

    /// <summary>
    /// 카드 효과 호출 함수
    /// </summary>
    /// <param name="target"></param>
    /// <param name="cardID"></param>
    public void UsingCard(int cardID, Character target)
    {
        Debug.Log($"target:{target} / card : {cardID}");
    }

이를 받아서 바로 처리하게 된다

 

대리자 패턴이다

    /// <summary>
    /// 카드 효과 호출 함수
    /// </summary>
    /// <param name="target"></param>
    /// <param name="cardID"></param>
    public void UsingCard(int cardID, Character target)
    {
        Debug.Log($"target:{target} / card : {cardID}");
        // forTest
        BattleManager.Instance.ApplyDamage(target, 5);
        Debug.Log("적에게 5 데미지를 주었습니다");
    }

테스트용으로 데미지를 준다

 

잘 출력되는 것을 확인할 수 있다

 

이제 사용된 카드가 무덤으로 들어가게 처리해준다

 

    // 드래그 종료 처리
    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(_cardID, hit.collider.gameObject.GetComponent<Character>());
            Destroy(gameObject);
        }
    }

카드 사용시 카드는 사실상 GUI이기에 삭제 후

    /// <summary>
    /// 카드 효과 호출 함수
    /// </summary>
    /// <param name="target"></param>
    /// <param name="cardID"></param>
    public void UsingCard(int cardID, Character target)
    {
        Debug.Log($"target:{target} / card : {cardID}");
        // forTest
        BattleManager.Instance.ApplyDamage(target, 5);
        Debug.Log("적에게 5 데미지를 주었습니다");

        _graveyardDeck.SetCardToTop(cardID);
    }

카드 매니저에서 덱의 탑으로 올려준다