게임

Unity에서 다른 게임 엔진으로 마이그레이션

Basil Shikin
9월 17일, 2023

지난 한주간 Unity에서 다른 게임 엔진으로 손쉽게 마이그레이션할 수 있는 툴에 대한 관심이 높아지고 있습니다. 이번 글에서는 마이그레이션의 기술적인 측면에 관한 저의 생각을 공유하고 과정을 단순화하기 위한 몇 가지 접근 방법을 제안하고자 합니다.

Unity 프로젝트를 구성하는 요소 중 마이그레이션이 필요한 요소는 다음과 같습니다:

  • 스크립트 (MonoBehaviour 클래스와 기타 .cs 파일)
  • 정적 에셋 (씬, 프리팹, 머티리얼, 사운드 등)
  • 플러그인

그럼 하나씩 알아보시죠.

스크립트 마이그레이션

스크립트 마이그레이션은 개발자에게 제일 어려운 과제일 거라고 생각합니다. Unity 엔진 API를 다른 플랫폼으로 포팅하기는 쉽지 않습니다. 뿐만 아니라 스크립트를 포팅하려면 타겟엔진의 동일한 역할을 하는 API에 대한 지식이 필요하고, 이는 새로운 기술을 학습하는 데 걸리는 시간을 고려하면 이 또한 문제가 될 것입니다.

하지만 최신 LLM(거대언어모델)은 언어 번역과 코딩에 탁월하여 활용할 경우, 마이그레이션을 대폭 간략화할 수 있습니다.

저희는 ChatGPT 3.5 Turbo를 사용해서 모든 프로젝트의 Unity 클래스를 GDScript (Godot 스크립트)에 마이그레이션하는 Proof of Concept를 만들었으며, 만족스러운 결과를 얻었습니다.

Unity C#GDScript
public class Player : MonoBehaviour 
{
  private SpriteRenderer spriteRenderer;
  public Sprite[] sprites;
  private int spriteIndex;

  public float strength = 5f;

  private Vector3 direction;

  private void Awake() 
  {
    spriteRenderer = GetComponent<SpriteRenderer>();
  }

  private void Start()
  {
    InvokeRepeating(
        nameof(AnimateSprite),
        0.15f,
        0.15f
    );
  }

  private void OnEnable()
  {
    Vector3 position = transform.position;
    position.y = 0f;
    transform.position = position;
    direction = Vector3.zero;
  }
}
class_name Player:

var sprite_renderer: SpriteRenderer
var sprites: Array
var sprite_index: int

var strength: float = 5.0

var direction: Vector3

func _ready():
  sprite_renderer = get_node("SpriteRenderer")

func _on_start():
  call_deferred("animate_sprite")
  set_process(true)

func _on_enable():
  var position = transform.position
  position.y = 0.0
  transform.position = position
  direction = Vector3.ZERO

보시다시피 대부분의 이름과 메소드는 올바르게 번역되었지만, 다소 수정이 필요한 부분도 있습니다(예를 들어 InvokeRepeating이 올바르게 포팅되지 않은 점, Start() 함수가 _ready()로 변환되지 않은 점 등) .

다음은 Unreal Engine(*)으로 마이그레이션된 동일한 코드입니다.

Unity C#Unreal Engine CPP
public class Player : MonoBehaviour 
{
  private SpriteRenderer spriteRenderer;
  public Sprite[] sprites;
  private int spriteIndex;

  public float strength = 5f;

  private Vector3 direction;

  private void Awake() 
  {
    spriteRenderer = GetComponent<SpriteRenderer>();
  }

  private void Start()
  {
    InvokeRepeating(
        nameof(AnimateSprite),
        0.15f,
        0.15f
    );
  }

  private void OnEnable()
  {
    Vector3 position = transform.position;
    position.y = 0f;
    transform.position = position;
    direction = Vector3.zero;
  }
}
APlayer::APlayer()
{
  // Set this actor to call Tick() every frame.
  PrimaryActorTick.bCanEverTick = true;

  // Create the root component
  RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));

  // Create and attach a static mesh component
  MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComponent"));
    MeshComponent->SetupAttachment(RootComponent);

  // Set default values
  Strength = 5.0f;
  SpriteIndex = 0;
}

void APlayer::BeginPlay()
{
  Super::BeginPlay();

  // Start the sprite animation timer
  GetWorldTimerManager().SetTimer(
    SpriteAnimationTimer,
    this,
    &APlayer::AnimateSprite,
    0.15f, 
    true, 
    0.15f
  );
}

void APlayer::OnConstruction(const FTransform& Transform)
{
  Super::OnConstruction(Transform);

  // Initialize the sprite renderer component
  MeshComponent->SetStaticMesh(SpriteMeshes[SpriteIndex]);
}

void APlayer::OnEnable()
{
  Super::OnEnable();

  // Reset the player's position and direction
  FVector Position = GetActorLocation();
  Position.Z = 0.0f;
  SetActorLocation(Position);
  Direction = FVector::ZeroVector;
}

GDScript와 같이 LLM을 통해 전체적인 로직을 잘 번역하였지만, 올바르게 작동하려면 조금의 개발자의 조정이 필요합니다.

LLM이 스크립트 마이그레이션 문제를 완전히 해결할 수는 없지만, LLM에서 생성된 코드는 빠른 마이그레이션을 위한 기준점이 될 것입니다.

이 결과는 상당히 실험적으로, 여러분도 오픈소스 마이그레이션 프로젝트에 참여하여 프롬프트를 발전시키거나 C# 파싱을 수정제안하거나 여러 엔진으로의 마이그레이션을 추가해 주세요.

정적 에셋 마이그레이션

정적 에셋 마이그레이션을 또 다른 챌린지로 볼 수도 있지만, 이 또한 자동화될 수 있습니다. 다만 정적 에셋 마이그레이션 프레임워크는 복잡해질 수 밖에 없습니다. Unity 에셋 저장 포맷은 에디터 버전마다 다르기 때문에 마이그레이션 툴은 다양한 포맷 대응에 더불어 에셋이 올바르게 마이그레이션되도록 잠재적 종속성, 에셋 간의 관계 및 호환성 문제를 인식하고 고려해야 하기 때문입니다.

몇 가지 에셋 마이그레이션 툴은 이미 공개되어 있으며(예를 들어 Godot FBX2glTFUnreal Engine의 네이티브 FBX 지원) 정적 에셋 마이그레이션의 기초로 사용할 수 있습니다.

마이그레이션 툴이 에셋 포팅에게 어떻게 도움이 될지에 대해서도 조사해야 합니다. 에셋 포맷은 엔진 버전마다 결정되기 때문에 “웰메이드” 마이그레이션 툴은 다양한 스튜디오 및 프로젝트에 활용할 수 있을 겁니다.

저희는 오픈소스의 Godot 마이그레이션 플러그인 중 하나를 실행하여 다음과 같은 결과를 얻었습니다.:

UnityGodot

플러그인 마이그레이션

수년간 Unity 엔진은 수백 명의 개발자가 플러그인, 모델, 툴을 제공하면서 풍부한 에코시스템을 구축해 왔습니다. 이러한 툴을 새로운 플랫폼으로 마이그레이션하려면 상당한 노력이 필요합니다. 하지만 Unity 프로젝트 마이그레이션 툴을 사용하면 플러그인 개발자가 다른 엔진으로 플러그인을 포팅하는 데도 도움이 될 수 있습니다.

AppLovin은 MAX 광고 솔루션에 사용할 수 있는 UnrealGodot 플러그인을 작성했습니다. 하지만 이것은 시작의 불과합니다. 저희가 다뤄야할 엔진은 아직 많이 남았습니다. 

프로젝트 비전

이 툴의 현재 버전은 Proof of Concept입니다. Unity에서 다른 엔진으로 마이그레이션할 때 최신 LLM의 활용 가능성을 확인하는 것이 목적이며, 초기 결과는 긍정적이였습니다. 기본적인 게임 엔진 구조는 비슷하지만 언어나 API는 크게 다릅니다. 아이디어를 일반화해서 번역하기, 이것이 바로 LLM이 잘 하는 것입니다.

정적 에셋 마이그레이션을 연동하고 LLM 프롬프트를 개선할 수 있다면 이 툴은 몇 백명의 개발자가 새로운 엔진에서 게임 전체를 처음부터 다시 개발안해도 되게 만들어 줄 겁니다. 

자동으로 마이그레이션된 프로젝트가 바로 작동할 가능성은 낮지만, 번역된 비즈니스 로직, 임포트된 에셋, 자동 생성된 주석이나 힌트를 활용하면 개발자들은 반복적이고 지루한 마이그레이션 작업으로 시간을 소비할 필요는 없어질 겁니다.

하지만 이를 실현하기 위해는 커뮤니티 지원이 필요합니다.

여러분의 힘이 필요합니다

개발자 커뮤니티는 다른 엔진들의 생태계를 강화하며, 반복적인 마이그레이션 작업을 줄이는 오픈소스 도구들을 만드는데 있어 필수적입니다.

저는 Unity를 넘어 더 나아가는 실질적인 방법을 여러분과 같이 만들 수 있다고 믿습니다.

저희 GitHub를 확인하세요. 아직 POC(Prool of Concept) 단계지만 적극적으로 활동하고 있습니다.  디자인 관련 질문이나 문제 추적, 지원을 위해 Discord 서버에 참여하세요.

  • Godot과 Unreal 대상 ChatGPT 프롬프트 리뷰 및 개선하기
  • Defold, Cocos2D를 비롯한 다른 엔진으로의 마이그레이션을 지원하는 프롬프트 및 툴 추가하기
  • 마이그레이션에 도움이 되는 기졸 플러그 리스트 업데이트하기
  • 이러한 툴을 Linux, MacOS, Windows에서 신규 개발자가 손쉽게 실행할 수 있도록 지원하기
  • 툴 아키텍처 관련 피드백 제공하기

이러한 툴들은 MIT 라이선스로 출시되었으며, 다음과 같은 방법으로 참여 및 기여할 수 있습니다


(*) 시인성 개선을 위해 코드 서식을 변경했습니다

Spread the love

인기 글

비즈니스 목표별 검색

리소스 기업정보