- 레이어 설계
- Enemy 레이어 생성. 모든 적 루트 오브젝트에 지정. 콜라이더는 적 루트나 하위에 1개만 유지.
- (선택) EnemyTarget 레이어 추가. 적 머리 위 큰 히트박스(타깃 전용 콜라이더)에 지정해 드랍 성공률을 높임.
- 드래그 프리뷰와 손패 UI는 서로 다른 목적:
- 프리뷰: Ignore Raycast 레이어. 콜라이더 없음.
- 손패 UI: 기본 레이어이든 UI 레이어든 상관없음. 단, “드랍하면 취소”가 되도록 UI 판정을 받게 둠.
우선 해당 부분을 구현하려 한다


적에게 사용할 적 레이어를 정의한다
해당 레이어에 ray를 마우스 좌표에서 인식하도록 한다


ray 충돌 감지를 위해서 collider와
카메라에 physics 2d raycaster를 세팅한다
https://docs.unity3d.com/Packages/com.unity.ugui@1.0/manual/script-Physics2DRaycaster.html
2d에서 ray를 사용하려면 해당 컴포넌트가 필수로 필요하다
원래라면 projectsetting에서 ray에 맞을 layer만 세팅해야 하지만, 현재는 미완전 단계이기에 스킵하고 진행한다
using TMPro;
using UnityEngine;
public class Card : MonoBehaviour
{
[Header("Card ID")]
[SerializeField]
private int _cardID = -1; // 초기값, -1인 상태로 사용되면 예외처리 핸들링
// public int CardID => _cardID;
/// <summary>
/// 카드의 id를 세팅 = card manager에서 call해서 사용
/// </summary>
/// <param name="id"></param>
public void SetCardID(int id)
{
_cardID = id;
transform.GetChild(0).GetComponent<TextMeshProUGUI>().text = _cardID.ToString();
}
public int GetCardID()
{
return _cardID;
}
}
현재 카드 컴포넌트이다
여기에 카드가 드래그 되는 기능을 작성한다

카드 프리팹에 layout element를 추가한다
이는 부모의 layout group에서 간섭을 피하게 해준다
이제 이전 글에서 작성한 코드인 cardarrowlinemaker를 사용한다
https://nonamed02.tistory.com/191
Unity 2D 게임 개발 - 객체 A B를 잇는 화살표 그리기
즐거운 라비닝을 들으며 개발하는 무언가 현재 덱빌딩 게임을 기획하고 있는데, 그중 카드를 끌어서 원하는 목표에 움직일시 위치를 시각적으로 보여주어야 하기에 그때 사용되는 기능이다 현
nonamed02.tistory.com
현재 몇몇 부분을 자신이 관리하게 되어 있는데, 이를 카드에 넘기도록 세팅한다
현재 해당 변수값이 입력을 인식하고 있는데 이를 card가 관리하도록 스왑해준다
....
근데 지금 보니까 구조가 잘못 되어 있다
public void OnBeginDrag(PointerEventData eventData)
{
Debug.Log("OnBeginDrag");
}
// 드래그 중 처리
public void OnDrag(PointerEventData eventData)
{
Debug.Log("OnDrag");
}
// 드래그 종료 처리
public void OnEndDrag(PointerEventData eventData)
{
Debug.Log("OnEndDrag");
}
기본적으로
https://docs.unity3d.com/kr/2021.3/Manual/EventSystem.html
이벤트 시스템 - Unity 매뉴얼
이벤트 시스템은 키보드, 마우스, 터치, 커스텀 입력 등 입력 기반 애플리케이션의 오브젝트에 이벤트를 전송하는 방법입니다. 이벤트 시스템은 이벤트를 전송에 함께 작용하는 일부 컴포넌트
docs.unity3d.com
유니티 event system을 활용해서 사용해야 하는데
void Update()
{
if (Input.GetMouseButtonDown(0) && IsPointerOverUI("Card"))
{
_isDragging = true;
}
if (Input.GetMouseButtonUp(0))
{
_isDragging = false;
}
UpdateArrowEndPoint();
_middlePoint = CalculateMiddlePointVertex();
DrawLine();
}
update로 그리고 있다....
진심인가?

다시 생각을 정의한다
카드에서 시작을 정의해서 드로어에게 Call을 날리고
이를 드로어에서 ing - end를 정의한다
// 드래그 시작 처리
public void OnBeginDrag(PointerEventData eventData)
{
Debug.Log("OnBeginDrag");
PointerEventData pointerData = new PointerEventData(EventSystem.current);
pointerData.position = Input.mousePosition;
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(pointerData, results);
foreach (RaycastResult result in results)
{
if (result.gameObject == gameObject)
{
CardArrowLineMaker.Instance.SetStartPoint(gameObject.transform);
}
}
}
// 드래그 중 처리
private void OnDrag(PointerEventData eventData)
{
_targets[1] = Input.mousePosition;
}
// 드래그 종료 처리
private void OnEndDrag(PointerEventData eventData)
{
Debug.Log("OnEndDrag");
}
/// <summary>
/// set linedrawer start point == selected card
/// </summary>
public void SetStartPoint(Transform cardPoint)
{
_targets[0] = cardPoint;
}
이때 드래그중 transform은 vector로 받을 수 없기에 world값으로 변경 해주어야 한다
// 드래그 중 처리
private void OnDrag(PointerEventData eventData)
{
Vector3 worldPos;
RectTransformUtility.ScreenPointToWorldPointInRectangle(
_targets[1].GetComponent<RectTransform>(),
Input.mousePosition,
Camera.main,
out worldPos);
_targets[1].position = worldPos;
}
recttransformutility를 사용한다
// 드래그 종료 처리
public void OnEndDrag(PointerEventData eventData)
{
Debug.Log("OnEndDrag");
for (int i = 0; i < _targets.Length; i++)
{
_targets[i] = null;
}
}
드래그 종료시 시작점, 끝점을 버리도록 한다
public void SetIsDragging(bool value)
{
_isDragging = value;
}
그릴지 말지 결정하는 flag를 세팅할 수 있도록 하고
// 드래그 시작 처리
public void OnBeginDrag(PointerEventData eventData)
{
Debug.Log("OnBeginDrag");
PointerEventData pointerData = new PointerEventData(EventSystem.current);
pointerData.position = Input.mousePosition;
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(pointerData, results);
foreach (RaycastResult result in results)
{
if (result.gameObject == gameObject)
{
CardArrowLineMaker.Instance.SetIsDragging(true);
CardArrowLineMaker.Instance.SetStartPoint(gameObject.transform);
}
}
}
// 드래그 종료 처리
public void OnEndDrag(PointerEventData eventData)
{
Debug.Log("OnEndDrag");
CardArrowLineMaker.Instance.SetIsDragging(false);
}
이를 연결해 준다
GPT에게 점검을 받는다
문제 핵심 6개.
- 드래그 이벤트 수신 주체가 잘못됨
IDragHandler는 UI 오브젝트에서만 동작합니다. CardArrowLineMaker는 UI가 아니니 OnDrag가 호출되지 않습니다.
→ 드래그 이벤트는 Card에서만 처리하세요. 드로어는 Begin/UpdateEnd/End만. - _targets[1] Null 참조 위험
드로어 OnDrag에서 _targets[1].GetComponent<RectTransform>() 호출하지만 _targets[1]은 어디서도 세팅되지 않음.
→ 끝점은 Transform 쓸 필요 없습니다. Vector3로 받고 내부에만 보관하세요. - 좌표 변환 기준 RectTransform이 잘못됨
ScreenPointToWorldPointInRectangle의 첫 파라미터에 _targets[1]의 RectTransform을 넘기면 안 됩니다. 보통 Canvas의 RectTransform을 넘깁니다. - Update()에서 항상 그리기 시도
드래그 안 해도 DrawLine()을 호출합니다.
→ 드로어 내부에 _active 체크로 가드하거나, 아예 이벤트 기반으로만 그리세요. - 배열 null 초기화 코드 오작동
foreach (var target in _targets) { target = null; }는 원소가 안 바뀝니다.
→ for (int i=0;i<_targets.Length;i++) _targets[i]=null; - Card.OnBeginDrag의 RaycastAll 남용
이미 그 카드에서 콜백이 왔습니다. 결과 리스트 검사 불필요.
→ 바로 CardArrowLineMaker.Instance.Begin(startWorld) 호출.
??? event가 ui에서만 작동한다고??????
다시 책임을 찟어야 한다
using System.Collections.Generic;
using Unity.VisualScripting.Dependencies.NCalc;
using UnityEngine;
using UnityEngine.EventSystems;
public class CardDragController : MonoBehaviour, IDragHandler, IEndDragHandler
{
// 드래그 중 처리
public void OnDrag(PointerEventData eventData)
{
Vector3 worldPos;
RectTransformUtility.ScreenPointToWorldPointInRectangle(
CardArrowLineMaker.Instance.Targets[1].GetComponent<RectTransform>(),
Input.mousePosition,
Camera.main,
out worldPos);
CardArrowLineMaker.Instance.Targets[1].position = worldPos;
}
// 드래그 종료 처리
public void OnEndDrag(PointerEventData eventData)
{
Debug.Log("OnEndDrag");
for (int i = 0; i < CardArrowLineMaker.Instance.Targets.Length; i++)
{
CardArrowLineMaker.Instance.Targets[i] = null;
}
}
}
드래그를 하는 부분만 따로 UI오브젝트에 할당해 준다
문제 요약과 해결안.
- 드래그 이벤트 분산
- IDragHandler는 “포인터 다운된 그 UI 오브젝트”에만 간다. Card는 Begin/End, CardDragController는 Drag로 쪼개면 같은 오브젝트에 둘 다 붙어있지 않으면 OnDrag가 오지 않는다.
→ 한 오브젝트(Card)가 IBeginDragHandler, IDragHandler, IEndDragHandler를 모두 받게 하거나, 이벤트를 Card→매니저로 이벤트로 중계하라.
- Drawer가 내부 상태를 외부에 노출
- Targets 배열을 외부에서 직접 건드린다 → NRE·순서 오류 위험.
→ Begin(start) / UpdateEnd(end) / End() 메서드만 공개.
- 좌표 변환 대상 오류
- ScreenPointToWorldPointInRectangle의 첫 매개변수에 Targets[1]의 RT를 넣음. 보통 Canvas의 RectTransform이나 카드의 RT로 변환한다.
- 비활성 시 그리기
- _isDragging이 false여도 _targets null 접근 가능.
→ 드로어는 활성 플래그로 가드하고 LineRenderer.enabled를 on/off.
- 불필요한 UI 레이캐스트
- Card.OnBeginDrag에서 RaycastAll로 자기 자신을 다시 찾는다.
→ 이미 해당 카드에서 콜백이 왔으니 직접 Begin 호출.
... 이번에는 분산되었다고 문제점을 지적한다
using System.Collections.Generic;
using Unity.VisualScripting.Dependencies.NCalc;
using UnityEngine;
using UnityEngine.EventSystems;
public class CardDragController : MonoBehaviour, IDragHandler, IEndDragHandler
{
// 드래그 중 처리
public void OnDrag(PointerEventData eventData)
{
Debug.Log("OnDrag");
Vector3 worldPos;
RectTransformUtility.ScreenPointToWorldPointInRectangle(
CardArrowLineMaker.Instance.Targets[1].GetComponent<RectTransform>(),
Input.mousePosition,
Camera.main,
out worldPos);
CardArrowLineMaker.Instance.Targets[1].position = worldPos;
}
// 드래그 종료 처리
public void OnEndDrag(PointerEventData eventData)
{
Debug.Log("OnEndDrag");
for (int i = 0; i < CardArrowLineMaker.Instance.Targets.Length; i++)
{
CardArrowLineMaker.Instance.Targets[i] = null;
}
}
}
그래서 그걸 총괄하는 매니저를 만들었는데
이게 canvas 하위에 빈 오브젝트에 들어가는 것이다
근데....
드래그 이벤트는 반드시 UI 그래픽 오브젝트에서 시작해야 한다.
- Image, Text, Button 같은 Graphic이 있어야 EventSystem이 PointerDown을 잡는다.
- 그냥 빈 오브젝트에 RectTransform만 있으면 이벤트가 절대 안 옴.
으아아아악
무조건 그래픽 요소가 있어야 작동하는 무언가이다
으아악
그냥 카드에 넣는게 최선이라는 결과값이다
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
public class Card : MonoBehaviour, IBeginDragHandler, IEndDragHandler
{
[Header("Card ID")]
[SerializeField]
private int _cardID = -1; // 초기값, -1인 상태로 사용되면 예외처리 핸들링
// public int CardID => _cardID;
/// <summary>
/// 카드의 id를 세팅 = card manager에서 call해서 사용
/// </summary>
/// <param name="id"></param>
public void SetCardID(int id)
{
_cardID = id;
transform.GetChild(0).GetComponent<TextMeshProUGUI>().text = _cardID.ToString();
}
public int GetCardID()
{
return _cardID;
}
// 드래그 시작 처리
public void OnBeginDrag(PointerEventData eventData)
{
Debug.Log("OnBeginDrag");
PointerEventData pointerData = new PointerEventData(EventSystem.current);
pointerData.position = Input.mousePosition;
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(pointerData, results);
foreach (RaycastResult result in results)
{
if (result.gameObject == gameObject)
{
CardArrowLineMaker.Instance.SetIsDragging(true);
CardArrowLineMaker.Instance.SetStartPoint(gameObject.transform);
}
}
}
// 드래그 중 처리
public void OnDrag(PointerEventData eventData)
{
Debug.Log("OnDrag");
Vector3 worldPos;
RectTransformUtility.ScreenPointToWorldPointInRectangle(
CardArrowLineMaker.Instance.Targets[1].GetComponent<RectTransform>(),
Input.mousePosition,
Camera.main,
out worldPos);
CardArrowLineMaker.Instance.Targets[1].position = worldPos;
}
// 드래그 종료 처리
public void OnEndDrag(PointerEventData eventData)
{
Debug.Log("OnEndDrag");
CardArrowLineMaker.Instance.SetIsDragging(false);
for (int i = 0; i < CardArrowLineMaker.Instance.Targets.Length; i++)
{
CardArrowLineMaker.Instance.Targets[i] = null;
}
}
}
카드에 드래그와 관련된 모든 코드를 박아넣는다
진짜로?

일단 카드에 넣고 확인하니 라인도 안그려지고 드래그도 작동하지 않는다
뭔가 잘못 짠 매니저에 있던 코드를 그대로 박아서 생기는 문제 같다
// 드래그 시작 처리
public void OnBeginDrag(PointerEventData eventData)
{
Debug.Log("OnBeginDrag");
CardArrowLineMaker.Instance.SetIsDragging(true);
CardArrowLineMaker.Instance.SetStartPoint(gameObject.transform);
}
드래그 시작시, 애초에 그래픽을 눌러야 처리가 되니, 그냥 조건 없이 바로 처리하면 된다
// 드래그 중 처리
public void OnDrag(PointerEventData eventData)
{
Debug.Log("OnDrag");
Vector3 worldPos;
RectTransformUtility.ScreenPointToWorldPointInRectangle(
CardArrowLineMaker.Instance.Targets[1].GetComponent<RectTransform>(),
Input.mousePosition,
Camera.main,
out worldPos);
CardArrowLineMaker.Instance.Targets[1].position = worldPos;
}
RectTransform기준으로 작성해야 하는데 그냥 vec3으로 받아서 처리하고 있다
private Canvas _canvas;
public void Init(Canvas canvas)
{
_canvas = canvas;
}
recttransform에서 마우스 좌표를 가저올 수 있는 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 void OnDrag(PointerEventData eventData)
{
Debug.Log("OnDrag");
RectTransform canvasRT = _canvas.GetComponent<RectTransform>();
Camera cam = null;
Vector3 worldPos;
RectTransformUtility.ScreenPointToWorldPointInRectangle(
canvasRT, // 변환 기준 RectTransform
eventData.position, // 현재 마우스/터치 스크린 좌표
cam, // 사용할 카메라 (또는 null)
out worldPos // 변환된 월드 좌표
);
CardArrowLineMaker.Instance.SetEndPoint(worldPos);
}
이렇게 세팅한 값을 바탕으로 드래그시 마우스 좌표값을 받아서 라인을 그린다

끔찍한 null의 지옥이 벌어진다
// 드래그 종료 처리
public void OnEndDrag(PointerEventData eventData)
{
Debug.Log("OnEndDrag");
CardArrowLineMaker.Instance.SetIsDragging(false);
for (int i = 0; i < CardArrowLineMaker.Instance.Targets.Length; i++)
{
CardArrowLineMaker.Instance.Targets[i] = null;
}
}
아마 종료시 null로 만들면 안되는 것 같다
// 드래그 시작 처리
public void OnBeginDrag(PointerEventData eventData)
{
Debug.Log("OnBeginDrag");
CardArrowLineMaker.Instance.ActiveLineDrawer(true);
CardArrowLineMaker.Instance.SetIsDragging(true);
CardArrowLineMaker.Instance.SetStartPoint(gameObject.transform);
}
// 드래그 중 처리
public void OnDrag(PointerEventData eventData)
{
Debug.Log("OnDrag");
RectTransform canvasRT = _canvas.GetComponent<RectTransform>();
Camera cam = null;
Vector3 worldPos;
RectTransformUtility.ScreenPointToWorldPointInRectangle(
canvasRT, // 변환 기준 RectTransform
eventData.position, // 현재 마우스/터치 스크린 좌표
cam, // 사용할 카메라 (또는 null)
out worldPos // 변환된 월드 좌표
);
CardArrowLineMaker.Instance.SetEndPoint(worldPos);
}
// 드래그 종료 처리
public void OnEndDrag(PointerEventData eventData)
{
Debug.Log("OnEndDrag");
CardArrowLineMaker.Instance.SetIsDragging(false);
CardArrowLineMaker.Instance.ActiveLineDrawer(false);
}
대충 시작과 끝에서 컴포넌트 자체를 끄고 켜는 것으로 처리한다

드래그시 언사인 레퍼런스가 나온다

그냥 세팅은 안해준 것이였다

세팅후 확인해보니
뭔가 마우스 끝 좌표가 이상하게 그려지고 있다
이렇게 카메라를 null로 두면 ScreenSpaceOverlay 모드에서만 맞는 값이 나와.
그런데 네 씬은 **월드 좌표(LineRenderer)**와 연결돼 있어서 화면 좌표가 월드에서 말도 안 되는 위치(수만 단위)로 변환되는 거야.
해결 방법
- UI 좌표 그대로 쓰려는 경우
LineRenderer도 UI용(UILineRenderer 같은 패키지)으로 바꿔야 해.
→ 하지만 지금은 3D 라인으로 그리니까 이건 아님. - 게임 월드 좌표로 쓰려는 경우
메인 카메라 기준으로 ScreenToWorldPoint를 써야 해.
- Canvas 모드 확인
- Canvas가 Screen Space - Overlay라면: RectTransformUtility는 UI 안에서 좌표 얻는 용도. → 월드 오브젝트용으로는 안 맞음.
- Canvas가 World Space라면: 카메라 지정해줘야 정상 변환됨.
카메라 세팅의 문제라고 한다
현재 z가 -10으로 세팅되어 있는데 이것이 연산에 오류를 만들어 낸다
-10만큼 보정이 들어가야 한다
// 드래그 시작 처리
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);
}
해당 보정을 처리해 준다
잘 그려지는 것을 확인할 수 있다
'개발일지 > 게임개발' 카테고리의 다른 글
| Project_DT - 카드 database 구축 (0) | 2025.10.01 |
|---|---|
| Project_DT - 카드 사용 구현 (0) | 2025.09.30 |
| Project_DT - 카드 사용 구현 (2) | 2025.09.23 |
| Project_DT - 플레이어 드로우 기능 (0) | 2025.09.23 |
| Project_DT - 덱 기능 설계, 구현 (0) | 2025.09.23 |