Android内嵌Unity混合开发 + Unity与Android通信 + Touch的坑

Android内嵌Unity混合开发 + Unity与Android通信 + Touch的坑,第1张

概述

本篇文章主要是展示一般Android与Unity混合开发的过程,并且说一些本人在混合开发中踩到的奇怪的坑,如果有错误欢迎大家指正!
此外本文除了通信不会在代码进行解释,需要一定的Unity和Android基础~

一般来说Android内嵌Unity开发主要是把Unity作为Android界面的一部分进行交互或者内容展示,主要是以Android为主体框架。

现在普遍使用的把Unity塞进Android里的方法主要有两个:

先在安卓创建项目,之后进行一系列配置,再创建Unity项目,开发之后export,把jar包塞进Android然后再进行一系列配置,再二次开发先在Unity创建项目,进行开发之后配置然后export出Android项目,直接在Android Studio中打开项目,进行配置之后可以二次开发

本人觉得第二种方法更加方便,而且对于我(Android基础比较薄弱)更容易理解,所以这次就以第二种方法为例子,展示一下整个过程。


Unity相关

首先是简易弄一个Unity项目:

主要是添加了一个手势识别移动相机的脚本,其中用到了Unity自带的TouchAPI,这里有一个坑是我踩到的,现在也没有解决,下面会提到。
然后几个Text展示与Android的交互信息,也有展示Touch的信息的。具体的函数绑定就不过多解释了。

这里分享一下我写的相机移动的脚本,最下面的函数主要是与AndroidHybrid的时候Unity的TouchAPI有关,后面会提到。
使用的相机移动脚本

主要的交互代码:

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

public class HybridTest : MonoBehaviour
{
    private HybridTest instance;
    public HybridTest GetInstance()
    {
        instance = this;
        if (instance)
        {
            return instance;
        }
        return null;
    }

    public Text textShow;
    public Text countShow;
    public int count = 0;

    AndroidJavaClass jc;
    AndroidJavaObject jo;

    public int layoutHeight;
    public int layoutWidth;
    public Text textSize;


    private void Start()
    {
        jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
        GetLayoutSize();
    }

    public void Add()
    {
        int sum = jo.Call<int>("Add", 2, 3);
        textShow.text = sum.ToString();
    }

    //调用安卓的Toast
    public void CallAndroidShowToast()
    {
        jo.Call("ShowToast", "Receive from Unity Successfully!");
    }

    //给Android调用的
    public void GetMessageFromAndroid(string content)
    {
        textShow.text = content;
        count++;
        countShow.text = count.ToString();
    }

    //Unity先发送一个拿到Android layout大小的申请,然后安卓再发送回来
    public void GetLayoutSize()
    {
        jo.Call("ReturnScreenSize", "");
    }

    public void SetLayoutSize(string content)
    {
        layoutWidth = int.Parse(content.Split('/')[0]);
        layoutHeight = int.Parse(content.Split('/')[1]);
        Debug.Log("unity get width" + layoutWidth);
        Debug.Log("unity get height" + layoutHeight);
        textSize.text = layoutWidth.ToString() + "/" + layoutHeight.ToString();
    }


}

Unity调用Android

可以看到在Start中实例化了两个东西,一个是jc一个是jo,具体就是Unity拿到安卓的UnityClass和Activity要用到的接口,是Unity发送Message给Android的关键。

然后以下两个函数就是负责调用Android中的函数,具体结构就是:

jo.Call<如果Android中有返回的值,值的类型>( “Android中的函数名,String类” , 要发送的参数,类型都可);

可以参考例子

Android调用Unity

下面这两个函数就是给Android调用的,主要是传回Android中Unity的Layout大小,用于交互范围判断,后面也会提到:

导出的时候记得添加Scene并且勾选Export,记得选择对应的安卓最高最低版本就ok了:


Android相关

直接用Android Studio打开Unity导出的项目,基础结构应该如下:

其中UnityPlayerActivity这个类是最重要的,负责Unity在Android中的显示和生命周期等等,如果没有二次开发的需求可以不用理会它。

我们当前的目标是在Android中弄一个FrameLayout用于展示Unity的内容,因为大小会自己适配,所以我们只用考虑FrameLayout的大小就好了。
ToastUtils小工具

新建一个MainActivity继承UnityPlayerActivity,然后就开始 *** 作啦,先整一个看起来还可以的布局:

然后再MainActivity中绑定Layout并且写一些和Unity交互的函数:

package com.unity3d.player;

import android.app.ActionBar;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;

public class MainActivity extends UnityPlayerActivity{
    FrameLayout unityLayout;
    int dpHeight;
    int dpWidth;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mUnityPlayer = new UnityPlayer(this);
        setContentView(R.layout.layout_main);

        unityLayout = (FrameLayout) findViewById(R.id.unity_layout);

        ActionBar.LayoutParams layoutParams = new ActionBar.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
        unityLayout.addView(mUnityPlayer.getView(), 0, layoutParams);

        //绑定按钮发送消息给Unity
        Button buttonSendToUnity = (Button) findViewById(R.id.btn_sendSize);
        buttonSendToUnity.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                GetLayoutSize();
                Log.i("toUnity", String.valueOf(dpHeight));
                Log.i("toUnity", String.valueOf(dpWidth));
                callUnityTest("MessageManager","SetLayoutSize",dpWidth + "/" + dpHeight);
            }
        });

        Button buttonSendSize = (Button) findViewById(R.id.btn_toUnity);
        buttonSendSize.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                callUnityTest("MessageManager","GetMessageFromAndroid","Receive from Android successfully!");
            }
        });
    }

    //Unity调用的显示Toast方法
    public void ShowToast(final String mStr2Show){
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                ToastUtils.show(getApplicationContext(),"Receive from Unity successfully!");
            }
        });
    }

    public void GetLayoutSize(){
//        DisplayMetrics metrics =new DisplayMetrics();
//        getWindowManager().getDefaultDisplay().getMetrics(metrics);
//        int dpi = metrics.densityDpi;
        dpWidth = unityLayout.getMeasuredWidth();
        dpHeight = unityLayout.getMeasuredHeight();

        //callUnityTest("MessageManager","SetLayoutSize",res);
    }

    public void ReturnScreenSize(){
        GetLayoutSize();
        //Log.i("SendToUnity", String.valueOf(dpWidth));
        //Log.i("SendToUnity", String.valueOf(dpHeight));
        String res = "";
        res += dpWidth;
        res += "/";
        res += dpHeight;
        callUnityTest("MessageManager","SetLayoutSize",res);
    }

    // 第一个参数是unity中的对象名字,记住是对象名字,不是脚本类名
    // 第二个参数是函数名
    // 第三个参数是传给函数的参数
    public void callUnityTest(String _objName , String _funcStr, String _content)
    {
        UnityPlayer.UnitySendMessage(_objName, _funcStr, _content);
    }

}

按钮事件绑定就不多解释了,开头就先照着UnityPlayerActivity把Layout绑定,再写按钮的事件。

Android调用Unity

这里我是把Unity调用安卓的方法封装了一下,不习惯的小伙伴可以直接用UnityPlayer.UnitySendMessage即可,具体格式如下:

  // 第一个参数是unity中的对象名字,记住是对象名字,不是脚本类名
    // 第二个参数是函数名
    // 第三个参数是传给函数的参数
    public void callUnityTest(String _objName , String _funcStr, String _content)
    {
        UnityPlayer.UnitySendMessage(_objName, _funcStr, _content);
    }

可以发现参数是String类型,所以在Unity中接收的时候还要进行解析 *** 作,上面Unity中的代码也有示范。

Android中的两个按钮作用就是给Unity发送信息啦,这里我是弄了两个按钮,一个可以改变Unity中的计数,一个可以发送给Unity在Android中Layout的大小,具体实现可以看上面的代码。

这里要注意我在Unity中写了个交互范围的判断,所以在SendSize把Layout大小发给Unity之后Unity接收到了才能进行交互,直接CV的小伙伴要注意哦。

Unity调用Android

这里就是Unity调用Android然后展示Toast,要记得在Android中写:

不要忘记在Manifest中注册一下,并且把一进去的MAINActivity换成MainActivity:


然后就可以直接在Android Studio中连接设备进行测试啦!值得注意的是Unity中的Debug也可以在Android中的Logcat中Log出来,所以还是挺方便的。

Touch的坑

因为是基于Touch实现的交互,所以后面就研究了一下,发现了就是在Android发送了Layout大小之后,在Unity的FramLayout中坐标是以FramLayout的左下角为原点,然后在FrameLayout中debug出来的TouchPosition都是正常的,但是在LinearLayout中点击的话,显示的坐标是Unity的FramLayout在MarginTop之前的坐标:

这样的话如果使用TouchPosition判断交互范围就很困难了,目前还没有想到什么好的解决方法,只能让Unity贴着上面了,如果各位大佬有什么解决方法希望能解答一下!谢谢!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/web/992775.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存