'Unity3D'에 해당되는 글 52건
- 2018.03.14 [Unity3D] 헥사게임 만들기 네 번째 - 마우스 처리2
- 2018.03.09 [Unity3D] 헥사게임 만들기 네 번째 - 마우스 처리. 2
- 2018.02.24 [Unity3D] 헥사게임 만들기 세 번째 - 블럭처리2. 2
- 2018.02.18 [Unity3D] 헥사게임 만들기 세 번째 - 블럭 처리.
- 2018.02.13 [Unity3D] 헥사게임 만들기 두 번째- 블럭보드 만들기2
- 2018.02.13 [Unity3D] 헥사게임 만들기 두 번째 - 블럭보드 만들기.
- 2018.02.11 [Unity3D] 헥사게임 만들기 첫 번째 - 리소스 준비하기 및 캔버스 크기 설정.
- 2017.05.09 서드파티 네트워크 게임 엔진.
- 2017.05.08 네트워크 (NetWork)
- 2017.05.03 [Unity3D] Canvas
[Unity3D] 헥사게임 만들기 네 번째 - 마우스 처리2
※ 소스 출처 : 네이버 카페 - 유니티 허브 - 햐얀바람님.
0. 리소스 준비하기.
1). 배경 이미지.
2). 블럭보드의 배경 이미지.
3). UI테두리 이미지.
4). 블럭 이미지.
1. 캔버스 크기 설정하기.
- 게임뷰어 및 캔버스 크기 설정.
2. 블럭보드 만들기.
1). 블럭 프리팹 만들기.
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
|
private float fMoveSpeed = 5f; // 블럭의 이동 속력.
private float fBlockMoveStep = 0f;
// 마우스로 블럭이동 처리.
void DragToMoveBlock()
{
if (!bBlockChange)
return;
if (TargetBlock.transform.position != StartPos1)
{
fBlockMoveStep += fMoveSpeed * Time.deltaTime;
SelectBlock.transform.position = Vector3.MoveTowards(StartPos1, EndPos1, fBlockMoveStep);
TargetBlock.transform.position = Vector3.MoveTowards(StartPos2, EndPos2, fBlockMoveStep);
}
else
{
// 이동 완료
bBlockChange = false;
fBlockMoveStep = 0f;
// 블럭 교환.
SwitchBoard();
// 블럭 매치 시작.
StartCoroutine(BlockCheck());
}
}
|
cs |
별거 없습니다.
DragToMoveBlock()함수는
bBlockChange 플래그가 활성화(true)되지 않으면 수행하지 않고 곧 바로 종료해 버립니다.
10라인의 if문은
전 글의 밑의 이미지를 보셨다면 대충 감이 오실겁니다.
TargetBlock은 마우스가 이동한 방향에 있던 블럭 객체 입니다.
그림으로 보자면
본 이미지의 파란색 동그라미에 존재하는 블럭이 TargetBlock이라고 생각하면 됩니다.
단순한 이미지로 표현하자면
노란색 박스는 현재 마우스로 선택한 박스고
초록색 박스는 현재 마우스로 드래그 방향에 있던 박스입니다.
이 초록색 박스가 TargetBlock입니다.
전 글부터 게속 똑같은 내용을 강조하는 이유는
헷갈리면 안돼는 부분이기 때문입니다.
교환이 시작되면 위 이미지를 기준으로
노란색 박스는 초록색 박스의 위치로
초록색 박스는 노란색 박스의 위치로 이동해야 합니다.
10라인의 if문에서 StartPos1는 전 글을 잘보셨던 분이시라면 자신이 선택한 블럭
즉, 노란색 박스의 위치라는걸 아실 겁니다.
각설하고 10라인 if문을 주석을 달아보자면
'초록색 박스의 위치가 노란색 박스의 위치와 같지 않으면'. 정도가 되겠습니다.
'초록색 박스의 위치가 노란색 박스의 위치와 같을 때까지' 라고 생각해도 좋습니다.
여기서 한 가지 의문을 느낄수도 있는데,
초록색 박스가 노란색 박스의 위치로 움직여야 한다면
노란색 박스도 초록색 박스의 위치로 움직여야 하는 구문이 있어야 하는거 아닌가?
라는 의문을 가질 수 있습니다.
하지만 13라인, 14라인을 보면 그런 의문은 지우실 수 있을 겁니다.
두 블럭의 움직이는 타이밍과 이동 속력이 완전히 똑같기 때문에
한 블럭의 도착만을 체크하면 됩니다.
그러니까 10라인은 if(SelectBlock.transform.position != EndPos1) 으로도 대체 할 수 있습니다.
'초록색 박스의 위치가 노란색 박스의 위치와 같을 때' 10라인의 if문은 거짓으 되므로
16라인부터 시작하는 else문이 수행됩니다.
19라인에 bBlockChange플래그를 false로 바꿔주어 DragToMoveBlock()함수가 수행되지 않게 바꿔주고
20라인에 fBlockMoveStep을 0으로 초기화 해줍니다.
이 값을 0으로 초기화 해주지 않으면 블럭을 교환 시킬때 마다 블럭의 이동 속력이 증가되는 것을 볼 수 있을 것입니다.
23라인의 새로운 함수가 나왔군요.
이 함수의 내용도 별거 없습니다.
블럭의 위치가 서로 교환되었다면,
그 블럭이 가지고 있는 위치정보의 갱신이 이루어 져야 합니다.
쉽게 말해서 노란색 박스가 초록색 박스의 위치로 이동하면
노란색 박스가 가지고 있던 위치 정보인 ( 3, 3 )이 ( 4, 3 ) 으로 바뀌어야 한다는 것입니다.
초록색 박스도 마찬가지겠죠?
이 위치 정보는 블럭이 가지고 있는 Block스크립트가 가지고 있습니다.
그리고 한 가지 더
위치 정보 뿐만이 아니라 타입도 서로 바꿔워야 합니다.
만약 바꿔주지 않는다면 똑같은 모양의 블럭이 3개 이상 모여도 있어도 매치가 되지않는 기이한 현상을 목격할 수 있을 겁니다.
4). 선택된 블럭과 이동 방향에 존재하는 블럭의 위치 교환 및 타입교환하는 함수 만들기.
아무튼 코드로 보자면
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// 블럭의 위치가 바뀌었으므로 보드에서의 블럭의 셋팅을 교환 해준다.
void SwitchBoard()
{
Block sBlock = SelectBlock.GetComponent<Block>();
sBlock.iX = (int)EndPos1.x;
sBlock.iY = (int)EndPos1.y;
sBlock = TargetBlock.GetComponent<Block>();
sBlock.iX = (int)EndPos2.x;
sBlock.iY = (int)EndPos2.y;
// 타입 스왑
int Tmptype = BlockBoard[(int)StartPos1.x][(int)StartPos1.y];
BlockBoard[(int)StartPos1.x][(int)StartPos1.y] = BlockBoard[(int)EndPos1.x][(int)EndPos1.y];
BlockBoard[(int)EndPos1.x][(int)EndPos1.y] = Tmptype;
}
|
cs |
이정도가 되겠군요.
단순한 대입과 스왑이므로 따로 설명은 하지 않겠습니다.
다시 3). 선택된 블럭을 마우스가 이동한 방향에 있는 블럭과 교환(이동)시키는 함수 만들기. 으로 돌아와서.
한 번의 블럭 교환이 이루어 졌으므로
26라인에 보이는것처럼 BlockCheck() 함수를 수행합니다.
그리고 BlockCheck() 함수로 들어가 새로운 코드를 추가합시다.
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
|
IEnumerator BlockCheck()
{
// 가로체크.
// 세로체크.
// 매치된 블럭이 없으면.
if (!NoMatch())
{
Debug.Log("매치된게 없음.");
if (bReCheck)
{
bReCheck = false;
bDrag = true;
}
else
{
Vector3 TmpStartPos = StartPos1;
StartPos1 = StartPos2;
StartPos2 = TmpStartPos;
Vector3 TmpEndPos = EndPos1;
EndPos1 = EndPos2;
EndPos2 = TmpEndPos;
// 블럭 원위치.
bBlockReChange = true;
}
}
// 매치된 블럭이 있다면.
else
{
yield return new WaitForSeconds(0.2f);
Debug.Log("매치된게 있음.");
SelectBlock = null;
TargetBlock = null;
BlockDelete();
}
}
|
cs |
7라인의 if문은 매치된 블럭이 없을 때고,
31라인의 else문은 매치된 블럭이 있을 때 입니다.
11라인의 bReCheck 플래그의 활용 용도는 뒤에서 설명하겠습니다.
아무튼 이 플래그를 기준으로
13 ~ 14라인 : 다시 드래그가 가능하게 해준다.
18 ~ 27라인 : 다시 블럭 교환을 시도하기 위한 위치 값 스왑이 이루어 지고 bBlockReChange플래그 값을 변환 시킨다.
※ bBlockReChange플래그 값이 true가 되면 Update() 구문에서 실제로 블럭교환을 수행한다.
else문 안에 36라인, 37라인 부분을 추가시켜 줍니다.
블럭 교환 시 매치된 블럭이 존재하기 때문에
SelectBlock이나 TargetBlock은 객체를 게속 가지고 있을 필요가 없어졌습니다.
그리고 20라인의 BlockDelete() 함수가 수행되고,
1
2
3
4
5
6
7
8
9
|
// 매치된 블럭 삭제하기.
void BlockDelete()
{
// 매치된 블럭이 생겨 빈 공간이 생겼는지 판단하는 플래그.
// 빈 공간이 생겼을 경우.
if (bMatch)
BlockDown();
}
|
cs |
다시 8라인의 구문이 수행되면서
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
private bool bReCheck = false;
// 블럭을 내려가게 셋팅한다.
// * 한 번 호출에 한 칸씩만 내려간다.
void BlockDown()
{
bool bBlockDown = false;
// 신경쓸 필요 없는 구문임.
// 빈 공간이 있다면 true
if (bBlockDown)
bBlockMoveDown = true;
else
{
bReCheck = true;
StartCoroutine(BlockCheck());
}
}
|
cs |
1라인에 보이는것 처럼 bReCheck 플래그가 추가되고
14라인에 빈 공간이 없을 때인 else문 안 16라인에 true로 초기화 됩니다.
bReCheck 플래그는 언제 필요하냐면
1. 마우스로 블럭 교환 시도.
2. 블럭 교환결과 매치된 블럭이 있음.
3. 매치된 블럭들이 삭제 됨.
4. 빈 공간에 다른 블럭들이 떨어짐.
5. 다시 매치 시작.
6. 매치된 블럭이 없음
=> 드래그가 가능하게 bDrag 플래그를 true로 바꿔줌
이 때 StartPos1, EndPos1, StartPos2, EndPos2 값의 스왑은 이루어 지지 않게 한다.
(왜냐하면 마우스 드래그로 인한 매치함수가 수행된게 아니기 때문이다.)
즉, 어떤 기준으로 갈라지게 되는거냐면 마우스 드래그로 인해 BlockCheck()함수가 수행되게 된다면
bReCheck플래그는 false가 되어 블럭간의 위차 값 스왑을 하게 되는 것이고,
매치된 블럭이 삭제되어 빈 공간을 채우게 됐을 때 다시 BlockCheck()함수를 수행하게 된다면
bReCheck플래그는 true가 되어 블럭을 다시금 드래그 할 수 있게 해주는 것이다.
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
|
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
enum BLOCK
{
BLANK = -1,
}
public class GameManager : MonoBehaviour {
public GameObject OriginBlock; // 블럭의 원본.
public Sprite[] BlockType; // 블럭 이미지 배열.
public int iBlockX, iBlockY; // 블럭보드의 가로 세로 크기.
public int[][] BlockBoard; // 블럭보드.
private bool bBlockMoveDown = false; // 블럭을 움직여주게 해주는 플래그.
private bool bBlockChange = false; // 블럭과 블럭을 바꾼다.
private bool bBlockReChange = false; // 블럭과 블럭이 바뀐 상태에서 다시 바꿔준다.
private bool bReCheck = false;
private Vector3 MouseStartPos; // 자신이 첫 번째로 선택한 블럭의 위치.
private Vector3 MouseEndPos; // 현재 드래그 중인 마우스의 위치.
private Vector3 MouseOffset; // 첫 번째로 선택한 블럭의 위치로부터 현재 마우스 위치 까지의 거리(?)
private Vector3 StartPos1, StartPos2;
private Vector3 EndPos1, EndPos2;
public GameObject SelectBlock; // 자신이 첫 번째로 선택한 블럭.
public GameObject TargetBlock;
private float fMouseMoveDis = 30f; // 첫 번째 블럭의 위치로부터 현재 마우스 까지의 허용 거리.
private float fMoveSpeed = 5f; // 블럭의 이동 속력.
private float fBlockMoveStep = 0f;
private bool bDrag = true; // 드래그 가능, 불가능 플래그.
void Awake()
{
BlockBoard = new int[iBlockX][];
for (int i = 0; i < iBlockX; i++)
BlockBoard[i] = new int[iBlockY];
CreateBlock();
}
void Update()
{
// 블럭을 삭제 후 움직이는지 체크.
BlockDeleteToMoveCheck();
// 마우스 클릭 처리.
MouseClick();
// 마우스로 블럭 이동.
DragToMoveBlock();
// 블럭을 다시 바꿔준다.
ReChangeBlock();
}
// 블럭이 이동중인지 체크.
void BlockDeleteToMoveCheck()
{
// 블럭 삭제 후 블럭이 이동중인지 체크.
if (bBlockMoveDown)
{
// 블럭이 움직인다: true
// 블럭이 움직이지 않는다 : false
bool bBlockMoveEnd = false;
GameObject[] blocks = GameObject.FindGameObjectsWithTag("BLOCK");
foreach (GameObject block in blocks)
{
// 움직이는 블럭이 있는지 bDown변수를 체크해여 확인.
// 조건 : 움직이는 블럭이 있다면.
if (block.GetComponent<Block>().bDown)
{
// 움직이는 블럭이 아직 있으므로 bBlockMoveEnd을 true로 변환.
bBlockMoveEnd = true;
break;
}
}
// 블럭이 움직이지 않는다면.
if (!bBlockMoveEnd)
{
bBlockMoveDown = false;
BlockDown();
}
}
}
// 마우스 클릭 이동.
void MouseClick()
{
// 마우스 왼쪽버튼 클릭.
if (Input.GetMouseButtonDown(0))
{
RaycastHit Hit;
MouseStartPos = Input.mousePosition; // 마우스의 현재 위치.
Ray ray = Camera.main.ScreenPointToRay(MouseStartPos); // 스크린에서의 마우스 위치를 레이로 변환.
// 레이발사.
if (Physics.Raycast(ray, out Hit, Mathf.Infinity))
{
// 레이에 맞은 오브젝트의 태그가 BLOCK이라면.
if (bDrag && Hit.collider.CompareTag("BLOCK"))
{
// 맞은 블럭의 오브젝트를 SelectBlock에 초기화.
SelectBlock = Hit.collider.gameObject;
StartPos1 = SelectBlock.transform.position;
}
}
}
// 마우스 드래그.
if (Input.GetMouseButton(0))
{
MouseEndPos = Input.mousePosition;
MouseOffset = MouseStartPos - MouseEndPos;
// 드래그가 활성화 중이고 선택한 블럭이 비어있지 않으면.
if (bDrag && SelectBlock != null)
{
// 왼쪽
if (MouseOffset.x > fMouseMoveDis)
{
if (SelectBlock.transform.position.x > 0)
MouseDirection(-1, 0);
}
// 오른쪽
if (MouseOffset.x < -fMouseMoveDis)
{
if (SelectBlock.transform.position.x < iBlockX - 1)
MouseDirection(1, 0);
}
// 위
if (MouseOffset.y < -fMouseMoveDis)
{
if (SelectBlock.transform.position.y < iBlockY - 1)
MouseDirection(0, 1);
}
// 아래
if (MouseOffset.y > fMouseMoveDis)
{
if (SelectBlock.transform.position.y > 0)
MouseDirection(0, -1);
}
}
}
}
// 마우스 방향에 따른 블럭 가져오기.
void MouseDirection(float _x, float _y)
{
EndPos1 = new Vector3(StartPos1.x + _x, StartPos1.y + _y, 0);
bDrag = false;
GameObject[] blocks = GameObject.FindGameObjectsWithTag("BLOCK");
foreach (GameObject block in blocks)
{
Block sBlock = block.GetComponent<Block>();
if (sBlock.iX == EndPos1.x && sBlock.iY == EndPos1.y)
TargetBlock = block;
}
StartPos2 = EndPos1;
EndPos2 = StartPos1;
bBlockChange = true;
}
// 마우스로 블럭이동 처리.
void DragToMoveBlock()
{
// 교환 플래그가 활성화 중이 아니라면 실행하지 않음.
if (!bBlockChange || TargetBlock == null)
return;
if (TargetBlock.transform.position != StartPos1)
{
fBlockMoveStep += fMoveSpeed * Time.deltaTime;
SelectBlock.transform.position = Vector3.MoveTowards(StartPos1, EndPos1, fBlockMoveStep);
TargetBlock.transform.position = Vector3.MoveTowards(StartPos2, EndPos2, fBlockMoveStep);
}
else
{
// 이동 완료
bBlockChange = false;
fBlockMoveStep = 0f;
// 블럭 교환.
SwitchBoard();
// 블럭 매치 시작.
StartCoroutine(BlockCheck());
}
}
// 블럭의 위치가 바뀌었으므로 보드에서의 블럭의 셋팅을 교환 해준다.
void SwitchBoard()
{
Block sBlock = SelectBlock.GetComponent<Block>();
sBlock.iX = (int)EndPos1.x;
sBlock.iY = (int)EndPos1.y;
sBlock = TargetBlock.GetComponent<Block>();
sBlock.iX = (int)EndPos2.x;
sBlock.iY = (int)EndPos2.y;
int Tmptype = BlockBoard[(int)StartPos1.x][(int)StartPos1.y];
BlockBoard[(int)StartPos1.x][(int)StartPos1.y] = BlockBoard[(int)EndPos1.x][(int)EndPos1.y];
BlockBoard[(int)EndPos1.x][(int)EndPos1.y] = Tmptype;
}
// 블럭과 블럭을 다시 교환해준다.
void ReChangeBlock()
{
if (bBlockReChange && TargetBlock)
{
if (TargetBlock.transform.position != EndPos2)
{
fBlockMoveStep += fMoveSpeed * Time.deltaTime;
SelectBlock.transform.position = Vector3.MoveTowards(StartPos1, EndPos1, fBlockMoveStep);
TargetBlock.transform.position = Vector3.MoveTowards(StartPos2, EndPos2, fBlockMoveStep);
}
else
{
fBlockMoveStep = 0f;
bBlockReChange = false;
SwitchBoard();
SelectBlock = null;
TargetBlock = null;
bDrag = true;
}
}
}
// 블럭을 만들어주는 함수.
void CreateBlock()
{
for (int y = 0; y < iBlockY; y++)
{
for (int x = 0; x < iBlockX; x++)
{
GameObject block = Instantiate(OriginBlock, new Vector3(x, y, 0), Quaternion.identity);
block.transform.SetParent(transform);
int iType = Random.Range(0, BlockType.Length);
Block sBlock = block.GetComponent<Block>();
sBlock.iX = x;
sBlock.iY = y;
sBlock.iType = iType;
sBlock.SetBlockImg(BlockType[iType]);
BlockBoard[x][y] = iType;
}
}
StartCoroutine(BlockCheck());
}
IEnumerator BlockCheck()
{
// 가로체크.
for (int y = 0; y < iBlockY; y++)
{
for (int x = 0; x < iBlockX - 2; x++)
{
if (BlockBoard[x][y] == BlockBoard[x + 1][y])
{
if (BlockBoard[x][y] == BlockBoard[x + 2][y])
{
GameObject[] blocks = GameObject.FindGameObjectsWithTag("BLOCK");
foreach (GameObject block in blocks)
{
Block sBlock = block.GetComponent<Block>();
if (sBlock.iY != y)
continue;
if (sBlock.iX == x && sBlock.iY == y)
{
sBlock.bDead = true;
continue;
}
if (sBlock.iX == x + 1 && sBlock.iY == y)
{
sBlock.bDead = true;
continue;
}
if (sBlock.iX == x + 2 && sBlock.iY == y)
sBlock.bDead = true;
}
}
}
}
}
// 세로체크.
for (int x = 0; x < iBlockX; x++)
{
for (int y = 0; y < iBlockY - 2; y++)
{
if (BlockBoard[x][y] == BlockBoard[x][y + 1])
{
if (BlockBoard[x][y] == BlockBoard[x][y + 2])
{
GameObject[] blocks = GameObject.FindGameObjectsWithTag("BLOCK");
foreach (GameObject block in blocks)
{
Block sBlock = block.GetComponent<Block>();
if (sBlock.iX != x)
continue;
if (sBlock.iX == x && sBlock.iY == y)
{
sBlock.bDead = true;
continue;
}
if (sBlock.iX == x && sBlock.iY == y + 1)
{
sBlock.bDead = true;
continue;
}
if (sBlock.iX == x && sBlock.iY == y + 2)
sBlock.bDead = true;
}
}
}
}
}
// 매치된 블럭이 없으면.
if (!NoMatch())
{
Debug.Log("매치된게 없음.");
if (bReCheck)
{
bReCheck = false;
bDrag = true;
}
else
{
Vector3 TmpStartPos = StartPos1;
StartPos1 = StartPos2;
StartPos2 = TmpStartPos;
Vector3 TmpEndPos = EndPos1;
EndPos1 = EndPos2;
EndPos2 = TmpEndPos;
// 블럭 원위치.
bBlockReChange = true;
}
}
// 매치된 블럭이 있다면.
else
{
yield return new WaitForSeconds(0.2f);
Debug.Log("매치된게 있음.");
SelectBlock = null;
TargetBlock = null;
BlockDelete();
}
}
// 블럭중 매치된 블럭이 있는지.
bool NoMatch()
{
GameObject[] blocks = GameObject.FindGameObjectsWithTag("BLOCK");
foreach (GameObject block in blocks)
{
if (block.GetComponent<Block>().bDead)
return true;
}
return false;
}
// 매치된 블럭 삭제하기.
void BlockDelete()
{
// 매치된 블럭이 생겨 빈 공간이 생겼는지 판단하는 플래그.
bool bMatch = false;
GameObject[] blocks = GameObject.FindGameObjectsWithTag("BLOCK");
foreach (GameObject block in blocks)
{
Block sBlock = block.GetComponent<Block>();
if (sBlock.bDead)
{
// 삭제된 블럭 칸을 -1로.
BlockBoard[sBlock.iX][sBlock.iY] = (int)BLOCK.BLANK;
Destroy(block);
bMatch = true;
}
}
// 빈 공간이 생겼을 경우.
if (bMatch)
BlockDown();
}
// 블럭을 내려가게 셋팅한다.
// * 한 번 호출에 한 칸씩만 내려간다.
void BlockDown()
{
bool bBlockDown = false;
for (int x = 0; x < iBlockX; x++)
{
bool BlankCheck = false;
for (int y = iBlockY - 1; y > -1; y--)
{
// x,y의 위치에 블럭이 비어있다면.
if (!BlankCheck && BlockBoard[x][y] == (int)BLOCK.BLANK)
{
BlankCheck = true;
bBlockDown = true;
//--------- (x, iBlockY) 위치에 블럭 생성.-------------
GameObject block = Instantiate(OriginBlock, new Vector3(x, iBlockY, 0), Quaternion.identity);
block.transform.SetParent(transform);
int iType = Random.Range(0,BlockType.Length);
Block sBlock = block.GetComponent<Block>();
sBlock.iX = x;
sBlock.iY = iBlockY;
sBlock.iType = iType;
sBlock.SetBlockImg(BlockType[iType]);
//-----------------------------------------------------
// 블럭을 내리기 위한 위치 셋팅.
for (int z = y + 1; z < iBlockY+1; z++)
{
GameObject[] blocks = GameObject.FindGameObjectsWithTag("BLOCK");
foreach (GameObject block2 in blocks)
{
sBlock = block2.GetComponent<Block>();
if (sBlock.iX == x && sBlock.iY == z)
{
sBlock.StartPos = new Vector3(x, z, 0);
sBlock.TartgetPos = new Vector3(x, z - 1, 0);
sBlock.bDown = true;
break;
}
}
}
}
}
}
if (bBlockDown)
bBlockMoveDown = true;
else
{
bReCheck = true;
StartCoroutine(BlockCheck());
}
}
}
|
cs |
'Unity3D > Project' 카테고리의 다른 글
[Unity3D] 헥사게임 만들기 네 번째 - 마우스 처리. (2) | 2018.03.09 |
---|---|
[Unity3D] 헥사게임 만들기 세 번째 - 블럭처리2. (2) | 2018.02.24 |
[Unity3D] 헥사게임 만들기 세 번째 - 블럭 처리. (0) | 2018.02.18 |
[Unity3D] 헥사게임 만들기 두 번째- 블럭보드 만들기2 (0) | 2018.02.13 |
[Unity3D] 헥사게임 만들기 두 번째 - 블럭보드 만들기. (0) | 2018.02.13 |
[Unity3D] 헥사게임 만들기 네 번째 - 마우스 처리.
※ 소스 출처 : 네이버 카페 - 유니티 허브 - 햐얀바람님.
0. 리소스 준비하기.
1). 배경 이미지.
2). 블럭보드의 배경 이미지.
3). UI테두리 이미지.
4). 블럭 이미지.
1. 캔버스 크기 설정하기.
- 게임뷰어 및 캔버스 크기 설정.
2. 블럭보드 만들기.
1). 블럭 프리팹 만들기.
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 | private Vector3 MouseStartPos; // 자신이 첫 번째로 선택한 블럭의 위치. private Vector3 StartPos1; private GameObject SelectBlock; // 자신이 첫 번째로 선택한 블럭. // 마우스 클릭처리. void MouseClick() { // 마우스 왼쪽버튼 클릭. if (Input.GetMouseButtonDown(0)) { RaycastHit Hit; MouseStartPos = Input.mousePosition; Ray ray = Camera.main.ScreenPointToRay(MouseStartPos); // 스크린에서의 마우스 위치를 레이로 변환. // 레이발사. if (Physics.Raycast(ray, out Hit, Mathf.Infinity)) { // 레이에 맞은 오브젝트의 태그가 BLOCK이라면. if (Hit.collider.CompareTag("BLOCK")) { // 맞은 블럭의 오브젝트를 SelectBlock에 초기화. SelectBlock = Hit.collider.gameObject; StartPos1 = SelectBlock.transform.position; } } } } | cs |
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 | private Vector3 MouseStartPos; // 자신이 첫 번째로 선택한 블럭의 위치. private Vector3 MouseEndPos; // 현재 드래그 중인 마우스의 위치. private Vector3 MouseOffset; // 첫 번째로 선택한 블럭의 위치로부터 현재 마우스 위치 까지의 거리(?) private Vector3 StartPos1; private GameObject SelectBlock; // 자신이 첫 번째로 선택한 블럭. private float fMouseMoveDis = 30f; // 첫 번째 블럭의 위치로부터 현재 마우스 까지의 허용 거리. private bool bDrag = true; // 드래그 가능, 불가능 플래그. // 마우스 클릭처리. void MouseClick() { // 마우스 왼쪽버튼 클릭. if (Input.GetMouseButtonDown(0)) { RaycastHit Hit; MouseStartPos = Input.mousePosition; Ray ray = Camera.main.ScreenPointToRay(MouseStartPos); // 스크린에서의 마우스 위치를 레이로 변환. // 레이발사. if (Physics.Raycast(ray, out Hit, Mathf.Infinity)) { // 레이에 맞은 오브젝트의 태그가 BLOCK이라면. if (bDrag && Hit.collider.CompareTag("BLOCK")) { // 맞은 블럭의 오브젝트를 SelectBlock에 초기화. SelectBlock = Hit.collider.gameObject; StartPos1 = SelectBlock.transform.position; } } } // 마우스 드래그. if (Input.GetMouseButton(0)) { MouseEndPos = Input.mousePosition; MouseOffset = MouseStartPos - MouseEndPos; // 드래그가 활성화 중이고 선택한 블럭이 비어있지 않으면. if (bDrag && SelectBlock != null) { // 왼쪽 if (MouseOffset.x > fMouseMoveDis) { if (SelectBlock.transform.position.x > 0) MouseDirection(-1, 0); } // 오른쪽 if (MouseOffset.x < -fMouseMoveDis) { if (SelectBlock.transform.position.x < iBlockX - 1) MouseDirection(1, 0); } // 위 if (MouseOffset.y < -fMouseMoveDis) { if (SelectBlock.transform.position.y < iBlockY - 1) MouseDirection(0, 1); } // 아래 if (MouseOffset.y > fMouseMoveDis) { if (SelectBlock.transform.position.y > 0) MouseDirection(0, -1); } } } } | cs |
빨간색의 네모난 박스가 현재 선택한 블럭입니다. MouseStartPos에 해당하죠.
그 옆에 파란색 동그라미는 현재 마우스의 위치 입니다. MouseEndPos에 해당합니다.
오른쪽 방향에 가까운지 if (MouseOffset.x > fMouseMoveDis)
왼쪽 방향에 가까운지 if (MouseOffset.x < -fMouseMoveDis)
위 방향에 가까운지 if (MouseOffset.y < -fMouseMoveDis)
아래 방향에 가까운지 if (MouseOffset.y > fMouseMoveDis)
비교하게 됩니다.
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 | // 드래그가 활성화 중이고 선택한 블럭이 비어있지 않으면. if (bDrag && SelectBlock != null) { // 왼쪽 if (MouseOffset.x > fMouseMoveDis) { if (SelectBlock.transform.position.x > 0) MouseDirection(-1, 0); } // 오른쪽 if (MouseOffset.x < -fMouseMoveDis) { if (SelectBlock.transform.position.x < iBlockX - 1) MouseDirection(1, 0); } // 위 if (MouseOffset.y < -fMouseMoveDis) { if (SelectBlock.transform.position.y < iBlockY - 1) MouseDirection(0, 1); } // 아래 if (MouseOffset.y > fMouseMoveDis) { if (SelectBlock.transform.position.y > 0) MouseDirection(0, -1); } } | cs |
한 가지 확실히 알아둬야 될것이 있는데 월드상의 거리와
스크린 상의 거리를 헷갈리면 안된다는 것입니다.
월드상의 거리로 했을 때 블럭과 블럭의 거리는 1입니다.
하지만 fMouseMoveDis은 30이죠. 이것은 스크린상의 거리로, 픽셀단위 거리라고 생각하시면 됩니다.
맨 처음 스크린을 구성했을 때 셋팅했던 가로 720 세로 1280은 스크린의 크기 입니다.
빨간색 원의 반 지름이 fMouseMoveDis입니다.
즉, 빨간색 원은 허용 범위가 되는 것이죠.
빨간색 원을 넘지 않으면 방향 확정은 일어나지 않습니다.
2). 마우스 방향에 따른 블럭 가져오는 함수 만들기.
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 | EndPos1 = new Vector3(StartPos1.x + _x, StartPos1.y + _y, 0); private bool bBlockChange = false; // 블럭과 블럭을 바꾼다. private Vector3 MouseStartPos; // 자신이 첫 번째로 선택한 블럭의 위치. private Vector3 MouseEndPos; // 현재 드래그 중인 마우스의 위치. private Vector3 MouseOffset; // 첫 번째로 선택한 블럭의 위치로부터 현재 마우스 위치 까지의 거리(?) private Vector3 StartPos1, StartPos2; private Vector3 EndPos1, EndPos2; private GameObject SelectBlock; // 자신이 첫 번째로 선택한 블럭. private GameObject TargetBlock; // 교환할 블럭. private float fMouseMoveDis = 30f; // 첫 번째 블럭의 위치로부터 현재 마우스 까지의 허용 거리. private bool bDrag = true; // 드래그 가능, 불가능 플래그. // 마우스 방향에 따른 블럭 가져오기. void MouseDirection(float _x, float _y) { EndPos1 = new Vector3(StartPos1.x + _x, StartPos1.y + _y, 0); bDrag = false; GameObject[] blocks = GameObject.FindGameObjectsWithTag("BLOCK"); foreach (GameObject block in blocks) { Block sBlock = block.GetComponent<Block>(); if (sBlock.iX == EndPos1.x && sBlock.iY == EndPos1.y) TargetBlock = block; } StartPos2 = EndPos1; EndPos2 = StartPos1; bBlockChange = true; } | cs |
별거 없습니다.
이 함수의 매개 변수는 두 개가 있는데, 이 두개의 매개 변수는 처음에 선택된 블럭을 기준으로
특정 방향에 있는 블럭을 가져오는데 사용됩니다.
20라인에 보면 StartPos1이 사용되는데,
위 내용을 자세히 읽어봤다면 알겠지만 처음 선택된 블럭의 위치가 StartPos1에 들어가 있습니다.
이 값의 x값 또는 y값을 더해주어 특정 방향에 있는 블럭의 위치를 가져올 수 있게 됩니다.
※ 매개변수
(x, y)
(1, 0) 오른쪽
(-1, 0) 왼쪽
(0, 1) 위
(0, -1) 아래
그 다음 22라인에 보면
bDrag값을 false로 바꿔주는데, 이 값을 false로 바꿔주지 않으면 MouseDirection(x,y)함수가
게속 수행되면서 TargetBlock의 값이 게속 바뀌게 됩니다.
그렇게 되면 처음 선택된 블럭과 교환해줄 다음 블럭이 게속 바뀌게 되면서 꼬이게 됩니다.
그냥 단순하게 생각해서 교환시킬 블럭을 구했으니 드래그를 못하게 하겠다 라고 생각하시면 됩니다.
위치 값(EndPos1)을 알았으니 24라인에 있는 구문처럼 BLOCK태그를 가진 모든 오브젝트(블럭 오브젝트)를 가져와서
위치 값을 검사하여 EndPos1과 같은 위치에 있는 블럭을 가져오면 됩니다.
=> TargetBlock = block;
다음 33, 34라인은 그림을 봅시다.
블럭이 교환 됐을 때 같은 타입의 블럭이 3개가 있다면 문제 없겠지만,
없을 때는 다시 교환해주어야 합니다. 그걸 위한 StartPos2, EndPos2 변수를 선언하여 초기화 했습니다.
다음 36라인의 bool변수인 bBlockChange을 true로 바꿔주면서
블럭과 블럭의 교환 수행을 허락해 주면 됩니다.
GameManager 스크립트의 여태까지 한 부분에 대한 풀소스 입니다.
글이 길어지니 끊고 다음 게시물로 이어가겠습니다.
'Unity3D > Project' 카테고리의 다른 글
[Unity3D] 헥사게임 만들기 네 번째 - 마우스 처리2 (0) | 2018.03.14 |
---|---|
[Unity3D] 헥사게임 만들기 세 번째 - 블럭처리2. (2) | 2018.02.24 |
[Unity3D] 헥사게임 만들기 세 번째 - 블럭 처리. (0) | 2018.02.18 |
[Unity3D] 헥사게임 만들기 두 번째- 블럭보드 만들기2 (0) | 2018.02.13 |
[Unity3D] 헥사게임 만들기 두 번째 - 블럭보드 만들기. (0) | 2018.02.13 |
[Unity3D] 헥사게임 만들기 세 번째 - 블럭처리2.
※ 소스 출처 : 네이버 카페 - 유니티 허브 - 햐얀바람님.
0. 리소스 준비하기.
1). 배경 이미지.
2). 블럭보드의 배경 이미지.
3). UI테두리 이미지.
4). 블럭 이미지.
1. 캔버스 크기 설정하기.
- 게임뷰어 및 캔버스 크기 설정.
2. 블럭보드 만들기.
1). 블럭 프리팹 만들기.
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 | // 매치된 블럭 삭제하기. void BlockDelete() { // 매치된 블럭이 생겨 빈 공간이 생겼는지 판단하는 플래그. bool bMatch = false; GameObject[] blocks = GameObject.FindGameObjectsWithTag("BLOCK"); foreach (GameObject block in blocks) { Block sBlock = block.GetComponent<Block>(); if (sBlock.bDead) { // 삭제된 블럭 칸의 을 -1로. BlockBoard[sBlock.iX][sBlock.iY] = (int)BLOCK.BLANK; Destroy(block); bMatch = true; } } // 빈 공간이 생겼을 경우. if (bMatch) BlockDown(); } | cs |
블럭삭제 함수안에 bool 변수인 bMatch가 새로 추가되었습니다.
이 변수의 초기값은 false로 하는 일은 삭제된 블럭이 하나라도 있는가 판단하는 일입니다.
bMatch가 true가 된다면 삭제한 블럭이 있다는 것이며, 삭제된 블럭이 있던 공간은 빈 공간이 됩니다.
빈 공간이 생겼을 경우 블럭은 떨어져야 합니다.
즉, 변수 bMatch는 BlockDown()함수를 호출 할 것인지 하지 않을것인지 판단하는 재료가 됩니다.
4). 떨어지는 블럭 함수 만들기.
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 | void BlockDown() { bool bBlockDown = false; for (int x = 0; x < iBlockX; x++) { bool BlankCheck = false; for (int y = iBlockY - 1; y > -1; y--) { // x,y의 위치에 블럭이 비어있다면. if (!BlankCheck && BlockBoard[x][y] == (int)BLOCK.BLANK) { BlankCheck = true; bBlockDown = true; //--------- (x, iBlockY) 위치에 블럭 생성.------------- GameObject block = Instantiate(OriginBlock, new Vector3(x, iBlockY, 0), Quaternion.identity); block.transform.SetParent(transform); int iType = Random.Range(0,BlockType.Length); Block sBlock = block.GetComponent<Block>(); sBlock.iX = x; sBlock.iY = iBlockY; sBlock.iType = iType; sBlock.SetBlockImg(BlockType[iType]); //----------------------------------------------------- // 블럭을 내리기 위한 위치 셋팅. for (int z = y + 1; z < iBlockY+1; z++) { GameObject[] blocks = GameObject.FindGameObjectsWithTag("BLOCK"); foreach (GameObject block2 in blocks) { sBlock = block2.GetComponent<Block>(); if (sBlock.iX == x && sBlock.iY == z) { sBlock.StartPos = new Vector3(x, z, 0); sBlock.TartgetPos = new Vector3(x, z - 1, 0); sBlock.bDown = true; break; } } } } } } if (bBlockDown) bBlockMoveDown = true; else StartCoroutine(BlockCheck()); } | cs |
1 | bool bBlockDown = false; | cs |
bool 변수은 bBlockDown변수가 하는 일은 블럭이 떨어져야 하느냐 판단하는 일입니다.
즉, 이 변수가 true가 된다면 모종의 셋팅이 된 블럭들을 떨어뜨리는 겁니다.
1 2 3 4 5 6 7 | for (int x = 0; x < iBlockX; x++) { bool BlankCheck = false; for (int y = iBlockY - 1; y > -1; y--) { } } | cs |
2중 for문은 블럭보드를 검사하는 구문입니다.
첫 번째 for문은 x값으로 열에 해당됩니다.
2번째 for문은 행에 해당됩니다.
초기값으로 iBlockY - 1인 이유는 밑에서 위로 검사하는 것이 아닌 위에서 아래 순서로 검사하기 위해서 입니다.
※ iBlockY변수는 블럭보드의 높이(블럭의 세로 개수) 입니다.
iBlockY 변수에 - 1을 한 이유는 제로(0)베이스 이기 때문입니다.
이렇게 높이를 1씩 감소시키면서 빈 공간을 찾아 한 줄을 검사 합니다.
그림으로 보자면 다음과 같은 줄만을 검사 합니다.
x값이 1 증가한다면 밑과 같이 검사 하겠군요.
첫 번째 for문과 두 번째 for문 사이에 있는 bool 변수 BlankCheck는 현재 검사하고 있는 줄에서
빈 공간이 있을 때 블럭을 한 번만 만들어 주기 위한 플래그 입니다.
검사하고 있는 줄에서 빈 칸이 2개 이상일 때, 이 플래그가 없다면 빈칸의 숫자만큼 한 번에 블럭을 만들어 주게 됩니다.
블럭의 위치를 이용하고 있는 상황에서 같은 위치에 한 번에 여러개의 블럭을 만든다는 것은 좋지 않은 일입니다.
1. 빈칸을 찾으면 하나의 블럭을 만든다.
2. 블럭들을 움직인다.
3. 다시 빈칸을 찾는다.
즉, 함수 한 번 호출당 한 줄, 한 빈칸에 블럭을 하나씩 생성해주고 한 칸씩만 이동한다고 생각하시면 됩니다.
다음 빈 칸을 채우는 것은 다음 함수가 호출될 때 입니다.
1 2 3 | if (!BlankCheck && BlockBoard[x][y] == (int)BLOCK.BLANK) { } | cs |
조건문입니다. 현재 BlockBoard[x][y] == 빈칸이냐?
라는 뜻이군요.
이 조건을 클리어 하면 2개의 플래그를 모두 true로 바꿔줍니다.
1 2 | BlankCheck = true; bBlockDown = true; | cs |
설명은 bool값 선언 부분에서 말 한것이 전부 입니다.
1 2 3 4 5 6 7 8 9 10 | GameObject block = Instantiate(OriginBlock, new Vector3(x, iBlockY, 0), Quaternion.identity); block.transform.SetParent(transform); int iType = Random.Range(0,BlockType.Length); Block sBlock = block.GetComponent<Block>(); sBlock.iX = x; sBlock.iY = iBlockY; sBlock.iType = iType; sBlock.SetBlockImg(BlockType[iType]); | cs |
빈 칸에 따른 새로운 블럭 생성 구문 입니다.
Instantiate(원본 프리팹, 위치, 회전상태) 함수로 새로운 블럭을 만들어 줍니다.
* new Vector3(현재 검사하고 있는 열, 열의 높이, 0)
블럭을 새로 생성시켰기 때문에 그 블록이 가진 정보들 여러가지를 초기화 시켜야 합니다.
1. 현재 블럭의 위치 값.
2. 현재 블럭의 타입.
3. 현재 블럭의 타입에 따른 이미지.
등을 초기화 시킵니다.
블럭을 새롭게 만들어 정보를 셋팅 시켰으니 다음은 블럭을 떨어뜨릴 차례입니다.
1 | for (int z = y + 1; z < iBlockY+1; z++) | cs |
이 구문의 뜻은 대략 이렇습니다.
z = y + 1
현재 y의 위치는 빈 칸입니다.
빈칸의 + 1칸은 빈 칸의 바로 윗 칸이 되겠군요.
빈 칸의 바로 윗칸(y + 1) 부터 블럭이 새롭게 생성된 위치의 칸 까지(iBlockY + 1) 포함해 모두 떨어뜨려야 합니다.
1 | if (sBlock.iX == x && sBlock.iY == z) | cs |
현재 검사하고 있는 블럭의 위치가 위 그림의 파란색 칸 위치의 블럭과 같다면.
1 2 3 4 | sBlock.StartPos = new Vector3(x, z, 0); sBlock.TartgetPos = new Vector3(x, z - 1, 0); sBlock.bDown = true; break; | cs |
1라인 == 파란색 칸의 위치
2라인 == 노랜색 칸의 위치
로 셋팅.
이렇게 셋팅된 위치를 토대로 블럭을 TargetPos까지 이동 시킨다.
3라인 == 이동을 허가하는 플래그 변수 (bDown)
여기서 일단 Block 스크립트로 넘어갑시다.
왜냐하면 3라인 에서 블럭 이동에 대한 허가가 내려왔기 때문입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | private float fStep = 0f; // 속력 축적 변수. private float fMoveSpeed = 10f; // 블럭 떨어지는 기본 속력. void Update() { if (bDown && transform.position != TartgetPos) { fStep += Time.deltaTime * fMoveSpeed; transform.position = Vector3.MoveTowards(StartPos, TartgetPos, fStep); if (transform.position == TartgetPos) { int X = (int)TartgetPos.x; int Y = (int)TartgetPos.y; gMar.BlockBoard[X][Y] = iType; iX = X; iY = Y; fStep = 0f; bDown = false; } } } | cs |
새롭게 추가된 구문은 위에나온 코드가 답니다.
6라인의 조건은 이렇습니다.
블럭이 떨어지는것에 대한 허가가 되었나? 그리고
현재 내 위치가 목표 위치와 다른가?
8라인 에서는 시간에 따른 블럭 이동 속도가 초기화 됩니다.
9라인 에서는 현재 내 위치를 변화 시켜줍니다.
MoveTowards() 함수를 이용하여 보간 이동 시켜주는군요.
11라인 에서의 조건은 이렇습니다.
내 위치가 목표위치에 도달했는가?
도달 했다면 목표 위치는 현재 내 위치와 같으므로
16라인에 블럭보드의 특정 위치에 현재 자신의 타입으로 초기화 시켜 줍니다.
17,18라인은 현재 자신의 위치가 목표위치(TartgetPos)와 같으므로 새롭게 정보를 갱신해 줍니다.
19 라인에서는 블럭의 떨어지는 속력을 0으로 초기화 합니다.
그렇지 않으면 다음에 블럭이 또 떨어지는 일이 생길때 너무 속력이 빨라 순간이동 수준으로 보일지도 모르기 때문입니다.
20 라인에서는 현재 내 위치가 목표 위치까지 도달했으므로 더 이상 움직일 필요가 없기 때문에 명령을 철회한다는 것과 같다고 생각 하시면 됩니다.
그럼 다시 GameManager에 BlockDown()함수로 돌아와서
1 2 3 4 | if (bBlockDown) bBlockMoveDown = true; else StartCoroutine(BlockCheck()); | cs |
이 구문은 bBlockDown변수 선언 이유와 함께 설명했으므로 생략 하겠습니다.
다만 bBlockMoveDown변수에 대해서는 설명하고자 합니다.
이 변수는 현재 블럭의 이동이 끝났다면 다음 블럭의 이동을 시작해주기 위한 플래그 변수 입니다.
현재 블럭의 이동이 끝난지 알수 없기 때문에 그에따른 체크를 해야 합니다.
Update() 함수로 이동합시다.
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 | void Update() { if (bBlockMoveDown) { // 블럭이 움직인다: true // 블럭이 움직이지 않는다 : false bool bBlockMoveEnd = false; GameObject[] blocks = GameObject.FindGameObjectsWithTag("BLOCK"); foreach (GameObject block in blocks) { // 움직이는 블럭이 있는지 bDown변수를 체크해여 확인. // 조건 : 움직이는 블럭이 있다면. if (block.GetComponent<Block>().bDown) { // 움직이는 블럭이 아직 있으므로 bBlockMoveEnd을 true로 변환. bBlockMoveEnd = true; break; } } // 블럭이 움직이지 않는다면. if (!bBlockMoveEnd) { bBlockMoveDown = false; BlockDown(); } } } | cs |
현재는 bBlockMoveDown변수가 true되었기 때문에 현재 블럭들이 이동을 하고있는지, 그렇지 않은지 체크가 가능해졌습니다.
모든 블럭의 bDown값을 체크 합니다. 만약 하나라도 true이라면 현재 이동하고 있는 블럭이 존재하는 것이므로,
7라인의 선언된 bool값을 true값으로 변환 시켜 줍니다.
만약 bDown값이 true인것이 하나도 없다면 이동하고 있는 블럭이 존재하지 않는다는 의미 이므로 다음 빈 칸 이동을 위한 BlockDown()함수(27라인)를 호출해 줍니다.
'Unity3D > Project' 카테고리의 다른 글
[Unity3D] 헥사게임 만들기 네 번째 - 마우스 처리2 (0) | 2018.03.14 |
---|---|
[Unity3D] 헥사게임 만들기 네 번째 - 마우스 처리. (2) | 2018.03.09 |
[Unity3D] 헥사게임 만들기 세 번째 - 블럭 처리. (0) | 2018.02.18 |
[Unity3D] 헥사게임 만들기 두 번째- 블럭보드 만들기2 (0) | 2018.02.13 |
[Unity3D] 헥사게임 만들기 두 번째 - 블럭보드 만들기. (0) | 2018.02.13 |
[Unity3D] 헥사게임 만들기 세 번째 - 블럭 처리.
※ 소스 출처 : 네이버 카페 - 유니티 허브 - 햐얀바람님.
0. 리소스 준비하기.
1). 배경 이미지.
2). 블럭보드의 배경 이미지.
3). UI테두리 이미지.
4). 블럭 이미지.
1. 캔버스 크기 설정하기.
- 게임뷰어 및 캔버스 크기 설정.
2. 블럭보드 만들기.
1). 블럭 프리팹 만들기.
이번 글에서 하고자하는 내용은 체크 및 삭제 입니다.
3개이상의 연속된 블럭이 있는지 체크하고 그 블럭을 삭제하는 것입니다.
이번에 짤 스크립트는 그에 관한 내용입니다.
1,2) 블럭체크 및 블럭매치 함수 코딩.
3). 블럭삭제 함수 코딩.
1. 블럭보드 전체를 체크하면서 3개 이상의 연속된 블럭이 있는지 찾고, 연속된 블럭이면 체크(삭제해야할 블럭이라고 마킹(bDead))한다.
=> BlockCheck() 코루틴.
블럭체크 코루틴에서는 가로체크와 세로체크의 기능을 따로 분리해서 체크했습니다.
그 편이 가독성이 좋기 때문이죠.
우선 가로체크부터 보도록 합시다.
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 | // 가로체크. for (int y = 0; y < iBlockY; y++) { for (int x = 0; x < iBlockX - 2; x++) { // 기준칸과 기준칸 + 1칸을 비교 검사. if (BlockBoard[x][y] == BlockBoard[x + 1][y]) { // 기준칸과 기준칸 + 2칸을 비교 검사. if (BlockBoard[x][y] == BlockBoard[x + 2][y]) { GameObject[] blocks = GameObject.FindGameObjectsWithTag("BLOCK"); foreach (GameObject block in blocks) { Block sBlock = block.GetComponent<Block>(); if (sBlock.iY != y) continue; if (sBlock.iX == x && sBlock.iY == y) { sBlock.bDead = true; continue; } if (sBlock.iX == x + 1 && sBlock.iY == y) { sBlock.bDead = true; continue; } if (sBlock.iX == x + 2 && sBlock.iY == y) sBlock.bDead = true; } } } } } | cs |
x는 열, y는 행으로 으로 보시면 되겠습니다.
0번째 행 0번째 열에는 2라는 값이 있습니다.
이 2라는 값이 연속되는지 확인하기 위해 위치(0,0) 기준으로 +1칸 +2칸째를 검사하도록 하겠습니다.
이런식으로 한칸 한칸을 기준칸으로 해서 +1칸 +2칸째를 검사하고
같다면 3개가 연속되므로 3개의 블럭의 스크립트의 bool변수인 bDead 변수를 true로 변환 합니다.
이렇게 모든 칸들의 블럭들을 체크 해주고, 체크가 끝나면 모든 블럭을 가져와 블럭의 bDead변수를 검사해 true인 블럭들을 삭제해주면 됩니다.
P.S
연속된 칸이 3칸이 아닌 4칸 이상 이여도 문제 없습니다.
왜냐하면 기준칸이 게속 달라지기 때문입니다.
한 번 체크가 끝난 칸이라고 해서 체크를 안하는게 아닙니다.
세로체크도 가로체크와 크게 다르지 않기 때문에 설명을 생략하겠습니다.
2. 전체 블럭을 가져와 체크(마킹)된 블럭이 있는지 찾는다.
=> NoMatch() 함수.
3. 마킹된 블럭 있다면 블럭을 삭제한다.
=> DeleteBlock() 함수.
'Unity3D > Project' 카테고리의 다른 글
[Unity3D] 헥사게임 만들기 네 번째 - 마우스 처리. (2) | 2018.03.09 |
---|---|
[Unity3D] 헥사게임 만들기 세 번째 - 블럭처리2. (2) | 2018.02.24 |
[Unity3D] 헥사게임 만들기 두 번째- 블럭보드 만들기2 (0) | 2018.02.13 |
[Unity3D] 헥사게임 만들기 두 번째 - 블럭보드 만들기. (0) | 2018.02.13 |
[Unity3D] 헥사게임 만들기 첫 번째 - 리소스 준비하기 및 캔버스 크기 설정. (0) | 2018.02.11 |
[Unity3D] 헥사게임 만들기 두 번째- 블럭보드 만들기2
※ 소스 출처 : 네이버 카페 - 유니티 허브 - 햐얀바람님.
0. 리소스 준비하기.
1). 배경 이미지.
2). 블럭보드의 배경 이미지.
3). UI테두리 이미지.
4). 블럭 이미지.
1. 캔버스 크기 설정하기.
- 게임뷰어 및 캔버스 크기 설정.
2. 블럭보드 만들기.
1). 블럭 프리팹 만들기.
아무래도 배경이 없는건 심심하니
기본적인 배경부터 넣고 블럭보드를 스크립트를 통해 만들어 봅시다.
(7).
하이러키 창에 빈 오브젝트를 하나 만들어 주고,
빈 오브젝트의 이름을 Map으로 하고 Transform컴포넌트를 Reset해 기본값으로 셋팅합니다.
이 오브젝트에는 배경 이미지 및 블럭보드의 배경 이미지 등이 자식으로 들어갈 겁니다.
(8).
프로젝트창 > 마테리얼 폴더 > 마테리얼을 하나 만들어 줍니다.
이름은 'BackGround'가 좋겠군요.
(9).
마테리얼의 셰이더를 Unlit > Texture로 바꿔 줍니다.
(10).
BackGround 이미지를 마테리얼에 드래그 하여 넣습니다.
(11.)
만들어 놓은 마테리얼을 클릭 한 뒤 Ctrl + D를 눌러 복사한 후.
복사된 마테리얼의 이름을 BlcokBoad_Bg로 바꾸고
ui_BackGround 이미지를 드래그하여 넣습니다.
(12).
마테리얼 작업이 완료되면 하이러키창에 Quad객체를 만듭니다.
이 객체안에 열심히 작업한 마테리얼을 넣어 배경을 만듭시다.
그리고 배경의 크기는 적당히 조절해 줍시다.
(13).
테두리는 월드상에 있는 오브젝트들이 이동해도 영향을 받지않게 캔버스(UI)에 작업해줍시다.
(14).
이제 위와같이 블럭이 만들어지도록 스크립트를 짜봅시다.
동영상을 따라해 봅시다.
동영상에서는 실수로 코드 한 줄을 빼먹었네요.
GameManager스크립트에 CreateBlock()함수에서 밑의 구문을 추가해주세요.
1 | BlockBoard[x][y] = iType; | cs |
1 2 | public int iBlockX, iBlockY; // 블럭보드의 가로 세로 크기. public int[][] BlockBoard; // 블럭보드. | cs |
블럭보드는 가로 세로의 크기를 받아 2차원 배열로 만들어 졌습니다.
그림으로 보자면 밑과 같은 그림이 되겠군요.
그렇다면 2차원 배열에는 어떤 정보가 들어갔을까요?
바로 블럭의 종류값이 들어갔습니다. 그림으로 보자면 밑과 같습니다.
이러한 값들이 들어간 이유는 같은 블럭이 3개이상 연속됐을 때 삭제해 주기 위함입니다.
실제 삭제를 위해서는 2차원 배열에 들어있는 값들을 비교하고 연속된 숫자가 나왔을 때
해당 위치에 있는 블럭을 삭제해야 합니다. 그러면 그 위치 값은 어디에 있을까요?
바로 블럭 자신이 가지고 있습니다.
블럭 스크립트의 일부분을 보면
1 2 | public int iX, iY; // 블럭의 위치. public int iType; // 블럭의 종류. | cs |
위와 같은 정보를 가지고 있습니다.
위치 뿐만 아니라 종류까지도 비교해서 검사하면 됩니다.
이 부분은 후에 '블럭삭제'부분에서 다룰려고 합니다.
블럭의 위치를 그림으로 보자면 밑과 같이 되어있습니다.
이렇게 된 이유는 2중 for문으로 블럭을 생성해줄 때 블럭보드의 가로 및 세로의 크기가
그대로 위치 정보로 들어갔기 때문입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | for (int y = 0; y < iBlockY; y++) { for (int x = 0; x < iBlockX; x++) { GameObject block = Instantiate(OriginBlock, new Vector3(x, y, 0), Quaternion.identity); block.transform.SetParent(transform); int iType = Random.Range(0, BlockType.Length); Block sBlock = block.GetComponent<Block>(); sBlock.iX = x; sBlock.iY = y; sBlock.iType = iType; sBlock.SetBlockImg(BlockType[iType]); BlockBoard[x][y] = iType; } } | cs |
그림으로 보자면 위의 2중 for문은 밑의 그림과 같은 순서로 생성시킵니다.
즉, 월드상의 블럭의 위치 값도 ( x , y, z )일 때 (0,0,0), (1,0,0), (2,0,0) ... 와 같이 셋팅 됩니다.
위의 정보를 봤을 때 블럭과 블럭의 사이에 거리가 1이 된다는 뜻인데, 블럭 이미지의 크기가 다를 경우에
밑과 같이 딱 나뉘어 떨어지지 않습니다.
그러한 사태를 방지하기 위해 리소스 준비단계에서 블럭의 크기를 100픽셀로 맞춰 만든 것입니다.
100픽셀 == 1 Scale == 1 Position 이라고 생각하면 헷갈림이 조금 덜 할듯 합니다.
다음은 블럭의 가로 세로 체크를 해보도록 하겠습니다.
'Unity3D > Project' 카테고리의 다른 글
[Unity3D] 헥사게임 만들기 세 번째 - 블럭처리2. (2) | 2018.02.24 |
---|---|
[Unity3D] 헥사게임 만들기 세 번째 - 블럭 처리. (0) | 2018.02.18 |
[Unity3D] 헥사게임 만들기 두 번째 - 블럭보드 만들기. (0) | 2018.02.13 |
[Unity3D] 헥사게임 만들기 첫 번째 - 리소스 준비하기 및 캔버스 크기 설정. (0) | 2018.02.11 |
[Unity3D] 인벤토리 [Part 4] - Load (3) | 2017.04.24 |
[Unity3D] 헥사게임 만들기 두 번째 - 블럭보드 만들기.
※ 소스 출처 : 네이버 카페 - 유니티 허브 - 햐얀바람님.
0. 리소스 준비하기.
1). 배경 이미지.
2). 블럭보드의 배경 이미지.
3). UI테두리 이미지.
4). 블럭 이미지.
1. 캔버스 크기 설정하기.
- 게임뷰어 및 캔버스 크기 설정.
2. 블럭보드 만들기.
1). 블럭 프리팹 만들기.
아래의 이미지까지 만들어 봅시다.
블럭 이미지를 모두 블랙설정한 뒤
Texture Type : Sprite (2D and UI)
Packing Tag : BLOCK
Pixels Per Unit : 100픽셀
위와같이 셋팅 후 Apply를 눌러준다.
(2).
실제로 블럭 하나를 만들기 위해
하이러키 창에서 마우스 오른쪽 클릭 후 Sprite를 만듭니다.
(3).
만들어진 Sprite의 Transform컴포넌트에 마우스를 갖다댄뒤 오른쪽 버튼을 클릭해 Reset을 눌러
Position,
Rotation,
Scale
위의 세 값을 기본값으로 만듭니다.
그리고 Sprite의 이름을 Block으로 바꿔줍니다.
(4).
다음은 블럭의 첫 번째 이미지를 Sprite Renderer컴포넌트의 Sprite에 드래그 해줌으로써 블럭을 하나 완성 시킵니다.
(5).
다음은 블럭의 태그를 정해줘야 합니다.
상단 메뉴중 Tag옆 'Untagged'라 써져있는 박스를 눌러
Add Tag를 누른뒤 'BLOCK'이라는 태그명을 추가해 줍니다.
※ 이렇게 태그를 정해주는 이유는 스크립트 상에서 특정 태그명을 가지고있는 객체를 모두 가져올 때 사용하기 위함입니다.
(6). 이렇게 하나의 블럭이 완성되었습니다.
이제 하이러키 창에 있는 블럭을 프로젝트창에 있는 Prefeb폴더에 드래그하여 넣어주시면 프리팹으로 만들어집니다.
그뒤 하이러키 창에있는 Block객체를 지워줍니다.
이제 이 블럭의 프리팹을 이용하여 블럭보드를 만들어 봅시다.
'Unity3D > Project' 카테고리의 다른 글
[Unity3D] 헥사게임 만들기 세 번째 - 블럭 처리. (0) | 2018.02.18 |
---|---|
[Unity3D] 헥사게임 만들기 두 번째- 블럭보드 만들기2 (0) | 2018.02.13 |
[Unity3D] 헥사게임 만들기 첫 번째 - 리소스 준비하기 및 캔버스 크기 설정. (0) | 2018.02.11 |
[Unity3D] 인벤토리 [Part 4] - Load (3) | 2017.04.24 |
[Unity3D] 인벤토리 [Part 4] - Save (3) | 2017.04.23 |
[Unity3D] 헥사게임 만들기 첫 번째 - 리소스 준비하기 및 캔버스 크기 설정.
※ 소스 출처 : 네이버 카페 > 유니티 허브 > 햐얀바람님.
0. 리소스 준비하기.
1). 배경 이미지.
2). 블럭보드의 배경 이미지.
3). UI테두리 이미지.
4). 블럭 이미지.
1. 캔버스 크기 설정하기.
- 게임뷰어 및 캔버스 크기 설정.
2. 블럭보드 만들기.
1). 블럭 프리팹 만들기.
-------------------------------------------------------------------------------------------
0. 리소스 준비하기.
- 배경이미지 (출처 : 구글링)
- 블럭보드의 배경이미지 (출처 : 포토샾으로 만듬)
- UI의 테두리 (하얀바람님 프로젝트 내장 리소스)
- 블럭 이미지 (하얀바람님 프로젝트 내장 리소스)
- 크기 : 가로 세로 : 100픽셀.
(출처 : 언더테일 구글링)
1. 캔버스 크기 설정하기.
1).
게임 뷰어를 720 x 1280으로 설정.
2).
마찬가지로 캔버스의 Canvas Scaler 컴포넌트의 UI Cale Mode를 Scale With Screen Size로 설정하고,
Reference Resolution을 게임뷰어와 같이 720 x 1280으로 설정한다.
'Unity3D > Project' 카테고리의 다른 글
[Unity3D] 헥사게임 만들기 두 번째- 블럭보드 만들기2 (0) | 2018.02.13 |
---|---|
[Unity3D] 헥사게임 만들기 두 번째 - 블럭보드 만들기. (0) | 2018.02.13 |
[Unity3D] 인벤토리 [Part 4] - Load (3) | 2017.04.24 |
[Unity3D] 인벤토리 [Part 4] - Save (3) | 2017.04.23 |
[Unity3D] 인벤토리 [Part 3] - Item Drag & Swap (5) | 2017.04.13 |
서드파티 네트워크 게임 엔진.
P2P 방식이 아닌 온라인 게임을 개발하려면 물리적인 서버와 네트워크 게임 서버가 필요하다.
네트워크 게임 서버를 자체적으로 개발하기 위해서는 오랜 시간과 개발 경험, 실력있는 개발자가 필요하다.
서버를 직접 개발하는 것이 현실적으로 어려울 때는 이미 검증된 네트워크 게임 엔진을 도입하는 것도 좋은 방법이다.
※ 유니티를 지원하는 대표적인 게임 엔진 목록.
제품명 |
홈페이지 |
Photon |
http://www.photonengine.com |
Muchefferent |
http://www.muchdifferent.co.kr |
ElectroServer |
http://www.electrotank.com |
SmartFox |
http://www.smartfoxserver.com |
프라우드넷 |
http://www.nettention.com |
포톤 네트워크 게임 엔진은 현재 유니티에서 가장 많이 사용하는 엔진 중 하나다.
또한 포톤 PUN(Photon Unity NetWorking)의 경우 20명의 동시 접속 사용자(CCU, ConcurrentUser)까지는 무료로 사용할 수 있다.
포톤 관련 제품은 다양하지만 크게 포톤 서버와 포톤 PUN으로 분류할 수 있다.
포톤 PUN은 포톤 클라우드와 동일하며, 이름만 포톤 PUN으로 바뀌었다.
포톤서버? 포톤 클라우드?
포톤 서버 : 물리적으로 서버를 운영하는 것.
포톤 클라우드 : 서버를 임대해 사용하는 것.
포톤 서버를 운영하려면 서버의 보안, 로드 밸런싱, 백업 및 네트워크 트래픽을 관리할 수 있는 전문 인력이 필요하다.
하지만, 포톤 클라우드는 이러한 문제를 전혀 신경쓰지 않아도 된다.
SaaS(Software as a Service)의 개념으로 소프트웨어를 임대해 사용하는 방식이다.
|
포톤 서버 |
포톤 클라우드 |
서버 운영 및 관리 |
필요 |
불필요 |
확장성(로드밸런싱) |
직접 관리 |
관리 없음 |
서버 사이드 게임 로직 |
커스터마이징 가능 |
불가능 |
라이선스 |
서버당 과금 체계 |
동시 접속 사용자별 과금 체계 |
※ 포톤 서버와 클라우드 비교
포톤 쿨라우드 서버스를 이용하려면 먼저 회원가입을 해야한다.
www.photonengine.com에 접속한 후 신규 가입 메뉴를 선택하면 회원가입 페이지가 나온다.
'Unity3D > NetWork' 카테고리의 다른 글
네트워크 (NetWork) (0) | 2017.05.08 |
---|
네트워크 (NetWork)
네트워크 게임 정의
네트워크 게임이란 물리적으로 떨어져 있는 다른 사용자와 통신망(LAN, 인터넷)을 이용해 서로 게임하는 것을 말한다.
네트워크 게임을 개발하려면 TCP/IP, UDP 같은 통신 프로토콜의 개념과 소켓 프로그래밍에 관한 지식뿐 아니라 서버와 네트워크에 관한 지식도 필요하다.
물론 단시간 내에 이런 지식과 경험을 쌓을 수 없을 정도로 방대하고 다양한 경우의 수가 있는 것이 바로 네트워크 게임 개발이다. 따라서 네트워크 게임을 개발해본 경험이 없는 대부분의 개발자가 쉽사리 접그하긴 어렵다.
이처럼 난이도가 높고 개발 실력이 있어야 하는 네트워크 게임을 유니티는 쉽게 개발할 수 있도록 고수준 API(High Level API)를 제공한다. 물론 쉽게 개발할 수 있다는 것이지 네트워크의 이론적 배경을 전혀 몰라도 된다는 것은 아니다.
네트워크 게임의 물리적 구조.
네트워크 게임의 물리적 구조에 따라 크게 두 가지로 나눌 수 있다.
1. P2P(Peer to Peer) 방식
2. 클라이언트 / 서버 방식
첫 번째 방식은 개인과 개인이 별도의 서버 없이 연결해 데이터를 송수신하는 구조를 말한다.
주로 메신저나 파일 공유 프로그램에서 사용하는 방식이다. 네트워크 상에 사용자가 직접 접속해 게임을 하며,
비교적 접속자 수가 적은 게임에 적용할 수 있다.
두 번째 방식은 별도의 게임 서버를 두고 여러 클라이언트(사용자)가 접속한 후 서로의 데이터를 게임 서버를 이용해 송수신하는 방식이다. 게임 서버의 역할은 접속한 사용자 간의 데이터를 릴레이 하고 게임 데이터를 데이터베이스에 저장하는 것이다.
대부분의 온라인 게임은 클라이언트/서버 방식으로 구현돼 있다.
네트워크 통신 프로토콜
프로토콜은 네트워크 상에서 데이터 통신을 하기 위한 일종의 규약이다.
다양한 프로토콜이 있지만, 게임에서 주로 TCP/IP, UDP 프로토콜을 사용한다.
정해진 프로토콜로 송수신하는 데이터의 단위를 패킷(Packet)이라 한다.
TCP/IP, UDP 프로토콜은 패킷 전송 방식과 관련해서 다음과 같은 차이점이 있다.
- TCP/IP
데이터의 유실 없이 100개를 보내면 상대편에서 100개를 받을 수 있게 정합성을 최우선으로 하는 통신 규약.
중간에 패킷이 유실됐을 때 다시 재전송 한다. 또한 패킷을 보내는 순서도 정확히 지켜 전송한다.
다만 속도가 UDP 프로토콜에 비해 상대적으로 느리다.
- UDP
TCP/IP와 달리 상대편이 받을 준비가 돼 있는지 확인하는 절차 없이 무조건 보내는 프로토콜.
매우 빠른 전송 속도를 보장한다. 또한 네트워크 상황에 따라 중간에 패킷이 유실된다.
패킷을 보내는 중간이 유실된다고 해도 재전송 절차가 없다. 또한 패킷의 순서도 보장할 수 없다.
중간에 패킷이 하나씩 유실돼도 크게 관계없는 환경에 주로 사용되며, FPS 게임처럼 액션의 비중이 크고 속도감 있는 게임에서는 주로 UDP 프로토콜을 사용한다.
'Unity3D > NetWork' 카테고리의 다른 글
서드파티 네트워크 게임 엔진. (0) | 2017.05.09 |
---|
[Unity3D] Canvas
캔버스 객체가 가진 컴포넌트
- Rect Transform.
- Canvas.
- Canvas Scaler.
- Graphic Raycaster.
이렇게 4개로 구성된 컴포넌트를 가지고 있다.
기존 GameObject와는 다르게 UI항목은 반드시 Rect Transform 컴포넌트를 가지고 있다.
※ Rect Transform
- 앵커 (Anchors)
- 피봇 (Pivot)
- 크기 (Width, Height)
- 위치 (PosX, PosY, PosZ)
간단히 생각해서 Rect Transform은 UI전용 Transform이라고 생각하면 된다.
Canvas 객체의 Rect Transform속성은 직접 수정할 수 없으며, 화면의 크기에 따라 자동으로 설정된다.
Canvas Component
Canvas 컴포넌트는 UI항목을 화면에 배치하고 렌더링하는 역할을 한다.
Render Mode 옵션에 따라 UI항목의 화면 배치 방식을 다음과 같이 결정할 수 있다.
1). Screen Space - Overlay
가장 일반적인 옵션이고 기본 설정값으로 UI항목은 씬의 가장 상위 계층에서 표현되며, 화면의 해상도에 맞춰
자동으로 스케일이 조절된다.
1920 x 1080
Free Aspect
2). Screen Space - Camera
씬의 가장 상위에 UI 항목이 표시되는것은 'Screen Space - Overaly'와 동일하지만,
UI항목을 렌더링 하는 별도의 카메라를 설정할 수 있다.
즉, 씬 전체를 비추는 Main Camera와 별도로 UI만을 위한 카메라로 이원화할 경우에 사용된다.
Render Mode 속성을 Screen Space - Camera로 변경하면 카메라를 연결할 수 있는 Render Camera 속성이 노출된다.
※ UI 항목을 렌더링 하기 위해 추가한 카메라는 기존의 Main Camera와 충돌이 없도록 반드시
Clear Flag, Culling Mask, Depth 속성을 적절히 설정해야 한다.
3). World Space
이 옵션은 씬 내에 있는 다른 게임오브젝트에 직접 UI 항목을 추가한다.
대표적인 사용례는 HUD(Head Up Display)를 구현할 때다.
특정 게임오브젝트에 Canvas 객체를 추가하고 Render Mode를 World Space로 설정하면 해당 Canvas는
Rect Transform의 영향을 받지 않게 되며, 해당 오브젝트의 위치에 영향을 받게 된다.
'Unity3D > Tip' 카테고리의 다른 글
[Unity3D] EventSystem (1) | 2017.05.03 |
---|---|
[Unity3D] 유니티 스플래시 스크린(로고 삽입) (0) | 2017.05.02 |
[Unity3D] 델리게이트 (Delegate) (0) | 2017.04.29 |
[Unity3D] 메카님으로 설정된 3D모델 최적화. (0) | 2017.04.29 |
[Unity3D] Shadow - 그림자 생성. (0) | 2017.04.27 |