본문 바로가기
개발 (Game)/(2021) Unity RTS 포트폴리오

[Day 2-2] 유닛 선택을 활용한 유닛 이동 구현

by 진현개발일기 2021. 7. 13.

[2021. 7. 13]

유닛의 이동 가능 거리가 1,2 일경우
이동 가능 거리가 3일 경우

 

처음에 머릿속에서 그려놓은 이미지가 딱 이런 방식이었다. 

각각의 유닛의 이동 가능 거리 스텟에 따라 그에 맞는 이동 가능한 타일들이 미리 그려지고 실제로 클릭 시 이동하는 것인데. 문제는 구글링이나 유튜브에서 찾아보면 대부분 이러한 것들을 2차원 배열에 넣어놓고 

 a*, 깊이우선, 너비 우선 탐색 등으로 해결하는 모습밖에 보지를 못했다.

 

일단 처음부터 내가 만든 맵에선 타일들을 2차원 배열에 넣는 것은 조금 불가능해 보였다.

(물론 계속 시도하면 무조건 방법을 찾게 될 것이지만 시간상 효율적인 방법을 생각해봐야 한다)

 

그 이유는 처음부터, Tile Palette에서 브러시로 맵을 만들 때, 가로와 세로 정렬을 생각하면서 만들지 않았기 때문이다.

이런 식으로 맵을 만들었다는 뜻이다

그렇기 때문에 배열에 타일들을 순서대로 넣을 수가 없는 것이다.

곰곰이 생각을 해봤고 두 가지 방법을 고민해봤다.

첫째로 Tile들이 가지고 있는 Coord의 속성들(x, y)을 활용할 것인지 ( 밑에 코드 있음 )

둘째로 Physics.Overlap을 활용하여 특정 범위 내에 타일들을 끌어 모은 뒤 내가 원하는 타일들만 골라내는 것이다.

 

첫 번째 방식으로 계속하여 고민하며 틀을 잡아봤는데, 많은 시간을 소비할 것 같아서, 두 번째 방식으로

최선을 다한 뒤에 안될 경우 활용하기로 결정하였다.

 

 

두번째 방법 활용 체크(가운데 박스 좌클릭 시), MoveStat은 2 이다.
두번째 방법 활용 체크(좌측 하단 박스 좌클릭 시), MoveStat은 2 이다.

여러 차례의 수정 및 실패를 계속 반복하다가, 몇 시간 뒤, 드디어 위 사진들 처럼 이동 가능 범위 내에 타일들이 표기가 되고 적군이 있는 곳은 빨간색으로 표기가 된다. 아군 유닛이 있는 곳은 이동 불가능한 상태이다. 그러나 이동 시

오류가 있었다.

 

고치기 전

 고치기 전 오류는 이전에 내가 있던 장소를 저장하는 변수가 있는데 이를 이동 전에 null을 시켜도

 위 사진처럼 저렇게 되는 것이다.  물론 실제 게임 유닛은 매 턴당 한 번 밖에 이동을 못하지만 위 예시는 실험용으로 제한을 걸어놓지 않았다.

 

UnitsGridSystem에서 변수들을 모두 초기화 시키는 Default 함수를 조금 변경하였다.

SelectManager에서 현재 선택된 유닛을 static 변수로 저장하고 UnitsGridSystem에서 활용하기 때문에 

처음에 고려하지 못했던 예외를 처리해줘야했다. 기존 코드가 없어서 비교는 못할것같다 ㅠ

 

 

고친 후

느낌 있어졌다. 다만 내일 수정해야할 부분은 다른 유닛들이 위치한 타일 뒷쪽은 이동 불가가 되어야한다.

 

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
using UnityEngine;
using YJH.World.Unit;
 
namespace YJH.World.Tile
{
 
    #region Tile Class
    public class WorldTile : MonoBehaviour
    {
        public Coord coord;
 
        /* 각 타일들의 coord의 차이는
          X축 기준 +2, -2, +4, -4, +8, -8
           Y축 기준  0, 13.5, -13.5 이다  */
 
        #region Tile Stats
 
        [Space(10)]
        [Header("▼ Current Type Express ")]  // GridUnitSystem.cs에서 제어한다.
        [Space(10)]
        public GameObject Selectable;
        public GameObject EnemySpotted; 
 
        [Space(10)]
        [Header("▼ Current Type")] // Units.cs에서 제어한다.
        [Space(10)]
        public TileType CurrentType;
 
        [Space(10)]
        [Header("▼ Movable")] // GridUnitSystem.cs에서 제어한다.
        [Space(10)]
        public bool Movable = false;
 
 
        [Space(10)]
        [Header("▼ Tile Owner")]  // Units.cs에서 제어한다.
        [Space(10)]
        public Units tileOwner;
 
        #endregion
 
 
        #region Unity Methods
 
        private void Awake()
        {
            coord = new Coord(transform.localPosition.x, transform.position.z);
                              // x 로컬 좌표가 작은 숫자라서 넣었고
                              // z 좌표는 월드 좌표가 더 명확한 숫자라서 월드로 넣었다.
        }
        #endregion
    }
    #endregion
 
 
    #region Coord Information
    [System.Serializable]
    public class Coord
    {
        public float x;
        public float y;
 
        public Coord(float _x, float _y)
        {
            this.x = _x;
            this.y = _y;
        }
    }
    #endregion
 
 
    public enum TileType
    {
        Empty,
        Movable,
        UnitExist
    }
 
}
 
cs

▲ WorldTile.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
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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
using YJH.World.Tile;
using YJH.World.SelectManager;
using YJH.World.Unit;
 
namespace YJH.World.Grid.UnitSystem
{
    public class GridUnitSystem : MonoBehaviour
    {
        public static GridUnitSystem current;
 
        //// 선택한 영역 및 주변 타일의 정보를 쉽게 받아오기 위해서 Dictionary를 활용한다.
        //private static Dictionary<WorldTile, TileType> tileBases = new Dictionary<WorldTile, TileType>();
 
        [Space(10)]
        [Header("▼ Unit onPlaced")]
        [Space(10)]
        [SerializeField]
        WorldTile unitPlaced;
        [SerializeField]
        Transform unit;
 
        [Space(10)]
        [Header("▼ Unit onPlaced")]
        [Space(10)]
        public float unitMoveStats;
        public List<WorldTile> selectableTiles = new List<WorldTile>();
 
 
        [Header("▼ clickable-Tiles Layer")]
        [Space(10)]
        public LayerMask interactableLayer = new LayerMask();
        RaycastHit hit;
 
        public bool SearchedTiles = false;
 
        #region Unity Methods
        private void Awake()
        {
            current = this;
        }
 
        public void Update()
        {
            if(unit)
            {
                UnitMove();
            }
 
 
            if(!SearchedTiles && unitPlaced)
            {
                SearchSelectables();
            }
 
           ConfigureTileUnit();
        }
        #endregion
       
 
        #region Tilemap Management
        void SearchSelectables()
        {
            // Overlap으로 일단 많은 타일을 잡는다.
            Collider[] numColliders = Physics.OverlapSphere(unit.position, 
                                                             50f,
                                                             1<<6
                                                             QueryTriggerInteraction.Collide);
 
          for(int i=0; i<numColliders.Length;i++)
          {
              selectableTiles.Add(numColliders[i].GetComponent<WorldTile>());
 
                // 잡은 타일 중에서 유닛이 이동 가능한 타일들만 표시해준다.
                // (유닛의 이동 스텟에 비례한 길이 이내에 위치한 타일만 이동 가능)
              if(Vector3.Magnitude(selectableTiles[i].transform.position - unit.position)< unitMoveStats*18f)
              {
                  if(selectableTiles[i]!=unitPlaced) // 현재 선택한 유닛이 밟고 있는 타일은 걸러낸다.
                  {
                     if(selectableTiles[i].CurrentType.Equals(TileType.Empty)) // 유닛이 없는 곳만 흰색 표시
                     {
                       selectableTiles[i].Movable = true;
                       selectableTiles[i].CurrentType = TileType.Movable;
                       selectableTiles[i].Selectable.SetActive(true);
                     }
                     else if (selectableTiles[i].CurrentType.Equals(TileType.UnitExist) &&
                              selectableTiles[i].tileOwner.tag.Equals("Enemy")) // 적 유닛이 존재하는 곳은 빨간색 표시
                     {
                        selectableTiles[i].EnemySpotted.SetActive(true);
                     }
                    
                  }
              }
          }
 
            SearchedTiles = true;
        }
 
        void UnitMove()
        {
            if (Input.GetMouseButtonDown(1)) // 오른쪽 클릭
            {
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                if (Physics.Raycast(ray, out hit, 200, interactableLayer))
                {
                    /* 이동 가능한 타일이면 해당 유닛을 그 위치로 이동 시켜준다. */
                    if (hit.transform.GetComponent<WorldTile>().CurrentType.Equals(TileType.Movable))
                     // && unit.GetComponent<Units>().MovableNum>0)
                    {
                        unit.position = hit.transform.position + Vector3.up * 6.5f;
                    // unit.GetComponent<Units>().MovableNum--;  
                        DefaultTiles();
                    // unit.GetComponent<Units>().AdjustMoveNum(1); 턴 끝나면 요거 해줘야함
                    }
                    else if (hit.transform.GetComponent<WorldTile>().CurrentType.Equals(TileType.UnitExist))
                    {
                        // if(tag.Equals(적)) +공격 함수
                    }
                }
            }
        }
 
 
        void DefaultTiles()
        {
            if(unitPlaced)
            unitPlaced = null;
 
            if(!unitMoveStats.Equals(0))
                unitMoveStats = 0;
 
            if(selectableTiles.Count>0)
            {
                for (int i = 0; i < selectableTiles.Count; i++)
                {
                    if (selectableTiles[i].CurrentType.Equals(TileType.Movable))
                    {
                        selectableTiles[i].CurrentType = TileType.Empty;
                    }
 
                    selectableTiles[i].Movable = false;
                    selectableTiles[i].Selectable.SetActive(false);
                    selectableTiles[i].EnemySpotted.SetActive(false);
                    if (i.Equals(selectableTiles.Count - 1))
                    {
                        selectableTiles.Clear();
                    }
                }
            }
            
           if(unit)
            unit = null;
           
           if(SearchedTiles)
            SearchedTiles = false;
 
           if (SelectManager.SelectManager.SelectedUnits)
            SelectManager.SelectManager.SelectedUnits = null;
 
        }
        #endregion
 
 
        #region Unit&Tile Configure
 
        void ConfigureTileUnit()
        {
            // 유닛 선택시 유닛이 있는 곳에 위치한 타일을 받아온다.
            if (SelectManager.SelectManager.SelectedUnits)
            {
                if (unit == null)
                {
                    unit = SelectManager.SelectManager.SelectedUnits;
                }
                else if (unit)
                {
                    if (unitPlaced == null)
                    {
                        unitMoveStats = unit.GetComponent<Units>().unitMoveStats;
                        unitPlaced = unit.GetComponent<Units>().CurrentTile;
                    }
                }
            }
            else // 다른 곳을 클릭 했을 때이다.
            {
                DefaultTiles();
            }
        }
        #endregion
 
    }
 
}
 
cs

▲ GridUnitSystem.cs(오늘자 최종) 이다.

    

728x90