Unity camera WorldToScreenPoint guide
Unity Camera WorldToScreenPoint κ°μ΄λ
Camera.WorldToScreenPoint λ©μλ κ°μ
Camera.WorldToScreenPointλ μλ μ’ν(World Position)λ₯Ό μ€ν¬λ¦° μ’ν(Screen Position)λ‘ λ³ννλ μ λν°μ λ©μλμ λλ€. μ΄λ 3D μλ 곡κ°μ μλ νΉμ μ§μ (μ: κ²μ μ€λΈμ νΈμ μμΉ)μ μΉ΄λ©λΌμ λ·°ν¬νΈλ₯Ό κΈ°μ€μΌλ‘ 2D μ€ν¬λ¦° 곡κ°μ λμνλ μ’νλ‘ λ³νν λ μ¬μ©λ©λλ€.
λ©μλ μκ·Έλμ²
public Vector3 Camera.WorldToScreenPoint(Vector3 position);
- λ§€κ°λ³μ position: λ³νν 3D μλ μ’ν (Vector3 νμ )
- λ°νκ°: 2D μ€ν¬λ¦° μ’ν (Vector3)
- x, y: μ€ν¬λ¦° μμ ν½μ μ’ν
- z: ν΄λΉ μ§μ κΉμ§μ μΉ΄λ©λΌλ‘λΆν° κΉμ΄(거리)
μ’νκ³ μ΄ν΄νκΈ°
μλ μ’ν(World Coordinates)
- μ λν°μ μλ 곡κ°(World Space)μμμ μμΉλ₯Ό λνλ
- κΈ°μ€μ κΈλ‘λ² μμ (0,0,0)μ΄λ©° λͺ¨λ κ²μ μ€λΈμ νΈλ μ΄ κΈ°μ€μ λ°νμΌλ‘ μμΉκ° κ²°μ λ¨
- μ: (5, 10, 0)μ΄λΌλ μ’νλ μλ 곡κ°μμ (x:5, y:10, z:0) μ§μ μ λνλ
μ€ν¬λ¦° μ’ν(Screen Coordinates)
- κ²μ νλ©΄ μμ ν½μ μμΉλ₯Ό κΈ°μ€μΌλ‘ μ μ
- μ’μΈ‘ νλ¨ (0, 0), μ°μΈ‘ μλ¨μ νλ©΄μ ν΄μλμ λ°λΌ (Screen.width, Screen.height)
- μ: ν΄μλκ° 1920x1080μΈ νλ©΄μμ (960, 540)μ νλ©΄μ μ μ€μμ μλ―Έ
κΈ°λ³Έ μ¬μ© μμ
using UnityEngine;
public class WorldToScreenExample : MonoBehaviour
{
public Camera mainCamera;
public Transform targetObject;
void Update()
{
// μλ μ’νμμ μ€ν¬λ¦° μ’νλ‘ λ³ν
Vector3 screenPos = mainCamera.WorldToScreenPoint(targetObject.position);
Debug.Log($"μ€ν¬λ¦° μ’ν: {screenPos}");
}
}
- μ΄ μ€ν¬λ¦½νΈλ targetObjectμ μλ μ’νλ₯Ό μ€μκ°μΌλ‘ μ€ν¬λ¦° μ’νλ‘ λ³ννμ¬ μΆλ ₯
- μ€ν¬λ¦° μ’νλ ν΄μλμ λ°λΌ λλ΅ (Screen.width / 2, Screen.height / 2)μ κ°κΉμ΄ κ°μΌλ‘ μΆλ ₯
보좩 μ€λͺ
- λ°νλ Vector3μ zκ°μ μΉ΄λ©λΌλ‘λΆν° μλ μ’νκΉμ§μ 거리λ₯Ό λνλ΄λ©°, κ°μ΄ 0λ³΄λ€ μμΌλ©΄ ν΄λΉ μ§μ μ΄ μΉ΄λ©λΌ λ€μ μλ€λ μλ―Έ
- UI μμλ₯Ό νΉμ μλ μμΉμ λ§μΆκ³ μΆμ λ μ£Όλ‘ μ¬μ©λ¨
λ·°ν¬νΈ μ’νμ μ€ν¬λ¦° μ’νμ μ°¨μ΄
- λ·°ν¬νΈ μ’ν(Viewport Coordinates): νλ©΄ μ 체λ₯Ό (0,0) ~ (1,1) λ²μλ‘ ννν μ’ν
- μ€ν¬λ¦° μ’ν(Screen Coordinates): ν½μ λ¨μλ‘ ννν μ’ν
μ’ν λ³ν λ©μλ:
- Camera.WorldToScreenPoint = μλμ’ν β μ€ν¬λ¦°μ’νλ‘ λ³ν
- Camera.WorldToViewportPoint = μλμ’ν β λ·°ν¬νΈμ’νλ‘ λ³ν
μ€μ νμ© μμ
1. μ μ 체λ ₯ λ° νμνκΈ°
μν©
- 3D μν κ²μμμ νλ μ΄μ΄κ° μ μ μ‘°μ€ν λ μ μ 머리 μμ 체λ ₯λ° νμ
- μ μ μλ 곡κ°μ μ‘΄μ¬νμ§λ§ 체λ ₯ λ°λ 2D UI μμμ΄λ―λ‘ μ€ν¬λ¦° μ’ν κΈ°μ€μΌλ‘ νμ
ꡬν μ½λ
using UnityEngine;
using UnityEngine.UI;
public class EnemyHealthBar : MonoBehaviour
{
public Camera mainCamera;
public Transform enemyTransform;
public RectTransform healthBarUI;
void Update()
{
// μ μ μλ μ’νλ₯Ό μ€ν¬λ¦° μ’νλ‘ λ³ν
Vector3 screenPos = mainCamera.WorldToScreenPoint(enemyTransform.position + Vector3.up * 2);
// μ μ΄ μΉ΄λ©λΌμ μμ μλ κ²½μ°μλ§ μ²΄λ ₯ λ° νμ
if (screenPos.z > 0)
{
healthBarUI.gameObject.SetActive(true);
healthBarUI.position = screenPos; // 체λ ₯ λ°λ₯Ό μ€ν¬λ¦° μ’νμ λ°°μΉ
}
else
{
healthBarUI.gameObject.SetActive(false); // μ μ΄ μΉ΄λ©λΌ λ€μ μμ κ²½μ° μ¨κΉ
}
}
}
2. νμ€νΈ λ§μ»€ νμνκΈ°
μν©
- RPG κ²μμμ νλ μ΄μ΄κ° λ§΅μ ννν λ μ€μν νμ€νΈ λͺ©ν μ§μ μ νλ©΄μ μμ΄μ½μΌλ‘ νμ
- νμ€νΈ λͺ©νλ 3D μλ 곡κ°μ μμΉνμ§λ§ νλ μ΄μ΄λ νμ 2D νλ©΄μμ μμΉλ‘ νμΈ κ°λ₯ν΄μΌ ν¨
ꡬν μ½λ
using UnityEngine;
using UnityEngine.UI;
public class QuestMarker : MonoBehaviour
{
public Camera mainCamera;
public Transform questTarget;
public RectTransform questMarkerUI;
public float screenEdgeBuffer = 50f; // κ°μ₯μ리μμ λ¨μ΄μ§ 거리
void Update()
{
// νμ€νΈ λͺ©νμ μλ μ’νλ₯Ό μ€ν¬λ¦° μ’νλ‘ λ³ν
Vector3 screenPos = mainCamera.WorldToScreenPoint(questTarget.position);
// λͺ©νκ° μΉ΄λ©λΌ μμ μλ κ²½μ°
if (screenPos.z > 0)
{
screenPos.x = Mathf.Clamp(screenPos.x, screenEdgeBuffer, Screen.width - screenEdgeBuffer);
screenPos.y = Mathf.Clamp(screenPos.y, screenEdgeBuffer, Screen.height - screenEdgeBuffer);
questMarkerUI.gameObject.SetActive(true);
questMarkerUI.position = screenPos; // μμ΄μ½μ μ€ν¬λ¦° μ’νμ λ°°μΉ
}
else
{
questMarkerUI.gameObject.SetActive(false); // μΉ΄λ©λΌ λ€μ μμΌλ©΄ μ¨κΉ
}
}
}
3. MOBA κ²μμμμ νμ© (리그 μ€λΈ λ μ λ μμ)
μ€ν¬ λ²μ 미리보기 ꡬν
using UnityEngine;
public class SkillRangeIndicator : MonoBehaviour
{
public Camera mainCamera;
public Transform skillTarget; // μ€ν¬ λ²μμ λͺ©ν μ§μ
public RectTransform skillRangeUI; // μ€ν¬ λ²μ 미리보기 UI
void Update()
{
// μ€ν¬ λͺ©ν μ§μ μ μλ μ’νμμ μ€ν¬λ¦° μ’νλ‘ λ³ν
Vector3 screenPos = mainCamera.WorldToScreenPoint(skillTarget.position);
if (screenPos.z > 0) // μΉ΄λ©λΌ μμ μλ κ²½μ°μλ§ νμ
{
skillRangeUI.gameObject.SetActive(true);
skillRangeUI.position = screenPos;
}
else
{
skillRangeUI.gameObject.SetActive(false);
}
}
}
μ€μ ꡬν μμ
private void LateUpdate()
{
// νλ©΄μ targetμ΄ λ³΄μ΄μ§ μμΌλ©΄ UI μμ
if (targetTransform == null)
{
Destroy(gameObject);
return;
}
// μ€λΈμ νΈμ μλ μ’νλ₯Ό κΈ°μ€μΌλ‘ νλ©΄μμμ μ’ν κ°μ ꡬν¨
Vector3 screenPosition = Camera.main.WorldToScreenPoint(targetTransform.position);
// νλ©΄λ΄μμ μ’ν + distanceλ§νΌ λ¨μ΄μ§ μμΉλ₯Ό UIμ μμΉλ‘ μ€μ
rectTransform.position = screenPosition + distance;
}
μ½λ μ€λͺ
- LateUpdate μ¬μ© μ΄μ : μ€λΈμ νΈμ μμΉκ° Updateμμ κ°±μ λ ν UIμ μμΉλ μ΅μ’ μ μΌλ‘ λ§μΆκΈ° μν¨
- screenPosition: λμ μ€λΈμ νΈκ° νλ©΄μμ μ΄λ μμΉμ μμ΄μΌ νλμ§ ν½μ μ’νλ‘ μ μ₯
- rectTransform.position: UIλ₯Ό λμ μ€λΈμ νΈμ μ€ν¬λ¦° μ’νμμ distanceλ§νΌ λ¨μ΄μ§ μμΉμ λ°°μΉ
κ²°λ‘
Camera.WorldToScreenPointλ κ²μμμ 3D μλ 곡κ°μ μ€λΈμ νΈλ₯Ό 2D νλ©΄ μμ(UI, μμ΄μ½ λ±)μ λκΈ°νν λ νμμ μΈ λ©μλμ λλ€. 체λ ₯ λ°, νμ€νΈ λ§μ»€, μ‘°μ€μ λ± λ§μ UI μμλ₯Ό μλ μ€λΈμ νΈμ λ§μΆμ΄ νμν λ μ μ©νκ² μ¬μ©λ©λλ€. 3D μλμ μ‘΄μ¬νμ§λ§ 2Dλ‘ λ³΄μ¬μ€μΌ νλ μμκ° μλ€λ©΄ μ΄ λ©μλλ₯Ό μ κ·Ή νμ©νμκΈ° λ°λλλ€.