Gaming

Migrating From Unity to Other Game Engines

Basil Shikin
Sep 18, 2023

Over the last week we saw a rising interest in tools that allow migration from Unity to other game engines. I want to share some thoughts on the technical aspects of the migration and propose a few approaches to simplify it.

A Unity project consists of the following conceptual parts that need migration:

  • Scripts (MonoBehaviour classes and other .cs files)
  • Static Assets (scenes, prefabs, materials, sounds, etc.)
  • Plugins

Let’s consider them one by one.

Migrating Scripts

Script migration is probably the biggest challenge for the developer. Unity’s engine APIs can not be easily ported to other platforms. Moreover, porting scripts requires an understanding of the target engine’s equivalent APIs, which could be problematic given a steep learning curve when moving to a new technology.

However, I believe that modern LLMs can greatly simplify the migration. LLMs excel at translating language and coding. We can leverage it to make our jobs easier.

We have created a Proof of Concept that uses ChatGPT 3.5-Turbo model to migrate all project Unity classes to GDScript (Godot script) and saw some satisfactory results (*):

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

As you can see, most of the names and methods were translated correctly. However, manual intervention will be required (for instance, InvokeRepeating was not ported correctly, Start() method name should have been _ready(), etc.).

Here is the same code migrated to 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;
}

As with the GDScript, LLM was able to translate overall logic reasonably well, but manual tweaks are required to make it work correctly.

Migrating to Cocos produces a similar quality of output, however, more work is needed to tweak the prompts.

I do not think LLMs can fully solve the script migration problem. However, LLM-generated code can create a baseline that can speed up the migration process.

The results above are highly experimental and we invite everyone to join the open source migration project, suggest prompt improvements, tweak C# parsing, and add other destination engines.

Migrating Static Assets

Migrating static assets is another challenge, but I believe that static asset migration can be highly automated. However, a framework to migrate static assets will inevitably end up complex. Unity’s asset storage format has been changing across editor versions, which means that a migration tool would have to be aware of multiple format variations, as well as potential dependencies, relationships between assets, and compatibility issues to ensure that the assets are migrated correctly.

Some asset migration tools are already in place (for instance, FBX2glTF for Godot, native FBX Support of Unreal, or FBX Smart Material Conversion for Cocos), which gives a solid foundation for static asset migration.

The next area of research should be figuring out how a migration tool can help port assets. A well-made migration tool should be reusable across multiple studios and projects since asset formats are deterministic (per engine version). 

We have experimented with one of the open source Godot migration plugins to obtain the following preliminary results:

UnityGodot

Migrating Plugins

Over the years, the Unity engine has built a rich ecosystem with hundreds of developers contributing plugins, models, and tools. It will take considerable effort to migrate those tools to the new platforms. However, a Unity project migration tool can also help developers port their work to other engines.

At AppLovin, we have created Unreal and Godot plugins for our MAX ad solution (with the Cocos MAX plugin arriving soon), but that is just a start. We recognize that there are many more engines to cover.

Project Vision

The current version of the tool is a proof of concept. I wanted to demonstrate that we can utilize modern LLMs to ease the transition between Unity and other engines and I think early results are positive. It makes sense, because fundamental game engine structures are similar, but language and APIs can vary a lot. This is exactly what LLMs are good at: generalizing and translating an idea.

Once we implement static asset migration, and improve LLM prompts, I believe this tool has the potential to help hundreds of developers ease the daunting task of re-writing the entire game from scratch on a new engine. 

While an auto-migrated project is unlikely to work out-of-the-box, the translated business logic, re-imported assets, auto-generated comments, and hints will spare engineers from days of tedious, repetitive, and formulaic migration work.

The first three engines we would focus on are Godot, Cocos, and Unreal.

But we need help from the community to get there.

Call to Action

It will take the developer community to enhance other engines’ ecosystems and it will take that community to create open source tools that reduce repetitive migration work.

I believe that together we can create viable options to move beyond Unity Game Engine.

Check out our migration proof of concept on GitHub. It is a POC now, but we are actively working on it. Join our Discord server for support, design questions, and issue tracking.

The project is released under MIT license, and if you’d like to contribute, you can by:

  • Reviewing and improving ChatGPT prompts for Godot and Unreal
  • Adding prompts/tools to migrate to Cocos
  • Adding static asset migration tools for Godot, Cocos, and Unreal
  • Contributing to a list of existing plugins that help migration
  • Making this tool easy to run for new developers across Linux, MacOS, and Windows
  • Providing any feedback on the architecture of the tool

Check out the GitHub Issues page for a list of concrete tasks that we need help with!


(*) Code formatting was changed for better display

Basil Shikin is the CTO of AppLovin

Share this: