[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 스크립트의 여태까지 한 부분에 대한 풀소스 입니다.


글이 길어지니 끊고 다음 게시물로 이어가겠습니다.