优化聊天部分
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
* 统一管理所有API相关的配置信息
|
||||
*/
|
||||
|
||||
import { DataId, DataManager } from "../data/DataManager";
|
||||
import { GlobalData } from "../data/GlobalData";
|
||||
import { ConfigManager } from "../manager/ConfigManager";
|
||||
|
||||
export interface AIConfig {
|
||||
@@ -41,15 +43,21 @@ export class ApiConfig {
|
||||
* TODO: 应该从环境变量或安全配置文件中读取
|
||||
*/
|
||||
private initConfig(): void {
|
||||
const config = ConfigManager.tables.TbGlobalConfig;
|
||||
//const config = DataManager.I.getDataById<GlobalData>(DataId.Global).getApiKey();
|
||||
this.config = {
|
||||
// 警告: API密钥不应该硬编码在代码中
|
||||
// 生产环境中应该从环境变量或安全配置文件中读取
|
||||
apiKey: config.ApiKey,
|
||||
model: config.Model,
|
||||
temperature: config.Temperature,
|
||||
maxTokens: config.MaxTokens,
|
||||
timeout: config.Timeout, // 30秒
|
||||
apiKey: DataManager.I.getDataById<GlobalData>(DataId.Global).getApiKey(),
|
||||
model: DataManager.I.getDataById<GlobalData>(DataId.Global).getModel(),
|
||||
temperature: DataManager.I.getDataById<GlobalData>(
|
||||
DataId.Global
|
||||
).getTemperature(),
|
||||
maxTokens: DataManager.I.getDataById<GlobalData>(
|
||||
DataId.Global
|
||||
).getMaxTokens(),
|
||||
timeout: DataManager.I.getDataById<GlobalData>(
|
||||
DataId.Global
|
||||
).getTimeout(), // 30秒
|
||||
};
|
||||
}
|
||||
|
||||
@@ -64,13 +72,18 @@ export class ApiConfig {
|
||||
* 获取情绪AI配置
|
||||
*/
|
||||
public getEmotionAIConfig(): AIConfig {
|
||||
const config = ConfigManager.tables.TbGlobalConfig;
|
||||
return {
|
||||
apiKey: config.emotionApiKey,
|
||||
model: config.Model,
|
||||
temperature: config.Temperature,
|
||||
maxTokens: config.MaxTokens,
|
||||
timeout: config.Timeout,
|
||||
apiKey: DataManager.I.getDataById<GlobalData>(
|
||||
DataId.Global
|
||||
).getEmotionApiKey(),
|
||||
model: DataManager.I.getDataById<GlobalData>(DataId.Global).getModel(),
|
||||
temperature: 0.2,
|
||||
maxTokens: DataManager.I.getDataById<GlobalData>(
|
||||
DataId.Global
|
||||
).getMaxTokens(),
|
||||
timeout: DataManager.I.getDataById<GlobalData>(
|
||||
DataId.Global
|
||||
).getTimeout(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -87,10 +87,9 @@ export class ChatAIService {
|
||||
const config = ApiConfig.Instance.getAIConfig();
|
||||
|
||||
// 使用 ChatHistoryManager 的统一加载方法(优先本地,无则获取服务端)
|
||||
const savedHistory = await ChatHistoryManager.Instance.loadChatHistory(
|
||||
let savedHistory = await ChatHistoryManager.Instance.loadChatHistory(
|
||||
roleId
|
||||
);
|
||||
|
||||
let chat;
|
||||
if (savedHistory && savedHistory.length > 0) {
|
||||
chat = this.ai.chats.create({
|
||||
|
||||
@@ -191,9 +191,6 @@ export class ChatController {
|
||||
throw new Error("Role ID is not available in ChatModel");
|
||||
}
|
||||
|
||||
// 通知界面消息发送开始
|
||||
this.callback?.onMessageSent(message);
|
||||
|
||||
// 添加用户消息到模型
|
||||
this.chatModel.addDialog(true, message);
|
||||
|
||||
@@ -201,6 +198,9 @@ export class ChatController {
|
||||
this.dialogManager?.updateDialog(true, message, true);
|
||||
this.callback?.onDialogUpdated();
|
||||
|
||||
// 通知界面消息发送开始
|
||||
this.callback?.onMessageSent(message);
|
||||
|
||||
// 显示加载中的对话
|
||||
this.dialogManager?.addLoadingDialog();
|
||||
|
||||
@@ -469,7 +469,7 @@ export class ChatController {
|
||||
* 检查是否有指定角色的数据
|
||||
* @param roleId 角色ID
|
||||
*/
|
||||
public hasRoleData(categoryId:string,roleId: number): boolean {
|
||||
public hasRoleData(categoryId: string, roleId: number): boolean {
|
||||
return this.chatModel.hasRoleData(roleId);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@ export class RoleConfigLoader {
|
||||
AiCharacter.basePrompt +
|
||||
"\n" +
|
||||
AiCharacter.additionPrompt;
|
||||
|
||||
console.log(AiCharacter.basePrompt);
|
||||
console.log(AiCharacter.additionPrompt);
|
||||
return prompt;
|
||||
}
|
||||
|
||||
@@ -30,7 +33,7 @@ export class RoleConfigLoader {
|
||||
*/
|
||||
public static getEmotionInstruction(roleId: number): string {
|
||||
const AiCharacter = ConfigManager.tables.TbAiCharacters.get(roleId);
|
||||
const globalPrompt = ConfigManager.tables.TbGlobalConfig.EmotionRating;
|
||||
const globalPrompt = ConfigManager.tables.TbGlobalConfig.EmotionBasePrompt;
|
||||
const prompt = globalPrompt + "\n" + AiCharacter.basePrompt;
|
||||
return prompt;
|
||||
}
|
||||
|
||||
@@ -4,16 +4,24 @@ export interface Dialog {
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
export class DemoData {
|
||||
export class DiaLogData {
|
||||
public Dialogs: Dialog[] = [];
|
||||
|
||||
public cleanDialog() {
|
||||
this.Dialogs = [];
|
||||
}
|
||||
|
||||
public pushDialog(isPlayer: boolean, str: string, isLoading: boolean = false) {
|
||||
public pushDialog(
|
||||
isPlayer: boolean,
|
||||
str: string,
|
||||
isLoading: boolean = false
|
||||
) {
|
||||
if (!this.Dialogs) this.Dialogs = [];
|
||||
this.Dialogs.push({ isPlayer: isPlayer, content: str, isLoading: isLoading });
|
||||
this.Dialogs.push({
|
||||
isPlayer: isPlayer,
|
||||
content: str,
|
||||
isLoading: isLoading,
|
||||
});
|
||||
}
|
||||
|
||||
public GetDialogs() {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { find } from "cc";
|
||||
import { ChatContentsLayout } from "../ui/components/ChatContentsLayout";
|
||||
import { DemoData, Dialog } from "../data/DialogData";
|
||||
import { NavigationManager } from "./NavigationManager";
|
||||
import { DiaLogData, Dialog } from "../data/DialogData";
|
||||
import Utils from "db://assets/Scripts/Main/Common/Utils";
|
||||
import { InnerMsgCode } from "db://assets/Scripts/Main/Config/InnerMsgCode";
|
||||
|
||||
@@ -31,19 +29,15 @@ export class DialogManager {
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
this.demoData = new DemoData();
|
||||
this.dialogData = new DiaLogData();
|
||||
}
|
||||
|
||||
/** 对话数据实例 */
|
||||
private demoData: DemoData;
|
||||
|
||||
/** 当前主题ID */
|
||||
private themeId = -1;
|
||||
private dialogData: DiaLogData;
|
||||
|
||||
/** 聊天内容布局组件引用 */
|
||||
public layoutout: ChatContentsLayout;
|
||||
|
||||
|
||||
/**
|
||||
* 更新对话内容
|
||||
*
|
||||
@@ -57,14 +51,9 @@ export class DialogManager {
|
||||
fromPlayer: boolean = false
|
||||
): void {
|
||||
if (fromPlayer) {
|
||||
this.demoData.cleanDialog();
|
||||
this.dialogData.cleanDialog();
|
||||
}
|
||||
this.demoData.pushDialog(isPlayer, str);
|
||||
console.log("Dialog updated:", {
|
||||
isPlayer,
|
||||
content: str.substring(0, 50) + "...",
|
||||
});
|
||||
Utils.sendInnerMsg(InnerMsgCode.Chat_DialogRefresh);
|
||||
this.dialogData.pushDialog(isPlayer, str);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,27 +69,25 @@ export class DialogManager {
|
||||
fromPlayer: boolean = false
|
||||
): void {
|
||||
if (fromPlayer) {
|
||||
this.demoData.cleanDialog();
|
||||
this.dialogData.cleanDialog();
|
||||
}
|
||||
|
||||
// 如果是玩家消息,直接添加单个气泡
|
||||
if (isPlayer) {
|
||||
this.demoData.pushDialog(isPlayer, str);
|
||||
this.dialogData.pushDialog(isPlayer, str);
|
||||
console.log("Dialog updated:", {
|
||||
isPlayer,
|
||||
content: str.substring(0, 50) + "...",
|
||||
});
|
||||
Utils.sendInnerMsg(InnerMsgCode.Chat_DialogRefresh);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是AI消息,进行分段处理
|
||||
const segments = this.splitMessageIntoSegments(str);
|
||||
|
||||
|
||||
if (segments.length <= 1) {
|
||||
// 如果只有一段,直接显示单个气泡
|
||||
this.demoData.pushDialog(isPlayer, str);
|
||||
Utils.sendInnerMsg(InnerMsgCode.Chat_DialogRefresh);
|
||||
this.dialogData.pushDialog(isPlayer, str);
|
||||
} else {
|
||||
// 多段内容,依次显示多个气泡
|
||||
this.displaySegmentsWithDelay(segments, isPlayer);
|
||||
@@ -115,16 +102,16 @@ export class DialogManager {
|
||||
private splitMessageIntoSegments(message: string): string[] {
|
||||
// 首先按双换行符分割
|
||||
let segments = message.split(/\n\n+/);
|
||||
|
||||
|
||||
// 如果只有一段,则按单换行符分割
|
||||
if (segments.length === 1) {
|
||||
segments = message.split(/\n+/);
|
||||
}
|
||||
|
||||
|
||||
// 过滤空段落并去除首尾空白
|
||||
return segments
|
||||
.map(segment => segment.trim())
|
||||
.filter(segment => segment.length > 0);
|
||||
.map((segment) => segment.trim())
|
||||
.filter((segment) => segment.length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,22 +119,17 @@ export class DialogManager {
|
||||
* @param segments 段落数组
|
||||
* @param isPlayer 是否为玩家消息
|
||||
*/
|
||||
private displaySegmentsWithDelay(segments: string[], isPlayer: boolean): void {
|
||||
let currentDelay = 500; // 基础延迟 500ms
|
||||
|
||||
private displaySegmentsWithDelay(
|
||||
segments: string[],
|
||||
isPlayer: boolean
|
||||
): void {
|
||||
// let currentDelay = 500; // 基础延迟 500ms
|
||||
// 每个后续段落增加随机延迟 (800-1500ms)
|
||||
|
||||
// currentDelay += 800 + Math.random() * 700;
|
||||
|
||||
segments.forEach((segment, index) => {
|
||||
setTimeout(() => {
|
||||
this.demoData.pushDialog(isPlayer, segment);
|
||||
console.log(`Segment ${index + 1}/${segments.length} displayed:`, {
|
||||
isPlayer,
|
||||
content: segment.substring(0, 30) + "...",
|
||||
delay: currentDelay
|
||||
});
|
||||
Utils.sendInnerMsg(InnerMsgCode.Chat_DialogRefresh);
|
||||
}, currentDelay);
|
||||
|
||||
// 每个后续段落增加随机延迟 (800-1500ms)
|
||||
currentDelay += 800 + Math.random() * 700;
|
||||
this.dialogData.pushDialog(isPlayer, segment);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -156,7 +138,7 @@ export class DialogManager {
|
||||
* 显示"..."循环动画表示AI正在回复
|
||||
*/
|
||||
public addLoadingDialog(): void {
|
||||
this.demoData.pushDialog(false, "...", true);
|
||||
this.dialogData.pushDialog(false, "...", true);
|
||||
console.log("Loading dialog added");
|
||||
Utils.sendInnerMsg(InnerMsgCode.Chat_DialogRefresh);
|
||||
}
|
||||
@@ -166,8 +148,8 @@ export class DialogManager {
|
||||
* 在收到AI回复或出错时调用
|
||||
*/
|
||||
public removeLoadingDialog(): void {
|
||||
const dialogs = this.demoData.GetDialogs();
|
||||
const loadingIndex = dialogs.findIndex(dialog => dialog.isLoading);
|
||||
const dialogs = this.dialogData.GetDialogs();
|
||||
const loadingIndex = dialogs.findIndex((dialog) => dialog.isLoading);
|
||||
if (loadingIndex !== -1) {
|
||||
dialogs.splice(loadingIndex, 1);
|
||||
console.log("Loading dialog removed");
|
||||
@@ -181,14 +163,14 @@ export class DialogManager {
|
||||
* @returns {Dialog[]} 对话记录数组
|
||||
*/
|
||||
public getDialogs() {
|
||||
return this.demoData.GetDialogs();
|
||||
return this.dialogData.GetDialogs();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空当前对话记录
|
||||
*/
|
||||
public clearDialogs(): void {
|
||||
this.demoData.cleanDialog();
|
||||
this.dialogData.cleanDialog();
|
||||
console.log("All dialogs cleared");
|
||||
Utils.sendInnerMsg(InnerMsgCode.Chat_DialogRefresh);
|
||||
}
|
||||
@@ -198,9 +180,13 @@ export class DialogManager {
|
||||
* @param dialogs 对话记录数组
|
||||
*/
|
||||
public setDialogs(dialogs: Dialog[]): void {
|
||||
this.demoData.cleanDialog();
|
||||
dialogs.forEach(dialog => {
|
||||
this.demoData.pushDialog(dialog.isPlayer, dialog.content, dialog.isLoading || false);
|
||||
this.dialogData.cleanDialog();
|
||||
dialogs.forEach((dialog) => {
|
||||
this.dialogData.pushDialog(
|
||||
dialog.isPlayer,
|
||||
dialog.content,
|
||||
dialog.isLoading || false
|
||||
);
|
||||
});
|
||||
console.log(`DialogManager: Set ${dialogs.length} dialogs`);
|
||||
Utils.sendInnerMsg(InnerMsgCode.Chat_DialogRefresh);
|
||||
@@ -219,14 +205,14 @@ export class DialogManager {
|
||||
* 获取对话记录数量
|
||||
*/
|
||||
public getDialogCount(): number {
|
||||
return this.demoData.GetDialogs().length;
|
||||
return this.dialogData.GetDialogs().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最后一条对话记录
|
||||
*/
|
||||
public getLastDialog(): Dialog | null {
|
||||
const dialogs = this.demoData.GetDialogs();
|
||||
const dialogs = this.dialogData.GetDialogs();
|
||||
return dialogs.length > 0 ? dialogs[dialogs.length - 1] : null;
|
||||
}
|
||||
|
||||
@@ -234,6 +220,6 @@ export class DialogManager {
|
||||
* 检查是否有对话记录
|
||||
*/
|
||||
public hasDialogs(): boolean {
|
||||
return this.demoData.GetDialogs().length > 0;
|
||||
return this.dialogData.GetDialogs().length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,9 @@ export class ChatContentsLayout extends Component {
|
||||
|
||||
fixMaxWidth: number;
|
||||
|
||||
// 用于跟踪延时任务,支持中断清理
|
||||
private delayedTasks: number[] = [];
|
||||
|
||||
protected start(): void {
|
||||
this.lBubble.node.active = false;
|
||||
this.rBubble.node.active = false;
|
||||
@@ -42,6 +45,9 @@ export class ChatContentsLayout extends Component {
|
||||
}
|
||||
|
||||
UpdateDialog(dialogs: Dialog[]) {
|
||||
// 清理所有正在进行的延时任务
|
||||
this.clearDelayedTasks();
|
||||
|
||||
// 隐藏当前使用的气泡并放入缓存
|
||||
for (let i = 0; i < this.bubbles.length; i++) {
|
||||
if (this.bubbles[i].node) {
|
||||
@@ -51,7 +57,7 @@ export class ChatContentsLayout extends Component {
|
||||
}
|
||||
this.bubbles = [];
|
||||
|
||||
this.updateDialogLayout(dialogs);
|
||||
this.updateDialogLayoutWithDelay(dialogs);
|
||||
|
||||
// 清理多余的缓存气泡,避免内存泄漏
|
||||
this.cleanupExcessCachedBubbles();
|
||||
@@ -93,7 +99,7 @@ export class ChatContentsLayout extends Component {
|
||||
? instantiate(this.rBubble.node)
|
||||
: instantiate(this.lBubble.node);
|
||||
newBubble = newBubbleNode.getComponent(DialogBubble);
|
||||
newBubble.init();
|
||||
newBubble.init(650);
|
||||
newBubbleNode.setParent(this.node);
|
||||
}
|
||||
|
||||
@@ -104,6 +110,7 @@ export class ChatContentsLayout extends Component {
|
||||
pendingUpdates++;
|
||||
newBubble.updateBubbleContent(
|
||||
dialog.content,
|
||||
dialog.isPlayer,
|
||||
(actualHeight: number) => {
|
||||
initPosY += actualHeight + 40;
|
||||
pendingUpdates--;
|
||||
@@ -135,4 +142,154 @@ export class ChatContentsLayout extends Component {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private clearDelayedTasks() {
|
||||
// 清理所有正在进行的延时任务
|
||||
for (const taskId of this.delayedTasks) {
|
||||
clearTimeout(taskId);
|
||||
}
|
||||
this.delayedTasks = [];
|
||||
}
|
||||
|
||||
private groupConsecutiveNonPlayerDialogs(dialogs: Dialog[]): Dialog[][] {
|
||||
const groups: Dialog[][] = [];
|
||||
let currentGroup: Dialog[] = [];
|
||||
|
||||
for (const dialog of dialogs) {
|
||||
if (dialog.isPlayer) {
|
||||
// 遇到player消息,结束当前非player组
|
||||
if (currentGroup.length > 0) {
|
||||
groups.push([...currentGroup]);
|
||||
currentGroup = [];
|
||||
}
|
||||
// player消息单独成组
|
||||
groups.push([dialog]);
|
||||
} else {
|
||||
// 非player消息加入当前组
|
||||
currentGroup.push(dialog);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理最后一组非player消息
|
||||
if (currentGroup.length > 0) {
|
||||
groups.push(currentGroup);
|
||||
}
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
private updateDialogLayoutWithDelay(dialogs: Dialog[]) {
|
||||
const groups = this.groupConsecutiveNonPlayerDialogs(dialogs);
|
||||
let initPosY: number = this.initPos.position.y;
|
||||
|
||||
// 倒序处理组,保持原有的显示顺序
|
||||
this.processGroupsWithDelay(groups, groups.length - 1, initPosY);
|
||||
}
|
||||
|
||||
private processGroupsWithDelay(
|
||||
groups: Dialog[][],
|
||||
groupIndex: number,
|
||||
currentPosY: number
|
||||
) {
|
||||
if (groupIndex < 0) return;
|
||||
|
||||
const currentGroup = groups[groupIndex];
|
||||
|
||||
// 直接渲染组,延时逻辑已移到单条消息级别
|
||||
this.renderDialogGroup(currentGroup, currentPosY, (newPosY) => {
|
||||
// 递归处理下一组,传递更新后的位置
|
||||
this.processGroupsWithDelay(groups, groupIndex - 1, newPosY);
|
||||
});
|
||||
}
|
||||
|
||||
private renderDialogGroup(
|
||||
dialogs: Dialog[],
|
||||
startPosY: number,
|
||||
onComplete: (finalPosY: number) => void
|
||||
) {
|
||||
let currentPosY = startPosY;
|
||||
|
||||
const updateNextBubble = (index: number) => {
|
||||
if (index < 0) {
|
||||
onComplete(currentPosY);
|
||||
return;
|
||||
}
|
||||
|
||||
const dialog = dialogs[index];
|
||||
|
||||
// 计算当前消息的延时:非player消息且不是组内最后一条时添加延时
|
||||
const isNonPlayerMessage = !dialog.isPlayer;
|
||||
const isLastInGroup = index === dialogs.length - 1;
|
||||
const delay =
|
||||
isNonPlayerMessage && !isLastInGroup ? Math.random() * 1000 + 500 : 0; // 500-1500ms随机延时
|
||||
|
||||
const renderCurrentBubble = () => {
|
||||
let newBubble: DialogBubble = null;
|
||||
|
||||
// 尝试从缓存中获取合适的气泡
|
||||
const isPlayerBubble = dialog.isPlayer;
|
||||
for (let j = 0; j < this.cachedBubbles.length; j++) {
|
||||
const cached = this.cachedBubbles[j];
|
||||
if (cached && cached.node) {
|
||||
const isRightBubble = cached.node.position.x > 0;
|
||||
if (
|
||||
(isPlayerBubble && isRightBubble) ||
|
||||
(!isPlayerBubble && !isRightBubble)
|
||||
) {
|
||||
newBubble = cached;
|
||||
this.cachedBubbles.splice(j, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果缓存中没有合适的气泡,创建新的
|
||||
if (!newBubble) {
|
||||
let newBubbleNode = isPlayerBubble
|
||||
? instantiate(this.rBubble.node)
|
||||
: instantiate(this.lBubble.node);
|
||||
newBubble = newBubbleNode.getComponent(DialogBubble);
|
||||
newBubble.init(650);
|
||||
newBubbleNode.setParent(this.node);
|
||||
}
|
||||
|
||||
newBubble.node.active = true;
|
||||
let pos = newBubble.node.position;
|
||||
newBubble.node.position = new Vec3(pos.x, currentPosY, pos.z);
|
||||
|
||||
newBubble.updateBubbleContent(
|
||||
dialog.content,
|
||||
dialog.isPlayer,
|
||||
(actualHeight: number) => {
|
||||
currentPosY += actualHeight + 40;
|
||||
// 渲染完当前气泡后,处理下一个
|
||||
updateNextBubble(index - 1);
|
||||
},
|
||||
dialog.isLoading || false
|
||||
);
|
||||
|
||||
this.bubbles.push(newBubble);
|
||||
};
|
||||
|
||||
if (delay > 0) {
|
||||
// 添加延时任务ID到跟踪数组
|
||||
const taskId = setTimeout(() => {
|
||||
// 从跟踪数组中移除已完成的任务
|
||||
const taskIndex = this.delayedTasks.indexOf(taskId);
|
||||
if (taskIndex > -1) {
|
||||
this.delayedTasks.splice(taskIndex, 1);
|
||||
}
|
||||
renderCurrentBubble();
|
||||
}, delay);
|
||||
|
||||
this.delayedTasks.push(taskId);
|
||||
} else {
|
||||
// 立即渲染
|
||||
renderCurrentBubble();
|
||||
}
|
||||
};
|
||||
|
||||
// 从组内最后一个对话开始处理(倒序)
|
||||
updateNextBubble(dialogs.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,21 @@ import {
|
||||
Node,
|
||||
Overflow,
|
||||
Size,
|
||||
Sprite,
|
||||
UITransform,
|
||||
view,
|
||||
} from "cc";
|
||||
import Tools from "../../utils/tools";
|
||||
import { GirlData } from "../../data/GirlData";
|
||||
import { DataId, DataManager } from "../../data/DataManager";
|
||||
import { NavigationManager } from "../../manager/NavigationManager";
|
||||
import ResManager from "../../../Main/Manager/ResManager";
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass("DialogBubble")
|
||||
export class DialogBubble extends Component {
|
||||
@property(Sprite)
|
||||
avatar: Sprite = null;
|
||||
@property(UITransform)
|
||||
bg: UITransform = null;
|
||||
@property(Label)
|
||||
@@ -21,8 +28,12 @@ export class DialogBubble extends Component {
|
||||
contentT: UITransform = null;
|
||||
|
||||
fixMaxWidth: number;
|
||||
private loadingAnimationId: number = null;
|
||||
|
||||
uiTransform: UITransform;
|
||||
|
||||
init(maxWidth?: number) {
|
||||
this.uiTransform = this.node.getComponent(UITransform);
|
||||
|
||||
// 如果没有提供maxWidth,使用屏幕宽度的80%
|
||||
if (maxWidth === undefined) {
|
||||
this.fixMaxWidth = view.getVisibleSize().width * 0.8;
|
||||
@@ -33,6 +44,7 @@ export class DialogBubble extends Component {
|
||||
|
||||
updateBubbleContent(
|
||||
str: string,
|
||||
isPlayer: boolean = false,
|
||||
callback?: (actualHeight: number) => void,
|
||||
isLoading: boolean = false
|
||||
) {
|
||||
@@ -80,7 +92,7 @@ export class DialogBubble extends Component {
|
||||
// 设置内容区域宽度,让Label自动计算高度
|
||||
this.contentT.setContentSize(new Size(contentWidth, 0));
|
||||
this.bg.setContentSize(new Size(contentWidth + 37, 0));
|
||||
|
||||
this.uiTransform.setContentSize(new Size(contentWidth + 37, 0));
|
||||
// 强制更新Label的渲染数据以获取正确的高度
|
||||
this.content.updateRenderData(true);
|
||||
|
||||
@@ -95,6 +107,9 @@ export class DialogBubble extends Component {
|
||||
// 根据内容大小调整背景大小,添加适当的内边距
|
||||
//const bgHeight = actualHeight + 17;
|
||||
this.bg.setContentSize(new Size(actualSize.x + 40, actualSize.y + 30));
|
||||
this.uiTransform.setContentSize(
|
||||
new Size(actualSize.x + 40, actualSize.y + 30)
|
||||
);
|
||||
|
||||
// 回调通知实际高度
|
||||
if (callback) {
|
||||
@@ -104,6 +119,19 @@ export class DialogBubble extends Component {
|
||||
|
||||
// 返回预估的背景高度(用于同步计算)
|
||||
const estimatedHeight = Math.max(lines.length * 35 + 20, 60); // 最小高度60
|
||||
|
||||
//设置头像
|
||||
if (isPlayer) {
|
||||
//获取玩家头像,等接入luffa
|
||||
} else {
|
||||
const girlData = DataManager.I.getDataById<GirlData>(DataId.Girl);
|
||||
const path = girlData.getGrilAvatar(
|
||||
NavigationManager.Instance.getSelectedCategoryId().toString(),
|
||||
NavigationManager.Instance.getSelectedGirlId()
|
||||
);
|
||||
ResManager.I.changeBundleSpriteFrame(this.avatar, path, "Girls");
|
||||
}
|
||||
|
||||
return estimatedHeight;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import { SceneBgVideoLayer } from "../../../Sub/UI/SceneBgVideoLayer";
|
||||
import { NavigationManager, PanelType } from "../../manager/NavigationManager";
|
||||
import { GirlService } from "../../network/services/GirlService";
|
||||
import proto from "db://assets/Scripts/proto/proto.pb.js";
|
||||
import { TipsPanel } from "./TipsPanel";
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass("ChatPanel")
|
||||
@@ -389,7 +390,10 @@ export class ChatPanel extends li_BaseView implements IChatPanelCallback {
|
||||
|
||||
public async OnClickSend() {
|
||||
const str = this.editBox.string;
|
||||
if (!str || str == "") return;
|
||||
if (!str || str == "") {
|
||||
TipsPanel.show("请输入内容");
|
||||
return;
|
||||
}
|
||||
|
||||
// 通过ChatController发送消息
|
||||
const succeed = await this.chatController.sendMessage(str);
|
||||
@@ -437,6 +441,7 @@ export class ChatPanel extends li_BaseView implements IChatPanelCallback {
|
||||
|
||||
onChatLimitReached(): void {
|
||||
this.payToTalkPanel.show(this.categoryId, this.id);
|
||||
TipsPanel.show(LanguageUtils.getText("chat_error_code_1001"));
|
||||
}
|
||||
/**
|
||||
* 情绪状态更新回调 (实现IChatPanelCallback接口)
|
||||
|
||||
@@ -232,7 +232,7 @@ export class GlobalConfig {
|
||||
this.OnceChatAddScore = _buf_.readInt()
|
||||
this.ScoreExchangeStarLevel = _buf_.readInt()
|
||||
this.GameName = _buf_.readString()
|
||||
this.EmotionRating = _buf_.readString()
|
||||
this.EmotionBasePrompt = _buf_.readString()
|
||||
this.girlBasePrompt = _buf_.readString()
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ export class GlobalConfig {
|
||||
/**
|
||||
* 情绪评分机器人system_prompt,需拼接角色prompt
|
||||
*/
|
||||
readonly EmotionRating: string
|
||||
readonly EmotionBasePrompt: string
|
||||
/**
|
||||
* ai机器人基础规则
|
||||
*/
|
||||
@@ -801,7 +801,7 @@ export class TbGlobalConfig {
|
||||
/**
|
||||
* 情绪评分机器人system_prompt,需拼接角色prompt
|
||||
*/
|
||||
get EmotionRating(): string { return this._data.EmotionRating; }
|
||||
get EmotionBasePrompt(): string { return this._data.EmotionBasePrompt; }
|
||||
/**
|
||||
* ai机器人基础规则
|
||||
*/
|
||||
|
||||
+1160
-470
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user