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

422 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}