[Unity3D] 헥사게임 만들기 네 번째 - 마우스 처리2

※ 소스 출처 : 네이버 카페 - 유니티 허브 - 햐얀바람님.

 

 

0. 리소스 준비하기.

 

1). 배경 이미지.

2). 블럭보드의 배경 이미지.

3). UI테두리 이미지.

4). 블럭 이미지.

 

 

1. 캔버스 크기 설정하기.

- 게임뷰어 및 캔버스 크기 설정.

 

 

2.  블럭보드 만들기.

1). 블럭 프리팹 만들기.

2). 배경 이미지 깔기.
3). 블럭배경 이미지 깔기.
4). 스크립트로 블럭 만들기.
 
 
3.  블럭 처리.
1). 가로 및 세로 체크 함수 만들기.
2). 매치된 블럭이 있는지 없는지 체크하는 함수 만들기.
3). 블럭 삭제 함수 만들기.
4). 떨어지는 블럭 함수 만들기.
 
 
4.  마우스 처리.
1). 마우스 클릭처리 함수 만들기.
2). 마우스 방향에 따른 블럭 가져오는 함수 만들기.
3). 선택된 블럭을 마우스가 이동한 방향에 있는 블럭과 교환(이동)시키는 함수 만들기.
4). 선택된 블럭과 이동 방향에 존재하는 블럭의 위치 교환 및 타입교환하는 함수 만들기.
5). 블럭 교환 후 매치된 블럭이 없을 때 다시 교환 시켜주는 함수 만들기.
 
 
-------------------------------------------------------------------------------------------
 
4.  마우스 처리.
 
 

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(-10);
                }
 
                // 오른쪽
                if (MouseOffset.x < -fMouseMoveDis)
                {
                    if (SelectBlock.transform.position.x < iBlockX - 1)
                        MouseDirection(10);
                }
 
                // 위
                if (MouseOffset.y < -fMouseMoveDis)
                {
                    if (SelectBlock.transform.position.y < iBlockY - 1)
                        MouseDirection(01);
                }
 
                // 아래
                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 - 10);
                                sBlock.bDown = true;
                                break;
                            }
                        }
                    }
                }
            }
        }
 
        if (bBlockDown)
            bBlockMoveDown = true;
        else
        {
            bReCheck = true;
            StartCoroutine(BlockCheck());
        }
    }
}
cs

 

 

 

[Unity3D] 헥사게임 만들기 네 번째 - 마우스 처리.

※ 소스 출처 : 네이버 카페 - 유니티 허브 - 햐얀바람님.



0. 리소스 준비하기.


1). 배경 이미지.

2). 블럭보드의 배경 이미지.

3). UI테두리 이미지.

4). 블럭 이미지.



1. 캔버스 크기 설정하기.

- 게임뷰어 및 캔버스 크기 설정.



2.  블럭보드 만들기.

1). 블럭 프리팹 만들기.

2). 배경 이미지 깔기.
3). 블럭배경 이미지 깔기.
4). 스크립트로 블럭 만들기.


3.  블럭 처리.
1). 가로 및 세로 체크 함수 만들기.
2). 매치된 블럭이 있는지 없는지 체크하는 함수 만들기.
3). 블럭 삭제 함수 만들기.
4). 떨어지는 블럭 함수 만들기.


4.  마우스 처리.
1). 마우스 클릭처리 함수 만들기.
2). 마우스 방향에 따른 블럭 가져오는 함수 만들기.
3). 선택된 블럭을 마우스가 이동한 방향에 있는 블럭과 교환(이동)시키는 함수 만들기.
4). 선택된 블럭과 이동 방향에 존재하는 블럭의 위치 교환 및 타입교환하는 함수 만들기.
5). 블럭 교환 후 매치된 블럭이 없을 때 다시 교환 시켜주는 함수 만들기.



-------------------------------------------------------------------------------------------

4.  마우스 처리.

사진

우리는 마우스 클릭과 드래그를 통해 블럭과 블럭의 교환을 수행할 겁니다.


1). 마우스 클릭처리 함수 만들기.

(1). 마우스 클릭.

마우스 클릭으로 자신이 선택한 블럭이 무엇인지 알아내기 위해서는 레이캐스트를 쏠 필요가 있습니다.
그리고 선택된 블럭 자신은 콜라이더를 가지고 있어야 자신이 Ray에 맞았는지 알 수 있습니다.

첫 번째로 할 일은 블럭 프리팹에 콜라이더를 씌워주는 일을 해야 합니다.



블럭 프리팹에 박스 콜라이더를 씌워줬다면 바로 스크립트를 작성하도록 합시다.

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


Ray는 2가지 정보를 가지고 있습니다. 시작점(위치)와 방향 입니다.

레이캐스트 함수를 사용할 때 시작점(위치)정보와 뱡향, 발사 거리 등의 정보가 필요합니다.
쉽게 말해서 어떤 위치에서 어떤 방향으로 얼마마큼의 거리로 쏠거냐 하는 겁니다.

Raycast(레이(위치와 방향),
          레이캐스트를 맞은 오브젝트의 정보를 저장할 변수,
          거리)

그렇게 Ray를 발사해서 레이에 맞은 오브젝트의 태그가 BLOCK이라면
그 블럭의 오브젝트를 SelectBlock에 초기화 시켜 줍니다.
그리고 위치정보를 StartPos1에 초기화 시킵니다.

SelectBlock은 마우스로 블럭을 클릭 했을 때 그 블럭이 선택 됐는지 파악하거나 그 블럭의 위치 정보를 사용할 때 사용됩니다.

StartPos1은 현재 블럭의 위치에서 어떤 방향으로 마우스를 움직였을 때 그 방향에 존재하는 블럭의 위치를 알아 낼때 씁니다.



예를들어 노란색 박스의 위치가 StartPos1의 위치라고 했을 때
마우스가 오른쪽 방향으로 드래그 한다면 그 방향에 존재하는 블럭의 위치는 초록색 박스의 위치가 될것 입니다.

쉽게 말해서 StartPos의 x값을 +1 해준다면 오른쪽 블럭이 되는거고
StartPos의 x값을 -1 한다면 왼쪽 위치에 블럭이 될 것입니다.
y값도 마찬가지겠죠?


(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
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(-10);
            }
 
            // 오른쪽
            if (MouseOffset.x < -fMouseMoveDis)
            {
                if (SelectBlock.transform.position.x < iBlockX - 1)
                    MouseDirection(10);
            }
 
            // 위
            if (MouseOffset.y < -fMouseMoveDis)
            {
                if (SelectBlock.transform.position.y < iBlockY - 1)
                    MouseDirection(01);
            }
 
            // 아래
            if (MouseOffset.y > fMouseMoveDis)
            {
                if (SelectBlock.transform.position.y > 0)
                    MouseDirection(0-1);
            }
        }
    }
}
cs


MouseEndPos 이 변수에는 마우스의 실시간 위치가 들어갑니다.

MouseOffset은 특정 위치에서 특정 위치를 뺀 남은 거리라고 생각하시면 됩니다.
이 거리를 이용하여 현재 블럭에 대해서 어떤 블럭이 있는 방향을 알 수 있게 해줍니다.
무슨 말인지 이해 안가시죠?

그림설명 들어갑니다.


빨간색의 네모난 박스가 현재 선택한 블럭입니다. MouseStartPos에 해당하죠.

그 옆에 파란색 동그라미는 현재 마우스의 위치 입니다. MouseEndPos에 해당합니다.


MouseStartPos MouseEndPos의 자료형은 Vector3입니다.
일단 여기서 y, z값은 제외하고 x값만을 생각해 봅시다.

x값만을 생각 했을 때 MouseStartPos의 x값은 스크린 왼쪽에서 이어지는 빨간색 선이라고 생각하시면 됩니다.
마찬가지로 MouseEndPos의 x값은 파란색 선이 됩니다. 

MouseOffset을 보면 MouseStartPos - MouseEndPos 입니다.
즉 파란색 선에서 빨간색 선의 길이만큼 빼면 노란색 선의 길이 만큼이 남습니다.

이 노란색 선은 현재 선택한 블럭의 위치로부터 마우스가 이동한 거리가 됩니다.

그럼 이 거리는 어디다 쓰는 걸까요?

노란색 거리는 임의로 정해둔 fMouseMoveDis의 거리를 넘어가게 된다면 비교 연산이 일어나게 됩니다.

오른쪽 방향에 가까운지 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(-10);
    }
 
    // 오른쪽
    if (MouseOffset.x < -fMouseMoveDis)
    {
        if (SelectBlock.transform.position.x < iBlockX - 1)
            MouseDirection(10);
    }
 
    // 위
    if (MouseOffset.y < -fMouseMoveDis)
    {
        if (SelectBlock.transform.position.y < iBlockY - 1)
            MouseDirection(01);
    }
 
    // 아래
    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] 헥사게임 만들기 세 번째 - 블럭처리2.

※ 소스 출처 : 네이버 카페 - 유니티 허브 - 햐얀바람님.



0. 리소스 준비하기.


1). 배경 이미지.

2). 블럭보드의 배경 이미지.

3). UI테두리 이미지.

4). 블럭 이미지.



1. 캔버스 크기 설정하기.

- 게임뷰어 및 캔버스 크기 설정.



2.  블럭보드 만들기.

1). 블럭 프리팹 만들기.

2). 배경 이미지 깔기.
3). 블럭배경 이미지 깔기.
4). 스크립트로 블럭 만들기.


3.  블럭 처리.
1). 가로 및 세로 체크 함수 만들기.
2). 매치된 블럭이 있는지 없는지 체크하는 함수 만들기.
3). 블럭 삭제 함수 만들기.
4). 떨어지는 블럭 함수 만들기.


4.  마우스 처리.
1). 마우스 클릭처리 함수 만들기.
2). 마우스 방향에 따른 블럭 가져오는 함수 만들기.
3). 선택된 블럭을 마우스가 이동한 방향에 있는 블럭과 교환(이동)시키는 함수 만들기.
4). 선택된 블럭과 이동 방향에 존재하는 블럭의 위치 교환 및 타입교환하는 함수 만들기.
5). 블럭 교환 후 매치된 블럭이 없을 때 다시 교환 시켜주는 함수 만들기.


-------------------------------------------------------------------------------------------

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
// 매치된 블럭 삭제하기.
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 - 10);
                            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 - 10);
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라인)를 호출해 줍니다.


 



prev 1 2 3 4 ··· 29 next