2022/12/22

노윤수·2022년 12월 22일

오늘 가입한 게시글이라 사용하는 법이라고는 1도 모르지만 하나씩 작성해보려고 합니다. 프로젝트의 규모가 점점 커짐에 따라서 제가 구현한 기능도 까먹을 것 같아 작성하기 시작합니다.

저는 게임 개발을 하고 있으며, 유니티를 사용한지는 2022/12/22 기준 1년정도 지났네요. 이번 프로젝트는 3개월전에 시작했는데, 게임 기획를 하던 중 1달 반동안 작업한 내용이 기획과는 거리가 멀다고 생각되어 폐기처분 했습니다.(눈물을 머금고)

먼저 지금까지 기획한 내용을 말씀드리겠습니다. 해당 게임은 추리+1대1 전투게임입니다. 1대1 전투라고 하면 다크소울, 엘든링(전형적인 보스몹 굉장히 강함+주인공 물주먹)을 예로 들 수 있겠습니다.

지금부터는 사진과 영상 자료를 첨부하면서 설명하겠습니다.

이쁘죠? 제가 만든 거냐고요?

아뇹

그냥 이쁜 에셋 구해왔습니다.

메인 시작화면입니다. 3인칭게임이며, 각각의 npc 들과 대화하면서 힌트를 찾아나야합니다. 아직 힌트를 찾을 명확한 방법이나, 대화내용을 구현하지는 않았습니다. 3인칭을 만드는데 있어서 유니티 에셋스토어에서 제공하는 third person controller를 사용해도 문제가 없긴하다만, 그래도 제손으로 구현하는 것이 의미가 있다고 생각해 에셋은 제거하고

Vector2 mouseDelta = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
로 마우스 이동 값을 받고, Mathf.Clamp로 tps카메라가 이동할 수 있는 범위를 제한했습니다.

오른쪽 위에는 미니맵입니다. 채팅과 미니맵 두개를 오늘 구현했는데 생각보다 어렵지는 않더라고요. (제 부족한 지식으로는 아무리 생각해도 구현이 안되서 찾아봤습니다)

단순하게 미니맵용 카메라를 주인공의 자식오브젝트로 넣고(저는 projection은 perspective으로 했습니다). (perspective-3d, orthographic-2d/원근법의 유무) canvas에 image를 하나 생성합니다. 이 부분이 중요한데, render texture를 하나 만들고 image에 넣어줘야합니다. minimapCam에도 target texture에 만든 render texture를 넣으면 짜잔..미니맵이 완성됩니다.


npc위에 커서를 올려놓으면 npc가 검은색으로 변하면서 대화가 가능한 상황이라는 것을 알려줍니다. onMouseEnter와 onMouseExit을 통해서 간단히 만들었고, 옆에 작은 canvas를 만들어 rendermode를 world space로 바꾸어 채팅 이미지가 뜰 수 있도록 구현했습니다. !


q버튼을 누르면 저렇게 전체 npc들이 보입니다. 각각 특징(왼손 잡이, 오른손잡이, 입고 있는 옷) 들을 체크해서 적이 누군지 특정해야하기 때문에 한눈에 보기 편하도록 저런 식으로 구현을 했습니다. 오른쪽 끝에서 오른쪽키를 누르면 왼쪽 끝으로 넘어갑니다. (index구현)

e버튼을 누르게 되면 채팅이 뜹니다.(오늘 구현한 개발자의 온기가 남아있는 기능) GameManager를 싱글톤으로 구현하고 TalkManager를 따로 만들었습니다. 대화내용은 후에 엑셀이나 json으로 변경하는 것을 목표로 하고 있으며, 현재는 딕셔너리로
talkData = new Dictionary<int, string[]>(); 구현했습니다.

public string getTalk(int id, int talkIndex)
string 배열이기에 한번 채팅쳐도 npc가 여러번 말을 걸 수 있도록 하였습니다.

이제 본격적으로 전투씬으로 넘어가기 위한 과정입니다. 해당 npc가 내가 찾아야하는 npc가 맞다! 확신하는 순간 마우스 왼쪽클릭으로 이 친구가 적이라고 정해줍니다. 클릭된 npc는 빨갛게 변하게 되는데, 이러면 주변에 있는 호위병이 당신을 향해 달려올 것입니다. 간단한 navmeshagent로 setdestination해서 구현했고, vector3.distance함수로 어느정도 가까워지면 loadscene으로 씬이동을 구현했습니다.

그럼, 전투씬으로 이동해볼까요?

전투 시작시 상황입니다. 제가 좋아하는 ui스타일이 있는데(ui로 욕먹었던 데스티니 가디언즈/해보진 않았슴다) 간단깔끔한걸 좋아합니다.

위쪽은 적의 체력, 아래는 주인공의 체력이며, 오른쪽아래는 스킬 콤보 쿨타임, 왼쪽 아래는 대쉬 쿨타임입니다.

소울류 게임에서 제일 중요한 것 하나면 대쉬스킬이죠. 코루틴을 통해서 대쉬를 하는 시간 동안 bool로 isDash 변수를 만들어 그동안에는 데미지를 받지 않도록 했습니다.

또한 중요한 파트! 데미지 구현을 어떻게 했느냐!

??:엥 그건 검에 그냥 box collider 붙이면 되는거 아님?

진짜..한번 휘두를때 데미지 3번 4번씩 들어갑니다. ontriggerenter랑 같이 쓰면 검을 화려하게 쓸때마다 캐릭터에 들어갔다...나갔다..반복하면서 스킬한번에 예상 할 수 없는 데미지가 누적됩니다.

그래서 어떻게 했느냐?

캐릭터와 적 앞에는 빈 collider가 붙어있습니다. isTrigger를 체크하고 상대가 내 공격범위안에 있는지 isInBox bool변수를 만들어요. 그리고 스킬을 사용할때 필요한 또다른 collider박스를 붙여놓습니다. 그리고 여기에는 onEnable 함수에 피격데미지를 구현하는 겁니다. 스킬을 쓸때+isInBox+얘가 구르고 있는지를 확인하는 isDash가 false인걸 확인하면 만들었던 또다른 collider박스를 setActive(true)로 바꾸어주면 됩니다!


(대충 구르고 있는 모습)

기존 소울류와 차별점이 있다면 스킬에 콤보가 있습니다. 총 7가지 스킬을 쓸 수 있으며, 3개는 스킬콤보가 1개더 , 1개는 스킬콤보가 2개더 있으니, 여러분이 보시는 스킬은 총 12가지가 있겠네요...애니메이션 짜느라 진짜 고생했습니다.

여기서 제일 중요하게 작용한 점은 콤보를 어떻게 구현했는가인데, 천천히 설명하겠습니다. 스킬 버튼을 누르는 순간 Time.time을 변수로 받아옵니다. 그리고 중요한 변수인 noOfClick을 만듭니다. 기본은 0이며, 누를때마다 1씩 증가하고, Time.time으로 부터 1초가 지나면 스킬 콤보를 쓸 수 없으므로 다시 0으로 변하는 변수입니다. 1초가 지나지 않았을경우, noOfClick을 1올려주면서, 다음 스킬을 시전할 수 있도록 해줍니다.

여기서 처음 알게된 두 기능이 있는데,

GetCurrentAnimatorStateInfo(0).normalizedTime

GetCurrentAnimatorStateInfo(0).IsName("")
입니다.

이게 뭐냐?

첫번째 지금 실행되고 있는 애니메이션의 진행상황입니다. 시작할 때는 0, 끝났을때는 1.0f를 반환하는 기능이죠.

두번째는 현재 실행 중인 애니메이션이 이게 맞는지 string값을 넣음 bool로 반환하는 기능입니다.

..신기하죠?

이걸 통해서 만약 콤보가 있는 스킬을 사용했다? 그러면 콤보가 있는 스킬인지 두번째 기능을 통해 확인한후, 다음 콤보를 bool파라미터로 true로 바꿔주는 겁니다. 물론 첫 기술이 끝나지 않게 hasExitTime은 켜야겠죠?

기술이 나가는 건 random+switch문으로 잡았습니다. 하지만 큰 문제점이 발생했었는데, switch(스킬 번호)를 넣었고 해당 case에서 random으로 스킬번호를 바꾸었더니 콤보가 안나가네요? (같은 스킬번호여야 콤보가 이어짐) (case마다 startcoroutine으로 스킬 시전했습니다)

간단한 해결책(반나절 소요)으로는 콤보스킬의 마지막에만 random함수로 스킬 번호를 바꾸는 것이었습니다.

잘 안보이시죠?
오른쪽 아래에 검은 색 쿨타임이 돌아가는 것 보이시나요?
스킬콤보가 끝나는 마지막 스킬일 경우 더 이상 눌러도 콤보 쿨타임이 초기화 되지 않으며, 색깔은 검은색으로 바꾸어 "이게 마지막 기술이에요~"를 표현했습니다. 물론 fillAmount를 이용했습니다.

길었죠?,,처음 써보는 글이라 제대로 썼는지 모르겠네요..ㅎㅎ

다음번에 기능추가할때마다 와서 쓸 수 있도록 '노력' 해보겠습니다.

이상입니다.

profile
코딩조아

0개의 댓글