Files
18xchat/assets/Scripts/Sub/UI/SceneBgVideoLayer.ts
T

422 lines
14 KiB
TypeScript
Raw Normal View History

2025-09-11 00:03:22 +08:00
import {
_decorator,
isValid,
Node,
VideoClip,
UITransform,
Vec3,
VideoPlayer,
Size,
} from "cc";
import Utils from "../../Main/Common/Utils";
import li_BaseView from "../../Main/Common/li_BaseView";
import ResManager from "../../Main/Manager/ResManager";
2025-07-17 17:18:21 +08:00
const { ccclass, property } = _decorator;
//场景背景层 - 视频模式
2025-09-11 00:03:22 +08:00
@ccclass("SceneBgVideoLayer")
2025-07-17 17:18:21 +08:00
export class SceneBgVideoLayer extends li_BaseView {
2025-09-11 00:03:22 +08:00
private _nodeTab: any = {};
private _data;
2025-07-17 17:18:21 +08:00
2025-09-11 00:03:22 +08:00
static handle: SceneBgVideoLayer | null = null;
2025-07-17 17:18:21 +08:00
2025-09-11 00:03:22 +08:00
private videoPlayer: VideoPlayer = null;
private isPlaying: boolean = false;
private currentVideoData: any = null;
2025-07-17 17:18:21 +08:00
2025-09-11 00:03:22 +08:00
//----重写父类接口---------------------------------------
onLoadCT() {
Utils.parseNode(this.node, this._nodeTab);
this.videoPlayer = this._nodeTab.videoPlayer.getComponent(VideoPlayer);
if (!this.videoPlayer) {
console.error("SceneBgVideoLayer: video节点未找到或没有VideoPlayer组件");
}
this.initVideoEvents();
SceneBgVideoLayer.handle = this;
}
openUIDataCT(data: any): void {
this._data = data;
}
2025-08-01 18:28:36 +08:00
2025-09-11 00:03:22 +08:00
/**初始化视频事件监听 */
private initVideoEvents() {
if (!this.videoPlayer) return;
// 移除之前的事件监听器,避免重复绑定
this.videoPlayer.node.off(
VideoPlayer.EventType.READY_TO_PLAY,
this.onVideoReadyToPlay,
this
);
//this.videoPlayer.node.off(VideoPlayer.EventType.VIDEO_PLAYER_EVENT, this.onVideoEvent, this);
// 添加新的事件监听
this.videoPlayer.node.on(
VideoPlayer.EventType.READY_TO_PLAY,
this.onVideoReadyToPlay,
this
);
// this.videoPlayer.node.on(VideoPlayer.EventType.VIDEO_PLAYER_EVENT, this.onVideoEvent, this);
}
//================== 公共 API 方法 ==================
/**
2025-09-11 10:54:06 +08:00
* 播放视频 - 统一的视频播放接口
2025-09-11 00:03:22 +08:00
* @param path 视频路径
2025-09-11 10:54:06 +08:00
* @param options 可选配置项
* @param options.loop 是否循环播放,默认true
* @param options.size 视频尺寸 {width, height}
* @param options.position 视频位置 {x, y, z}
* @param options.scale 视频缩放值
* @param options.autoFill 是否自动填充到指定尺寸
2025-09-11 00:03:22 +08:00
*/
2025-09-11 10:54:06 +08:00
playVideo(
path: string,
options?: {
loop?: boolean;
size?: { width: number; height: number } | Size;
position?: { x: number; y: number; z?: number } | Vec3;
scale?: number | Vec3;
}
) {
2025-09-11 00:03:22 +08:00
if (!this.videoPlayer) {
console.error("SceneBgVideoLayer: VideoPlayer未初始化");
return;
2025-07-17 17:18:21 +08:00
}
2025-09-11 10:54:06 +08:00
// 默认参数
const config = {
loop: options?.loop ?? true,
size: options?.size,
position: options?.position,
};
// 先停止当前视频
if (this.isPlaying) {
2025-09-11 17:07:29 +08:00
this.pause();
2025-09-11 10:54:06 +08:00
}
2025-09-11 00:03:22 +08:00
// 加载本地视频资源
ResManager.I.changeBundleVideo(
this.videoPlayer,
path,
"Girls",
(res: VideoClip) => {
2025-09-11 10:54:06 +08:00
this.videoPlayer.loop = config.loop;
2025-09-11 17:07:29 +08:00
2025-09-11 10:54:06 +08:00
// 设置锚点为中心,确保位置对齐
const uiTransform = this.videoPlayer.node.getComponent(UITransform);
if (uiTransform) {
uiTransform.setAnchorPoint(0.5, 0.5);
2025-09-11 17:07:29 +08:00
console.log("SceneBgVideoLayer: 设置视频锚点为 (0.5, 0.5)");
2025-09-11 10:54:06 +08:00
}
2025-09-11 17:07:29 +08:00
// 应用位置设置(先尝试本地坐标,再尝试世界坐标)
2025-09-11 10:54:06 +08:00
if (config.position) {
const pos = config.position;
2025-09-11 17:07:29 +08:00
let targetWorldPos: Vec3;
2025-09-11 10:54:06 +08:00
if (pos instanceof Vec3) {
2025-09-11 17:07:29 +08:00
targetWorldPos = pos;
2025-09-11 10:54:06 +08:00
} else {
2025-09-11 17:07:29 +08:00
targetWorldPos = new Vec3(pos.x, pos.y, pos.z || 0);
2025-09-11 10:54:06 +08:00
}
2025-09-11 17:07:29 +08:00
// 获取 videoPlayer 的父节点信息
const parent = this.videoPlayer.node.parent;
if (parent) {
const parentWorldPos = parent.getWorldPosition();
const parentWorldScale = parent.getWorldScale();
console.log(`SceneBgVideoLayer: VideoPlayer 父节点世界位置: (${parentWorldPos.x}, ${parentWorldPos.y}, ${parentWorldPos.z})`);
console.log(`SceneBgVideoLayer: VideoPlayer 父节点世界缩放: (${parentWorldScale.x}, ${parentWorldScale.y}, ${parentWorldScale.z})`);
// 尝试方法1:计算相对于父节点的本地位置
const localPos = new Vec3();
localPos.x = (targetWorldPos.x - parentWorldPos.x) / parentWorldScale.x;
localPos.y = (targetWorldPos.y - parentWorldPos.y) / parentWorldScale.y;
localPos.z = (targetWorldPos.z - parentWorldPos.z) / parentWorldScale.z;
this.videoPlayer.node.setPosition(localPos);
console.log(`SceneBgVideoLayer: 设置本地位置: (${localPos.x.toFixed(2)}, ${localPos.y.toFixed(2)}, ${localPos.z.toFixed(2)})`);
} else {
// 如果没有父节点,直接使用世界坐标
this.videoPlayer.node.setWorldPosition(targetWorldPos);
console.log(`SceneBgVideoLayer: 无父节点,使用世界坐标: (${targetWorldPos.x}, ${targetWorldPos.y}, ${targetWorldPos.z})`);
}
// 验证位置是否设置正确
const actualWorldPos = this.videoPlayer.node.getWorldPosition();
const actualLocalPos = this.videoPlayer.node.getPosition();
console.log(`SceneBgVideoLayer: 目标世界位置: (${targetWorldPos.x}, ${targetWorldPos.y}, ${targetWorldPos.z})`);
console.log(`SceneBgVideoLayer: 实际世界位置: (${actualWorldPos.x}, ${actualWorldPos.y}, ${actualWorldPos.z})`);
console.log(`SceneBgVideoLayer: 实际本地位置: (${actualLocalPos.x.toFixed(2)}, ${actualLocalPos.y.toFixed(2)}, ${actualLocalPos.z.toFixed(2)})`);
2025-09-11 10:54:06 +08:00
}
// 如果传入了目标尺寸,计算并应用缩放
if (config.size && uiTransform) {
// 获取视频的原始尺寸
const videoWidth = uiTransform.contentSize.width;
const videoHeight = uiTransform.contentSize.height;
2025-09-11 17:07:29 +08:00
2025-09-11 10:54:06 +08:00
if (videoWidth > 0 && videoHeight > 0) {
2025-09-11 17:07:29 +08:00
const targetWidth =
config.size instanceof Size
? config.size.width
: config.size.width;
const targetHeight =
config.size instanceof Size
? config.size.height
: config.size.height;
2025-09-11 10:54:06 +08:00
// 计算缩放比例(填满整个区域)
const scaleX = targetWidth / videoWidth;
const scaleY = targetHeight / videoHeight;
const scale = Math.max(scaleX, scaleY);
2025-09-11 17:07:29 +08:00
2025-09-11 10:54:06 +08:00
this.videoPlayer.node.setScale(new Vec3(scale, scale, 1));
2025-09-11 17:07:29 +08:00
console.log(
`SceneBgVideoLayer: 视频原始尺寸: ${videoWidth}x${videoHeight}, 目标尺寸: ${targetWidth}x${targetHeight}`
);
console.log(
`SceneBgVideoLayer: 缩放比例 - X: ${scaleX.toFixed(3)}, Y: ${scaleY.toFixed(3)}, 最终: ${scale.toFixed(3)}`
);
2025-09-11 10:54:06 +08:00
} else {
// 如果视频尺寸还未准备好,延迟重试
this.scheduleOnce(() => {
if (config.size && uiTransform) {
const videoWidth = uiTransform.contentSize.width;
const videoHeight = uiTransform.contentSize.height;
2025-09-11 17:07:29 +08:00
2025-09-11 10:54:06 +08:00
if (videoWidth > 0 && videoHeight > 0) {
2025-09-11 17:07:29 +08:00
const targetWidth =
config.size instanceof Size
? config.size.width
: config.size.width;
const targetHeight =
config.size instanceof Size
? config.size.height
: config.size.height;
2025-09-11 10:54:06 +08:00
const scaleX = targetWidth / videoWidth;
const scaleY = targetHeight / videoHeight;
const scale = Math.max(scaleX, scaleY);
2025-09-11 17:07:29 +08:00
2025-09-11 10:54:06 +08:00
this.videoPlayer.node.setScale(new Vec3(scale, scale, 1));
2025-09-11 17:07:29 +08:00
console.log(
`SceneBgVideoLayer: (延迟) 视频原始尺寸: ${videoWidth}x${videoHeight}, 目标尺寸: ${targetWidth}x${targetHeight}`
);
console.log(
`SceneBgVideoLayer: (延迟) 缩放比例 - X: ${scaleX.toFixed(3)}, Y: ${scaleY.toFixed(3)}, 最终: ${scale.toFixed(3)}`
);
2025-09-11 10:54:06 +08:00
}
}
}, 0.1);
}
2025-09-11 17:07:29 +08:00
} else {
// 没有指定尺寸,保持原始缩放
this.videoPlayer.node.setScale(Vec3.ONE);
console.log("SceneBgVideoLayer: 未指定尺寸,保持默认缩放 (1, 1, 1)");
2025-09-11 10:54:06 +08:00
}
// 如果显式传入了 scale 参数,覆盖自动计算的缩放
2025-09-11 00:03:22 +08:00
this.videoPlayer.play();
this.isPlaying = true;
2025-09-11 10:54:06 +08:00
console.log(`SceneBgVideoLayer: 播放视频 ${path}, 配置:`, config);
2025-09-11 17:07:29 +08:00
// 最终验证:确保视频节点的状态正确
setTimeout(() => {
const finalWorldPos = this.videoPlayer.node.getWorldPosition();
const finalLocalPos = this.videoPlayer.node.getPosition();
const finalScale = this.videoPlayer.node.getScale();
const finalTransform = this.videoPlayer.node.getComponent(UITransform);
if (finalTransform) {
console.log(`SceneBgVideoLayer: === 最终状态验证 ===`);
console.log(`SceneBgVideoLayer: 最终世界位置: (${finalWorldPos.x.toFixed(2)}, ${finalWorldPos.y.toFixed(2)}, ${finalWorldPos.z.toFixed(2)})`);
console.log(`SceneBgVideoLayer: 最终本地位置: (${finalLocalPos.x.toFixed(2)}, ${finalLocalPos.y.toFixed(2)}, ${finalLocalPos.z.toFixed(2)})`);
console.log(`SceneBgVideoLayer: 最终缩放: (${finalScale.x.toFixed(3)}, ${finalScale.y.toFixed(3)}, ${finalScale.z.toFixed(3)})`);
console.log(`SceneBgVideoLayer: 最终锚点: (${finalTransform.anchorX.toFixed(2)}, ${finalTransform.anchorY.toFixed(2)})`);
console.log(`SceneBgVideoLayer: 最终内容尺寸: ${finalTransform.contentSize.width.toFixed(0)}x${finalTransform.contentSize.height.toFixed(0)}`);
// 计算实际渲染尺寸(内容尺寸 * 缩放)
const renderWidth = finalTransform.contentSize.width * finalScale.x;
const renderHeight = finalTransform.contentSize.height * finalScale.y;
console.log(`SceneBgVideoLayer: 计算的渲染尺寸: ${renderWidth.toFixed(0)}x${renderHeight.toFixed(0)}`);
console.log(`SceneBgVideoLayer: === 验证结束 ===`);
}
}, 200);
2025-09-11 00:03:22 +08:00
}
);
}
/**
* 暂停视频
*/
pause() {
if (this.videoPlayer && this.isPlaying) {
this.videoPlayer.pause();
console.log("SceneBgVideoLayer: 暂停视频");
}
}
/**
* 继续播放视频
*/
resume() {
if (this.videoPlayer && !this.videoPlayer.isPlaying) {
this.videoPlayer.play();
console.log("SceneBgVideoLayer: 继续播放视频");
}
}
/**
* 停止视频
*/
stop() {
if (this.videoPlayer) {
this.videoPlayer.stop();
this.isPlaying = false;
console.log("SceneBgVideoLayer: 停止视频");
}
}
/**
* 设置视频缩放
* @param scale 缩放值(数字或Vec3
*/
setVideoScale(scale: number | Vec3) {
if (this.videoPlayer) {
if (typeof scale === "number") {
this.videoPlayer.node.setScale(new Vec3(scale, scale, 1));
console.log(`SceneBgVideoLayer: 设置视频缩放 ${scale}`);
} else {
this.videoPlayer.node.setScale(scale);
console.log(
`SceneBgVideoLayer: 设置视频缩放 (${scale.x}, ${scale.y}, ${scale.z})`
);
}
}
}
/**
* 调整视频以填满指定区域
* @param targetSize 目标尺寸
* @param position 可选的位置
*/
adjustToFillArea(targetSize: Size, position?: Vec3) {
if (!this.videoPlayer) return;
const tryAdjust = () => {
const uiTransform = this.videoPlayer.node.getComponent(UITransform);
if (!uiTransform) return false;
const videoSize = uiTransform.contentSize;
if (videoSize.width > 0 && videoSize.height > 0) {
// 计算宽度和高度的缩放比例
const scaleX = targetSize.width / videoSize.width;
const scaleY = targetSize.height / videoSize.height;
// 选择较大的缩放比例以填满整个区域
const scale = Math.max(scaleX, scaleY);
this.videoPlayer.node.setScale(new Vec3(scale, scale, 1));
if (position) {
2025-09-11 10:54:06 +08:00
this.videoPlayer.node.setWorldPosition(position);
2025-07-17 17:18:21 +08:00
}
2025-09-11 00:03:22 +08:00
console.log(`SceneBgVideoLayer: 自适应填充区域,缩放: ${scale}`);
return true;
}
return false;
};
2025-07-17 17:18:21 +08:00
2025-09-11 00:03:22 +08:00
// 立即尝试一次
if (!tryAdjust()) {
// 如果失败,延迟尝试
this.scheduleOnce(() => {
2025-08-13 16:17:44 +08:00
if (!tryAdjust()) {
2025-09-11 00:03:22 +08:00
this.scheduleOnce(() => {
tryAdjust();
}, 0.5);
2025-08-13 16:17:44 +08:00
}
2025-09-11 00:03:22 +08:00
}, 0.1);
2025-08-13 16:17:44 +08:00
}
2025-09-11 00:03:22 +08:00
}
2025-08-13 16:17:44 +08:00
2025-09-11 00:03:22 +08:00
//================== 事件回调 ==================
2025-07-17 17:18:21 +08:00
2025-09-11 00:03:22 +08:00
private onVideoReadyToPlay = () => {
console.log("SceneBgVideoLayer: 视频准备完成");
this.onVideoReady();
};
2025-07-17 17:18:21 +08:00
2025-09-11 00:03:22 +08:00
private onVideoEvent = (event: any, eventType: string) => {
switch (eventType) {
case "completed":
console.log("SceneBgVideoLayer: 视频播放完成");
this.isPlaying = false;
this.onVideoEnd();
break;
case "error":
console.error("SceneBgVideoLayer: 视频播放错误");
this.isPlaying = false;
this.onVideoError();
break;
}
};
2025-07-17 17:18:21 +08:00
2025-09-11 00:03:22 +08:00
/**
* 视频准备完成回调(可重写)
*/
protected onVideoReady() {
// 由子类重写或外部监听
}
2025-07-17 17:18:21 +08:00
2025-09-11 00:03:22 +08:00
/**
* 视频播放完成回调(可重写)
*/
protected onVideoEnd() {
// 由子类重写或外部监听
}
2025-07-17 17:18:21 +08:00
2025-09-11 00:03:22 +08:00
/**
* 视频播放错误回调(可重写)
*/
protected onVideoError() {
// 由子类重写或外部监听
}
2025-07-17 17:18:21 +08:00
2025-09-11 00:03:22 +08:00
//================== 其他方法 ==================
/**
* 获取当前播放状态
*/
getIsPlaying(): boolean {
return this.isPlaying;
}
/**
* 获取VideoPlayer组件引用
*/
getVideoPlayer(): VideoPlayer | null {
return this.videoPlayer;
}
/**
* 销毁时清理资源
*/
onDestroy() {
if (this.videoPlayer) {
this.videoPlayer.node.off(
VideoPlayer.EventType.READY_TO_PLAY,
this.onVideoReadyToPlay,
this
);
//this.videoPlayer.node.off(VideoPlayer.EventType.VIDEO_PLAYER_EVENT, this.onVideoEvent, this);
}
SceneBgVideoLayer.handle = null;
}
2025-07-17 17:18:21 +08:00
}