몬스터 AI#1

CJB_ny·2022년 1월 4일
0

Mini_MMO_RPG

목록 보기
10/16
post-thumbnail

플레이어만 몬스터를 때리면 불공평 하니까

몬스터도 플레이를 따라가서 때리는 작업을 해보도록 하자.

어떤식으로 구현을 할것이냐면은

몬스터가

여기 입구쪽에 있게 될텐데

Player가 일정 범위 안에 들어오면은 Player를 인식을 하여

따라와서 때릴것이다.

그래서 영혼의 맞다이를 만들것인데

몬스터가 Player를 인식하고 따라와서 맞다이를 까는 것을 만들어 볼 것이다.

1. 공용으로 사용할것들 구별후 코드로 정리

그러면 몬스터의 AI구현을 어떻게 할지 굉장히 고민이 되는데

그래서

몬스터를 Controller하는 것이니까 PlayerController와 마찬가지로

MonsterController가 있으면 좋을 거 같다라는 생각이 든다.

그래서 일단 이렇게 MonsterController를 만들고 시작을 해보도록 하겠다.


그래서 PlayerController에서 가서 컨트롤 M O로 다 접은 상태에서

이것을 참고를 해서 MonsterController도 만들면 될 거같은데

그리고 State같은 경우에는

Player 뿐만 아니라 몬스터도 Idle, Die, Attack, Moving이 있을 것이다.

PlayerController에 있는 것처럼

그리고 Update에서 이런식으로 State패턴을 이용하는것 또한 똑같이 들어갈 것이다.

(이렇게)

그리고 결국에는

이 UpdateIdle, 이 네가지 함수들도 똑같이 들어갈거같다.

그리고 또 뭐가 공용으로 들어 갈 거 같냐면은 _destPos도 몬스터의 _destPos가 들어 갈것이고(Player가 _destPos가 될것이다)

마지막으로

_lockTarget의 경우 Player는 마우스로 찍은 녀석을 _lockTarget으로 하여 달려 갔지만

몬스터의 경우

AI로 판별한 그러니까 대충 주변에 있는 Player하나를

_lockTarget으로 찍어가지고 그 _lockTarget한테 다가 올 것이다.

그러니까 _lockTarget도 공용으로 사용이 될 것이다.

그래서

한마디로

정말 많은 부분들이 곂처서 재사용 된다는 말이 되는데


그래서 이런부분들을 굳이 복붙해서 MonsterController를 작성을 하는 것보다는

이럴때는 그냥 BaseController라는 공용 Controller를 그냥 하나를 파가지고

공용적인 부분을 그냥 다 위로 올려보낸 다음에

Monster와 Player둘다 BaseController를 상속받아가지고

구현을 한다면 구현이 왠지 굉장히 간단해 질거 같다라는 생각이 든다.

그래서 유니티로 다시 돌아와서

살아 있는 몬스터나 플레이어 등 살아 있는 것들의 컨트롤을 담당하는

BaseController 를 하나 만들어 주도록 하자.


이렇게 만들어주고

까먹지 않도록 PlayerController, MonsterController둘다 BaseController를 상속 받도록 수정을 해주자.


이런식으로(이거는 알잖아 ㅎ)

그러면 이제 해야 할것은 뭐냐 -> PlayerController에 있는 것들을 다 BaseController로 이사를 시켜야 할것이다.

그런데 PlayerController에 있는

public enum PlayerState는 몬스터도 공용으로 사용할 것이 되니까

Die Moving Idle Skill 이 4가지 상태만 있으면 몬스터 AI도 충분히 구현을 할 수 있다.

그래서 PlayerState를 PlayerState로 놔두지말고 공용으로 사용하는

Define.State로 사용하면 좋을거 같다라는 생각이 든다.

그래서 PlayerState를 복사한다음에

컨트롤 + 쉬프트 + f 를 눌러서

-> 파일에서 바꾸기로 가서

사진처럼 PlayerState를 Define.State로 바꾼다고 해주자.


그러면 이렇게 뜬다.

그래서 일단 신나게 바꾸었는데

이제 좀 정리를 해야 한다.


일단 Define으로 와서 이런식으로 수정을 해주도록 하자.

그다음 다시 PlayerController로 돌아와가지고

이어서 정리 작업을 이어 가 보도록하자.

그래서 지금


Define.State랑

Vector3 _destPos와 Define.State _state = Define.State.Idle; 까지

Define으로 옮겨도 괜찮을꺼 같다라는 생각이 드네요~~

PlayerState을 제외하고.


이렇게 SerializeField 를 다 붙여 준다음에


애내들 컨트롤 + x로 짜른다음에

BaseController로 가서 옮겨 놓도록 하자.

그리고 아직 다 안 옮겼으니까 다시 PlayerController로 와가지고

뭘 옮길지 생각을 해보면은


Update문 같은 경우에도 짤라서 BaseController에 옮기도록 하자.


이렇게 옮겨주고

[SerializeField] 붙여준애들 앞에 protected를 앞에 붙여주자

=> 그래야 상속 받은 애들이 사용이 가능하니까!!


그다음에 이런 Update시리즈들

이런 Idle, Moving, Skill, Die를 다 가지고있겠지만은

다 똑같은 방식으로 구현을 한다는 보장은 없을 것이다.

왜그렇냐 하면은 이게 Player이냐 Monster이냐에 따라가지고

움직이는 것이 다 다를테니까

그래서 이런 Update시리즈들은 BaseController에서 인터페이스만 파주도록 하자.


앞에 protected붙여주고 인터 페이스만 파주자

=> 그러면 protected붙였으니까 상속받은 애들은 사용할 수 있고
virtual로 인터페이스만 파두었으니까

상속받은 아이들이 안에를 구현을 알아서 하면된다.(아까말한 몬스터나 플레이어에 따라 애니매이션이 다를테니까)

그래서 일단

이런식으로 하나하나씩 만들어 주도록 하자.

그다음에 이제 PlayerController로 돌아가면은


이렇게 아무것도 안하는 것들은 삭제를 하자.


Idle도 아무것도 안하니까 삭제를 하자.


UpdateMoving같은 경우에는 protected override 를 붙여 주어야 한다.

BaseController에서 인터페이스만 파주었으니까 재정의를 하는 것이기 때문에


이렇게 UpdateSkill도 override 해주자

그리고

OnMouseEvent, OnMouseEvent_IdleRun, OnKeyBoardPressed 같은 경우에는 ㄹㅇ Player에 종속적인 경우이니까 옮길 수가 없다.


이렇게해서 거의다 옮긴거같고


여기 Start에 있는 함수를 init함수로 따로 빼줄 것인데.

이작업은 이전에 여러번 해봤제?

BaseController로 가가지고

public으로

이렇게 파주고(그런데 protected로 수정을 해주는게 더 맞으니까 protected로 바꿔주자)

Start안에 init을 호출 하도록 만들어주자

이렇게

이런다음에 PlayerController로 돌아가서


이녀석 같은 경우에는 Start에다가 넣어 주는게 아니라

이것을 지워주고


protected overrride void init으로 바꿔주고

안전빵으로 base.init();을 해주자.

이렇게 되면 PlayerController는 init을 통해서 Start를 하게될 것이고

나머지 부분은 딱히 바꿀 것들이 없다.

그래서 우리는 결국

이런 UpdateMoving이나 UpdateSkill같은 부분들만 잘 바꿔치기해서 사용하면 될 것이다.

그리고 마지막으로 이 State같은 경우

공용으로 사용해도 될거같은데

혹시라도 나중에 누군가가 바꿔서 사용하고싶은 경우가 생길 수 있으니까

나중에 알아서 override 하라고 하고 (그렇게 하기위해서)virtual로 바꿔 주도록 하자.

이렇게!

프로퍼티도 virtual을 붙일 수 있다는 것도 알아라!

그래서 일단 이까지하고 유니티에서 잘되는지 함 확인해보면


모두 다 잘 된다.

자, 그럼 이제 이어가지고 MonsterCOntroller를 만들기 시작하면 될거같다.

우리가 이전에 UI_Base를 만들 때


public abstract void init();
이렇게 abstract void 로 init을 만들었었는데

그래서 만약 UI_Base를 상속을 받으면

바로 init을 구현을 해라고 해가지고 Error가 떳었는데

그렇게 만드는 것이 사용성에서 좀더 편리 한거같으니까

그래서

protected virtual void init()을 삭제를 해주고


이렇게 만들어주고

클래스에도

이렇게 abstarct를 붙여줘야 에러 안뜸

그리고 PlayerController에서도 init을 public으로 맞춰 줘야한다.


이것을


이렇게 뜨니까


똑같이 추상클래스를 구현을 해주어야 한다.

그래서 결국 여기 안에다가 init을 넣어 줄 것인데.

여기 안에다가 채워 넣어야 할거같은데

PlayerController에서 컨닝을 잠깐 해보자면은


MouseAction 을 매핑하는 부분과 KeyBoardAction을 매핑하는 것을 제외 하고는 똑같이 필요할거같다.


그래서 Stat _stat; 이렇게 만들어 주고

필요 없는 것들은 삭제를 해주도록 하자.


그리고 보면은 Make3D_UI를 통해서 < UI_HPBar >를 붙여주고 있는데

혹시라도 Knight 산하에 컴포넌트로


이렇게 붙여 주었다면은

두번 포함하는 것이 되니까

이 HPBar가 있는지는 체크를 해주도록 하자.


그래서 이렇게 있는지만 체크를 좀 해주도록 하자.

그리고 PlayerController에서도

이부분을 똑같이 넣어주도록하자.

그다음에 뭐가 필요할까 생각을 좀 해보면

MonsterController에서

상속받은 인터페이스들을 구현을 해주어야 한다.

UpdateIdle인 상태에서는 Player를 찾는 Searching하는 그런 부분을 담당을 해주고

UpdateMoving은 Idle인 상태에서 플레이어를 찾아서 다가가는 그런 부분 담당을 해주면 될 것이다.


여기서도 똑같이 Skill안에서도 뭔가가 들어 갈 것이다.

그런데 참고로 지금 BaseController 안에

이런식으로 텅빈 함수로 남겨 놓았기 때문에


이런식으로 해주어도 되고 안해도 됨 -> 크게 상관이 없다.

그리고 몬스터도 때릴때

OnHitEvent처럼 함수가 발생을 할 텐데

이것도 정의는 해주어야 나중에 에러가 나지 않을 것이다.


그리고 이부분 까지도 온히트 이벤트까지 넣어 주도록 하자.

그래서 이까지 몬스터 로그들이 잘 찍히는지 유니티로 돌아가보면

유니티로 왔는데

몬스터 한테도 스크립트는 붙여줘야 작동을 할테니까


Knight한테 스크립트 붙여주고 실행하면


이렇게 로그 ㅈㄴ 찍히는것을 볼 수 있다.

그리고 유니티짱의 애니매이션 인터페이스를

몬스터도 똑같이 맞춰주도록 하자.


이렇게 맞춰주자(걷는게 있어서 WALK로 해놓음)

플레이어는 ATTACK을 할 경우에 앞으로 움직이는 에러가 잇었는데

Knight에서

Apply Root Motion이 꺼져있는것을 반드시 체크를 해주어야함(기억 안나면 애니매이션 함 보기!)

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

0개의 댓글