티스토리 뷰

1. Hand (손, 패)

Hand는 말 그대로 플레이어의 손을 의미합니다.

원래는 손에 들고 있는 카드를 의미하지만

List로 손에 들고 있는 Card 오브젝트를 관리하는 class입니다.

    [SerializeField] List<Card> list = new List<Card>();

인스펙터에서 확인할 수 있도록 어트리뷰트를 추가했습니다.

 

    public int[] AllIDs
    {
        get
        {
            int[] ids = new int[list.Count];

            for (int i = 0; i < ids.Length; i++)
            {
                ids[i] = list[i].ID;
            }

            return ids;
        }
    }

여차하면 카드 목록을 전달할 수 있게,

특히 카드의 ID를 전달하기 위해 int 배열 함수를 준비했습니다.

null에 대한 예외 처리가 필요할 수 있습니다.

 

    public Card this[int index]
    {
        get
        {
            if (index < 0 || index >= list.Count)
            {
                Debug.Log($"Index 범위 오류");
                return null;
            }

            return list[index];
        }
    }

요새 인덱서를 사용하는게 재미있습니다...

손 패 리스트에서 n번째 카드를 반환합니다.

하지만 [Command] 같은 메서드를 사용할 땐 Card가 아니라 int를 전달해야할 수 있으므로

public int this[int index]로 바꿔야 할 수 있겠습니다.

 


2. 카드 이동

카드를 움직이는데엔 Dotween을 사용하기로 합니다.

    [HideInInspector]
    public Vector3 targetPosition = Vector3.zero;
    [HideInInspector]
    public Quaternion targetRotation = Quaternion.identity;
    public float duration = 0.5f;

    public void DoMove(float delay = 0, Ease setEase = Ease.InOutQuint)
    {
        transform.DOKill();
        transform.DOMove(targetPosition, duration).SetEase(setEase).SetDelay(delay);
        transform.DORotateQuaternion(targetRotation, duration).SetEase(setEase).SetDelay(delay);
    }

카드의 목적지를 Vector3로 정의합니다.

카드가 새로운 목적지를 받지 않는 이상

이 값을 원래 위치로 돌아가는데 사용합니다.

Quaternion 값도 마찬지입니다.

 

public void DoMove(float delay = 0, Ease setEase = Ease.InOutQuint)

DoMove가 실행되면 카드의 transform 두트윈이 실행됩니다.

매개변수로 두트윈의 실행을 지연시키거나 원하는 EaseInOut을 설정할 수 있습니다.

기본값을 주어 생략할 수도 있습니다.

delay 매개변수를 앞에 적은 것은 보통 Ease를 생략하는 경우가 더 많기 때문입니다.  

 

public float duration = 0.5f;

트윈의 속도는 게임 전체의 속도에 영향을 줍니다. 

 

transform.DOKill();

두트윈이 중복되지 않도록 선행하고 있던 트윈을 Kill 합니다. (선행된 트윈이 있다면 말입니다.)

 

 

이제 카드가 움직이는 경우를 먼저 정의합니다.

덱에서 필드로, 아니면 누군가의 패로 이동할 수 있습니다.

패의 카드를 필드에 내거나 다른 사람의 패로 이동할 수 있습니다. 덱에 다시 넣을 수 있습니다.

필드의 카드 역시 누군가의 패나 덱으로 되돌아 갈 수 있습니다.

 

플레이어가 마우스로 조작하여 카드를 직접 옮기는 경우가 바로

패에서 필드로 낼 카드를 선택할 때입니다.

 Raycast를 사용하거나 OnMouseDrag와 IDragHandler를 사용할 수도 있습니다.

 

 

어떤 방법을 선택하든지 bool 값이 필요합니다.

패의 카드를 필드에 내는 경우에만 카드를 드래그 할 수 있어야 하기 때문입니다. 

 


3. 카드 정렬

현재 온라인 카드 게임의 대표는 단연 하스스톤이 아닌가 생각합니다.

(저는 안 합니다만...)

하스스톤에서 플레이어의 패는 정해진 범위 안에서

부채꼴 모양으로 펼쳐지게 되어 있습니다.

물론 패가 너무 많아지면 그 범위를 넘어 갈 수 있습니다만,

양 끝점으로 사용할 오브젝트를 만들고 그 Transform을 사용하여 패를 정렬합니다.

 

두 오브젝트의 위치와 회전 값에 Lerp와 Slerp를 사용하여

그 사이에 존재하는 카드를 정렬하는 것입니다.

 

x축 위치를 구하는 것은 어렵지 않습니다만

선택사항이 두 가지 존재합니다.

 

양 끝점은 Lerp의 0과 1에 해당합니다.

패의 첫 번째 카드를 0의 위치에,

패의 마지막 카드를 1의 위치에 배치할 수 있지만

각 카드들을 0과 1 사이에만 배치하는 것이다.

 

카드의 간격을 구하는 식에서 차이가 생기는데,

float interval = 1f / (list.Count + 1);

 

이 경우는 두 간격 사이에 카드를 두는 것이고

float interval = 1f / (list.Count - 1);

이 경우는 첫 번째 카드과 마지막 카드를 시작점과 끝점에 위치시키는 것이다.

 

 public void UpdateHand()
    {
        float interval = 1f / (list.Count + 1f);

        for (int i = 0; i < list.Count; i++)
        {
            CardHandler handler = list[i].GetComponent<CardHandler>();
            Vector3 targetPosition = Vector3.Lerp(left.position, right.position, interval * (i + 1));
            handler.targetPosition = targetPosition;
            handler.targetRotation = Quaternion.identity;

            if (list.Count > 6)
            {
                handler.targetPosition.y = center.position.y +
                    Mathf.Sqrt(Mathf.Pow(radius, 2) - Mathf.Pow(targetPosition.x - transform.position.x, 2));
                handler.targetRotation = Quaternion.Slerp(left.rotation, right.rotation, interval * (i + 1));
            }

            handler.DoMove();
        }
    }

 

 

 

 

public List<DoMove> list = new List<DoMove>();

public void HandAlignment()
    {
        int count = list.Count;

        float rightEndAngle = Mathf.Clamp(intervalAngle * count * .5f, 0, maxAngle);
        bool isOver = rightEndAngle == maxAngle;

        float leftEndAngle = -rightEndAngle;
        float interval = isOver ? 1f / (count - 1) : 1f / (count + 1);

        int selectedNum = -1;
        for (int i = 0; i < count; i++)
        {
            if (list[i].IsOnMouse)
            {
                selectedNum = i;
                break;
            }
        }

        for (int i = 0; i < count; i++)
        {
            list[i].SprtRend.sortingOrder = 1 + i;

            if (list[i].IsOnMouse) continue;

            float angle = Mathf.Lerp(leftEndAngle, rightEndAngle, interval * (i + (isOver ? 0 : 1)));

            float radians = Mathf.Deg2Rad * angle;

            Vector3 position = new Vector3(Mathf.Sin(radians), Mathf.Cos(radians)) * radius;
            position = transform.position + new Vector3(position.x, position.y * height, 0);

            Quaternion targetRotation;

            if (selectedNum != -1)
            {
                if (i < selectedNum)
                {
                    float t = 1f / selectedNum;
                    position.x -= 0.8f;
                    position.y += Mathf.Lerp(0, height, t * i);
                    targetRotation = Quaternion.Euler(0, 0, -Mathf.Lerp(leftEndAngle, 0, t * i) * height);
                }
                else
                {
                    float t = 1f / (count - 1 - selectedNum);
                    position.x += 0.8f;
                    position.y += Mathf.Lerp(height, 0, t * (i - selectedNum));
                    targetRotation = Quaternion.Euler(0, 0, -Mathf.Lerp(0, rightEndAngle, t * (i - selectedNum)) * height);
                }
            }
            else
            {
                targetRotation = Quaternion.Euler(0, 0, -angle * height);
            }

            list[i].SetTargetPosition(position);
            list[i].SetTargetQuaternion(targetRotation);

            list[i].DoMove();
        }
    }

}
using System;
using UnityEngine;
using UnityEngine.EventSystems;
using DG.Tweening;

public class DoMove : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
    public Vector3 targetPosition;
    public Quaternion targetRotation;

    //Selecting Layer
    public SpriteRenderer sprite;
    public bool isOnMouse = false;

    public event Action OnMouseEvent;

    private void Awake()
    {
        sprite = GetComponent<SpriteRenderer>();
    }

    public void OnPointerEnter(PointerEventData eventData)
    {
        OnEnter();
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        OnExit();
    }

    void OnEnter()
    {
        isOnMouse = true;
        //sprite.sortingLayerName = "Selecting Layer";
        //transform.localScale = Vector3.one * 1.3f;

        transform.DOKill();
        transform.DOMove(targetPosition + Vector3.up *.7f, .2f);
        transform.DORotateQuaternion(Quaternion.identity, .2f);
        OnMouseEvent?.Invoke();
    }

    void OnExit()
    {
        isOnMouse = false;
        //sprite.sortingLayerID = 0;
        //transform.localScale = Vector3.one;

        transform.DOKill();
        transform.DOMove(targetPosition, .5f);
        transform.DORotateQuaternion(targetRotation, .5f);
        OnMouseEvent?.Invoke();
    }

    public void StartTween()
    {
        transform.DOKill();
        transform.DOMove(targetPosition, .5f);
        transform.DORotateQuaternion(targetRotation, .5f);
    }

    public void Remove()
    {
        transform.DOKill();
        transform.DOMove(targetPosition + Vector3.up * 3f, .5f);
        transform.DORotateQuaternion(targetRotation, .5f).OnComplete(()=> {
            Destroy(gameObject);
        });
        enabled = false;
    }

    private void OnDestroy()
    {
        transform.DOKill();
    }
}
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함