일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- string
- Express.js
- 백준 32028번
- JavaScript
- ERD
- localstorage
- pm2
- 더 흔한 색칠 타일 문제
- ucpc 2023 예선 d번
- Next
- ucpc 2024 예선 e번
- 그리디
- 백준 28303번
- 자바스크립트
- branch
- Github
- Prisma
- ccw 알고리즘
- 지금 자면 꿈을 꾸지만
- router
- html5
- 백준 32029번
- HTTP
- MongoDB
- PROJECT
- ucpc 2023 예선 i번
- map
- 게임 서버 아키텍처
- MySQL
- insomnia
- Today
- Total
dh_0e
[Project] 타워 디펜스 게임 (팀 프로젝트) 본문
이번 팀 프로젝트의 주제는 튜터님께서 만드신 타워 디펜스 게임 클라이언트의 서버를 만들어 적용시키는 것이다. 웹소켓으로 서버를 열고, 검증 로직, 다양한 기능을 추가하여 서버를 구성하였다.
서버 기능 명세 (필수 구현)
- 회원가입 / 로그인 기능 (REST API로 통신)
- 로그인 성공 시 Access Token을 발급받도록 해주세요.
- 이후에 WebSocket 통신에서 해당 Access Token을 기반으로 유저 인증을 할 때 쓸 것입니다.
- 로그인 성공 시 Access Token을 발급받도록 해주세요.
- 유저 별 게임 데이터 관리
- 클라이언트 코드에 있는 해당 변수들은 서버로부터 동기화를 받아야 하며 유저별로 관리가 되어야 해요!
- 공통 데이터
- 기지 체력
- 최초 유저 골드
- 타워 구입 비용
- 초기 타워 개수
- 유저 데이터
- 몬스터 레벨
- 몬스터 생성 주기
- 게임 점수
- 기존 최고 점수
- 타워 좌표들 (배열)
- 공통 데이터
- 클라이언트 코드에 있는 해당 변수들은 서버로부터 동기화를 받아야 하며 유저별로 관리가 되어야 해요!
- let userGold = 0; // 유저 골드 let baseHp = 0; // 기지 체력 let towerCost = 0; // 타워 구입 비용 let numOfInitialTowers = 0; // 초기 타워 개수 let monsterLevel = 0; // 몬스터 레벨 let monsterSpawnInterval = 0; // 몬스터 생성 주기 let score = 0; // 게임 점수 let highScore = 0; // 기존 최고 점수
- 클라이언트가 서버로부터 수신하는 이벤트 종류 정의 및 코드 구현 (WebSocket으로 통신)
- 커넥션 성공 이벤트
- 커넥션 실패 이벤트
- 상태 동기화 이벤트
- 해당 이벤트를 통해 게임에서 필요한 데이터들을 서버로부터 받고 상태를 동기화 합니다.
- 우선은, 최초에 게임이 시작될 때 필요한 모든 게임 데이터들을 전달 받아야겠죠?
- 이후에는 상태 동기화를 해야 될 때마다 필요한 게임 데이터만 선택적으로 전달을 받으면 됩니다.
- 예를 들어, 몬스터를 한 번 죽였을 때 스코어가 100점이 올라야겠죠?
- 서버는 해당 유저의 스코어를 +100점 한 후 현재 스코어를 클라이언트에게 전달합니다.
- 타워 디펜스 게임 규칙 섹션을 잘 숙지하시고 언제 어떤 데이터들로 상태 동기화가 되어야하는지는 여러분들이 스스로 잘 판단을 해보시길 바랍니다!
- 클라이언트가 서버로 송신하는 이벤트 종류 정의 및 코드 구현 (WebSocket으로 통신)
- 게임 시작 이벤트
- 커넥션 성공했다고 서버로부터 이벤트를 수신하면 해당 이벤트를 바로 보내시면 됩니다.
- 최초 타워 추가 이벤트
- 최초 타워는 numOfInitialTowers 값 만큼 공짜로 추가를 해줘야 합니다.
- 자세한 것은 game.js의 placeInitialTowers 함수를 참고해주세요!
- 해당 이벤트에는 { x, y } 좌표를 추가적으로 보내주세요.
- 서버에서는 유저 별로 최소한 어떤 좌표들에 타워가 있는지를 체크하기 위함입니다!
- 타워 구입 이벤트
- 타워 구입은 골드를 지불해야겠죠? 근데, 돈이 모자라면 구입이 되면 안됩니다.
- 자세한 것은 game.js의 placeNewTower 함수를 참고해주세요!
- 해당 이벤트에는 { x, y } 좌표를 추가적으로 보내주세요.
- 서버에서는 유저 별로 최소한 어떤 좌표들에 타워가 있는지를 체크하기 위함입니다!
- 몬스터 죽이는 이벤트
- 몬스터가 죽으면 클라이언트가 서버에 몬스터를 죽였다라고 이벤트를 보내줘야 합니다!
- 게임 오버 이벤트
- 기지가 파괴가 되면 게임 오버 이벤트도 보내줘야겠죠!
- 게임 시작 이벤트
- 유저 별 최고 기록 스코어 저장
- 유저 정보와 최고 기록 스코어를 기록할 최소한의 데이터베이스는 당연히 있어야겠죠?
더 재밌는 게임을 만들기 위한 도전! (도전 과제)
- 타워 환불 기능
- 타워 위치가 너무 별로이면 타워 환불을 할 수도 있으면 좋겠네요!
- 랜덤 배치라는 특성을 고려하면 판매시 골드는 구입 골드로 환불을 해주고요!
- 환불이 되면 타워는 당연히 맵상에서 사라지고 서버상에서도 사라져야겠죠?
- 특정 타워 업그레이드 기능
- 타워 업그레이드를 하면 새로운 타워 스프라이트도 적용해서 진짜 업그레이드 느낌이 나는거죠!
- 공격력이 오르고 레이저 빔 발사 주기도 조금 더 짧아질 수 있겠네요.
- 물론, 업그레이드도 돈이 들어야겠죠?
- 랜덤 배치된 타워에서 특정 타워를 어떻게 선택해서 업그레이드 할 수 있을까요?
- 만약, 이게 힘들다면 랜덤하게 업그레이드를 하는 기능도 방법입니다!
- 보물 고블린 몬스터 출연 기능
- 이따금씩 몬스터 웨이브 사이에 보물 고블린 몬스터를 출연시켜서 이 친구를 잡게 된다면 그 즉시 골드 보상을 해주거나 타워 보상을 해주면 그것도 재밌을 것 같습니다!
게임 작동 화면
서버와 통신 로그
게임 시연 영상
프로젝트 Github URL: https://github.com/znfnfns0365/tower-defense-game
- Readme 파일에 게임 설명, AWS 배포 링크, 기능, 패킷 구조, 데이터 테이블, 폴더 구조 등이 나와있다.
Trouble Shooting
처음엔 전 프로젝트까지 쓰던 Prisma로 사용자의 정보(아이디, 패스워드)와 uuid, 유저 최고기록을 저장하였다. 이 과정에서 사용자의 정보같은 가벼운 데이터를 굳이 Prisma에 저장하지 말고 가벼운 데이터들을 저장할 때 주로 쓰는 redis에 저장하자는 의견이 나왔다.
Problem: Prisma를 redis로 리팩토링하는 과정에서, 관계형 데이터베이스인 Prisma에서 find할 때 where구문으로 일치하는 데이터를 찾아서 가져오는 과정을 "key-value" 형식의 비관계형 데이터베이스인 redis에서는 직접 구현해야하는 상황이 발생했다. (key값으로 설정한 데이터(=username)를 찾을 때는 바로 찾을 수 있어서 편했지만, value에 들어가있는 데이터(=uuid)로 유저 데이터를 불러와야하는 상황)
Solve: redis의 keys('*') 메소드를 이용해서(모든 key 값을 불러옴) 모든 value를 불러온 뒤, uuid(key가 아닌 데이터)가 일치하는 key값을 찾아 return해주는 함수를 짰고, 이를 다른 redis 함수들과 함께 userData.handle.js 파일에 함수만 따로 저장해서 혼잡할 수 있는 코드를 깔끔하게 정리하여 해결했다.
But !!
Redis에서 *(와일드카드)로 키 목록을 스캔하는 것은 매우 위험한 행위이다. 서버의 유저가 많아질 수록 몇 명의 유저가 본인의 이름을 조회만 하려고 해도 이 게임 서버는 마비가 될 수 있다는 튜터님의 피드백이 있었고, 해결 방안으로 위와 같은 안티패턴 대신 username에 맞는 키 값을 미리 캐싱을 해놓거나, username 자체를 이용하여 키를 만들고 바로 조회되도록 수정하라 하셨다.
서버에서 유저의 정보를 가져올 때, Prima를 사용할 당시에 where 구문을 사용했기 때문에, find할 데이터가 username, uuid 등 여러가지를 사용했는데 이 부분을 처음부터 하나의 데이터로 통일했으면 이런 문제가 일어나지 않았을 것이다.
앞으로 프로젝트를 진행할 때 이 부분을 팀원들과 미리 조율하고, 구현 전에 통일시키는 것이 중요할 것 같다!
소감
사다리타기를 통해 결정된 팀장을 정하고 진행했던 프로젝트라 처음엔 걱정이 됐다. 하지만 팀원 모두가 열심히 참여하며 조원들끼리 똘똘 뭉쳐 역할을 분담한 뒤 진행한 결과, 프로젝트를 성공적으로 완성하였고, 이는 오히려 긍정적인 영향을 많이 미친 것 같다. 누구 한 명의 일방적인 캐리없이 모두가 자신의 역량을 발휘하여 완성한 결과물이라 조원들 모두가 뿌듯했고, 이번 프로젝트를 통해 다 함께 성장했다는 느낌을 받았다.
'내일배움캠프 > Project' 카테고리의 다른 글
[Project] 멀티 플레이 TCP 서버 구현하기 (개인과제) (0) | 2024.07.10 |
---|---|
[Project] 풋살 온라인 게임 (팀 프로젝트) (0) | 2024.06.26 |
[Project] Insomnia에서 팀원들과 협업하기 (0) | 2024.06.05 |
[Project] 협업 시 유용한 사이트 및 정보 (0) | 2024.05.31 |
[Project] 고도화된 게임 아이템 시뮬레이터 서비스 (개인 과제) (1) | 2024.05.29 |