인디케이터(Indicator)는 화면밖에 있는 적을 표시하기 위해 사용됨
OffScreen Indicator라고도 함
예시는 아래와 같음
1. 구현방법 생각
- 각도 구해서 인디케이터 방향 설정하기
- Camera.main.WorldToViewPort과 닮은 꼴을 이용하여 위치 설정하기
- 어디서 인디케이터를 생성하고 꺼줄지 생각하기
2. 구현
- 각도 구해서 인디케이터 방향 설정하기
먼저 defaultAngle을 구해서 위쪽, 아래쪽, 오른쪽, 왼쪽에 대한 연산을 하면 됨
편의상 dA라고 칭하겠음
위쪽 : -dA ~ dA
오른쪽 : dA ~ 180 - dA
왼쪽 : -180 + dA ~ -dA
아래쪽 : -180 ~ -180 + dA 또는 180 - dA ~ 180
그래서 defualtAngle을 구하는 코드
위쪽 벡터랑(0,1) Screen폭에 해당하는 벡터와 각도를 구하여 dA를 구함
또한 적과 플레이어 간의 각도를 구해야 함
-> 그래야 어디 부분인지 알 수 있음
Vector2.Angle() 이 양수만 반환함
부호는 player의 x보다 왼쪽에 있으면 -, player의 x보다 오른쪽이면 +
위의 각도를 아래와 같이 이용할 것
- Camera.main.WorldToViewPort과 닮은 꼴을 이용하여 위치 설정하기
위의 삼각형을 닮은 꼴을 이용하여 y'의 위치를 넣어야 함
x : y = x' : y' 이므로 y' = yx'/x 임
여기서 x'는 임의의 고정된 값이므로 y'을 구할 수 있는 것
근데 여기서 좌표계의 생각 혹은 비율에 대한 생각이 필요
만약 x,y를 World좌표계로 사용하고 x', y'를 화면 비율로 생각하여 통일 시키지 않으면 큰 오산
위의 게임 화면은 1920x1080으로 정사각형이 아님
그래서 만약 x, y가 World좌표계고 x', y'가 화면에 대한 비율계이며
x = 540 이고 y = 270 이며 x'가 0.5라고해서 닮은 꼴에 의해 y'가 0.25가 아님
역으로 생각하면 x' = 0.5이고 y'가 0.25일 때 화면 비율에 대한 값이므로
x = 540이고 y = 480임
이러면 오차가 발생하므로 x,y,x',y'을 하나의 좌표계로 통일 시켜줘야함
>>> Screen에 대한 비율로 x,y,x',y'하면 됨
그래서 Camera.main.WorldToViewPort이용
이러면 x, y 둘 다 화면에 대한 비율임
0.5를 빼준 이유는 화면 왼쪽하단이 0, 0이라서 그럼 (중심이동)
"left" 상태일 때 if문임
"up", "right", "down"도 거의 동일과정임
- 어디서 인디케이터를 생성하고 꺼줄지 생각하기
1) 적의 인디케이터 이므로 Enemy 스크립트에서 모두 제어함
2) 인디케이터는 화면밖에 있을 때만 활성화 하므로 화면안에서는 꺼줌
3) Enemy가 생성되자마자 인디케이터를 생성함
4) 인디케이터는 UI부분이므로 생성 후 canvas의 자식으로 넣어줌
5) 인디케이터는 매 프레임 움직이므로 Update문에 함수를 실행시킴
3. 코드
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Enemy : MonoBehaviour
{
private Rigidbody2D rigid;
public GameObject indicator;
public GameObject indicatorCanvas;
public GameObject player;
private GameObject instance;
private float defaultAngle;
private void Start()
{
rigid = GetComponent<Rigidbody2D>();
instance = Instantiate(indicator);
instance.transform.SetParent(indicatorCanvas.transform);
instance.transform.localScale = new Vector3(1, 1, 1);
Vector2 dir = new Vector2(Screen.width, Screen.height);
defaultAngle = Vector2.Angle(new Vector2(0, 1), dir);
}
void Update()
{
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
rigid.velocity = new Vector3(horizontal, vertical, 0) * 5.0f;
SetIndicator();
}
public void SetIndicator()
{
if (!isOffScreen()) return;
float angle = Vector2.Angle(new Vector2(0, 1), transform.position - player.transform.position);
int sign = player.transform.position.x > transform.position.x ? -1 : 1;
angle *= sign;
Vector3 target = Camera.main.WorldToViewportPoint(transform.position);
float x = target.x - 0.5f;
float y = target.y - 0.5f;
RectTransform indicatorRect = instance.GetComponent<RectTransform>();
if (-defaultAngle <= angle && angle <= defaultAngle)
{
Debug.Log("up");
//anchor minY, maxY 0.96
float anchorMinMaxY = 0.96f;
float anchorMinMaxX = x * (anchorMinMaxY-0.5f) / y + 0.5f;
if (anchorMinMaxX >= 0.94f) anchorMinMaxX = 0.94f;
else if (anchorMinMaxX <= 0.06f) anchorMinMaxX = 0.06f;
indicatorRect.anchorMin = new Vector2(anchorMinMaxX, anchorMinMaxY);
indicatorRect.anchorMax = new Vector2(anchorMinMaxX, anchorMinMaxY);
}
else if (defaultAngle <= angle && angle <= 180 - defaultAngle)
{
Debug.Log("right");
//anchor minX, maxX 0.94
float anchorMinMaxX = 0.94f;
float anchorMinMaxY = y * (anchorMinMaxX - 0.5f) / x + 0.5f;
if (anchorMinMaxY >= 0.96f) anchorMinMaxY = 0.96f;
else if (anchorMinMaxY <= 0.04f) anchorMinMaxY = 0.04f;
indicatorRect.anchorMin = new Vector2(anchorMinMaxX, anchorMinMaxY);
indicatorRect.anchorMax = new Vector2(anchorMinMaxX, anchorMinMaxY);
}
else if (-180 + defaultAngle <= angle && angle <= -defaultAngle)
{
Debug.Log("left");
//anchor minX, maxX 0.06
float anchorMinMaxX = 0.06f;
float anchorMinMaxY = ( y * (anchorMinMaxX - 0.5f) / x ) + 0.5f;
if (anchorMinMaxY >= 0.96f) anchorMinMaxY = 0.96f;
else if (anchorMinMaxY <= 0.04f) anchorMinMaxY = 0.04f;
indicatorRect.anchorMin = new Vector2(anchorMinMaxX, anchorMinMaxY);
indicatorRect.anchorMax = new Vector2(anchorMinMaxX, anchorMinMaxY);
}
else if(-180 <= angle && angle <= -180 + defaultAngle || 180 - defaultAngle <=angle && angle <= 180)
{
Debug.Log("down");
//anchor minY, maxY 0.04
float anchorMinMaxY = 0.04f;
float anchorMinMaxX = x * (anchorMinMaxY - 0.5f) / y + 0.5f;
if (anchorMinMaxX >= 0.94f) anchorMinMaxX = 0.94f;
else if (anchorMinMaxX <= 0.06f) anchorMinMaxX = 0.06f;
indicatorRect.anchorMin = new Vector2(anchorMinMaxX, anchorMinMaxY);
indicatorRect.anchorMax = new Vector2(anchorMinMaxX, anchorMinMaxY);
}
indicatorRect.anchoredPosition = new Vector3(0, 0, 0);
}
private bool isOffScreen()
{
Vector2 vec = Camera.main.WorldToViewportPoint(transform.position);
if (vec.x >= 0 && vec.x <= 1 && vec.y >= 0 && vec.y <= 1)
{
instance.SetActive(false);
return false;
}
else
{
instance.SetActive(true);
return true;
}
}
}
|
cs |
=== 참고 ===
Hierarchy 구성
cf) Indicator Canvas는 렌더모드를 Scale Space - Camera로 구성 + 메인카메라 넣어줌
'Unity' 카테고리의 다른 글
Unity - Pause 버튼 구현하기(Time.scaleTime) (3) | 2020.02.24 |
---|---|
Unity - 블랙홀(Point Effector 2D) (1) | 2020.02.20 |
Unity - 총알 구현하기(Bullet) (0) | 2020.02.17 |
Unity - 스폰(Spawn) 구현하기 (0) | 2020.02.17 |
Unity - 조이스틱(Joystick) (0) | 2020.02.15 |