diff --git a/app/note/design_doc b/app/note/design_doc index 023133b..970e838 100644 --- a/app/note/design_doc +++ b/app/note/design_doc @@ -303,4 +303,65 @@ https://tianchi.aliyun.com/dataset/93864 - 将 tuanjieLibrary/build.gradle 替换为完整的 Android library 模块构建文件(apply plugin: 'com.android.library'), 参考 launcher/build.gradle 的配置(compileSdkVersion、ndkVersion、ndkPath、aaptOptions、buildTypes 等), 并在 dependencies 中添加 implementation files('libs/unity-classes.jar')。 - - 注意:每次从 Tuanjie 重新导出后都需要重新替换该文件。 \ No newline at end of file + - 注意:每次从 Tuanjie 重新导出后都需要重新替换该文件。 + +14. 从 Unity 导出 Android 项目(IL2CPP)推荐步骤 + 下面按 Unity 2021/2022+ 通用流程写(菜单名可能略有差异,但逻辑一致)。 + + 1. 切到 Android 平台 + File → Build Settings… + 左侧选中 Android + 若未切换过,点 Switch Platform(等切换完成) + 2. Player Settings(Android) + Build Settings 窗口里点 Player Settings…(或 Edit → Project Settings → Player) + + 选中 Android 图标(或 Android 标签页) + + 在 Other Settings 里检查并设置: + + Scripting Backend:选 IL2CPP + Target Architectures:勾选 ARM64(你现在 app 的 abiFilters 也是 arm64-v8a,要一致) + Minimum API Level:与你们 Android 工程要求一致(你工程 minSdk 22,Unity 不要设得更高除非你有意为之) + (可选但常见)Publishing Settings / Keystore:导出后若要直接装真机,后面 Build 时可用 debug;发版再配正式签名。 + + 3. 选择导出方式 + 在 Build Settings: + + Export Project ✅ 勾选(你们要集成到现有 Android Studio 工程,必须勾) + Development Build:调试可开,发版关 + 4. Export 输出目录 + 点 Export(不要只点“生成文件夹”就关掉),选一个空目录或新版本目录,例如: + + D:\unity_export\MyProject_Android_IL2CPP_20260424\ + + 等 Unity 完整跑完(控制台无失败)。 + + 5. 在导出结果里确认 libil2cpp.so + 在导出根目录里找 unityLibrary(或你们模块名): + + unityLibrary\src\main\jniLibs\arm64-v8a\libil2cpp.so + 同目录通常还有 libmain.so、libunity.so 等 + 若这里仍然没有,再回到 Player Settings → Android → Scripting Backend 逐项核对(尤其是是否被 Override 成 Mono)。 + + 6. 集成到你们 digital_person 工程 + 原则:同一轮 Export 的 jniLibs/arm64-v8a 整包拷贝到你们 tuanjieLibrary/src/main/jniLibs/arm64-v8a/(或你们实际使用的 Unity module),不要“只替换较新文件”,也不要混两次导出的 so。 + +15. Unity导出安卓工程目录之后,找不到 libil2cpp.so,可以通过先不export,先编译出apk,得到这个so,然后复制到对应的目录下: + + power shell 命令 + # 源文件路径 + $source = "C:\Users\42087\My project\Library\Bee\Android\Prj\IL2CPP\Gradle\tuanjieLibrary\build\intermediates\stripped_native_libs\release\out\lib\arm64-v8a\libil2cpp.so" + + # 目标路径(你的导出工程) + $targetDir = "D:\code\digital_person\tuanjieLibrary\src\main\jniLibs\arm64-v8a" + + # 创建目标目录(如果不存在) + New-Item -ItemType Directory -Force -Path $targetDir + + # 复制文件 + Copy-Item -Path $source -Destination $targetDir -Force + + # 验证复制成功 + Get-ChildItem -Path $targetDir\libil2cpp.so | Select-Object Name, Length + +16. 有些Unity导出的问题,可以参考:https://blog.csdn.net/weixin_45774916/article/details/144171875 \ No newline at end of file diff --git a/app/src/main/java/com/digitalperson/UnityDigitalPersonActivity.kt b/app/src/main/java/com/digitalperson/UnityDigitalPersonActivity.kt index aa3e7cc..876bcc6 100644 --- a/app/src/main/java/com/digitalperson/UnityDigitalPersonActivity.kt +++ b/app/src/main/java/com/digitalperson/UnityDigitalPersonActivity.kt @@ -503,6 +503,10 @@ class UnityDigitalPersonActivity : UnityPlayerActivity(), LifecycleOwner { val filteredText = ttsController.speakLlmResponse(finalText) Log.d("UnityDigitalPerson", "LOCAL filtered: ${filteredText?.take(60)}") if (filteredText != null) appendChat("助手: $filteredText") + // 本地 LLM 回复也要进入互动状态机:否则不会进入 WAITING_REPLY / PROACTIVE 的计时流程 + val spoken = filteredText ?: finalText.trim() + maybeShowMatchedRefImage(spoken) + interactionCoordinator.onCloudFinalResponse(spoken) } localThoughtSilentMode = false } diff --git a/app/src/main/java/com/digitalperson/config/AppConfig.kt b/app/src/main/java/com/digitalperson/config/AppConfig.kt index ced450e..8a29c09 100644 --- a/app/src/main/java/com/digitalperson/config/AppConfig.kt +++ b/app/src/main/java/com/digitalperson/config/AppConfig.kt @@ -9,7 +9,7 @@ object AppConfig { const val SAMPLE_RATE = 16000 const val WINDOW_SIZE = 512 - const val SHOW_DEBUG_TEXT = true + const val SHOW_DEBUG_TEXT = false const val USE_HOLD_TO_SPEAK = true // true: 按住说话, false: 传统按钮 object Tts { diff --git a/app/src/main/java/com/digitalperson/interaction/BaseDigitalPersonCoordinator.kt b/app/src/main/java/com/digitalperson/interaction/BaseDigitalPersonCoordinator.kt index 897a3bb..20b48a3 100644 --- a/app/src/main/java/com/digitalperson/interaction/BaseDigitalPersonCoordinator.kt +++ b/app/src/main/java/com/digitalperson/interaction/BaseDigitalPersonCoordinator.kt @@ -148,12 +148,13 @@ abstract class BaseDigitalPersonCoordinator( * (i.e. after a cloud LLM response), NOT after greeting / farewell / proactive TTS. */ fun onTtsPlaybackCompleted() { - // Let the controller advance its own timers (greeting/proactive/dlg all count as assistant speaking). - controller.onAssistantTtsPlaybackCompleted() if (pendingDialogueFinish) { pendingDialogueFinish = false controller.onDialogueResponseFinished() } + // Let the controller advance its own timers after state transitions above. + // (greeting/proactive/dlg all count as assistant speaking) + controller.onAssistantTtsPlaybackCompleted() } fun isLlmInFlight(): Boolean = llmInFlight diff --git a/app/src/main/res/layout/activity_unity_digital_person.xml b/app/src/main/res/layout/activity_unity_digital_person.xml index caa728d..3e1c9a3 100644 --- a/app/src/main/res/layout/activity_unity_digital_person.xml +++ b/app/src/main/res/layout/activity_unity_digital_person.xml @@ -7,6 +7,27 @@ android:background="@android:color/transparent" tools:context="com.digitalperson.UnityDigitalPersonActivity"> + + + + + app:layout_constraintTop_toTopOf="parent" + tools:visibility="visible"> - - 开始 结束 点击“开始”说话;识别后会请求大模型并用 TTS 播放回复。 - 你是一个特殊学校一年级的数字人老师,你的名字叫,小鱼老师,你的任务是教这些特殊学校的学生一些基础的生活常识,如生活语文,生活数学,生活适应等课程。和这些小学生说话要有耐心,一定要讲明白,尽量用简短的语句、活泼的语气来回复。你可以和他们日常对话和《教材》相关的话题。在生成回复后,请你先检查一下内容是否符合我们约定的主题。请使用口语对话的形式跟学生聊天。在每次回复的最前面,用方括号标注你的心情,格式为[中性、悲伤、高兴、生气、恐惧、撒娇、震惊、厌恶],例如:[高兴]同学你好呀!请问有什么问题吗? + 你是一个特殊学校一年级的数字人老师,你的名字叫,小鱼老师,你是女性,你的任务是教这些特殊学校的学生一些基础的生活常识,如生活语文,生活数学,生活适应等课程。和这些小学生说话要有耐心,一定要讲明白,尽量用简短的语句、活泼的语气来回复。你可以和他们日常对话和《教材》相关的话题。在生成回复后,请你先检查一下内容是否符合我们约定的主题。请使用口语对话的形式跟学生聊天。在每次回复的最前面,用方括号标注你的心情,格式为[中性、悲伤、高兴、生气、恐惧、撒娇、震惊、厌恶],例如:[高兴]同学你好呀!请问有什么问题吗?