세부적인 기획이 확정되면서, 요마전에는 더욱 다양한 스킬액션이 요구되게 되었습니다. 지난번 포스트에 있는 자유궤적에 이어서 이번에는 클래스별로 다양한 스킬구현을 시작하게 되었는데, 지난번 자유궤적이 마치 체인라이트닝과 같은 스킬이라면, 이번 포스트에서 보여드릴 스킬은 바로 전사의 스킬 중 하나인 "대쉬"입니다.
요마전에서 대쉬는 플레이어 캐릭터를 마치 새총의 총알처럼 끌어당긴후 놓으면 그 방향으로 재빠르게 달려가 적과 충돌할 수 있도록 설계되어야 했습니다. 아래 그림처럼 1/2/3 개의 포인트를 가지고 보시면 됩니다. 스킬의 구현은 아래 그림의 Sequence 순서대로 진행됩니다. 따라서 플레이어 위치가 1->2 로 바뀌고 후에 2->3 으로 바뀝니다. 플레이어 캐릭터의 선택과 이동 그리고 판별은 지난번 포스트와 같이 Ray를 이용하여서 구현하였습니다.
스킬이 발동되는 순간 카메라는 고정되며 플레이어가 당길 수 있고 그에따른 장력은 시간으로 구현하였습니다. 또한 CharacterController의 SimpleMove로 인해 플레이어가 화면밖으로 나가서 사라지는 것을 방지하는 코드가 존재합니다. (정확히 당긴 거리만큼만 이동할 수 있도록)
다만, 새총매커니즘과 다른점은 시간을 통한 장력을 구현하였기 때문에 거리가 길던 짧던 일정 시간이 지나면 플레이어의 위치를 더이상 당길 수 없는 단점이 존재하고 있습니다. 구현된 코드는 아래와 같습니다. 기존 스킬액션 구현하기 #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 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 | using UnityEngine; using System.Collections; using System.Collections.Generic; // ai 연구 시작 12-05 public class LinesGL : MonoBehaviour { /* 변수 설명부분 * [GameObject] player : 플레이어 캐릭터 * [GameObject] gb : 불꽃이펙트 (Trail Renderer) * [GameObject] gb2 : 선 이펙트 (Trail Renderer) * [GameObject] g : 안쓰는 오브젝트1 * [Vector3 배열] lp : * [Vector3 배열] lp2 : * s : Vector3 형 변수 (초기값은 자동으로 0, 0, 0) * s2 : Vector3 형 변수 (초기값은 자동으로 0, 0, 0) * aa : 스킬버튼 눌렀을때(-1), 보통상태(0), 이펙트가움직이는동안(1) * time : 이펙트가 그려질때 사용되는 시간변수 * time2 : 초기화부분에서 사용되는 시간변수 * time3 : 불꽃이펙트의 시작에 사용되는 시간변수 * a : 배열 위치변수 * bb : 이펙트가 스킬을 그린지점까지 도착여부를 알리는 스테이트 */ public GameObject player; public GameObject gb; public GameObject gb2; public GameObject g; private Vector3[] lp; private Vector3[] lp2; public GameObject me; private Vector3 s; private Vector3 s2; static public int aa = 0; float time = 0; float time2 = 0; float time3 = 0; float dash_lerp_time = 1; float dash_lerp_time2 = 0.5f; int dash_buttonup = 0; int a = 0; int bb = 0; bool playerTouch = false; private bool monster_collider; Vector3 clickPosition; Vector3 dir; Vector3 dir2; float distance; void Start() { lp = new Vector3[0]; lp2 = new Vector3[0]; //Debug.Log(s); //Debug.Log(s2); } void Update() { // 스킬버튼을 눌렀을 시 if (aa == -1) { // 불꽃이펙트의 시작위치를 플레이어의 위치로 설정 gb.transform.position = player.transform.position; time3 += Time.deltaTime; // 버튼 누르고 그리기까지 0.5초 if (time3 > 0.5f) { time3 = 0; aa = 0; } } // 두번째 스킬버튼 눌렀을때 (새총같은 스킬) if (aa == 3) { // 마우스 버튼을 누르는 동안 if (Input.GetMouseButtonDown(0)) { Time.timeScale = 0.4f; // Ray 사용 선언부 Ray ray3 = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hitInfo3; // Ray를 쏨 if (Physics.Raycast(ray3, out hitInfo3)) { //Ray를 맞은 GameObject가 me(Player 자신)이면 if (hitInfo3.transform.gameObject == me) { // 첫 클릭 포지션(새총의 중심점)은 플레이어의 포지션이 되어야 함 clickPosition = me.transform.position; playerTouch = true; Debug.Log("aaaaa"); } } } // 스킬버튼2를 눌렀을때 항상 처음은 playerTouch가 false로 시작 if (playerTouch == false) { //Debug.Log("dis = " + distance); Debug.Log("dash_lerp_time2 = " + dash_lerp_time2); // distance2 = 마우스 클릭한 포지션과 플레이어 사이의 거리 float distance2 = Vector3.Distance(clickPosition, me.transform.position); //Debug.Log("dis2 = " + distance2); // dash_buttonup이 떼어졌음이 확인이 되면 그때부터 시간을 측정 비교(dash_buttonup이 존재하는 이유) if (dash_buttonup == 1) { if (dash_lerp_time2 > 0) { dash_lerp_time2 -= Time.deltaTime; } else { dash_lerp_time2 = 0; } } //마우스 클릭한 포지션과 me(플레이어)포지션 사이의 거리만큼 이동시킨다.(0에 점점 가까워졌다가 다시 커져서 거리만큼) //SimpleMove는 목적지를 알수 없기 때문에 무한히 이동할 수 있음. if (distance >= distance2 - 0.3f) { Debug.Log("enter"); me.GetComponent<CharacterController>().SimpleMove(dir * dash_lerp_time2 * distance * 7); } } // 플레이어를 클릭하고 마우스가 드래그 상태임이 확인이 되었으면 if (playerTouch == true) { if (Input.GetMouseButton(0)) { // dash_lerp_time 이 점점 감소하여 마치 새총에서 고무줄을 당길수록 느려지는 것을 표현 if (dash_lerp_time > 0) { dash_lerp_time -= Time.deltaTime; } // Ray 사용 선언부 Ray DashPoint = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hitInfo3; // Ray를 쏨 if (Physics.Raycast(DashPoint, out hitInfo3)) { // dir = 플레이어 위치에서 맨 처음 클릭한 마우스 포지션을 가리키는 벡터 dir = clickPosition - me.transform.position; // dir2 = 마우스포지션에서 플레이어를 가리키는 벡터 dir2 = me.transform.position - hitInfo3.point; // distance = 마우스 클릭한 포지션과 me(플레이어)포지션 사이의 거리 distance = Vector3.Distance(clickPosition, me.transform.position); // Ray를 맞은 부분의 정보(hitInfo3)가 "PLANE"(바닥)이면 if (hitInfo3.collider.tag == "PLANE") { Debug.Log(Mathf.Lerp(0.3f, 10f, 1f)); // 맨처음 클릭한 마우스 포지션에서 플레이어의 원래 위치방향으로 바라봄 Quaternion toTarget = Quaternion.LookRotation(dir); me.transform.rotation = toTarget; // 새총을 당긴것처럼 캐릭터를 당겼으므로 반대로 가는것을 표현하기위해 -dir2 가됨 me.GetComponent<CharacterController>().SimpleMove(-dir2 * dash_lerp_time * 5); // me.transform.position =Vector3.Lerp(me.transform.position, hitInfo3.point, 0.05f); } } } // 마우스 버튼을 땟을때 (드래그상태에서 벗어남과 함께 초기화) if (Input.GetMouseButtonUp(0)) { playerTouch = false; dash_buttonup = 1; //me.GetComponent<CharacterController>().rigidbody.AddForce(dir2 * distance); //me.GetComponent<CharacterController>().SimpleMove(dir2 * Mathf.Lerp(0.3f, distance, time4)); } } } // 캐릭터의 움직임 스테이트가 정지상태일때 (스킬시전때문에) if (CharacterRayMove.cha_state == 1) { if (aa == 0) { // 마우스가 드래그 상태이면(버튼클릭이 유지되는 상태) if (Input.GetMouseButton(0)) { // 선 이펙트의 움직임을 인풋의 경로로 받아온다. gb2.transform.position = GetNewPoint(); // e를 카메라 좌표로 받아옴 Vector3 e = GetNewPoint(); // e2를 마우스 좌표로 받아옴 Vector3 e2 = GetNewPoint2(); // 받아온 s 좌표가 0이 아니면.. (처음이 아닌이상 대부분 거침..) if (s != Vector3.zero) { // lp 배열에 좌표추가(카메라) lp = AddLine(lp, s, e, false); } // 받아온 s2 좌표가 0이 아니면.. if (s2 != Vector3.zero) { // lp2 배열에 좌표추가(마우스) lp2 = AddLine(lp2, s2, e2, false); } // s2 변수에 e2 변수의 값(인풋포지션)을 대입 s2 = e2; // s 변수에 월드좌표로 변환한 e 변수의 값(*,*,10)을 대입 s = e; } else { s2 = Vector3.zero; s = Vector3.zero; } // 마우스 드래그가 끝나는 순간 = 마우스 버튼클릭이 끝나는 순간 if (Input.GetMouseButtonUp(0)) { // 상태를 이펙트가 움직이는 동안의 상태로 바꿈 aa = 1; } } // 이펙트가 움직이는 동안의 상태 if (aa == 1) { time += Time.deltaTime; // 이펙트랑 lp배열0번째 거리 비교해서 1.0 이상이면(거리가 있으면) 이펙트를 그려줌. if (Vector3.Distance(gb.transform.position, lp[0]) > 1.0f) { Vector3 vec = lp[0] - player.transform.position; // 이펙트를 움직이는 부분 gb.transform.Translate(vec * 0.08f); // Debug.Log(Vector3.Distance(gb.transform.position, lp[0])); } else { // 이펙트가 플레이어위치에서 스킬을 시전한 시작위치에 도달하면 상태를 바꿔줌 bb = 1; } // lp 0번배열에 도착했으니 이펙트를 그려라. if (bb == 1) { // 시간이 0.01초 지나면 if (time > 0.01f) { // lp배열의 크기가 a(??)보다 클 경우 if (lp.Length > a) { // 이펙트의 위치를 lp배열의 a 위치로 잡는다. gb.transform.position = lp[a]; // Ray ray2는 카메라에서 쏘는 것으로 Ray ray2 = Camera.main.ScreenPointToRay(lp2[a]); // RaycastHit 판별변수(hitInfo2) 선언 RaycastHit hitInfo2; // 화면상에 레이를 표시(Debug용) Debug.DrawRay(ray2.origin, ray2.direction * 100, Color.yellow); // ray2가 쏴진 정보가 hitInfo2에 저장 if (Physics.Raycast(ray2, out hitInfo2)) { Debug.Log(hitInfo2.collider.tag); // Ray를 충돌한 오브젝트 정보가 "MONSTER"이면 해당오브젝트를 소멸 if (hitInfo2.collider.tag == "MONSTER") { Debug.Log("!!!!!"); Destroy(hitInfo2.collider.transform.gameObject); } } time = 0; a++; } else { // 스킬을 다 그렸으므로 초기화상태로 돌림 bb = 0; aa = 2; } } } } // 스킬 이펙트 그려진 이후 초기화 부분 if (aa == 2) { //time2 += Time.deltaTime; //if (time2 > 1.5f) // { lp = new Vector3[0]; lp2 = new Vector3[0]; aa = 0; a = 0; Debug.Log("change"); CharacterRayMove.cha_state = 0; //} } } } /* 배열에 점을 추가하는 함수 (s = start, e = end, tmp = true/false) */ Vector3[] AddLine(Vector3[] l, Vector3 s, Vector3 e, bool tmp) { int vl = l.Length; if (!tmp || vl == 0) { l = resizeVertices(l, 2); } else { vl -= 2; } l[vl] = s; l[vl + 1] = e; return l; } /* 배열을 받아서 배열이 꽉 차면 늘리는 함수 */ Vector3[] resizeVertices(Vector3[] ovs, int ns) { Vector3[] nvs = new Vector3[ovs.Length + ns]; for (int i = 0; i < ovs.Length; i++) { nvs[i] = ovs[i]; } return nvs; } /* 인풋(마우스)좌표를 월드좌표로 변환해서 리턴하는 함수 */ Vector3 GetNewPoint() { return Camera.main.ScreenToWorldPoint( new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10)); } /* 인풋(마우스)좌표를 리턴하는 함수 */ Vector3 GetNewPoint2() { return Input.mousePosition; } } |
'프로그래밍 > 프로젝트' 카테고리의 다른 글
[개발일지] 요마전 #2 (5) | 2014.02.14 |
---|---|
[요마전] 스킬액션 구현하기 #3 (0) | 2013.12.12 |
[요마전] 스킬액션 구현하기 #1 (0) | 2013.12.07 |
[개발일지] 요마전 #1 (1) | 2013.12.03 |
[미니프로젝트] AIE 리뷰 - 코드 분석 (2) | 2013.10.11 |