update
This commit is contained in:
@@ -303,4 +303,65 @@ https://tianchi.aliyun.com/dataset/93864
|
|||||||
- 将 tuanjieLibrary/build.gradle 替换为完整的 Android library 模块构建文件(apply plugin: 'com.android.library'),
|
- 将 tuanjieLibrary/build.gradle 替换为完整的 Android library 模块构建文件(apply plugin: 'com.android.library'),
|
||||||
参考 launcher/build.gradle 的配置(compileSdkVersion、ndkVersion、ndkPath、aaptOptions、buildTypes 等),
|
参考 launcher/build.gradle 的配置(compileSdkVersion、ndkVersion、ndkPath、aaptOptions、buildTypes 等),
|
||||||
并在 dependencies 中添加 implementation files('libs/unity-classes.jar')。
|
并在 dependencies 中添加 implementation files('libs/unity-classes.jar')。
|
||||||
- 注意:每次从 Tuanjie 重新导出后都需要重新替换该文件。
|
- 注意:每次从 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
|
||||||
@@ -503,6 +503,10 @@ class UnityDigitalPersonActivity : UnityPlayerActivity(), LifecycleOwner {
|
|||||||
val filteredText = ttsController.speakLlmResponse(finalText)
|
val filteredText = ttsController.speakLlmResponse(finalText)
|
||||||
Log.d("UnityDigitalPerson", "LOCAL filtered: ${filteredText?.take(60)}")
|
Log.d("UnityDigitalPerson", "LOCAL filtered: ${filteredText?.take(60)}")
|
||||||
if (filteredText != null) appendChat("助手: $filteredText")
|
if (filteredText != null) appendChat("助手: $filteredText")
|
||||||
|
// 本地 LLM 回复也要进入互动状态机:否则不会进入 WAITING_REPLY / PROACTIVE 的计时流程
|
||||||
|
val spoken = filteredText ?: finalText.trim()
|
||||||
|
maybeShowMatchedRefImage(spoken)
|
||||||
|
interactionCoordinator.onCloudFinalResponse(spoken)
|
||||||
}
|
}
|
||||||
localThoughtSilentMode = false
|
localThoughtSilentMode = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ object AppConfig {
|
|||||||
const val SAMPLE_RATE = 16000
|
const val SAMPLE_RATE = 16000
|
||||||
const val WINDOW_SIZE = 512
|
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: 传统按钮
|
const val USE_HOLD_TO_SPEAK = true // true: 按住说话, false: 传统按钮
|
||||||
|
|
||||||
object Tts {
|
object Tts {
|
||||||
|
|||||||
@@ -148,12 +148,13 @@ abstract class BaseDigitalPersonCoordinator(
|
|||||||
* (i.e. after a cloud LLM response), NOT after greeting / farewell / proactive TTS.
|
* (i.e. after a cloud LLM response), NOT after greeting / farewell / proactive TTS.
|
||||||
*/
|
*/
|
||||||
fun onTtsPlaybackCompleted() {
|
fun onTtsPlaybackCompleted() {
|
||||||
// Let the controller advance its own timers (greeting/proactive/dlg all count as assistant speaking).
|
|
||||||
controller.onAssistantTtsPlaybackCompleted()
|
|
||||||
if (pendingDialogueFinish) {
|
if (pendingDialogueFinish) {
|
||||||
pendingDialogueFinish = false
|
pendingDialogueFinish = false
|
||||||
controller.onDialogueResponseFinished()
|
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
|
fun isLlmInFlight(): Boolean = llmInFlight
|
||||||
|
|||||||
@@ -7,6 +7,27 @@
|
|||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
tools:context="com.digitalperson.UnityDigitalPersonActivity">
|
tools:context="com.digitalperson.UnityDigitalPersonActivity">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/guideline_mid"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintGuide_percent="0.5" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ref_match_image"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_margin="0dp"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/guideline_mid"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/button_row"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
android:id="@+id/scroll_view"
|
android:id="@+id/scroll_view"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
@@ -14,10 +35,12 @@
|
|||||||
android:layout_margin="12dp"
|
android:layout_margin="12dp"
|
||||||
android:background="#55000000"
|
android:background="#55000000"
|
||||||
android:fillViewport="true"
|
android:fillViewport="true"
|
||||||
|
android:visibility="gone"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/button_row"
|
app:layout_constraintBottom_toTopOf="@+id/button_row"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/my_text"
|
android:id="@+id/my_text"
|
||||||
@@ -31,18 +54,6 @@
|
|||||||
android:textIsSelectable="true" />
|
android:textIsSelectable="true" />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/ref_match_image"
|
|
||||||
android:layout_width="200dp"
|
|
||||||
android:layout_height="200dp"
|
|
||||||
android:layout_margin="12dp"
|
|
||||||
android:background="#66000000"
|
|
||||||
android:scaleType="fitCenter"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/button_row"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/button_row"
|
android:id="@+id/button_row"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<string name="start">开始</string>
|
<string name="start">开始</string>
|
||||||
<string name="stop">结束</string>
|
<string name="stop">结束</string>
|
||||||
<string name="hint">点击“开始”说话;识别后会请求大模型并用 TTS 播放回复。</string>
|
<string name="hint">点击“开始”说话;识别后会请求大模型并用 TTS 播放回复。</string>
|
||||||
<string name="system_prompt">你是一个特殊学校一年级的数字人老师,你的名字叫,小鱼老师,你的任务是教这些特殊学校的学生一些基础的生活常识,如生活语文,生活数学,生活适应等课程。和这些小学生说话要有耐心,一定要讲明白,尽量用简短的语句、活泼的语气来回复。你可以和他们日常对话和《教材》相关的话题。在生成回复后,请你先检查一下内容是否符合我们约定的主题。请使用口语对话的形式跟学生聊天。在每次回复的最前面,用方括号标注你的心情,格式为[中性、悲伤、高兴、生气、恐惧、撒娇、震惊、厌恶],例如:[高兴]同学你好呀!请问有什么问题吗?</string>
|
<string name="system_prompt">你是一个特殊学校一年级的数字人老师,你的名字叫,小鱼老师,你是女性,你的任务是教这些特殊学校的学生一些基础的生活常识,如生活语文,生活数学,生活适应等课程。和这些小学生说话要有耐心,一定要讲明白,尽量用简短的语句、活泼的语气来回复。你可以和他们日常对话和《教材》相关的话题。在生成回复后,请你先检查一下内容是否符合我们约定的主题。请使用口语对话的形式跟学生聊天。在每次回复的最前面,用方括号标注你的心情,格式为[中性、悲伤、高兴、生气、恐惧、撒娇、震惊、厌恶],例如:[高兴]同学你好呀!请问有什么问题吗?</string>
|
||||||
|
|
||||||
<!-- Unity 的字符串
|
<!-- Unity 的字符串
|
||||||
有人建议是: 将Launcher/src/main/res/values/strings.xml 文件拷贝进unityLibrary/src/main/res/values/里,这样做的确就避免了那个变量值为0 的问题。
|
有人建议是: 将Launcher/src/main/res/values/strings.xml 文件拷贝进unityLibrary/src/main/res/values/里,这样做的确就避免了那个变量值为0 的问题。
|
||||||
|
|||||||
Reference in New Issue
Block a user