赞
踩
使用的引擎工具:
Unity2021.3.19
android-studio-2021.1.21
第一步:
新建一个Android项目(工程名字随便啦)
然后新建一个library
(同上,库名自己命名吧)
Android环境目前就算是初步建立好了。
第二步:
导包
libs文件夹里面放入这4个文件,arm64-v8a,armeabi-v7a,Msc.jar这三个文件是讯飞官网下载下来的demo项目里面的,直接复制到libs里面就好,classes.jar包是在下面这个路径下的
(注:Classes.jar用mono还是IL2CPP得和Unity-PlayerSetting-ScriptBackend一致)
第三步:
倒入UnityPlayerActivity
最新版本Unity里面的classes.jar包文件里面以及不包含UnityPlayerActivity了,所以我们需要自己导入UnityPlayerActivity(或者自己编写一个也可以,回头可以再出一篇)
文件位置:
第四步:
在AndroidStudio实现供unity调用的接口方法,直接上代码了(讯飞APPID自己填写)
- package com.example.mylibrary;
-
- import android.content.Context;
- import android.os.Bundle;
- import android.util.Log;
- import android.widget.Toast;
-
- import com.iflytek.cloud.ErrorCode;
- import com.iflytek.cloud.InitListener;
- import com.iflytek.cloud.RecognizerListener;
- import com.iflytek.cloud.RecognizerResult;
- import com.iflytek.cloud.SpeechConstant;
- import com.iflytek.cloud.SpeechError;
- import com.iflytek.cloud.SpeechRecognizer;
- import com.iflytek.cloud.SpeechUtility;
- import com.unity.upa.UnityPlayerActivity;
- import com.unity3d.player.UnityPlayer;
-
- import org.json.JSONException;
- import org.json.JSONObject;
-
- import java.util.HashMap;
- import java.util.LinkedHashMap;
-
- public class XunFeiSdk extends UnityPlayerActivity {
-
- private HashMap<String, String> mIatResults = new LinkedHashMap<String, String>();
- SpeechRecognizer mIAT;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- SpeechUtility.createUtility(this, SpeechConstant.APPID + "=xxxxxx");//这里是你在讯飞官网上的APPID
- mIAT=SpeechRecognizer.createRecognizer(this,null);
- mIAT.setParameter(SpeechConstant.DOMAIN,"iat");
- // 语言
- mIAT.setParameter(SpeechConstant.LANGUAGE,"zh_cn");
- // 接收语言的类型
- mIAT.setParameter(SpeechConstant.ACCENT,"mandarin");
- // 使用什么引擎
- mIAT.setParameter(SpeechConstant.ENGINE_TYPE,SpeechConstant.TYPE_CLOUD);
- }
-
- public void StartListening() {
-
- mIAT.startListening(recognizerListener);
- }
-
- RecognizerListener recognizerListener = new RecognizerListener() {
- @Override
- public void onVolumeChanged(int i, byte[] bytes) {
- UnityPlayer.UnitySendMessage("XfManager","OnVolumeChanged","");
- }
-
- @Override
- public void onBeginOfSpeech() {
- UnityPlayer.UnitySendMessage("XfManager","OnBeginOfSpeech","");
- }
-
- @Override
- public void onEndOfSpeech() {
- UnityPlayer.UnitySendMessage("XfManager","OnEndOfSpeech","");
- }
-
- @Override
- public void onResult(RecognizerResult recognizerResult, boolean b) {
- printResult(recognizerResult);
- }
-
- @Override
- public void onError(SpeechError speechError) {
- UnityPlayer.UnitySendMessage("XfManager","OnError",speechError.getErrorDescription());
- }
-
- @Override
- public void onEvent(int i, int i1, int i2, Bundle bundle) {
-
- }
- };
-
- private void printResult(com.iflytek.cloud.RecognizerResult results) {
- // JsonParser是一个工具类
- String text = JsonParser.parseIatResult(results.getResultString());
-
- String sn = null;
- // 读取json结果中的sn字段
- try {
- JSONObject resultJson = new JSONObject(results.getResultString());
- sn = resultJson.optString("sn");
- } catch (JSONException e) {
- e.printStackTrace();
- }
- mIatResults.put(sn, text);
- // resultBuffer 为最终返回的结果
-
- StringBuffer resultBuffer = new StringBuffer();
- for (String key : mIatResults.keySet()) {
- resultBuffer.append(mIatResults.get(key));
- }
- // 把得到的结果返回给Unity 第一个参数为unity种的游戏物体 第二个参数为 这个游戏物体身上脚本的方法 第三个参数为讯飞返回的最终结果
- UnityPlayer.UnitySendMessage("XfManager","OnResult",resultBuffer.toString());
- }
-
- public void VoidTest()
- {
- UnityPlayer.UnitySendMessage("XfManager","FromAndroid","Android:消息发送至Unity");
- }
- }

Json类(这个是讯飞demo里面的)
- package com.example.mylibrary;
-
- import org.json.JSONArray;
- import org.json.JSONObject;
- import org.json.JSONTokener;
-
- /**
- * Json结果解析类
- */
- public class JsonParser {
-
- public static String parseIatResult(String json) {
- StringBuffer ret = new StringBuffer();
- try {
- JSONTokener tokener = new JSONTokener(json);
- JSONObject joResult = new JSONObject(tokener);
-
- JSONArray words = joResult.getJSONArray("ws");
- for (int i = 0; i < words.length(); i++) {
- // 转写结果词,默认使用第一个结果
- JSONArray items = words.getJSONObject(i).getJSONArray("cw");
- JSONObject obj = items.getJSONObject(0);
- ret.append(obj.getString("w"));
- // 如果需要多候选结果,解析数组其他字段
- // for(int j = 0; j < items.length(); j++)
- // {
- // JSONObject obj = items.getJSONObject(j);
- // ret.append(obj.getString("w"));
- // }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return ret.toString();
- }
-
- public static String parseGrammarResult(String json, String engType) {
- StringBuffer ret = new StringBuffer();
- try {
- JSONTokener tokener = new JSONTokener(json);
- JSONObject joResult = new JSONObject(tokener);
-
- JSONArray words = joResult.getJSONArray("ws");
- // 云端和本地结果分情况解析
- if ("cloud".equals(engType)) {
- for (int i = 0; i < words.length(); i++) {
- JSONArray items = words.getJSONObject(i).getJSONArray("cw");
- for (int j = 0; j < items.length(); j++) {
- JSONObject obj = items.getJSONObject(j);
- if (obj.getString("w").contains("nomatch")) {
- ret.append("没有匹配结果.");
- return ret.toString();
- }
- ret.append("【结果】" + obj.getString("w"));
- ret.append("【置信度】" + obj.getInt("sc"));
- ret.append("\n");
- }
- }
- } else if ("local".equals(engType)) {
- ret.append("【结果】");
- for (int i = 0; i < words.length(); i++) {
- JSONObject wsItem = words.getJSONObject(i);
- JSONArray items = wsItem.getJSONArray("cw");
- if ("<contact>".equals(wsItem.getString("slot"))) {
- // 可能会有多个联系人供选择,用中括号括起来,这些候选项具有相同的置信度
- ret.append("【");
- for (int j = 0; j < items.length(); j++) {
- JSONObject obj = items.getJSONObject(j);
- if (obj.getString("w").contains("nomatch")) {
- ret.append("没有匹配结果.");
- return ret.toString();
- }
- ret.append(obj.getString("w")).append("|");
- }
- ret.setCharAt(ret.length() - 1, '】');
- } else {
- //本地多候选按照置信度高低排序,一般选取第一个结果即可
- JSONObject obj = items.getJSONObject(0);
- if (obj.getString("w").contains("nomatch")) {
- ret.append("没有匹配结果.");
- return ret.toString();
- }
- ret.append(obj.getString("w"));
- }
- }
- ret.append("【置信度】" + joResult.getInt("sc"));
- ret.append("\n");
- }
-
- } catch (Exception e) {
- e.printStackTrace();
- ret.append("没有匹配结果.");
- }
- return ret.toString();
- }
-
- public static String parseGrammarResult(String json) {
- StringBuffer ret = new StringBuffer();
- try {
- JSONTokener tokener = new JSONTokener(json);
- JSONObject joResult = new JSONObject(tokener);
-
- JSONArray words = joResult.getJSONArray("ws");
- for (int i = 0; i < words.length(); i++) {
- JSONArray items = words.getJSONObject(i).getJSONArray("cw");
- for (int j = 0; j < items.length(); j++) {
- JSONObject obj = items.getJSONObject(j);
- if (obj.getString("w").contains("nomatch")) {
- ret.append("没有匹配结果.");
- return ret.toString();
- }
- ret.append("【结果】" + obj.getString("w"));
- ret.append("【置信度】" + obj.getInt("sc"));
- ret.append("\n");
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- ret.append("没有匹配结果.");
- }
- return ret.toString();
- }
-
- public static String parseLocalGrammarResult(String json) {
- StringBuffer ret = new StringBuffer();
- try {
- JSONTokener tokener = new JSONTokener(json);
- JSONObject joResult = new JSONObject(tokener);
-
- JSONArray words = joResult.getJSONArray("ws");
- for (int i = 0; i < words.length(); i++) {
- JSONArray items = words.getJSONObject(i).getJSONArray("cw");
- for (int j = 0; j < items.length(); j++) {
- JSONObject obj = items.getJSONObject(j);
- if (obj.getString("w").contains("nomatch")) {
- ret.append("没有匹配结果.");
- return ret.toString();
- }
- ret.append("【结果】" + obj.getString("w"));
- ret.append("\n");
- }
- }
- ret.append("【置信度】" + joResult.optInt("sc"));
-
- } catch (Exception e) {
- e.printStackTrace();
- ret.append("没有匹配结果.");
- }
- return ret.toString();
- }
-
- public static String parseTransResult(String json, String key) {
- StringBuffer ret = new StringBuffer();
- try {
- JSONTokener tokener = new JSONTokener(json);
- JSONObject joResult = new JSONObject(tokener);
- String errorCode = joResult.optString("ret");
- if (!errorCode.equals("0")) {
- return joResult.optString("errmsg");
- }
- JSONObject transResult = joResult.optJSONObject("trans_result");
- ret.append(transResult.optString(key));
- /*JSONArray words = joResult.getJSONArray("results");
- for (int i = 0; i < words.length(); i++) {
- JSONObject obj = words.getJSONObject(i);
- ret.append(obj.getString(key));
- }*/
- } catch (Exception e) {
- e.printStackTrace();
- }
- return ret.toString();
- }
- }

代码就是以上这些了,接下来就是修改AndroidManifest
第五步:
AndroidManifest.xml文件
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:tools="http://schemas.android.com/tools"
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.mylibrary">
-
- <application
- android:allowBackup="true"
- android:supportsRtl="true">
- <activity android:name=".XunFeiSdk"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
-
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <meta-data android:name="unityplayer.UnityActivity" android:value="true"/>
- </application>
-
-
- <uses-permission android:name="android.permission.RECORD_AUDIO" />
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
- <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
- <uses-permission android:name="android.permission.READ_PHONE_STATE" />
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- <uses-permission android:name="android.permission.READ_CONTACTS" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.WRITE_SETTINGS"
- tools:ignore="ProtectedPermissions" />
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- </manifest>

第六步:
完成以上这些步骤就可以打包成arr文件了
选中你的库文件然后点击build-Make Module,如图所示
arr生成路径是
结果如下
把mylibrary.arr文件根目录里的classes.jar和AndroidManifest复制出来(拖出来)
下面这个是从arr包里取出来的AndroidManifest文件,把<uses-sdk android:minSdkVersion="26" />删掉
第七步:
下面就是Unity这边了,Unity端比较简单,直接看图吧
这个AndroidManifest文件就是第六步的AndroidManifest文件,
classes.jar文件放在Plugins-Android-bin目录下
libs目录下放的是同第二步一样的3个文件
然后Unity测试界面
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using TMPro;
- using UnityEngine;
- using UnityEngine.UI;
-
- public class XfManager : MonoBehaviour
- {
- private AndroidJavaClass ajc;
- private AndroidJavaObject ajo;
- //private AndroidJavaObject XunFeiSdk;
- public Button StartButton,testBtn;
- public TextMeshProUGUI ResultText;
-
- private void Start()
- {
- ajc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
- ajo = ajc.GetStatic<AndroidJavaObject>("currentActivity");
- //XunFeiSdk = new AndroidJavaObject("com.example.mylibrary.XunFeiSdk");
- testBtn.onClick.AddListener(TestConnect);
- if (StartButton)
- {
- StartButton.onClick.AddListener(() => { StartListening(); });
- }
- }
-
- private void TestConnect()
- {
- ajo.Call("VoidTest");
- }
-
-
- public void FromAndroid(string s)
- {
- ResultText.text = s;
- }
-
- public void StartListening()
- {
- ajo.Call("StartListening");
- }
-
- public void OnStartListening(string ret)
- {
- int result = int.Parse(ret);
- StartButton.interactable = result == 0;
- }
-
- public void OnResult(string result)
- {
- ResultText.text = result;
- }
-
- public void OnError(string errorMessage)
- {
- ResultText.text = errorMessage;
- }
-
- public void OnEndOfSpeech()
- {
- StartButton.GetComponentInChildren<TextMeshProUGUI>().text = "已结束,点击聆听";
- StartButton.interactable = true;
- }
-
- public void OnBeginOfSpeech()
- {
- StartButton.GetComponentInChildren<TextMeshProUGUI>().text = "聆听ing";
- StartButton.interactable = false;
- }
- }

最后就是打包了
注:这里的包名必须跟你配置文件里面的一致才行,还有就是再重复一遍,Classes.jar用mono还是IL2CPP得和Unity-PlayerSetting-ScriptBackend一致。
打包成功,测试正常(注意开启权限哦)
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。