Convai 集成
Convai 集成 - 用于无缝集成的 PlayCanvas 插件指南。
Convai 初始化脚本
var ConvaiNpc = pc.createScript('convaiNpc');
/** 加载 convai-sdk cdn */
var convaiClient = null;
var userTextStream = ""; // 存储用户的文本输入
var npcTextStream = ""; // 存储 NPC 的文本回复
var keyPressed = false; // 记录 "T" 键是否被按下
var isTalking = false; // 指示 NPC 当前是否正在说话
var conversationActive = false; // 指示当前是否有正在进行的对话
var visemeData = []; // 存储用于口型同步动画的 viseme 数据
var visemeDataActive = false; // 指示是否有 viseme 数据可用
var formLoaded = false; // 指示表单是否已加载
let timeoutId;
/**
* 使用 API 密钥、角色 ID 以及用于启用音频录制器和播放器的标志来初始化 ConvaiClient。请先从 Start() 调用此函数
* 设置多个回调,以处理来自 Convai Client 的不同类型响应。
* @param {string} apiKey - Convai 服务的 API 密钥。
* @param {string} characterId - 在 Convai 服务中使用的角色 ID。
* @param {boolean} enableAudioRecorder - 启用音频录制器的标志。
* @param {boolean} enableAudioPlayer - 启用音频播放器的标志。
* @param {number} faceModal - 角色要使用的 face modal。
* @param {boolean} enableFacialData - 启用面部数据的标志。
*/
function initializeConvaiClient () {
console.log("Convai client initiated.");
convaiClient = new ConvaiClient({
apiKey: "", // 替换为你的 API 密钥
characterId: "", // 替换为你的角色 ID
enableAudio: true,
faceModal: 3,
enableFacialData: true,
});
// 错误回调
convaiClient.setErrorCallback(function(type, statusMessage) {
console.log("Error Callback", type, statusMessage);
});
// 响应回调
convaiClient.setResponseCallback(function(response) {
// 处理用户查询
if (response.hasUserQuery) {
var transcript = response.getUserQuery();
if (transcript) {
var isFinal = transcript?.getIsFinal();
var transcriptText = transcript.getTextData();
if (isFinal) {
userTextStream += " " + transcriptText;
}
userTextStream = transcriptText;
}
}
// 处理音频响应
if (response.hasAudioResponse()) {
var audioResponse = response.getAudioResponse();
npcTextStream += " " + audioResponse.getTextData();
// 处理用于口型同步动画的 viseme 数据
if (audioResponse.hasVisemesData()) {
let lipsyncData = audioResponse.getVisemesData().array[0];
if (lipsyncData[0] !== -2) {
visemeData.push(lipsyncData);
visemeDataActive = true;
}
} else {
visemeDataActive = false;
}
}
});
}
// 处理按键按下事件以开始音频输入
function handleKeyDown(e) {
const convaiFormEl = document.getElementById("convai-input");
if (convaiClient && e.keyCode === 84 && !keyPressed && !(document.activeElement === convaiFormEl)) {
e.stopPropagation();
e.preventDefault();
keyPressed = true;
userTextStream = "";
npcTextStream = "";
convaiClient.startAudioChunk();
conversationActive = true;
}
}
// 处理按键抬起事件以停止音频输入
function handleKeyUp(e) {
const convaiFormEl = document.getElementById("convai-input");
if (convaiClient && e.keyCode === 84 && keyPressed && !(document.activeElement === convaiFormEl)) {
e.preventDefault();
keyPressed = false;
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
convaiClient.endAudioChunk();
}, 500);
}
}
// 添加按键按下和按键抬起的事件监听器
window.addEventListener("keydown", handleKeyDown);
window.addEventListener("keyup", handleKeyUp);
// 每个实体只调用一次的初始化代码
ConvaiNpc.prototype.initialize = function() {
initializeConvaiClient();
// 设置音频播放的回调
convaiClient.onAudioPlay(() => {
isTalking = true;
});
convaiClient.onAudioStop(() => {
isTalking = false;
conversationActive = false;
});
};
// 根据说话状态处理动画
ConvaiNpc.prototype.handleAnimation = function() {
this.entity.anim.setBoolean("Talking", isTalking);
};
// 每帧调用的更新代码
ConvaiNpc.prototype.update = function(dt) {
this.handleAnimation();
// 检查表单是否已加载并添加事件监听器
if (!formLoaded && document.getElementById("convai-form")) {
this.formListener();
formLoaded = true;
}
};
// 添加表单提交事件监听器
ConvaiNpc.prototype.formListener = function() {
document.getElementById("convai-form").addEventListener("submit", (e) => {
e.preventDefault();
var inputValue = document.getElementById("convai-input").value;
window.convaiClient.sendTextChunk(inputValue);
userTextStream = inputValue;
document.getElementById("convai-form").reset();
conversationActive = true;
npcTextStream = "";
});
}PlayerAnimationHandler 脚本
口型同步脚本
头部追踪脚本

最后更新于
这有帮助吗?