2025/Unity

Unity3D - indicator 구현

rimugiri 2025. 3. 13. 19:03
728x90

에셋으로 팔지만 물리지식이 있다면 간단하게 구현 가능하고 좀더 세밀하게 조정하여 에셋에 돈을 지불하는것을 방지할 수 있다

 

2D에 z축이 추가된 사항뿐이며 UI는 camera의 far을 넘어가게 되면 보이지 않게된다 (나는 이를 고려하지는 않았다)

 

코드를 바로 보면 이해가 될거다

 

using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class Waypoint : MonoBehaviour
{
    [Header("Image Settings")]
    public Image screenOffImage;
    public Image screenOnImage;

    [Header("Transform")]
    public Transform wayPointTarget;
    public Transform playerTarget;

    [Header("Util Settings")]
    public Vector3 screenOnImageOffset;
    public TMP_Text meter;

    private float minX, maxX, minY, maxY;
    private Vector3 screenCenter;

    private void Start()
    {
        screenCenter = new Vector3(Screen.width / 2, Screen.height / 2, 0);

        float halfWidth = screenOffImage.GetPixelAdjustedRect().width / 2;
        float halfHeight = screenOffImage.GetPixelAdjustedRect().height / 2;

        minX = halfWidth;
        maxX = Screen.width - halfWidth;
        minY = halfHeight;
        maxY = Screen.height - halfHeight;
    }

    private void Update()
    {
        if (!wayPointTarget || !playerTarget || !Camera.main) return;

        // 카메라가 Waypoint를 보고 있는지 체크
        if (Vector3.Dot(wayPointTarget.position - playerTarget.position, playerTarget.forward) < 0)
        {
            screenOffImage.enabled = false;
            return;
        }

        Vector3 screenPos = Camera.main.WorldToScreenPoint(wayPointTarget.position);

        // 화면 안에 있는 경우
        if (screenPos.x >= 0 && screenPos.x <= Screen.width && screenPos.y >= 0 && screenPos.y <= Screen.height)
        {
            ScreenOn(screenPos);
        }
        else
        {
            ScreenOff(ClampToScreenBounds(screenPos));
        }
    }

    private Vector3 ClampToScreenBounds(Vector3 screenPos)
    {
        Vector3 dir = screenPos - screenCenter;
        float dirSlope = dir.y / dir.x;
        float screenSlope = screenCenter.y / screenCenter.x;

        if (Mathf.Abs(dirSlope) > screenSlope)
        {
            screenPos.y = dir.y > 0 ? maxY : minY;
            screenPos.x = screenCenter.x + (screenPos.y - screenCenter.y) / dirSlope;
        }
        else
        {
            screenPos.x = dir.x > 0 ? maxX : minX;
            screenPos.y = screenCenter.y + (screenPos.x - screenCenter.x) * dirSlope;
        }

        return new Vector3(
            Mathf.Clamp(screenPos.x, minX, maxX),
            Mathf.Clamp(screenPos.y, minY, maxY),
            screenPos.z
        );
    }

    private void ScreenOn(Vector3 position)
    {
        screenOnImage.gameObject.SetActive(true);
        meter.gameObject.SetActive(true);
        screenOffImage.gameObject.SetActive(false);

        screenOnImage.transform.position = position + screenOnImageOffset;
        meter.text = $"{Vector3.Distance(playerTarget.position, wayPointTarget.position):F1}m";
    }

    private void ScreenOff(Vector3 position)
    {
        screenOnImage.gameObject.SetActive(false);
        meter.gameObject.SetActive(false);
        screenOffImage.gameObject.SetActive(true);

        screenOffImage.transform.position = position;

        Vector3 direction = position - screenCenter;
        screenOffImage.transform.rotation = Quaternion.Euler(0, 0, Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg);
    }
}

 

 

 

728x90