개발관련

Unity 게임 번역기 개발기 #1: 이게 시작이었다

삽질연구소 소장 2025. 10. 9. 15:20

마지막 개발중에 돌린 내용

사건의 발단

魔法少女ノ魔女裁判 라는 일본 비주얼 노벨 게임을 발견했다. 단간론파 비슷한 느낌이다. 재밌어 보인다.

문제: 한글이 없다. 중국어는 있다.

일본어는 어느정도 할 줄 안다. 하지만 편하게 우리말로 하고 싶었다.

중국어를 한글로 바꾸기만 하면 되는 거 아니야? 이게 모든 삽질의 시작이었다.

Unity Bundle 파헤치기

Unity 게임의 텍스트는 .bundle 파일에 들어있다. 암호문 같다.

구글링 1시간 → UnityPy 라이브러리 발견.

import UnityPy

env = UnityPy.load("game.bundle")
for obj in env.objects:
    if obj.type.name == 'TextAsset':
        data = obj.read()
        text = data.m_Script  # 됐다!

성공. 근데...

Naninovel 포맷이라는 복병

# CharacterID
; 日本語原文
中文翻译

이게 뭐야?

검색 결과: Naninovel이라는 엔진이다.

  • ; (세미콜론) = 일본어 주석
  • 그 아래 줄 = 실제 표시되는 텍스트

일본어 주석을 번역하면 중국어 대신 한국어가 나온다. 구조는 파악했다.

Google Translate 돌려보자

24개 스크립트 파일을 Google Translate API로 번역. 10분 완료.

게임 실행. 한글 나온다!

...근데 화면에 이상한 게 보인다.

<ruby="くびつ">首吊</ruby>り縄

루비 태그가 그대로 화면에 나온다.

목을 매는 밧줄이라고 써야 하는데 <ruby="くびつ">首吊</ruby>り縄 이게 그대로 나온다.

Ruby 태그와의 전쟁

정규표현식으로 태그를 제거했다.

def clean_ruby_tags(text):
    import re
    return re.sub(r'<ruby="[^"]*">([^<]+)</ruby>', r'\1', text)

<ruby="くびつ">首吊</ruby>り縄首吊り縄

깔끔. 이제 번역하면 되겠네?

근데 번역 품질이 별로다. 기계적이다. 어색하다.

Qwen AI 투입 (로컬의 맛)

Qwen2.5-7B 모델 발견. RTX 4080에서 돌아간다.

from transformers import AutoModelForCausalLM, AutoTokenizer

model = AutoModelForCausalLM.from_pretrained(
    "Qwen/Qwen2.5-7B-Instruct",
    device_map="auto",
    load_in_8bit=True  # VRAM 7GB
)

문맥을 줘봤다

이전 3개 대사를 context로 제공.

context = previous_lines[-3:]  # 이전 3개 대사
prompt = f"""
이전 대사:
{context}

번역할 문장:
{japanese_text}
"""

왜 3개?

  • 반말/존댓말 왔다갔다 안 하게
  • 말줄임(...) 같은 거 제대로 처리하게

성능

RTX 4080 16GB 기준:

  • 8비트 양자화: 문장당 1-2초
  • 챕터 1 전체: 1.5시간

결과: 어색하지만 게임은 플레이 가능.

GPU 팬 소리가 비행기 이륙하는 소리 같았다.

배운 것

  1. 도구부터 찾아라
    UnityPy 없었으면 바이너리 파싱부터 했을 것이다. 인생 낭비.
  2. 문제는 부딪히면서 해결
    Ruby 태그? 게임 켜보고 발견. 미리 알 수 없다.
  3. 문맥이 중요하다
    3개 대사 참고하니까 번역이 좀 낫다. 아주 조금.
  4. 로컬 AI의 장점
    무료다. 무제한이다. 전기세? 그건 나중 문제다.

다음 편: 로컬 모델은 한계가 있다는 걸 깨닫는 순간