티스토리 뷰
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();
}
}
'게임 개발 > 게임 개발 프로젝트' 카테고리의 다른 글
카드 게임 개발 일지 #02 (6) | 2024.09.07 |
---|---|
카드 게임 개발 일지 #01 (0) | 2024.09.07 |
카드 게임 개발 일지 #00 (0) | 2024.09.07 |
[팀 프로젝트] 'The Last Hunt' 개발 후기 - 03 (0) | 2024.08.23 |
[팀 프로젝트] 'The Last Hunt' 개발 후기 - 02 (0) | 2024.08.20 |