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"; const { ccclass, property } = _decorator; //场景背景层 - 视频模式 @ccclass("SceneBgVideoLayer") export class SceneBgVideoLayer extends li_BaseView { private _nodeTab: any = {}; private _data; static handle: SceneBgVideoLayer | null = null; private videoPlayer: VideoPlayer = null; private isPlaying: boolean = false; private currentVideoData: any = null; //----重写父类接口--------------------------------------- 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; } /**初始化视频事件监听 */ 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 方法 ================== /** * 播放视频 - 统一的视频播放接口 * @param path 视频路径 * @param options 可选配置项 * @param options.loop 是否循环播放,默认true * @param options.size 视频尺寸 {width, height} * @param options.position 视频位置 {x, y, z} * @param options.scale 视频缩放值 * @param options.autoFill 是否自动填充到指定尺寸 */ playVideo( path: string, options?: { loop?: boolean; size?: { width: number; height: number } | Size; position?: { x: number; y: number; z?: number } | Vec3; scale?: number | Vec3; } ) { if (!this.videoPlayer) { console.error("SceneBgVideoLayer: VideoPlayer未初始化"); return; } // 默认参数 const config = { loop: options?.loop ?? true, size: options?.size, position: options?.position, }; // 先停止当前视频 if (this.isPlaying) { this.pause(); } // 加载本地视频资源 ResManager.I.changeBundleVideo( this.videoPlayer, path, "Girls", (res: VideoClip) => { this.videoPlayer.loop = config.loop; // 设置锚点为中心,确保位置对齐 const uiTransform = this.videoPlayer.node.getComponent(UITransform); if (uiTransform) { uiTransform.setAnchorPoint(0.5, 0.5); console.log("SceneBgVideoLayer: 设置视频锚点为 (0.5, 0.5)"); } // 应用位置设置(先尝试本地坐标,再尝试世界坐标) if (config.position) { const pos = config.position; let targetWorldPos: Vec3; if (pos instanceof Vec3) { targetWorldPos = pos; } else { targetWorldPos = new Vec3(pos.x, pos.y, pos.z || 0); } // 获取 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)})`); } // 如果传入了目标尺寸,计算并应用缩放 if (config.size && uiTransform) { // 获取视频的原始尺寸 const videoWidth = uiTransform.contentSize.width; const videoHeight = uiTransform.contentSize.height; if (videoWidth > 0 && videoHeight > 0) { const targetWidth = config.size instanceof Size ? config.size.width : config.size.width; const targetHeight = config.size instanceof Size ? config.size.height : config.size.height; // 计算缩放比例(填满整个区域) const scaleX = targetWidth / videoWidth; const scaleY = targetHeight / videoHeight; const scale = Math.max(scaleX, scaleY); this.videoPlayer.node.setScale(new Vec3(scale, scale, 1)); console.log( `SceneBgVideoLayer: 视频原始尺寸: ${videoWidth}x${videoHeight}, 目标尺寸: ${targetWidth}x${targetHeight}` ); console.log( `SceneBgVideoLayer: 缩放比例 - X: ${scaleX.toFixed(3)}, Y: ${scaleY.toFixed(3)}, 最终: ${scale.toFixed(3)}` ); } else { // 如果视频尺寸还未准备好,延迟重试 this.scheduleOnce(() => { if (config.size && uiTransform) { const videoWidth = uiTransform.contentSize.width; const videoHeight = uiTransform.contentSize.height; if (videoWidth > 0 && videoHeight > 0) { const targetWidth = config.size instanceof Size ? config.size.width : config.size.width; const targetHeight = config.size instanceof Size ? config.size.height : config.size.height; const scaleX = targetWidth / videoWidth; const scaleY = targetHeight / videoHeight; const scale = Math.max(scaleX, scaleY); this.videoPlayer.node.setScale(new Vec3(scale, scale, 1)); console.log( `SceneBgVideoLayer: (延迟) 视频原始尺寸: ${videoWidth}x${videoHeight}, 目标尺寸: ${targetWidth}x${targetHeight}` ); console.log( `SceneBgVideoLayer: (延迟) 缩放比例 - X: ${scaleX.toFixed(3)}, Y: ${scaleY.toFixed(3)}, 最终: ${scale.toFixed(3)}` ); } } }, 0.1); } } else { // 没有指定尺寸,保持原始缩放 this.videoPlayer.node.setScale(Vec3.ONE); console.log("SceneBgVideoLayer: 未指定尺寸,保持默认缩放 (1, 1, 1)"); } // 如果显式传入了 scale 参数,覆盖自动计算的缩放 this.videoPlayer.play(); this.isPlaying = true; console.log(`SceneBgVideoLayer: 播放视频 ${path}, 配置:`, config); // 最终验证:确保视频节点的状态正确 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); } ); } /** * 暂停视频 */ 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) { this.videoPlayer.node.setWorldPosition(position); } console.log(`SceneBgVideoLayer: 自适应填充区域,缩放: ${scale}`); return true; } return false; }; // 立即尝试一次 if (!tryAdjust()) { // 如果失败,延迟尝试 this.scheduleOnce(() => { if (!tryAdjust()) { this.scheduleOnce(() => { tryAdjust(); }, 0.5); } }, 0.1); } } //================== 事件回调 ================== private onVideoReadyToPlay = () => { console.log("SceneBgVideoLayer: 视频准备完成"); this.onVideoReady(); }; 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; } }; /** * 视频准备完成回调(可重写) */ protected onVideoReady() { // 由子类重写或外部监听 } /** * 视频播放完成回调(可重写) */ protected onVideoEnd() { // 由子类重写或外部监听 } /** * 视频播放错误回调(可重写) */ protected onVideoError() { // 由子类重写或外部监听 } //================== 其他方法 ================== /** * 获取当前播放状态 */ 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; } }