공격#2

CJB_ny·2021년 12월 29일
0

Mini_MMO_RPG

목록 보기
7/16
post-thumbnail

공격#2

자 이제 공격#1에 이어서 코드로 돌아와서 작업을 할 것인데

한동안 작업을 오래했으니까 정리를 함 하고 가보도록 하자.

1.코드 잠깐 정리

단축키 중에서 컨트롤+m+o를 누르면


이렇게 싸그리 다 접히는 기능이 있다.

싸그리 다시 다 피고 싶으면 컨트롤+m+l을 누르면 된다.


이렇게 Start위로 옮기고 _mask랑 _lockTarget도 옮기자


이런식으로 다 정리해주고

이제 우리가 해야되는 것은

2.OnHitEvent


이쪽에서 뭔가를 이제 해줘야 하는데

우리가 마우스를 누르고 있었냐 아니냐에 따라서 스킬을 계속 쓸지 아니면
State를 Moving으로 돌아갈지를 선택을 해주면 될 것이다.

그리고 마우스를 내리다가 보면은

OnMouseEvent에서

이부분이 사실 굉장히 불안하긴 한데

뭐냐하면은 지금 Die인 상태는 return;으로 바로 종료해버리는 식으로 건너뛰고

Idle이랑 Moving인 상태만을 고려해서 만들었었다.

그러면은 이제 Skill인 상태에서도 OnMouseEvent가 똑같이 들어올텐데


여기에서 조금이라도 꼬인다면 State가

여기서 이렇게 Moving으로 바뀌는 일이 일어 날 것이다.

그래서 이런것들을 보자마자 최대한 정리를 할 필요가 생기는데

애초에 지금 OnMouseEvent에 있는 내용들이 애니매이션중에 IDIL, RUN 상태일때를 가정하고 만들었으니 따로 관리를 해주도록 하자.

그래서 PlayerController제일밑에

이렇게 만들어주자 Define.MouseEvent evt 를 받는것까지는 똑같다.

그래서 원래 OnMouseEvent에 있던것들을 일로 이사시키자

그래서 OnMouseEvent에서는 내가 어떤 상황이냐에 따라가지고

호출을 해주면 될텐데

Idle과 Moving인 상태일때는

OnMouseEvent_IdleRun 이녀석을 호출을 해주면 될 것이다.
이런식으로 하니까 보기가 한결 낫다!!(ㄹㅇ)

스킬을 사용할때는 OnMouseEvent_IdleRun()함수안의 코드에 들어오지 않을 것이니 훨씬더 깰끔하다라는 것을 알 수 있다.


그리고 위의 사진에서 _destPos = _lockTarget.transform.position 이 _lockTarget의 transform에 접근을 해서 position을 뽑아 오는 것인데 이것은 무의미 하다.

왜냐하면 이미 UpdateMoving에서 이부분을 구현을 해놓았기 때문이다.

이렇게 해놓음.

3.코드가 중간에 어떻게 돌아가는지

(코드가 어떻게 돌아가는 것이냐하면은

Update()함수안에서 State를 계속 확인을 하면서 (만약 Moving상태인 경우)

UpdateMoving()함수를 호출하게 된다.

근데 실제로 플레이어가 움직이는 것은 어떻게 아냐? 하면은 InputManager에서 마우스 클릭을 실시간으로 관리를 하고있기 때문에

OnUpdate에서 이렇게 어떤 Event가 있는지 관리 중임 그래서 여기서

MouseAction이 들어오면 _pressed인지 아닌지에 따라서 PointerDown인지

Press상태인지 Click인지 구별을 해서 Invoke로 해당 이벤트가 들어 왔다고 알려주는 것이다.

그러면 알려준것이 OnMouseEvent() 함수의 인자에 evt로 들어가게 되어서

해당 상태의 evt를 함수를 호출하게 되는 것이다. )

(궁금한것은 지금 Update문 안에서는 PlayerState들만 받고있는데 어떻게
OnMouseEvent나 OnMouseEvent_IdleRun같은 함수가 호출이 될 수 있는 것인가???)

4.다시 공부

그래서 _lockTarget의 destPos를 Moving하는 함수에 넣어 놨으니까
UpdateMoving함수에 무의미하기 때문에 삭제를 해주고

이런식으로 코드를 좀 줄여 주도록 하자.

5.TODO

그리고 다시 작업을 해야 할 부분이 ![](https://velog.velcdn.com/images%2Fstarkshn%2Fpost%2Fd4de58bd-1c92-4a59-8997-cea839edb1c5%2Fimage.png) TODO를 채워 주어야 하는데

내가 스킬을 쓰는 도중에 한번이라도 마우스를 땟다라는 것을 어디에선가는 알고 있어야 하는데

그렇다는 것은

이곳에서 뭔가를 추가하면 되지 않을까라는 생각이 든다.

만약 "내가 Skill을 쓰는 중이였다"라고하면은

이부분안에 뭔가를 채워주어야 하는데 만약 이부분이 너무 커지면 새로운 함수로 분리를 하면 될 것이다.


evt가 마우스를 땐 상태 PointerUp일 경우 스킬이 안나가도록 해주면 될 것이다.

이 evt == Define.MouseEvent.PointerUp부분을 어딘가에 변수로 저장을 해주면 될거같다.

_stopSkill을 처음에는 false로 만들어주고

PointerUp인 경우 _stopSkill을 true로 다시 해주자.

그래서 결국에는 OnHitEvent로 가가지고

_stopSkill이다 아니다에 따라서 다르게 작동하면 될거같다.


이렇게 _stopSkill이 true일 경우 State를 Idle로 아니면 그대로 Skill인 상태로 놔두자.

이렇게 굳이 넣어주는 이유는 나중에 살펴 볼 것인데 일단 이대로 작업을 해주도록 하자.

그래서 이대로 테스트를 해보면 한대 치고 돌아가는지 확인을 해보면


한대치고 원래 대로 안돌아가고 이동도 안되는 상황이다.

개선하기는 커녕 버그를 하나 만들었는데 왜 그런지 함 보자.

6.버그 수정


현재 Define.MouseEvent evt를 받아서 상태가 Skill이라면
PointerUp일때 false였던 _stopSkill을 true로 변경을 해준다.

그리고


OnHitEvent에서 _stopSkill이 true일 경우 State를 Idle로 바꿔주는데
지금 _stopSkill이 false인 상태이니까 지금 State가 Idle로 안바뀐것 같다.

그러면 _stopSkill이 PointerUp일때 true로 안바뀌었다는 말인데


애당초 여기 안들어왔다는 말이다. -> PointerUp이 실행이 안됬다는 말인데

그래서 여러가지 정보를 조합을 해보면


OnMouseEvent부분이 문제라고 볼 수 있겠다.

왜냐하면 우리가 처음에 Idle상태이거나 Run상태일때 살짝(클릭한번으로)몬스터를

클릭을 하면은 바로 Skill인 상태로 바뀌는 것이 아니라

Moving중인 상태로 다가가다가 일정거리 이하가 되면은 State를 Skill로 바꿔주었다.

그런데

이부분에서는 그 처리를 안해 준 것이다.

그러니까 OnMouseEvent_IdleRun함수에서 한번만 클릭을 할 시

이부분을 넣어 주어야 한다.

왜냐하면 한번만 치기로 했으니까 _stopSkill을 true로 놔두어야함.


그래서 PointerUp일때 true로 만들어 주어야함.

그리고_stopSkill을 true로 만들어 주었으면 어디선가는 false로 만들어 주어야 하는데


여기에서 일단 어떻게 될지 모르니까 스킬을 시전준비? 시전한다고 가정을 하고 false로 해두도록하자.

내가 살짝 클릭을 해가지고 다시 놨으면은 일회용 스킬이 되는 것이니까

애는 일회용 스킬이 되는 것이니까


true로 만들어줘서 스킬이 끝날때쯤에 바로 종료가되도록 만들어 주면 될거같고

그리고_stopSkill을 true로 만들어 주었으면 어디선가는 false로 만들어 주어야 하는데

누르는 순간은 스킬을 시전한다고 가정을 하고

대기하다가
_stopSkill을 false라고 가정을 하고

다른 부분에서

이런식으로 마우스를 땟으면

_stopSkill을 true로 바꿔주면 된다.

그러면 이제 이부분이 잘 작동하는지 유니티에서 테스트를 해보도록 하자.


한번만 클릭( 몬스터를 눌럿다가 바로 때면은 다가가서 한대치고 다시 Idle상태로 돌아옴)
했을때 잘 된다.

그래서 이렇게 상태 관리하는것이 굉장히 어렵다. 디아블로 같은 게임을 만드는 것이 쉬운 것이 아니다.

7.누르는 상태일 경우 구현

그래서 이제 한번 클릭을 해서 돌아오는 것까지는 잘 되었다.

몬스터를 누르고 계속 누르는 상태일 때는 어떨까?


이런식으로 ATTACK인 상태에서 계속 머무르고 있다.

(_stopSkill이 true에서 안바뀌거나 다른문제일듯?)

그런데 우리가 애니매이션 시간일 때를 생각을 해보면

애니매이션도 루프애니매이션이 있었고 한번만 트는 애니매이션이 있었다.


WAIT나 RUN은 반복되는 애니매이션인데 ATTACK의 경우 한대만 툭 치고 끝내는 그런 애니매이션이여가지고 발생하는 문제가 아닌가라는 합리적인 의심을 해볼 수 있다..!

그래서 ATTACK_1애니매이션 가가지고

Loop Time을 켜준다음에 Apply를 눌러주면은


잘 된다 마우스를 땟을때 애니매이션이 멈춤

(근데 Loop Time이 뭐하는 기능인가..?
애니매이션 반복해서 틀어주는 것임
그냥 애니매이션을 반복해서 트는 애니매이션이다! 라고 설정을 해주어서
클릭할시에 _stopSkill을 true false이런식으로 클릭히면 한번만 그런식으로 하는거같다.
그러니까 Loop Time을 끄면은 코드가 제대로 돌아가도 그냥 한번만 틀어지는 거라서 안되는듯..?!)

그런데 지금

꾹 누른 상태에서 때면은 Idle로 바로 돌아가지않고 잠깐 RUN상태였다가 Idle로 돌아가는데

이것을 해결을 해보자!

8.애니매이션 코드로 관리하기


그래서 ATTACK의 Transition을 가보면 RUN이 1순위 이고 WAIT가 2순위로 되어있는것을 볼 수 있는데

우리가 조건을 보면은
attack이 false면 WAIT로갈 수 있고 attack이 false이면 RUN으로도 갈 수 있게 했었다.


둘다 attack이 false일 경우 condition이다.

그래서 양갈래로 갈 수 있으니까 우선순위인

RUN으로 이동한다는 것을 알 수 있다.

그렇다는 것은 우리 condition이 뭔가 문제가 있다는 것이다.

그래서 ATTACK이 끝나고 WAIT으로 갈지 RUN으로 갈지를 고르고 싶으면은


이전에 우리가 추가한 speed라는 녀석을 condition에 추가를 해서 조건을 달아 주어야 할거같은데

이게 상당히 벌써 짜증나기 시작한다.ㅅㅂ

그런데 이게 뭐 엄청 간단한 게임이라고 해가지고

간단하게 parameter를 추가를 해가지고 애니매이션이랑 코드랑 맞출 수 있으면 좋겠지만

사실 우리가 코드에서 작업하던 것을 유심히 살펴보면은

모든것을 우리는 State로 관리를 하고있었다.

이렇게.

우리의

_state만 어거지로 anim.SetBool("attack", false); 뭐 이런식으로
어거지로 맞추고 있었다.

그런데 이렇게 쓴다는 것은...

과연 굳이 우리가 animation transition을 tool에서 제공하는 방식으로 써야하는 지가 굉장히 의문이 들기는 합니다~


이런식으로 "attack", false "speed", 0 이런식으로 쓰지 않고

그냥 간단하게 이야기를 해보면은


여기 애니매이션 파라미터들을 다 지운다음에

Int 파라미터를 하나 만들어가지고
state라는 변수를 하나 만들고

이것을 이용하여 모든 애니매이션을 매핑을 하면 더 편하지 않을까? 라는 생각이 든다.


오른쪽에 RUN WAIT ATTACK등등

대신 애니매이션 파라미터인 state는

Die부터 0번 Idle = 1번, Moving = 2번, Skill = 3번 으로 맞추어 주면은 나머지는 쉽게 구현이 될 것이다.

그래서 만약 이제 Idle일 경우

SetBool, SetFloat이런식으로 구별할 필요 없이 그냥


이런식으로 처리를 해주면 한번에 처리가 될 거같다라는 생각이 드네요~!

그러면은 이제

이런식으로 처리를 할 필요가 없어진다는 말이 된다.

그래서 이게 툴에서 제공하는 기능이라고해서 무조건 사용해야되는 것은 아니고

우리가 구현하는 코드에 따라가지고 편한 쪽으로 작업을 하면된다.


그래서 여기 흰색화살표의 조건들은 결국


이 state에 따라서 왓다 갓다 할 것인데

이렇게되면 애당초 조건(흰색화살표)를 넣어줄 이유가 없어진다.

그냥 state값이 뭔지 ( (int)_state) 에 따라서 애니매이션이 바뀔 것이니까.


그래서 이렇게 조건을 하나도 사용하지않고 맨처음 배운대로 했던것?

처럼 하는것도 나쁘지않다라는 생각이 든다

그래서 여기서 또다른 방법이 하나가 있는데

애니매이션에 바로 접근을 해가지고

Animator anim을 이용을 해가지고


anim.SetBool("attack", false);이런식으로 파라미터로 접근을 하는 것이 아니라


이런식으로 구현을해도 더 편하게 구현을 할 수 있게 되겠다라는 말이다.

그런데 이렇게 했을때 단점은..

이게 사실 돌고도는 것인데


애니매이션들이 뚝뚝 바로 끊겨버리는듯한 동작이 굉장히 맘에 안들었던 것이다.

그해서 이것을 정말 이쁘게 처리하는것이 중요하지 않다고 하면은

사실은 Play말고 블렌딩을 지원하는 버젼이 하나 더 있기는 하다.


바로 CrossFade라는 함수인데 Play와 똑같이 애니매이션 이름넣어주고 그다음 인자는

trasitionDuration이라고 애니매이션 블렌딩을 어느정도의 시간으로 자연스럽게? 다음 애니매이션으로 넘어갈지의 시간을 넣어주는 것이다.

그래서 0.1f초로 해주면 이것이 생각보다 괜찮게 돌아갑니다~

그래서 우리가 speed, bool attack 이런식의 파라미터로 접근을 하는것이 완전히 필수적인 접근 방법은 아니라는 것이다.

그래서 이것이 합리적인거 같다.

왜냐하면 나중에 스킬도 막 늘어나는데

그때마다

이곳에 파라미터를 하나하나 늘려가지고

파라미터랑 코드랑 맞추는 것도 은근히 번거로운 작업이기 떄문에

코드에서 바로 접근을 해서 막바로 맞추는 것도 괜찮은 방법이다.

그래서 이 프로젝트에서는 이대로 진행을 하도록 하겠다.

이런식으로!

그리고 이렇게 코드로 관리하면 좋은점이

무작정 condition에 따라서 애니매이션이 넘어가는 것이 아니라 우리가 강제로 셋팅을 할 수 있다 보니까

우리가 아까 ATTACK같은 경우


Loop Time을 강제로 켜줬었는데

만약 Loop Time이 없다고 가정을 하고 다시 Apply를 하고 유니티를 실행을 해보면


이렇게 꾹 누르고 있더라도 연속해서 ATTACK애니매이션을 실행하지 않는다.

그런데 코드에서는 이것을 제어를 할 수가 있다.

CrossFade를 보면은 버전이 여러개가 있다.


이 버전은 위에 anim.CrossFade("ATTACK", 0.1f);와 같은데

인자를 두개를 더 받는다.

세번째 인자는 Layer인데 (사용하지 않을것 or 신경을 안쓸것이니까) -1로 놔두고

4번째 인자가 중요한데 normalizedTimeOffset 이라는 건데 이것을 0으로 놔두면은 무슨 뜻이냐면은


0으로 하면 첫 위치로 돌아가가지고 처음부터 재생을 해주게 된다라는 말이다.

그렇다는 것은 아까와같이 유니티 툴에서 애니매이션에서 Loop Time을 굳이 켜주지 않더라도


(누르고 있으면 연속해서 계속 때린다)

아까 Loop Time을 켜두었을 때랑 똑같이 잘 작동을 하고있다.

그래서 CrossFade 네번째 인자 normalizedOffSet 이 anim을 처음부터 들어주는 그런 역할을 하는것인데

그대신 우리가 Event를 맞춰준

ATTACK의 파란게이지칸에서 다시 0으로(애니매이션이 처음으로 돌아감)가는데

이것은 그냥 우리가 Event지점을 재 설정 해주면 되는 쉬운 부분이다.

그냥 Event위치를 조절하면 되는 것.

그러면 우리가 구현하는 것은 거의 다됬다.

9.망치 들고 있게 하기

그런데 Player는 맨손으로 처 때리고 몬스터는 검을 들고있으니까 이것이 불공평하니까

Player에게도 검을 쥐어 주도록 하자.

나는 Knight 프리팹에 문제가 생겨서 에셋에 굴러다니는 망치 들고왔다.


transform의 좌표는 reset을 시켜주고


그다음에 유니티짱을 다시 열어가지고

이제 이녀석한테 망치망치를 쥐어 줘야하는데


이렇게 구조를 하나하나 파고 들어가다 보면은


여기 RightHand라는 애가 딱 뜨는 것을 볼 수 있다.


RightHand안에 Create Empty를 하나 만들어서


RightHandSocket이라고 해서 여기다가 뭔가를 쥐고 있겠다 라는 의미이고

그리고 망치망치를 Socket산하에 위치를 시켜주자


ㅋㅋ;


위치를 조금 조절해주고 실행하면 망치망치도 같이 움직인다.

뼈구조상 오른손에 위치를 하고 있기 때문에 같이 흔들리면서 이동이 가능함

그리고 버그가 하나 있는데


가끔가다가 엉뚱한 방향을 바라보고 때리고있다.

이동을 할때는 마우스를 누른 방향으로 잘 이동이 되는데 칠때는 그작업을 안해놔서

이런 버그가 발생하는거 같다.

10.방향벡터

그래서 이부분만 수정을 하고 마무리를 짓도록 하자.

그래서 UpdateSkill일때 지금 치고있는 몬스터를 바라보세요 라고 수정을 하면 될 거같다.


_lockTarget이 null이 아닐때, 즉

내가 타겟을 지정을 해가지고 뭔가를 하고있을 때

그 타겟을 향해 고개를 돌리도록 하자.


방향벡터를 하나 만들어주고

_lockTarget의 좌표에서 나의 좌표를 빼면은

나에서 몬스터로 가는 방향 벡터가 나온다.

그리고 그 방향으로 보게 하려면


잠깐 회전에 대해서 보자면은

회전을 이용을 할때 Quaternion 개념을 사용하면 된다.


Quaternion을 사용을 하는데 Quaternion.Lookforward가 있었는데

버전이 여러가지가 있는데 내가 바라보고싶은 방향벡터를 넣어주면 Quaternion이 만들어 진다.


그래서 transform.rotation에 넣어주면 되는데

바로 넣어주면 뚝뚝 끊기는 현상이 발생을 하니까, 보간을 하기 위해서

Lerp를 사용을 할 것인데

transform.rotation = Quaternion.Lerp(transform.rotation, quat, 0.2f);
이렇게 Lerp의 첫번째 인자에는 trasnform.ratation 나의 rotation에서

내가 가고싶은 quaternion을 입력을 한다음에

세번째 인자에는 대충 가고싶은 회전하는 고정시간을 입력을 해주면된다.


나는 처음에는 0.2f로 했다가 20 * Time.deltaTime으로 함(강의에서는)

아직 Time.deltaTime의 개념을 잘 모르겠다.

그래서 아무튼 이렇게 해서 버그가 다 고쳐졌는지 새로운 마음으로 실행을 해보면은

잘된다

좀 애매한 위치에서도 몬스터를 바라보고 있지 않더라도 클릭으하면 팬다.

-굿-

profile
https://cjbworld.tistory.com/ <- 이사중

0개의 댓글