프로젝트

타일맵 기반 3D게임(개발중)

카드와 자원을 활용해 진행하는 타일맵 기반 3D 게임입니다.

타일맵 기반 3D게임(개발중) 이미지 1/1

이미지 출처: 담당한 타일 영역 경계 메시 생성 기능의 Unity 에디터 화면

담당 업무

게임플레이 API와 비동기 실행 처리, 영역 표시 기능을 개발하고 있습니다. 현재 프로젝트에서 수행한 콘텐츠 제작 지원, 성능 개선, 입력 취소와 연출 처리를 아래 사례에 정리했습니다.

기술 사례

콘텐츠 제작을 위한 C# API

기획 시트의 Lua 스크립트에서 사용할 자원 증감, 카드 사용, 이벤트·스테이지 진행 API를 개발했습니다. 공통 게임 기능은 C#으로 제공하고, 콘텐츠별 조건과 효과는 스크립트에서 작성할 수 있도록 연결했습니다.

사용자 선택이 필요한 기능은 입력을 기다린 뒤 스크립트 실행을 이어가도록 구성했습니다. 카드의 상태가 바뀌면 설명에 표시되는 값도 갱신되도록 변경 이벤트를 연결했습니다.

Lua VM 반복 처리 최적화

Profiler에서 Lua VM의 반복 처리로 인한 CPU 병목을 확인했습니다. 카드 사용 조건을 평가할 때마다 실행 객체를 생성하고 API 등록, 스크립트 실행과 함수 조회를 반복하고 있었습니다.

조건 평가 경로의 변경
처리기존변경 후
실행 객체·API 등록평가할 때마다 생성·등록스크립트별 보관·재사용
스크립트 실행·함수 조회평가할 때마다 수행최초 준비 후 함수 참조 재사용
대상 카드·게임 수치호출 시 전달호출 직전 현재 값으로 갱신
조건 판정매번 실행매번 실행

실행 객체와 함수 참조를 캐시하고, 조건의 반환값은 매번 다시 계산했습니다. 보유 자원이나 대상 카드가 바뀌면 판정 결과도 달라져야 하기 때문입니다.

스테이지 효과와 카드 드로우 콜백에도 실행 환경과 함수 조회 결과의 재사용을 적용했습니다. 스테이지 효과가 바뀌면 해당 실행 환경을 초기화하도록 처리했습니다.

비동기 실행의 교착과 취소 처리

사용자 선택을 기다리거나 중간에 취소할 수 있는 게임 기능을 개발했습니다. 중첩 호출의 교착을 해결하고, 취소 시 게임 상태를 복원하도록 구현했습니다.

Lua에서 C# 비동기 함수를 호출한 뒤 다시 Lua 실행으로 이어지는 경로에서 교착이 발생했습니다. 입력 작업 전체의 종료를 기다리는 과정에 현재 작업 자신까지 포함되어 있었습니다.

상위 작업하위 작업 종료 대기 →하위 작업
← 전체 작업 종료 대기에 상위 작업까지 포함
수정 전 교착의 원인 · 작업 간 대기 관계

중첩 실행에서는 하위 비동기 작업을 직접 await하도록 변경했습니다. 입력 작업 큐를 기다리는 경로는 실행 전 작업 수를 기준으로 대기를 종료하도록 수정했습니다.

사용 중 자원이 소모되거나 카드가 이동한 뒤에도 취소가 가능해야 했습니다. 카드 사용 전 스냅샷을 남겨 상태를 복원하고, 취소한 작업에 예약된 후속 연출도 제거했습니다.

완료·취소 처리
처리 대상완료취소
게임 상태변경한 상태 확정사용 전 자원·카드 상태 복원
비동기 작업하위 작업 종료 후 진행상위 호출로 취소 전파
예약 연출정해진 순서로 실행취소한 작업의 예약 연출 제거

타일 영역 경계 메시 생성

높이가 서로 다른 육각형 타일 위에 점령 영역을 표시하는 기능입니다. 점령한 타일들을 하나의 영역으로 묶고, 바깥 테두리를 따라 일정한 폭의 띠를 그려야 했습니다. LineRenderer로 표시했을 때 선이 비틀리는 문제가 있어, 영역의 외곽을 계산하고 메시를 직접 생성하는 방식으로 변경했습니다.

먼저 서로 맞닿은 타일 사이의 변을 제외하고, 남은 변을 연결해 영역의 외곽 경로를 구했습니다. 외곽 경로를 찾는 부분과 실제로 화면에 그릴 메시를 만드는 부분은 별도 클래스로 나눴습니다.

메시를 만들 때는 테두리의 폭과 타일의 높이를 따로 계산했습니다. 위에서 내려다본 평면인 XZ 평면에서 띠의 양쪽 가장자리를 구하고, 타일 간 높이 차이는 수직 연결 면으로 처리했습니다. 높낮이 변화가 띠의 폭을 계산하는 방향에 영향을 주지 않도록 한 것입니다.

모서리에서는 두 띠가 만나는 부분을 마이터 조인(miter join)으로 연결했습니다. 각도가 좁아질수록 접합부가 길게 튀어나갈 수 있으므로, 테두리 폭을 기준으로 접합 길이에 상한을 두었습니다.

타일 영역 메시 생성 코드

추가 작업

키보드로 카드를 선택하고 사용하는 기능

Input System으로 카드 선택과 주요 기능의 키보드 입력을 연결하고, 단축키 표시와 배속 조작을 구현했습니다.

키보드와 게임패드의 컨트롤 스키마를 정의했습니다.

정해진 카드 구성을 유지하는 비복원추출

카드팩은 기획 데이터에 카드별 수량과 한 번에 뽑을 장수가 정해져 있었습니다. 여러 번에 걸쳐 개봉하더라도 전체 획득 수량은 처음 정한 구성과 일치해야 했습니다. 매번 같은 목록에서 독립적으로 뽑는 대신, 남아 있는 카드 중에서 선택하고 뽑힌 수량만큼 제외하는 비복원추출을 적용했습니다.

같은 종류의 카드도 구성에 여러 장 포함되어 있으면 그 수량만큼 나올 수 있습니다. 카드 종류의 중복을 막는 것이 아니라, 구성된 수량을 초과해 뽑히지 않도록 하는 방식입니다. 남은 카드와 이미 뽑은 카드, 개봉 횟수를 함께 저장해 다음 개봉에서도 같은 진행 상태를 이어가도록 구현했습니다.

추출 목록의 순서는 결과에 영향을 주지 않으므로, 선택한 항목을 마지막 항목으로 덮어쓴 뒤 목록 끝에서 제거했습니다. 중간 항목을 삭제할 때마다 뒤쪽 요소를 이동시키는 비용을 없애, 한 장을 제외하는 연산을 O(1)로 처리했습니다.