프로젝트 일지

[Spring Boot Web Chatting] ChatForYou - 화상채팅 CatchMind 게임 만들기 : 모바일 이벤트 구현 및 베타 서비스 시작!

TerianP 2024. 5. 1.
728x90

1. 시작하면서

이번에도 오랜만에 글을 작성해보네요. 사실 저번에 게임 기획 된 내용에 정리할 겸 코드 정리할 겸 곧 돌아오겠습니다!! 라고 자신만만하게 이야기했지만...결국 이리저리 바쁘기도 하고 친구들과 버그 테스트 하면서 고칠것도 많고 했어서 작성은 못했습니다ㅠㅠ

 

그렇지만!! 이제 정말 마무리가 다 되어가고 있습니다! 제가 생각한 것에서 대략 80% 혹은 많게는 85% 까지 구현이 되었다고 생각이 듭니다. 물론 아직 버그 테스트도 많이해야하고, 엣지 케이스도 생각해야하고, 코드 정리나 python 쪽 속도 문제나 정말 많은 문제가 있지만 그래도 이제 정말 '게임' 으로서 완성되어가고 있다는게 보여질 정도입니다.

 

이번에 일지에 기록할 내용은 '모바일' 환경 개발에 대한 부분입니다. 친구들과 게임하면서 가장 많이 받았던 요청 중 하나였습니다.

'나 집이 아니다, 모바일로 되냐?', '컴터 켜기 귀찮다 모바일로 되냐?' 등등...테스트 하면서 계속 '모바일' 서비스에 관한 내용이 있어왔고, 실제로 모바일로 할 수 있으면 접근성에서 더 좋겠다라는 생각이 들어서 모바일에서 catchmind 를 할 수 있도록 만들어봤습니다.

실제로 앱을 개발한 것은 전혀 아니고, 단순히 안드로이드 크롬, 웨일 등 웹앱에서 제 채팅 서버 접속 후 게임이 가능하도록만 개발되었습니다.

 

모바일 테스트 환경 및 개발 환경은 안드로이드 / 레노버 Y702, 갤럭시 Note 10+ / chrome , 웨일 앱을 사용했습니다.

 

또한 오늘 저녁 최소 내일에는 제 서버에 현재까지 개발된 내용을 모두 올려서 서버를 열어둘 생각이고 이를 통해 오픈 베타 테스트를 할 예정입니다. 혹시 관심이 있다면 많은 참여와 함께...QA 부탁드립니다(_ _)

 

2. catchmind 모바일 이벤트 구현

사실 모바일 이벤트 즉, 터치 이벤트 자체는 그렇게 어려운 것은 아니었습니다. 예제 코드들도 굉장히 많았고, '터치' 와 '모바일 구분'이 된다는 가정하에 그림을 그리는 이벤트는 기존의 마우스 이벤트를 그대로 따라가면 되는거였거든요. 물론 처음에는 투닥거리는 부분도 많았고, 기존 이벤트를 날려먹어서 다시 불러오는 불상사까지 있었으나 어찌저찌 이벤트, 기능 동작은 무리없이 가능했습니다.

initMobileCanvasEvents: function () {
    let self = this;

    // 터치로 그리기 시작
    self.canvas.addEventListener('touchstart', function (e) {
        e.preventDefault();  // 기본 터치 스크롤 방지
        if (self.isTimeRemain) {
            let touch = e.touches[0];
            self.setMousePosition(touch);  // 터치 위치 설정
            self.drawing = true;

            const pos = {
                "gameEvent": "mouseEvent",
                "mouseInit": true,
                "mouseX": self.lastX,
                "mouseY": self.lastY
            };
            dataChannel.sendMessage(pos, 'gameEvent');
        }
    }, { passive: false });

    // 터치로 그리기
    self.canvas.addEventListener('touchmove', function (e) {
        e.preventDefault(); // 기본 터치 스크롤 방지
        if (self.drawing && self.isGameStart) {
            let touch = e.touches[0];
            self.ctx.beginPath();
            self.ctx.moveTo(self.lastX, self.lastY);
            self.setMousePosition(touch);
            self.ctx.lineTo(self.lastX, self.lastY);
            self.ctx.stroke();

            const pos = {
                "gameEvent": "mouseEvent",
                "mouseX": self.lastX,
                "mouseY": self.lastY
            };
            dataChannel.sendMessage(pos, 'gameEvent');
        }
    }, { passive: false });

    // 터치 끝
    self.canvas.addEventListener('touchend', function (e) {
        self.drawing = false;
    });

    self.canvas.addEventListener('touchcancel', function (e) {
        self.drawing = false;
    });

    $('#answerBtn').text('Type Your Answer!');
},

 

 

다만 이번에는 저말 아쉬운 점도 굉장히 많았습니다. 첫째로 정답을 맞추는 '음성' 이벤트입니다. 제가 사용했던 것은 SpeechRecognition 으로 음성 인식을 도와주는 Web Speech API 인데 문제는 이 친구가 모바일에서는 계속 에러를 내뱉는다는 점이었습니다.

정확히는 모바일에서는 SpeechRecognition 대신 webkitSpeechRecognition 를 사용하는데 문제는 해당 객체를 불러오는 것까지는 성공하는데 실제로 호출 후 사용할때는 음성 인식이 전혀 안되는 문제가 있었습니다. 추가로 start() 로 음성 인식 이벤트를 시작하고 stop() 으로 중지한 후 다시 start() 를 하게되면, 이미 start() 되어있는 상태이기 때문에 실행할 수 없다 라는 에러를 불러내더라구요.

--> 혹시나 해결책을 아시는 분...댓 부탁드립니다ㅠㅠㅠ

this.recognition = new webkitSpeechRecognition() || new SpeechRecognition();

 

결국 모바일 환경에서는 음성 인식이 아닌 정답을 직접 타이핑하는 방향으로 전환할 수 밖에 없었습니다ㅠㅠ

 

두번째는 모바일 UI 입니다. 사실 이 부분은 이전부터 문제가 많았던 부분이긴 한데, 이번에 모바일로 접속을 더 해보면서 정말 정말...많이 느꼈습니다. 모바일 UI 가 필요하구나...하고 말이죠ㅠㅠ 이 부분은 단순히 지금 해결될 부분은 아닌 것 같고, 추후 시간을 들여 UI 디자인을 어느 정도 잡고 개발하는 것을 목표로 하고 있습니다.

 

3. 이전에 받은 주제(subjects) 제외하기

다음으로 테스트하면서 가장 많이 느낀 것은 특정 catchmind 주제 선정 시 중복된 subjects 가 너무 많다는 점이었습니다. 예를 들어 '게임' 을 이번 주제로 선정한 후 받은 subjects 가 ['롤', '도타', '오버워치', '서든어택', '스타크래프트'] 일 때 이후에 다시 게임 주제를 선정하게 되면 subjects 에서 이전에 받았던 subjects 인 ['롤', '도타', '오버워치', '서든어택', '스타크래프트']  와 중복되는 경우가 매우 높았습니다. 많게는 5개의 주제 중 많게는 4개(80%) 최소 2개(40%) 정도는 중복되서 나오는 경우가 많더라구요.

 

특히 '비디오 게임' , '애니메이션' 같은 주제는 더 심했고, '과일' '나라 이름' 등의 주제도 비슷한 경향을 보였습니다.

처음에는 모든걸 프롬프트로 해결하려고 했으나...프롬프트를 건드릴 수도록 chatgpt 께서 주제를 만드는데 걸리는 시간이 오래걸리게 되었고, 중간중간 이상한 답변을 내놓는 경우도 있었습니다(title 이 '게임' 인데 대답은 '삼겹살'을 내놓는 경우)

 

결국 방법을 살짝 바꿔서 프롬프트와 코드 모두를 수정하게 되었습니다. 

서버쪽에서는 각 라운드마다 현재 선택된 대주제(title) 와 소주제(subjects)를 저장해둔 후 이를 사용할 수 있도록 변경하였습니다. 코드는 정말 단순한데 ptyhon server 로 요청하기 전에 이전에 해당 대주제가 선택된 적이 있다면 가져와서 세팅하고 요청하게 됩니다. 결과를 받은 뒤에는 현재 대주제와 소주제를 before_subjects map 에 저장해두게 됩니다. 이렇게하면 게임이 진행되면서 동일한 title 이 선택될 때마다 이전의 subjects 를 python server 로 보내게되고 이쪽에서는 해당 주제를 제외한뒤 결과를 반환하게 됩니다.

 

물론 이렇게 해서 그런지 답변까지의 시간이 많이 느려진 듯 한데ㅠㅠ 이것도 추후 꼭 해결해야할 과제가 될 듯 합니다.

private GameSubjects setBeforeSubjects(GameSettingInfo gameSettingInfo, GameSubjects gameSubjects) {
    if (CollectionUtils.isEmpty(gameSettingInfo.getBeforeSubjects())) {
        Map<String, List<String>> beforeSubjects = new ConcurrentHashMap<>();
        beforeSubjects.put(gameSubjects.getTitle(), Collections.emptyList());
        gameSettingInfo.setBeforeSubjects(beforeSubjects);
    } else {
        List<String> beforeSubjects = gameSettingInfo.getBeforeSubjects()
                .getOrDefault(gameSubjects.getTitle(), Collections.emptyList());
        gameSubjects.setBeforeSubjects(beforeSubjects);
    }
    return gameSubjects;
}

    public GameSubjects getSubjects(String roomId, GameSubjects gameSubjects) throws Exception {

        try{

            setBeforeSubjects(gameSettingInfo, gameSubjects); // 이전 주제 세팅
            gameSubjects = HttpUtil.post(catchMindAPI.getUrl()+ gameSubjectUrl, new HttpHeaders(), new ConcurrentHashMap<>(), gameSubjects, GameSubjects.class);
            gameSettingInfo.getBeforeSubjects().put(gameSubjects.getTitle(), gameSubjects.getSubjects()); // 현재 주제를 이전주제로 세팅
            log.info("subjects :: {}",gameSubjects.toString());

            return gameSubjects;
        } catch (Exception e){ // 예외 발생 시 기본 리스트를 반환
            e.printStackTrace();
            return gameSubjects;
        }
    }

 

4. catchmind 모바일 테스트 영상

대망의 모바일 테스트 영상입니다. 베드민턴이...이제보니까 베드민턴이ㅋㅋㅋㅋㅋㅋㅋㅋㅋ

모바일로 그리는거니까 이해해주세요! 저 이것보다는 잘 그려요! 아마도..?

 

댓글