Files
18xchat/assets/Scripts/chat18x/ui/panels/GirlDetailPanel.ts
T

469 lines
13 KiB
TypeScript

import {
_decorator,
Label,
Node,
Sprite,
VideoPlayer,
UITransform,
Size,
Vec3,
tween,
view,
screen,
} from "cc";
import li_BaseView from "db://assets/Scripts/Main/Common/li_BaseView";
import ResManager from "db://assets/Scripts/Main/Manager/ResManager";
import { DetailImageItem } from "../../uiitems/DetailImageItem";
import {
NavigationManager,
PageTransitionType,
PanelType,
} from "../../manager/NavigationManager";
import { DetailImageItemPool } from "../../manager/DetailImageItemPool";
import LanguageUtils from "../../../Main/Common/LanguageUtils";
import Utils from "../../../Main/Common/Utils";
import { InnerMsgCode } from "../../../Main/Config/InnerMsgCode";
import { SimpleToggle } from "../components/SimpleToggle";
import { DataManager, DataId } from "../../data/DataManager";
import { GirlData } from "../../data/GirlData";
import { GirlService } from "db://assets/Scripts/chat18x/network/services/GirlService";
import proto from "db://assets/Scripts/proto/proto.pb.js";
import { SceneBgVideoLayer } from "../../../Sub/UI/SceneBgVideoLayer";
import { logger } from "db://assets/Scripts/Main/Common/Logger";
const { ccclass, property } = _decorator;
@ccclass("GirlDetailPanel")
export class GirlDetailPanel extends li_BaseView {
@property(UITransform)
videoArea: UITransform;
// 移除直接的 VideoPlayer 引用,改用 SceneBgVideoLayer 单例
// @property(VideoPlayer)
// avatarVideo: VideoPlayer;
@property(Label)
girlName: Label;
/** 获取 SceneBgVideoLayer 单例实例 */
private get videoLayer(): SceneBgVideoLayer | null {
return SceneBgVideoLayer.handle;
}
@property(SimpleToggle)
imgToggle: SimpleToggle = null;
@property(SimpleToggle)
videoToggle: SimpleToggle = null;
@property(Node)
starParent: Node;
@property(Label)
tags: Label;
@property(Label)
descAge: Label;
@property(Label)
desc: Label;
@property(Node)
chatBtn: Node;
@property(Node)
descContent: Node;
id = 0;
@property(DetailImageItem)
imgItemInst: DetailImageItem;
@property(Node)
imgsLayout: Node;
videoAreaPos: Vec3;
@property(Label)
girlPrice: Label;
@property(Node)
sendMsgText: Node;
private _nodeTab: any = {};
ImgVidSelect: Node;
ImgVidFakePos: Node;
// 跟随控制相关属性
private isFollowing: boolean = true; // 当前是否处于跟随状态
private lastFakePosPosition: Vec3 = new Vec3(); // 记录上次的位置
protected onEnable(): void {
this.refresh();
}
onLoadCT() {
super.onLoadCT();
Utils.parseNode(this.node, this._nodeTab);
this.ImgVidSelect = this._nodeTab.ImgVidSelect;
this.ImgVidFakePos = this._nodeTab.ImgVidFakePos;
// 初始化位置跟随
if (this.ImgVidFakePos) {
this.lastFakePosPosition = this.ImgVidFakePos.position.clone();
}
DetailImageItemPool.Instance.setTemplate(this.imgItemInst.node);
Utils.addInnerEL(InnerMsgCode.LanguageChange, this, () => {
this.girlName.string = LanguageUtils.getText(this.nameKey);
let desc = "";
const tags = LanguageUtils.getText(this.tagKey).split("|");
for (let i = 0; i < tags.length; i++) {
if (i != 0) desc += "\n";
desc += tags[i];
}
this.tags.string = desc;
this.desc.string = LanguageUtils.getText(this.descKey);
});
this.imgToggle.callback = this.bindImgToggle.bind(this);
this.videoToggle.callback = this.bindVideoToggle.bind(this);
}
setVideEnable(enable: boolean) {
if (!this.videoLayer) {
logger.error("GirlDetailPanel: SceneBgVideoLayer 不可用");
return;
}
if (enable) {
// 恢复播放
this.videoLayer.resume();
logger.log("GirlDetailPanel: 开启视频播放");
} else {
// 暂停播放
this.videoLayer.pause();
logger.log("GirlDetailPanel: 暂停视频播放");
}
}
/**
* 获取目标视频尺寸
* 从 videoArea 获取实际需要填充的区域尺寸
*/
private getTargetVideoSize(): Size | null {
// 从 videoArea 获取目标尺寸
if (this.videoArea) {
return new Size(this.videoArea.width, this.videoArea.height);
}
// 如果没有 videoArea,使用默认尺寸
logger.warn("GirlDetailPanel: videoArea 未设置,使用默认尺寸");
return new Size(600, 800);
}
nameKey: string;
tagKey: string;
descKey: string;
category: number;
async refresh() {
this.id = NavigationManager.Instance.getSelectedGirlId();
this.category = NavigationManager.Instance.getSelectedCategoryId();
this.imgItemInst.node.active = false;
this.videoAreaPos = new Vec3(540, 1384 - 60);
const pos = new Vec3(
this.descContent.position.x,
this.descContent.position.y,
this.descContent.position.z
);
this.descContent.position = new Vec3(pos.x, pos.y - 1000, pos.z);
//this.node.scale = Vec3.ZERO;
tween(this.descContent)
.to(0.3, { position: pos }, { easing: "quadOut" })
.start();
// 检查 SceneBgVideoLayer 是否可用
if (!this.videoLayer) {
logger.error("GirlDetailPanel: SceneBgVideoLayer.handle 未初始化");
} else {
logger.log("GirlDetailPanel: 成功获取 SceneBgVideoLayer 实例");
// 初始化视频显示位置
//this.initVideoPosition();
}
const girlData = DataManager.I.getDataById<GirlData>(DataId.Girl);
const isRelease = girlData.getIsRelease(this.category.toString(), this.id);
const isFree = girlData.isGirlFreeType(this.category.toString(), this.id);
//if (isRelease || isFree) {
// 先设置选中的角色ID
this.girlPrice.node.active = false;
this.sendMsgText.active = true;
// } else {
// this.girlPrice.node.active = true;
// this.sendMsgText.active = false;
// this.girlPrice.string = girlData
// .getGrilOriginalPrice(this.category.toString(), this.id)
// .toString();
// }
// 请求详细数据
const reqData = {
id: this.id,
};
let res = await GirlService.I.reqGetGirlDetail(reqData);
if (res && res.code === proto.cs.EnmRetCode.SUCCESS) {
// 保存数据
girlData.setGirlDetailData(this.category.toString(), res.data);
// 根据数据,刷新界面
this.nameKey = girlData.getGrilName(this.category.toString(), this.id);
this.girlName.string = LanguageUtils.getText(this.nameKey);
this.descKey = girlData.getGrilDesc(this.category.toString(), this.id);
this.desc.string = LanguageUtils.getText(this.descKey);
let desc = "";
this.tagKey = girlData.getGrilTagKey(this.category.toString(), this.id);
const tags = LanguageUtils.getText(this.tagKey).split("|");
for (let i = 0; i < tags.length; i++) {
if (i != 0) desc += "\n";
desc += tags[i];
}
this.tags.string = desc;
const star = girlData.getStar(this.category.toString(), this.id);
const stars = this.starParent.getComponentsInChildren(Sprite);
logger.log("好感度:" + star);
for (let i = 0; i < stars.length; i++) {
const element = stars[i];
if (star > i) {
ResManager.I.changeResourceSpriteFrame(element, "ui/common/heart");
} else {
ResManager.I.changeResourceSpriteFrame(
element,
"ui/common/heart_empty"
);
}
}
let age = girlData.getGrilAge(this.category.toString(), this.id);
this.descAge.string =
LanguageUtils.getText("girldetailpanel.age") + age.toString();
const firstPath = girlData.getFirstGrilVideoPath(
this.category.toString(),
this.id
);
// 使用 SceneBgVideoLayer 加载并播放视频
if (this.videoLayer && firstPath) {
logger.log(`GirlDetailPanel: 加载视频 ${firstPath}`);
// 获取 videoArea 的尺寸和位置
const targetSize = this.videoArea
? new Size(this.videoArea.width, this.videoArea.height)
: new Size(600, 800);
// 获取 videoArea 的中心位置,确保使用正确的世界坐标
// 使用新的统一 playVideo 方法
this.videoLayer.playVideo(firstPath, {
loop: true,
size: targetSize,
position: this.videoAreaPos,
});
}
let resIds = girlData.getAllDisplayGrilPhotoId(
this.category.toString(),
this.id
);
this.refreshImgs(
proto.cs.EnmResType.ERT_Image,
this.category,
this.id,
resIds
);
}
}
bindImgToggle() {
const girlData = DataManager.I.getDataById<GirlData>(DataId.Girl);
let resIds = girlData.getAllDisplayGrilPhotoId(
this.category.toString(),
this.id
);
this.refreshImgs(
proto.cs.EnmResType.ERT_Image,
this.category,
this.id,
resIds
);
}
bindVideoToggle() {
const girlData = DataManager.I.getDataById<GirlData>(DataId.Girl);
let resIds = girlData.getAllDisplayGrilVideoId(
this.category.toString(),
this.id
);
this.refreshImgs(
proto.cs.EnmResType.ERT_Video,
this.category,
this.id,
resIds
);
}
refreshImgs(
type: proto.cs.EnmResType,
category: number,
id: number,
resIds: number[]
) {
for (let i = this.imgsLayout.children.length - 1; i >= 0; i--) {
DetailImageItemPool.Instance.put(this.imgsLayout.children[i]);
}
let count = resIds.length;
for (let i = 0; i < count; i++) {
let newNode = DetailImageItemPool.Instance.get();
if (newNode) {
let item = newNode.getComponent(DetailImageItem);
let resId = resIds[i];
item.refresh(this, type, category, id, resId);
newNode.active = true;
this.imgsLayout.addChild(newNode);
}
}
}
OnClickChatBtn() {
const girlData = DataManager.I.getDataById<GirlData>(DataId.Girl);
const isRelease = girlData.getIsRelease(this.category.toString(), this.id);
const isFree = girlData.isGirlFreeType(this.category.toString(), this.id);
//if (isRelease || isFree) {
// 使用带过渡动画的导航方法
NavigationManager.Instance.switchToPanel(
PanelType.PAST_GIRL_LIST,
false,
(base) => {
const chatData = { girlId: this.id, base: base };
NavigationManager.Instance.openSubPanel("ChatPanel", base, chatData, {
openAnimation: PageTransitionType.SLIDE_LEFT,
closeAnimation: PageTransitionType.SLIDE_RIGHT,
});
}
);
// } else {
// //未解锁
// NavigationManager.Instance.openPopupPanel("GirlListPopupPanel", {
// base: this,
// category: this.category,
// id: this.id,
// });
// }
}
returnBtn() {
NavigationManager.Instance.switchToPanel(PanelType.THEME);
}
/**
* 面板关闭时的清理工作
*/
onClose() {
// 停止视频播放,释放资源
if (this.videoLayer) {
//this.videoLayer.stop();
//logger.log("GirlDetailPanel: 停止视频播放");
}
super.onClose();
}
/**
* 检测节点是否在屏幕可见区域内
* @param node 要检测的节点
* @returns 是否在屏幕内
*/
private isNodeInScreen(node: Node): boolean {
if (!node) return false;
const uiTransform = node.getComponent(UITransform);
if (!uiTransform) return false;
// 获取屏幕尺寸
const screenSize = screen.windowSize;
const designResolution = view.getDesignResolutionSize();
// 获取节点的世界坐标
const worldPos = uiTransform.convertToWorldSpaceAR(Vec3.ZERO);
// 获取节点的尺寸
const nodeSize = uiTransform.contentSize;
// 计算节点的边界
const left = worldPos.x - nodeSize.width * uiTransform.anchorX;
const right = worldPos.x + nodeSize.width * (1 - uiTransform.anchorX);
const bottom = worldPos.y - nodeSize.height * uiTransform.anchorY;
const top = worldPos.y + nodeSize.height * (1 - uiTransform.anchorY);
// 检测是否完全在屏幕内
const isInScreen =
left >= 0 &&
right <= designResolution.width &&
bottom >= 0 &&
top <= designResolution.height - 160;
return isInScreen;
}
/**
* 实时更新ImgVidSelect跟随ImgVidFakePos的逻辑
*/
lateUpdate(dt: number) {
if (!this.ImgVidSelect || !this.ImgVidFakePos) {
return;
}
// 检测ImgVidFakePos是否在屏幕内
const isInScreen = this.isNodeInScreen(this.ImgVidFakePos);
if (isInScreen) {
// 在屏幕内,开始或继续跟随
if (!this.isFollowing) {
this.isFollowing = true;
logger.log("ImgVidSelect开始跟随ImgVidFakePos");
}
// 更新ImgVidSelect的位置到ImgVidFakePos的位置
this.ImgVidSelect.worldPosition =
this.ImgVidFakePos.worldPosition.clone();
this.lastFakePosPosition = this.ImgVidFakePos.worldPosition.clone();
} else {
// 超出屏幕,停止跟随
if (this.isFollowing) {
this.isFollowing = false;
logger.log("ImgVidSelect停止跟随ImgVidFakePos(超出屏幕)");
// ImgVidSelect保持在最后的跟随位置
}
}
}
/**
* 面板销毁时的清理工作
*/
onDestroy() {
// 确保视频资源被释放
if (this.videoLayer) {
//this.videoLayer.stop();
//logger.log("GirlDetailPanel: 销毁时停止视频播放");
}
// 回收所有使用中的节点到对象池
for (let i = this.imgsLayout.children.length - 1; i >= 0; i--) {
DetailImageItemPool.Instance.put(this.imgsLayout.children[i]);
}
super.onDestroy();
}
}