현제의 현재이야기

[MapleStory Worlds] 2개월 동안의 MOD 해커톤 후기 본문

MOD

[MapleStory Worlds] 2개월 동안의 MOD 해커톤 후기

현재의 현제 2022. 9. 4. 12:50

어제 수료식 및 수상식을 다녀오고.. 넥슨 사옥을 보고 뽕을 맞아 작성하는 방학 동안의 mod 해커톤 후기

 

지원동기(지원서)

메이플스토리라는 게임을 15년동안 해오면서, 저에게 있어서는 단지 유년기의 추억을 넘어, 현재 진행형으로 저와 함께 성장해나가고 있는 동반자입니다. 유년기 시절 저는 아기자기한 메이플 캐릭터와 더불어서 당시에 한 대륙을 넘어 다른 대륙으로 배를 타고 가서 두근거리는 모험을 즐길 수 있는 방대한 세계에 매료되었습니다. 특히 저는 캐릭터 육성보다는 맵과 맵의 연결성과 지역에 따라서 지형 오브젝트의 특성이 달라지는 것에 큰 관심을 두었습니다. 그래서 메이플 스토리에서 출간하는 공식 가이드북 중에서 육성 편이 아닌, 맵에 관련된 두꺼운 가이드북을 구매하여서 새로운 지역에 대한 호기심과 배경, 오브젝트들을 제 머리 속에 담아두었습니다. 그리고 형과 함께 흰 종이에 저만의 메이플 스토리 맵을 만들어서 하나의 지역과 대륙을 만드는 놀이까지 했던 기억이 있습니다. 여기서 저의 메이플스토리 맵에 대한 열정은 멈추지 않았습니다. 넥슨에서 허가한 정상적인 프로그램을 쓴 것은 아니라서 떳떳하지는 않지만, 한 발자국 더 나아가서 메이플스토리 클라이언트의 map.wz 파일을 해부해서 각종 맵 지형과 오브젝트를 추출하고, 나만의 맵을 만들 수 있는 repacker와 remap이라는 프로그램을 통해서 예전에 종이에 그렸던 저만의 세계를 실제 맵으로 제작하여서 즐기기도 하였습니다. 그러다가 지난 메이플스토리 실시간 간담회때  발표한 mod 프로젝트가 제가 에전부터 관심두고 상상했던 플랫폼이라고 생각했고,’ 꼭 출시되면 열심히 즐기겠노라’라고 생각했습니다. 메이플스토리는 지난 몇 십 년간 축적된 방대한 오브젝트 데이터로 유저 개인이 상상하는 세계를 충분히 그려낼 수 있고, 또 단순히 맵 제작을 넘어서 하나의 게임과 같은 서비스를 제 손으로 만들 수 있다는 사실이 저를 매우 흥분하게 하였습니다.  그 찰나에 개발자가 되기로 마음을 단단히 먹고 지원한 멋쟁이사자에서 초반에 언급한 넥슨과의 연계 프로젝트가 이 mod 프로젝트라는 것을 알고, 평소에 관심을 두고 있었던 프로젝트를 멋쟁이사자 의 훌륭하신 분들과 함께 해커톤을 진행한다니 함께 무조건 참여하겠다고 결심하게 되었습니다. 저는 이 mod supporters hackathon을 통해서 게임 개발의 기초를 경험하고 제가 생각한 것을 제 손으로 만들 수 있다라는 자신감을 얻어 향후에 훌륭한 개발자가 될 수 있다는 자신감을 얻고 싶습니다. 또한 관심사가 같은 분들과의 협업을 통해서 개발자의 역량 중에서 가장 중요한 ‘협력’이라는 가치를 제 몸으로 배우고 싶어서 지원하게 되었습니댜.

해커톤 목표

이번 해커톤을 통해서 개발해보고 싶은 것은 크게 세가지 입니다. 우선 여타 블리자드 게임에 비해서 넥슨 게임은 유저 참여 모드, 흔히 말해서유즈맵 만들 있는 툴이 있지 않았습니다. 메이플스토리만 하더라도 당장 제가 떠오르는 유즈맵 아이디어가 장애물을 피해서 목표에 도달하는 점프맵이나 달리기 시합을 있는 맵을 유저들이 직접 참여하여서 변경 또는 개발을 있게 하고 싶습니다.  또한, 메이플스토리나 다른 바람의 나라의 오브젝트를 통해서 포트나이트 게임 처럼 지형 지물을 소환하거나 이용해서 적과 싸우거나 최종 목표에 도달할 있는 게임을 만들어 보고 싶습니다. 그리고 메이플스토리2에는 있지만 메이플스토리 1에는 없는 나만의 꾸미기 프로젝트도 만들어 보고 싶습니다. 아기자기한 오브젝트를 통해서 자신만의 집을 만들고, 다양한 몬스터를 자신의 목장에 풀어두고 다른 유저들과 상호작용하는 힐링 게임 프로젝트 또한 개발해보고 싶습니다. 이번 mod 프로젝트를 통해서 다양한 프로젝트에 참여하고 싶습니다!

 

메이플과 프리메이플 제작을 재밌게 해온 내가 멋사에서 이런 해커톤을 진행한다니 못참고 바로 지원해버렸다.

그렇게 만들어진 결과물이 이 las petgas.

기본 적인 컨셉은 돈을 벌어서 펫을 뽑는 것이다. 재화가 부족하면 코코넛 아일랜드에서 코코넛을 따서 돈을 벌 수 있고, 그 돈으로 경마장과 투기장에서 돈을 벌어 펫을 뽑을 수 있다. 만약에 경마장과 투기장에서 돈을 다 잃버린다면 노역장으로 강제 이동되어서 광물을 캐서 빚을 매꾸거나, 점프맵을 통과해 탈출하여 다시 게임으로 복귀할 수 있다.

 

내가 제작한 파트에 대해서 설명하겠다. 단순 맵 구현으로는 아래의 라스펫가스 입구와 코코넛 아일랜드로 가는 항구를 제작하였고, 맵과 더불어 로직도 짠 것은 코코넛 아일랜드와 노역장이다. 

 

입구, 항구

맵을 구현하고, npc를 클릭하면 대화창이 나오고, 대화가 끝나면 라스펫가스나 코코넛 아일랜드로 이동하는 로직을 구현하였다.

밑은 npc에 추가한 컴포넌트 스크립트이다.

shownexttext 함수가 호출되면 미리 작성해둔 npc 대화 데이터베이스를 한 열씩 다음으로 넘겨서 카운터를 올린 후 대화를 보여준다.

미리 만들어 둔 npc database와 ui를 onbegin play 함수, 즉 시작할 때 지정을 해주고 npc 대화 count를 1로 지정해준다. 그리고 만약에 npc를 터치하면 HandleTouchEvent가 발생하여 컴포넌트 내 ShowNextText 함수가 실행되어 count가 1회 증가하고, npc 대화창이 열리게 된다 그리고 z를 눌러 KeyDownEvent가 발생하면 ShowNextText의 카운터 1이 증가되고, 다음으로 넘어가가 된다. 또한 대화가 마지막이면 _Userservice를 통해서 해당 플레이러를 코코넛아일랜드로 이동해준다.

 

-어려웠던 점-

 

 ui와 npc 대화 data를 만드는 것은 어렵지 않았으나, npc를 누르면 대화창이 나오는 것을 어떻게 할까 고민하다가 ShowNextText를 불러서 다음으로 이동하게 하는 아이디어가 생각났었다. 또한, keydownevent에는 triggerevent와 달리 self.triggerbody와 같이 해당 플레이어를 지정해주는 파라미터가 없어서 어떻게 플레이어를 코코넛아일랜드로 보낼까 고민을 하다가 userservice.localplayer를 통해서 해당 플레이어를 myPlayerEntity에 넣고, MoveMapPostion을 통해서 코코넛 아일랜드로 보내주었다.

 

코코넛아일랜드

이 곳은 앞서 말한 코코넛 아일랜드로, 기존 메이플스토리의 플로리나 비치를 모델로 했다. 도박하다가 지치면 휴양의 느낌으로 가는 것도 나쁘지 않겠다고 해서 시원한 바다의 배경으로 만들었다. 왼쪽 배 옆 선장을 누르게 되면 다시 항구로 나가지는 것을 구현하였다. 밑은 중간에 보이는 코코넛 성애자 npc에게 닿으면 돈이 추가되는 컴포넌트의 스크립트이다.

우선 저 Npc에 몸이 닿으면 triggerevent가 발생하고, triggerbodyentity의 name을 받아와서 userID에 저장한다. 그리고 userID에 해당하는 money와 coconut의 값을 불러오고 저장된 coconut의 값이 0이 될 때 까지 coconut을 -1하고, 돈을 5원씩 추가하여 갱ㅇ신해준다. 그리고 coconut과 money를 프로퍼티 변수인 UIcoconut과 UImoney로 저장한다. 이것은 서버 온리인 triggerevent이고, 밑의 클라이언트 온리의 triggerevent는 몸이 부딫히면 아까 저장해둔 UIcoconut와 UImoney를 돈과 코코넛 값을 보여주는 ui로 값을 넘겨준다.

-어려웠던 점-

우선 유저아이디를 불러와 코코넛과 돈을 갱신하는 작업은 서버에서 하는 것이나, ui로 값을 보여주는 것은 클라이언트온리라서 어떻게 할까 궁리하다가 triggerevent를 서버와 클라이언트로 나누어서 서버에서 프로퍼티 값을 저장하고, 그 값을 클라이언트 함수가 받아오는 형식으로 작성하였다. 

 

밑은 코코넛을 따는 로직이다.

 

attack 컴포넌트를 가진 플레이어가 hit 컴포넌트를 가진 코코넛을 때리면 우선 onhit함수가 발동하여서 프로퍼티의 cnt를 하나 올려준다. 

그리고 아까와 같이 attackerEntity, 즉 때린 사람의 name을 불러와 userID에 저장해준다. 이 값 역시 프로퍼티의 변수로 저장되어 self.userID의 형식으로 저장해준다. 그리고 cnt의 값이 증가하여 5가 되면, coconut의 데이터 값을 1 올려주고 코코넛을 SetEnable(false)를 통해서 안 보이게한다. 그리고 timerservice를 통해서 25초 뒤에 다시 true로 바꾸어서 코코넛이 재생성되게 구현하였다. 그리고 프로퍼티의 변수로 만들어 두었던 UIcoconut에 증가된 coconut 값을 저장했다.

이것도 코코넛을 파는 것과 마찬가지로 clinent only로 hitevent를 하나 더 만들어서 아까 저장해둔 프로퍼티의 UIcoconut 값을 코코넛 값을 보여주는 ui에 뿌려주었다.

 

-어려웠던 점-

 

이 것도 서버와 클라이언트를 나누는 것이 어려웠고, 이유는 모르겠으나 ui의 값이 한박자 늦게 갱신하는 버그가 있다. 서버와 클라이언트 개념이 미숙했기 때문에 어떤 때는 ui의 코코넛 값이 아예 안 보일 때도 있었고, 0으로 머물러 있을 때도 있었다. 이게 data를 불러오는dagastorageservice의 문제인가 값을 저장하는 문제인가 Ui의 문제인가 몰라서 한참을 헤맨 기억이 있다.

노역장

나의 13년 메이플 내공이 만들어낸 역작 노역장이다. 도착하자마자 채무자 npc가 정신이 드냐고 말을 건다. 맵에 캐릭터가 등장하자마자 어떻게 하면 npc가 바로 말 걸 수 있을까 고민을 했는데, 이는 OnbeginPlay 함수에 항구에서 봤던 npc 스크립트의 shownexttext 함수를 넣어서 맵에 등장하자마자 카운트 +1 이 되어 대화창을 켜고 진행시켰다.

 이 노역장은 기본적으로 밑은 광물을 캘 수 있도록 광물을 배치해두었고, 광물을 캐서 탈출하거나 위의 점프맵, 일명 인내의 숲을 통과하거나 둘 중에 하나를 선택해야한다. 가장 고민했던 점이 노동과 점프 맵 간의 밸런스 였는데 둘 중에 하나가 너무 쉬우면 나머지 하나는 버려지는 컨텐츠가 되기 때문이다. 따라서 광물 하나를 1원으로 하고, 점프맵 또한 어렵게 만들어서 손이 안 좋은 유저는 점프 맵을 숙련한 유저들에 비해서 더 많은 시간을 소모하여 노역장에 있어야 하고, 점프 맵을 지속적으로 시도하는 유저는 더 빠르게 노역장을 나갈 수 있도록 밸런스를 맞추었다.

밑은 광물을 캐는 로직으로, 위에 있던 코코넛 캐는 것과 로직이 똑같다.

 

그리고 코코넛아일랜드의 코코넛성애자 npc와 비슷한 총괄자이다. 코코넛 성애자 npc와는 몸에 닿으면 돈이나 빚이 코코넛의 개수와 광물의 개수에 따라서 변동하는 것은 같으나, 이 총괄자 Npc 는 광물을 팔다가 빚이 0이 되면 다시 라스펫가스로 복귀하는 로직을 갖고 있다. 밑이 스크립트이다.

ㄴㅇㄹㅁ

기본 적으로 코코넛을 파는 로직과 같으나 if debtinDB가 0보다 작게되면 빚을 0으로 설정하고 MoveToMapPostion을 통해서 밖으로 봬주었다

-어려웠던 점-

빚을 다 탕감하면 npc대화창이 열리면서 다시는 오지마! 라는 대사를 넣고 싶었으나 값을 못찾는다는 에러가 뜨면서 뜨지 않는 오류가 발생했다.

밑의 영상을 보면 알겠지만, 점프맵에는 닿으면 팅겨저 나가는 표창과 발판에 닿으면 가속이 되거나 뒤로 밀리는 레일까지 구현하였다. 레일은 customfootholdcomponent의 footholdwalkspeed 프로퍼티와 footholdforce 프로퍼티의 값을 수정해서 구현하였다. 또한 표창이 좌우로 움직이는 효과는 tweenlinecomponent의 position 프로퍼티를 활용해서 구현하였다. 닿으면 튕겨져 나가는 로직은 밑의 몬스터 trigger 컴포넌트와 동일하기 때문에 밑에서 설명하겠다. 

위로 올라가보면 이런 엘나스 발판이 나오는데 어떤 발판은 밟지 못하게 하여서 지속적인 시도로 길을 찾아가게 만들었다. 결코 쉽지 않게 만들었으며 한번에 두칸을 점프해아 발판을 밟을 수 있게 만들어서 길을 찾는데 어려움을 느끼게 하였다. 또한 잘못 밟게되면 밑으로 떨어지게 되어 자칫하면 처음부터 다시 시작하도록 만들었다. 그리고 발판은 customfootholdcomponent의 footholddrag 컴포넌트를 통해서 마찰력을 줄여, 엘나스 처럼 미끄러지는 발판을 구현하였다. 이는 더욱 이 스테이지를 통과하는데 어려움을 준다.

마지막 스테이지는 몬스터들이 삼단계로 존재하고, 갈수록 발판이 좁아지는 동시에 몬스터 속도를 빠르게 하여 타이밍을 재기 어렵게 만들었다. 표창에 적용된 컴포넌트와 동일한 컴포넌트가 몬스터에게도 적용하여, trigger component가 적용된 몬스터와 플레이어아가 부딪히게 되면 멀리 튕겨져 나가는 것으로 구현하였다.

 

개발자 분의 피드백

최종 기획안을 제출하고 피드백의 내용이었다. 우리가 고민을 했던 내용을 정확하게 짚어내셔서 과연 프로는 프로구나라고 생각했다. 도박 컨텐츠를 만들고, 이 도박을 통해 얻은 재화를 이용하는 성장요소에 대한 팀원들의 고민이 깊었다. 단순히 펫을 뽑기위해 도박을 하는 것은 노역장에 가는 리스크를 질 만큼의 동기가 되지 못했고, 펫을 얻음에 따라서 변화되는 요소들을 고민했다. 그 중 나온 것이 펫을 통한 노동 능력의 변화라던가, 자신의 펫을 경마장에 내보낼 수 있는 로직을 짜려고했으나 시간 상의 문제로 실패하였다.

 

플레이 영상

 

 

 

팀원 중 준규님이 멋있게 영상을 편집해주셨다. 

 

프로젝트 결과는 데이터 부분 빼고 전부 구현하였다. 우리는 Ui를 통해서 게임이 진행이 되는데 클라이언트와 서버 로직의 충돌로 플레이어의 재화를 컨트롤 하는 것에 큰 어려움이 있었다. 근본적인 문제는 다른 팀들은 단판으로 데이터를 저장하고, 다시 게임이 시작되면 모든 데이터가 초기화 되는 미니게임 형식으로 게임을 만들었으나, 우리는 게임을 나가도 자신의 재화와 코코넛이 저장되고 다시 접속하면 불러오는 rpg형식의 게임을 만들어서 userdatastorage를 다루고, 구현하는 것이 상당히 어려웠다.

 굳이 이런 어려운 기술에 손을 댄 이유는 애초에 게임 기획을 할 때, '본섭의 미니게임 같이 유저들이 억지로 하는 미니게임을 과연 mod에서도 지속적으로 하게될까?' 라는 의문이 들어서, 미니게임과 차별되고 계속 지속적으로 접속해서 플레이 하게 하는 게임을 만들고 싶었다. 이러한 욕심이 userdatastorage라는 기술을 손대게 하였고 팀원들이 많은 고민을 하고, 많은 시간을 소비하여 다른 부분의 퀄리티를 신경쓰지 못했었다. 수상작들을 보면 우리와 같은 rpg형식은 존재하지 않았고, 모두 단판성 게임이였어서 '차라리 우리도 단판성 게임으로 멋지게 만들었으면..' 하는 큰 아쉬움이 남았다.

디스코드를 통해서도 많은 질문을 했지만 마땅한 방법을 찾지 못해서 data 문제를 해결하지 못한 채로 결과물을 제출하였다.

팀원들의 공통된 의견이 코딩을 어느정도 접한 멋쟁이사자 동아리원들도 게임을 만드는데 어려움이 많은데 코딩을 모르는 일반인들은 손도 대지 못할 만큼 어려운 난이도를 자랑했다. 또한 공식 문서 또한 이론적인 부분만 설명하고 응용에 대한 것은 적은 예시만 보여주기 때문에 우리가 원하는 것은 맨 땅에 헤딩을하며 팀원들과 토론을하고 만들어냈다. 가장 중요한 어려움은 구글링을 해도 아무 것도 나오지 않는 다는 것이다. 이 부분이 정말 앞길을 막막하게 해주었다. 특히 api문서는 아무런 예제 코드 없이 함수에 대한 설명만 적혀있어서 도대체 이것을 어떻게 사용해야할지 모르는 문제점이 있었다. 이 부분에 있어서는 앞으로의 개선점이 필요할 것 같다.

 

멋진 완기님의 발표 영상이다.

 

총평

 

상을 따겠다는 호기로운 시작으로 접하였으나 2개월이라는 짧은 시간 안에 새로운 언어와 새로운 툴, 새로운 개념으로 게임을 하나 만든 다는 것이 상당히 어려웠다. 메이플 스토리에서 쉽게 생각했던 npc 클릭과 대화창 넘기기, 돈 저장하기가 이렇게 하나하나 구현을 해야한다는 사실이 '게임 개발이라는 것은 정말 하나의 세상을 창조하는 것이구나' 라는 것을 느꼈다. 특히 서버와 클라이언트 개념은 게임에서 중요한 개념으로, 이번 기회를 통해서 서버에서 처리되는 것과 클라이언트 부분에서 처리되는 것이 다르고, only가 붙는 이유도 알게되었다. 클라이언트 부분에서 악의적으로 변경된 내용이 서버에서도 변경되면 다른 클라이언트들도 큰 혼동이 있기 때문이다. 이러한 개념은 내가 여태 배웠던 그 어떤 공부에서도 하지 못했던 부분이다.

 또한 코딩 문법등을 공부하면서 알고리즘 문제를 푸는 것을 제외하고 이런 것들을 어디에 사용할까? 했던 것들이 이번 mod 를 통해서 반복문을 비롯하여 많은 로직들을 문법을 통해서 구현할 수 있다는 것을 알게되어 매우 신기하였다. 나의 코드하나가 게임 내 플레이어의 행동에 영향이 가는 것이 '정말 이래서 사람들이 게임 개발에 뛰어드는구나' 라고 생각했다.

 그리고 가장 나에게 좋은 영향은 준 것은 바로 협업의 개념이었다. 누구하나 할 것 없이 이 어려운 mod에 대해서 열정을 가지고 연구하는 태도를 보였으며, 같이 고민하고, 같이 토론하는 경험을 통해서 협업에 대한 중요성을 느꼈다. 혼자 만드는 것이 아니라 기술 하나하나 서로 접목하여서 결과물을 만들어내고 피드백을 하는 것이 개발을 하는데 큰 힘으로 다가왔다.

 

매주 회의를 노션으로 작성한 것이다. 팀원들 모두 열정적으로 참여해주었고, 아이디어 또한 활발하게 주고받았다. 이렇게 긴 시간동안 익숙하지 않은 기술로 하나의 결과물을 향해서 팀원과 함께 달려나가는 경험은 나에게 협업하는 개발자로의 성장에 큰 도움이 되었다. 그리고 짧은 시간내에 새로운 언어, 툴에 적응하여 결과물을 만들어낸 만큼, 앞으로 내가 다른 개발 공부를 할 때 새로운 것도 쉽게 공부할 수 있는 중요한 힘을 이번 기회에 만들어졌다. 따라서 지속적으로 새로운 개념, 언어, 툴을 공부해야하는 개발자의 길을 가는 나에게 할 수 있다라는 자신감을 준 소중한 경험이었다!

'MOD' 카테고리의 다른 글

[넥슨/MOD] 알아두면 유용할 것들  (0) 2022.07.22
[넥슨/MOD] UI 에디터의 이해  (0) 2022.07.22
[넥슨/MOD] 컴포넌트 활용  (0) 2022.07.20
[넥슨/MOD] Event와 컴포넌트 확장  (0) 2022.07.19
[넥슨/MOD] 네트워크의 이해  (0) 2022.07.18
Comments