ゲーム化!tomo_manaのブログ

ゲーム化!tomo-manaのブログ

Unityでゲームを作る方法について紹介しています

Unity・Activity・Service間のやりとり (Unity 2020.3.26f1・Android 10, 11)

f:id:tomo_mana:20220330165308p:plain

Unity、Activity、Service間のアクセスの方法。

最初に、各クラスの呼び出し方をシーケンスにしました。サンプルコードはこの記事の最後にあります。

f:id:tomo_mana:20220326111601p:plain
シーケンス図

この方法しかないわけではありません。

ただ、ゼロから探すよりは、一つの方法が分かっている方が、他の選択肢も検討しやすいかと思いました。

Unity → Activity

Unity側:AndroidJavaObject.Call()

Unity(C#)

AndroidJavaClass c = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject activity = c<AndroidJavaObject>("currentActivity");
activity.Call("callTestFunc");

3行目:Call() の第一引数は関数名、第二引数以降はAndroid側の呼び出し関数の引数です。

Android側:UnityPlayerActivity継承クラス+AndroidManifest.xml

UnityPlayerActivity継承クラス(Java)

package com.companyname.appname;

import com.unity3d.player.UnityPlayerActivity;

public class MyActivity extends UnityPlayerActivity {
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
    }
    public void callTestFunc()
    {
        Log.d( "[Unity Test]", "callTestFunc called" );
    }
}

11行目:Unityから呼び出される関数。


自作クラスをメインアクティビティにするため、併せて AndroidManifest.xml を設置します。

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <application>
    <activity android:name="com.companyname.appname.MyActivity"
             android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
  </application>
</manifest>

4行目:"com.companyname.appname.MyActivity" の部分は、自作クラスの [パッケージ名.Activity名]。


AndroidManifest.xml は、Assets\Plugins\Android直下に置きます。

f:id:tomo_mana:20220323235011p:plain
AndroidManifest.xmlの置き場

(参考)
tomo-mana.hatenablog.com

Android → Unity

Android側:UnityPlayer.sendMessage()

UnityPlayerActivity継承クラス(Java)

import com.unity3d.player.UnityPlayer;

UnityPlayer.UnitySendMessage("ゲームオブジェクト名", "関数名", "CALLBACK_ID");


3行目:UnitySendMessage()
 第1引数:ゲームオブジェクト名
 第2引数:コンポーネントの関数名
 第3引数:string

第1引数は、クラス(コンポーネント名)でなく、そのコンポーネントを取り付けたゲームオブジェクト名です。

第3引数は、関数の呼び側と受け側でやりとりができる唯一の領域になります。そのため、IDを入れるのが通例ではないかと思います。


第1引数、第2引数は、UnityからActivityに名前を渡しておくことで、より汎用的に使用できます。
kan-kikuchi.hatenablog.com

Unity側:コールバック関数

Unity(C#)

public void onCallBack(string message)
{
    if( message == "CALLBACK_ID" ){
        Debug.Log( "Unity : called onCallBack" );
    }
}

関数の名称は任意です。
(先述の UnitySendMessage() 第2引数 に指定)

Activity → Service

Activity側:startService(Intent)+AndroidManifest.xml

UnityPlayerActivity継承クラス(Java)

import android.content.Intent;

Intent serviceIntent = new Intent(MyActivity.this, サービス名.class);
serviceIntent.putExtra( "REQUEST_CODE", 1 );
startService(serviceIntent);

3行目:Intentは使いづらいイメージがあるかもしれませんが、ただの容れ物です。値をセットして、様々な関数に引数で渡すだけです。

Intent(Context packageContext, Class cls)
 第1引数:呼び元(Contextへの参照)
 第2引数:呼び先(newしたいクラスの名前)

Service側:onStartCommand()

Service(Java)

package com.companyname.appname;

import android.content.Intent;

public class MyService extends Service 
{
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) 
    {
        modeRequested = intent.getIntExtra( "REQUEST_CODE", 0 );
        if( requestCode == 1 ){
            Log.d("onStartCommand called");
        }
    }
}

特別な識別子(ID)を含めます。上のコードの場合は"REQUEST_CODE"というキーを使用して確認しています。


AndroidManifest.xml の <activity> タグ内にサービスの利用を宣言します。

AndroidManifest.xml

<application
    <!-- Service定義 -->
    <service android:name="com.companyname.appname.MyService" />
</application>

Service → Activity

Service側:Context.sendBroadcast(Intent)

Service(Java)

Intent broadcastIntent = new Intent();
broadcastIntent.putExtra( "REQUEST_CODE", 10 );
getBaseContext().sendBroadcast(broadcastIntent);

※BroadcastReceiveerを使わない方法もあるかもしれません。

Activity側:BroadcastReceiver.onReceive()

UnityPlayerActivity継承クラス(Java)

import android.content.BroadcastReceiver;

// bradcastReceiverの定義
public class MyBroadcastReceiver extends BroadcastReceiver 
{
    @Override
    public void onReceive(Context context, Intent intent) 
    {
        // intentからのデータの取り出し
        int requestCode = intent.getIntExtra("REQUEST_CODE", 0);
        
        if( requestCode == 10 ){
            Log.e("[Unity Test]", "MyBroadcastReceiver received");
        }
    }
}
// broadcastReceiverの登録
receiver = new MyBroadcastReceiver();
intentFilter = new IntentFilter();
intentFilter.addAction("MY_ACTION");  // 必ずActionを含める
registerReceiver(receiver, intentFilter);

サンプルコード

[Unity]TestComponent.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestComponent : MonoBehaviour
{
    void Start(){
        AndroidJavaClass c = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject activity = c<AndroidJavaObject>("currentActivity");
        activity.Call("callTestFunc");
    }
    
    // Activity → Unity
    public void onCallBack(string message)
    {
        if( message == "CALLBACK_ID" ){
            Debug.Log( "Unity : called onCallBack from Android." );
        }
    }
}

[Java]MyActivity.java

package com.companyname.appname;

// Unity → Activity
import com.unity3d.player.UnityPlayerActivity;
// Activity → Unity
import com.unity3d.player.UnityPlayer;
// Activity → Service
import android.content.Intent;
// Service → Activity
import android.content.BroadcastReceiver;

public class MyActivity extends UnityPlayerActivity 
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
    }
    
    // Unity → Activity
    public void callTestFunc()
    {
        Log.d( "[Unity Test]", "callTestFunc called" );
        
        // broadcastReceiverの登録
        receiver = new MyBroadcastReceiver();
        intentFilter = new IntentFilter();
        intentFilter.addAction("MY_ACTION");  // 必ずActionを含める
        registerReceiver(receiver, intentFilter);
        
        // Activity → Service
        Intent serviceIntent = new Intent(MyActivity.this, MyService.class);
        serviceIntent.putExtra( "REQUEST_CODE", 1 );
        startService(serviceIntent);
    }

    // Service → Activity
    public class MyBroadcastReceiver extends BroadcastReceiver 
    {
        @Override
        public void onReceive(Context context, Intent intent) 
        {
            // intentからのデータの取り出し
            int requestCode = intent.getIntExtra("REQUEST_CODE", 0);
            
            if( requestCode == 10 ){
                Log.e("[Unity Test]", "MyBroadcastReceiver received");
                
                // Activity → Unity
                UnityPlayer.UnitySendMessage(unityGameObjectName, unityCallbackFunc, "CALLBACK_ID");
            }
        }
    }
}

[Java]MyService.java

package com.companyname.appname;

import android.content.Intent;

public class MyService extends Service 
{
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) 
    {
        modeRequested = intent.getIntExtra( "REQUEST_CODE", 0 );
        if( requestCode == 1 ){
            Log.d("onStartCommand called");
            
            // Service → Activity
            Intent broadcastIntent = new Intent();
            broadcastIntent.putExtra( "REQUEST_CODE", 10 );
            getBaseContext().sendBroadcast(broadcastIntent);
        }
    }
}

[XML]AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <application>
    <activity android:name="com.companyname.appname.MyActivity"
             android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    
    <!-- Service定義 -->
    <service android:name="com.companyname.appname.MyService" />
  </application>
</manifest>

(以上)