본문으로 바로가기

세부적인 기획이 확정되면서, 요마전에는 더욱 다양한 스킬액션이 요구되게 되었습니다. 지난번 포스트에 있는 자유궤적에 이어서 이번에는 클래스별로 다양한 스킬구현을 시작하게 되었는데, 지난번 자유궤적이 마치 체인라이트닝과 같은 스킬이라면, 이번 포스트에서 보여드릴 스킬은 바로 전사의 스킬 중 하나인 "대쉬"입니다.


요마전에서 대쉬는 플레이어 캐릭터를 마치 새총의 총알처럼 끌어당긴후 놓으면 그 방향으로 재빠르게 달려가 적과 충돌할 수 있도록 설계되어야 했습니다. 아래 그림처럼 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;
    }
 
}