プロダクトニュース

Unity から他ゲームエンジンへの移行

Basil Shikin
9月 18日, 2023

先週の発表以降、Unity から他のゲームエンジンへの移行を可能にするツールへの関心が高まっています。今回は、その技術的な側面について私の考えを共有し、移行を簡単にするためのいくつかのアプローチを提案します。

Unity プロジェクトを構成する要素のうち、移行が必要な部分:

  • スクリプト (MonoBehaviour クラスとその他 .cs ファイル)
  • 静的アセット (シーン、プレハブ、 マテリアル、サウンドなど)
  • プラグイン

では、一つずつ見ていきましょう。

スクリプトの移行

スクリプトの移行は、おそらくデベロッパーにとっての最大の課題でしょう。Unity の API は、他のプラットフォームに簡単に移植することができません。さらに、スクリプトを移植するには移行先のエンジンのAPIも同程度に理解している必要があり、新しい技術を学ぶためにかかる時間を考えると、これも問題となるでしょう。

しかし、最新の LLM (大規模言語モデル, Large Language Model)を使えば、移行を大幅に簡略化できます。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#のパースを改良したり、他の移行先エンジンを追加してください。

静的アセットの移行

静的アセットの移行はもうひとつの課題ですが、これに関しては、多くの部分で自動化できると考えています。しかし、静的アセットの移行フレームワークはどうしても複雑になってしまいます。アセットの保存フォーマットはエディターのバージョンによって変化しているため、移行ツールは複数のフォーマットのバリエーションに加え、アセットが正しく移行されるように、考えられる依存関係、アセット間の関係、互換性の問題を認識する必要があるためです。

いくつかのアセット移行ツールはすでに公開されており (たとえば、Godot の FBX2glTF や、Unreal のネイティブ FBX サポート)、静的アセット移行のための強固な基盤となっています。

次の研究分野は、移行 ツールがアセットの移植にどのように役立つかを解明することです。アセットフォーマットはエンジンのバージョンごとに決まっているため、”よくできた”移行ツールは、複数のスタジオやプロジェクトで再利用可能なはずです。

私たちは、オープンソースの godot の移行プラグインのひとつを使って実験し、以下のような結果を確認しました。:

UnityGodot

プラグインの移行

長年にわたり、Unity エンジンは、何百人もの開発者がプラグイン、モデル、ツールを提供する豊かなエコシステムを構築してきました。これらのツールを新しいプラットフォームに移行するにはかなりの労力が必要です。しかし、Unity プロジェクトの移行ツールは、プラグイン開発者が他のエンジンにプラグインを移植するのにも有用です。

AppLovin では、広告ソリューションの MAX 用にUnrealGodotのプラグインを作成しましたが、これはほんの手始めに過ぎません。私たちがカバーすべきエンジンは、まだまだたくさんあります。

プロジェクトビジョン

このツールの現在のバージョンはProof of Concept(実証実験)です。Unity から他エンジンへ容易に移行するために、最新の LLM 利用の可能性を実証するのが狙いであり、初期の結果はポジティブだと思います。基本的なゲームエンジンの構造は似ていますが、言語や API は大きく異なります。アイデアを一般化して翻訳する、これはまさに LLM が得意とすることです。

静的アセットの移行を実装して LLM のプロンプトを改善することができれば、このツールは、新しいエンジンでゲーム全体をゼロから書き直すという、何百人もの開発者を待ち受ける大変な作業を簡単にする可能性があるでしょう。

自動で移行されたプロジェクトがそのまま機能する可能性は低いものの、翻訳されたビジネスロジック、再インポートされたアセット、自動生成されたコメントやヒントにより、エンジニアは反復的、定型的で退屈な移行作業に何日も費やす必要がなくなるはずです。

しかし、そのためにはコミュニティの支援が必要です。

皆さんの協力が必要です

他エンジンのエコシステムを強化したり、移行作業の繰り返しを減らすオープンソースツールを作成するには開発者コミュニティが必要です。

私は、Unity を超えるための実行可能な選択肢を皆さんと一緒に作り上げることができると信じています。

私たちのGitHub をチェックしてください。今はPOC(Prool of Concept、実証実験)段階ですが、積極的に取り組んでいます。Discordサーバーに参加し、サポート、デザインに関する質問、デバッグにご協力ください。

このプロジェクトは MIT ライセンスでリリースされており、以下の方法で参加し、貢献することができます:

  • Gotdot と Unreal 用の ChatGPT プロンプトのレビューと改善
  • Defold、Cocos2D、その他のエンジンに移行するためのプロンプトやツールの追加
  • 移行に役立つ既存のプラグインのリストを更新
  • このツールを Linux、MacOS、Windows で新規開発者が簡単に実行できるように支援
  • ツールのアーキテクチャに関するフィードバックの提供

GitHub の Issues ページで、皆さんの助けを必要としているタスクリストをご確認いただけます。


(*)より見やすくするためにコードの書式を変更しました

Spread the love