😺

ReactアプリでFigmaデザインからUnityゲームを起動

2025/01/27に公開

初めに

本記事は、UnityエンジニアがfigmaでデザインされたReactアプリ内にUnity as a Libraryとして開発したゲームを機能させるために書き上げています。

前提としての手順をまとめており、細かい内容を実装しながら直していく段取りとなっています。
随時追記、修正があるますことをご理解いただけたらと思います。

上記の背景とは別に、エンジニアがデザイナーと同連携をとるか、ひとくくりにデザイナーといっても、エンジニアから見たらWebのデザイナーもイラストレーターも同じデザイナーとして見えてしまいますので、ここはあえてfigmaを使用するWebデザイナーにゲームUIをお願いした時の手順に特化して実装していきたいと思います。

第1章: Figmaを活用したゲームタイトル画面のデザイン

この章の目的

本章では、Figmaを使用してゲームのタイトル画面をデザインする手順を解説します。Figmaは、ブラウザ上で動作するデザインツールであり、無料で利用可能、Webベースで場所を選ばない、複数人で同時に編集できる、豊富なプラグインによる拡張性など、多くのメリットがあります。 デザインの学び舎これらの特徴により、デザイン性の高さと効率的なワークフローを実現できます。

1.1 Figmaのセットアップ

  • アカウント作成: Figmaの公式サイトfigma.comでアカウントを作成します。

  • 新規ファイルの作成: ダッシュボードから新しいデザインファイルを作成します。

1.2 フレームの作成

  • フレームツールの選択: 左上の「#」アイコン(フレームツール)を選択します。

  • フレームの描画: キャンバス上でドラッグしてフレームを作成します。サイズはターゲットとするデバイスに合わせて設定してください(例:スマートフォンの場合、縦向きで375x812ピクセル)。

1.3 背景の追加

  • 矩形の挿入: フレーム内に矩形ツールを使用して、フレーム全体を覆う矩形を描画します。

  • 色の設定: 右側のプロパティパネルで、矩形の塗りつぶしカラーを設定します。単色やグラデーション、または画像を背景として使用できます。

1.4 ゲームタイトルの追加

  • テキストツールの選択: 上部の「T」アイコン(テキストツール)を選択します。

  • タイトルの入力: フレーム内の適切な位置をクリックし、ゲームのタイトルを入力します。

  • フォントとスタイルの設定: 右側のプロパティパネルで、フォント、サイズ、色、スタイル(太字、斜体など)を設定します。タイトルが目立つように大きめのフォントサイズや鮮やかな色を選択すると効果的です。

1.5 「Game Start」ボタンの作成

  • ボタンのベース作成:

    • 矩形の挿入: 矩形ツールで、適切なサイズのボタンを描画します(例:幅200ピクセル、高さ50ピクセル)。
    • 角の丸み: 右側のプロパティパネルで、角の半径を設定し、ボタンの角を丸くします(例:10ピクセル)。
    • 色の設定: ボタンの塗りつぶしカラーを設定します。背景とコントラストのある色を選ぶと視認性が向上します。
  • ボタンテキストの追加:

    • テキストの挿入: ボタンの中央に「Game Start」と入力します。
    • フォントとスタイルの設定: 右側のプロパティパネルで、フォント、サイズ、色を設定します。ボタンの色と対照的なテキストカラーを選択し、読みやすさを確保します。
  • ボタンとテキストのグループ化:

    • 要素の選択: ボタンの矩形とテキストを両方選択します。
    • グループ化: 右クリックして「グループ化」を選択し、一つのボタン要素としてまとめます。

1.6 デザインの調整

  • 要素の配置: ゲームタイトルと「Game Start」ボタンをフレーム内で適切に配置します。中央揃えや上下のスペースを考慮し、バランスの取れたレイアウトにします。

  • 視覚的な調整: 必要に応じて、影や境界線などの効果を追加し、デザインに深みや立体感を持たせます。

1.7 プロトタイプの設定(オプション)

  • プロトタイプモードへの切り替え: 右上の「Prototype」タブを選択します。

  • インタラクションの設定: 「Game Start」ボタンを選択し、右側のプロパティパネルで「On Click」などのトリガーを設定し、遷移先のフレームやアクションを指定します。

  • プレビュー: 右上の再生ボタンをクリックして、プロトタイプの動作を確認します。

第2章: Unity as a Libraryの取り込み

本章では、React NativeアプリケーションにUnityをライブラリとして組み込む手順を解説します。これにより、React NativeからUnityの機能を呼び出すだけでなく、UnityからReact Nativeの機能を呼び出す双方向の連携が可能となります。具体的には、Unityプロジェクトの設定、iOSおよびAndroidプロジェクトへのUnityの統合、そしてUnityとネイティブアプリ間の通信方法について詳しく説明します。

2.1 Unityプロジェクトの設定

UnityをReact Nativeアプリケーションに組み込むための基本的な設定手順は以下のとおりです。

  1. Unityプロジェクトの作成:

    • Unity Hubを使用して新しいプロジェクトを作成します。
    • プロジェクトのテンプレートとして「3D」や「2D」を選択し、適切なプロジェクト名と保存場所を指定します。
  2. ビルド設定の構成:

    • Unityエディターのメニューから「File」>「Build Settings...」を選択します。
    • プラットフォームの選択:
      • iOSの場合: 「iOS」を選択し、「Switch Platform」をクリックします。
      • Androidの場合: 「Android」を選択し、「Switch Platform」をクリックします。
    • シーンの追加:
      • ビルドに含めるシーンが「Scenes In Build」リストに表示されていることを確認します。
      • 表示されていない場合は、「Add Open Scenes」ボタンをクリックして追加します。
  3. Player Settingsの調整:

    • 「Build Settings」ウィンドウ内の「Player Settings...」ボタンをクリックします。
    • iOSの場合:
      • Scripting Backend: 「IL2CPP」を選択します。
      • Architecture: 「ARM64」を選択します。
    • Androidの場合:
      • Scripting Backend: 「IL2CPP」を選択します。
      • Target Architectures: 「ARMv7」と「ARM64」の両方を選択します。
  4. プロジェクトのエクスポート:

    • iOS:
      • 「Build Settings」ウィンドウで「Export Project」オプションにチェックを入れ、「Build」ボタンをクリックしてXcodeプロジェクトとしてエクスポートします。
    • Android:
      • 「Build System」を「Gradle」に設定し、「Export Project」オプションにチェックを入れ、「Build」ボタンをクリックしてAndroid Studioプロジェクトとしてエクスポートします。

2.2 iOSおよびAndroidプロジェクトへのUnity統合

エクスポートしたUnityプロジェクトをReact Nativeアプリケーションに統合する手順は以下のとおりです。

iOSの場合:

  1. Xcodeプロジェクトへの統合:

    • UnityでエクスポートしたXcodeプロジェクトを開きます。
    • 既存のiOSアプリケーションのXcodeプロジェクトに、エクスポートしたUnityプロジェクト内の「Unity-iPhone.xcodeproj」をドラッグ&ドロップで追加します。
    • 「Unity-iPhone」ターゲットの「Build Phases」で、必要なライブラリやフレームワークがリンクされていることを確認します。
  2. Unityの起動:

    • ネイティブコードからUnityを起動するために、「UnityFramework」をロードし、「runUIApplicationMainWithArgc」関数を使用してUnityを起動します。

Androidの場合:

  1. Android Studioプロジェクトへの統合:

    • Unityでエクスポートしたプロジェクト内の「unityLibrary」フォルダを、既存のAndroidプロジェクトにコピーします。
    • 「settings.gradle」ファイルに以下の行を追加して、「unityLibrary」モジュールをインクルードします:
      include ':unityLibrary'
      project(':unityLibrary').projectDir = file('unityLibrary')
      
    • 「app」モジュールの「build.gradle」ファイルに、「unityLibrary」への依存関係を追加します:
      implementation project(':unityLibrary')
      
  2. Unityの起動:

    • ネイティブコードからUnityを起動するために、「UnityPlayerActivity」を開始します。

2.3 Unityとネイティブアプリ間の通信

UnityとReact Nativeアプリケーション間で双方向の通信を行うための手法は以下のとおりです。

Androidの場合:

  • Unityからネイティブへの通信:

    • UnityのC#コードからJavaのメソッドを呼び出すために、AndroidJavaClassAndroidJavaObjectを使用します。
    • 手順:
      1. Javaメソッドの作成:
        • Androidプロジェクト内で、Unityから呼び出されるメソッドを含むクラスを作成します。
        • 例:
          public class NativeBridge {
              public static void showToast(Context context, String message) {
                  Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
              }
          }
          
      2. Unity側での呼び出し:
        • UnityのC#スクリプト内で、AndroidJavaClassを使用してJavaクラスを参照し、メソッドを呼び出します。
        • 例:
          using UnityEngine;
          
          public class UnityToAndroid : MonoBehaviour {
              public void CallAndroidMethod() {
                  using (AndroidJavaClass jc = new AndroidJavaClass("com.example.NativeBridge")) {
                      using (AndroidJavaObject activity = jc.GetStatic<AndroidJavaObject>("currentActivity")) {
                          jc.CallStatic("showToast", activity, "Hello from Unity");
                      }
                  }
              }
          }
          
  • ネイティブからUnityへの通信:

    • JavaコードからUnityのC#メソッドを呼び出すために、UnityPlayer.UnitySendMessageメソッドを使用します。
    • 手順:
      1. UnityのC#スクリプトでメソッドを定義:
        • Unity内で、Javaから呼び出されるメソッドを定義します。
        • 例:
          using UnityEngine;
          
          public class AndroidToUnity : MonoBehaviour {
              public void ShowMessage(string message) {
                  Debug.Log("Message from Android: " + message);
              }
          }
          
      2. JavaコードからUnityのメソッドを呼び出す:
        • UnityPlayer.UnitySendMessageを使用して、Unity内のメソッドを呼び出します。
        • 例:
          import com.unity3d.player.UnityPlayer;
          
          public class NativeBridge {
              public static void sendMessageToUnity(String gameObjectName, String methodName, String message) {
                  UnityPlayer.UnitySendMessage(gameObjectName, methodName, message);
              }
          }
          
        • この場合、gameObjectNameはUnity内の対象オブジェクトの名前、methodNameは呼び出すメソッド名、messageは渡す引数となります。

第3章: ブリッジコードの実装

本章では、React NativeアプリケーションとUnity間の通信を実現するためのブリッジコードの実装方法について解説します。これにより、React NativeからUnityの機能を呼び出したり、逆にUnityからReact Nativeの機能を呼び出すことが可能となります。具体的には、iOSおよびAndroidプラットフォームにおけるブリッジコードの実装手順と、React Native側からこれらを利用する方法について詳しく説明します。

3.1 iOSにおけるブリッジコードの実装

iOSプラットフォームでReact NativeとUnity間の通信を行うためのブリッジコードの実装手順は以下のとおりです。

  1. Objective-Cブリッジヘッダーの作成:

    • React Nativeプロジェクト内に、Unityのヘッダーファイルをインポートするためのブリッジヘッダー(例: UnityBridge.h)を作成します。
    • このヘッダーファイルで、Unityの関数やメソッドを宣言します。
  2. UnityFrameworkのロード:

    • UnityBridge.mファイル内で、UnityFrameworkインスタンスをロードし、初期化します。
    • 例:
      #import "UnityBridge.h"
      #import <UnityFramework/UnityFramework.h>
      
      @implementation UnityBridge
      
      static UnityFramework* ufw = nil;
      
      + (void)loadUnity {
          if (ufw == nil) {
              NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
              NSString* frameworkPath = [bundlePath stringByAppendingPathComponent:@"Frameworks/UnityFramework.framework"];
              NSBundle* bundle = [NSBundle bundleWithPath:frameworkPath];
              if ([bundle isLoaded] == NO) [bundle load];
              ufw = [bundle.principalClass getInstance];
              [ufw setExecuteHeader: &_mh_execute_header];
              [ufw runEmbeddedWithArgc: gArgc argv: gArgv];
          }
      }
      
      @end
      
  3. React Nativeモジュールの作成:

    • UnityBridgeをReact Nativeから呼び出せるように、RCTBridgeModuleプロトコルに準拠したObjective-Cクラスを作成します。
    • 例:
      #import <React/RCTBridgeModule.h>
      
      @interface RCTUnityBridge : NSObject <RCTBridgeModule>
      @end
      
      @implementation RCTUnityBridge
      
      RCT_EXPORT_MODULE();
      
      RCT_EXPORT_METHOD(loadUnity) {
          [UnityBridge loadUnity];
      }
      
      @end
      

3.2 Androidにおけるブリッジコードの実装

AndroidプラットフォームでReact NativeとUnity間の通信を行うためのブリッジコードの実装手順は以下のとおりです。

  1. UnityPlayerActivityの設定:

    • AndroidManifest.xmlUnityPlayerActivityを宣言し、必要な設定を行います。
    • 例:
      <activity
          android:name="com.unity3d.player.UnityPlayerActivity"
          android:theme="@style/UnityThemeSelector"
          android:screenOrientation="landscape"
          android:configChanges="orientation|screenSize">
      </activity>
      
  2. React Nativeモジュールの作成:

    • UnityBridgeModule.javaクラスを作成し、ReactContextBaseJavaModuleを継承します。
    • 例:
      package com.yourapp;
      
      import com.facebook.react.bridge.ReactApplicationContext;
      import com.facebook.react.bridge.ReactContextBaseJavaModule;
      import com.facebook.react.bridge.ReactMethod;
      import android.content.Intent;
      
      public class UnityBridgeModule extends ReactContextBaseJavaModule {
      
          public UnityBridgeModule(ReactApplicationContext reactContext) {
              super(reactContext);
          }
      
          @Override
          public String getName() {
              return "UnityBridge";
          }
      
          @ReactMethod
          public void loadUnity() {
              ReactApplicationContext context = getReactApplicationContext();
              Intent intent = new Intent(context, UnityPlayerActivity.class);
              intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
              context.startActivity(intent);
          }
      }
      
  3. パッケージの設定:

    • UnityBridgePackage.javaを作成し、ReactPackageを実装します。
    • 例:
      package com.yourapp;
      
      import com.facebook.react.ReactPackage;
      import com.facebook.react.bridge.NativeModule;
      import com.facebook.react.bridge.ReactApplicationContext;
      import com.facebook.react.uimanager.ViewManager;
      
      import java.util.ArrayList;
      import java.util.Collections;
      import java.util.List;
      
      public class UnityBridgePackage implements ReactPackage {
      
          @Override
          public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
              List<NativeModule> modules = new ArrayList<>();
              modules.add(new UnityBridgeModule(reactContext));
              return modules;
          }
      
          @Override
          public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
              return Collections.emptyList();
          }
      }
      
  4. アプリケーションクラスの設定:

    • MainApplication.javaUnityBridgePackageを追加します。
    • 例:
      @Override
      protected List<ReactPackage> getPackages() {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();
          packages.add(new UnityBridgePackage());
          return packages;
      }
      

3.3 React Nativeからのブリッジコードの利用

React Nativeからネイティブモジュールを利用することで、Unityの機能を呼び出すことが可能になります。以下に、React Native側での実装手順を示します。

  1. ネイティブモジュールのインポート:

    • JavaScriptコード内で、先ほど作成したネイティブモジュールをインポートします。
    • 例:
      import { NativeModules } from 'react-native';
      const { UnityBridge } = NativeModules;
      
  2. Unityの起動:

    • インポートしたUnityBridgeモジュールを使用して、Unityを起動します。
    • 例:
      const startUnity = () => {
        UnityBridge.loadUnity();
      };
      
    • この関数を、ボタンのonPressイベントなどに割り当てて、ユーザーが操作した際にUnityが起動するようにします。
  3. 双方向通信の実装:

    • UnityからReact Nativeへの通信を実現するためには、イベントエミッターなどを使用して、Unityからのメッセージを受け取る仕組みを構築します。
    • 例:
      import { DeviceEventEmitter } from 'react-native';
      
      // イベントリスナーの登録
      useEffect(() => {
        const subscription = DeviceEventEmitter.addListener('UnityMessage', (message) => {
          console.log('Received message from Unity:', message);
          // ここでReact Native側の処理を実行
        });
      
        // クリーンアップ
        return () => {
          subscription.remove();
        };
      }, []);
      
    • Unity側からは、ネイティブコードを介してDeviceEventEmitterにイベントを送信します。

申し訳ございません。前回の回答で見出しや小見出しのフォーマットが崩れてしまい、ご不便をおかけしました。以下に、適切なフォーマットで章見出しと小見出しを含めて再構成いたします。


第4章: ReactからUnityFrameworkの呼び出し方法

この章の目的

本章では、React NativeアプリケーションからUnityFrameworkを呼び出し、Unityの機能をReactコンポーネント内で利用する方法について解説します。具体的には、React Nativeからネイティブモジュールを介してUnityを起動し、双方向のデータ通信とイベントハンドリングを実現する手法を詳しく説明します。

4.1 React Nativeからネイティブモジュールの呼び出し

React Nativeでは、ネイティブコード(Objective-CやJava)と連携するために、ネイティブモジュールを作成します。これにより、JavaScriptからネイティブの機能を直接呼び出すことが可能になります。

  • ネイティブモジュールの作成手順(iOSの場合):

    1. ヘッダーファイルの作成:

      • UnityBridge.hという名前で以下のコードを作成します。

        #import <React/RCTBridgeModule.h>
        
        @interface UnityBridge : NSObject <RCTBridgeModule>
        - (void)startUnity;
        @end
        
    2. 実装ファイルの作成:

      • UnityBridge.mという名前で以下のコードを作成します。

        #import "UnityBridge.h"
        #import <UnityFramework/UnityFramework.h>
        
        @implementation UnityBridge
        
        RCT_EXPORT_MODULE();
        
        RCT_EXPORT_METHOD(startUnity)
        {
          dispatch_async(dispatch_get_main_queue(), ^{
            UnityFramework *ufw = [self loadUnityFramework];
            [ufw runEmbeddedWithArgc:0 argv:NULL appLaunchOpts:nil];
          });
        }
        
        - (UnityFramework *)loadUnityFramework
        {
          NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
          NSString *frameworkPath = [bundlePath stringByAppendingPathComponent:@"Frameworks/UnityFramework.framework"];
          NSBundle *bundle = [NSBundle bundleWithPath:frameworkPath];
          if (![bundle isLoaded]) {
            [bundle load];
          }
          UnityFramework *ufw = [bundle.principalClass getInstance];
          if (![ufw appController]) {
            [ufw setAppController:(UnityAppController *)[UIApplication sharedApplication].delegate];
          }
          return ufw;
        }
        
        @end
        

        このコードでは、startUnityメソッドを介してUnityを起動します。loadUnityFrameworkメソッドは、UnityFrameworkインスタンスをロードし、Unityを組み込みモードで実行します。

4.2 Unityの機能をReactコンポーネントで利用する

作成したネイティブモジュールをReact Nativeのコンポーネント内で利用することで、ユーザーインターフェースからUnityの機能を呼び出すことができます。

  • Reactコンポーネントの作成:

    1. JavaScriptコード:
      • GameStartButtonコンポーネントを作成し、ユーザーがボタンを押すとUnityが起動するように設定します。

        import React from 'react';
        import { Button } from 'react-native';
        import { NativeModules } from 'react-native';
        const { UnityBridge } = NativeModules;
        
        const GameStartButton = () => {
          const startUnity = () => {
            UnityBridge.startUnity();
          };
        
          return (
            <Button
              title="Game Start"
              onPress={startUnity}
            />
          );
        };
        
        export default GameStartButton;
        

        このコードでは、Game Startボタンが押されたときに、UnityBridgeネイティブモジュールのstartUnityメソッドを呼び出し、Unityを起動します。

4.3 双方向のデータ通信とイベントハンドリング

React NativeとUnity間で双方向のデータ通信やイベントハンドリングを行うことで、両者の連携を強化できます。

  • UnityからReact Nativeへの通信:

    • UnityのC#コードからネイティブコードを介してReact Nativeのメソッドを呼び出すことが可能です。例えば、Unityからイベントが発生した際に、React Native側に通知することができます。
  • React NativeからUnityへの通信:

    • 前述のネイティブモジュールを介して、React NativeからUnityの特定の機能やシーンを呼び出すことができます。

これらの手法を組み合わせることで、React NativeとUnity間のシームレスな連携を実現し、ユーザーに対してリッチでインタラクティブな体験を提供することが可能です。


Discussion