提交pv
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: WnNK5CurUX7eLhb2j9pccpst4FlQvWWo1dIvwTsstWgBgYCFcFKyBGs=
|
||||
VideoClipImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
frameRange: 0
|
||||
startFrame: -1
|
||||
endFrame: -1
|
||||
colorSpace: 0
|
||||
deinterlace: 0
|
||||
encodeAlpha: 0
|
||||
flipVertical: 0
|
||||
flipHorizontal: 0
|
||||
importAudio: 1
|
||||
targetSettings: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -15,6 +15,10 @@ namespace GameFramework
|
||||
[SerializeField] Button settingsButton;
|
||||
[SerializeField] Button creditsButton;
|
||||
[SerializeField] Button quitButton;
|
||||
[SerializeField] Button storyReplayButton;
|
||||
|
||||
[Header("剧情 PV")]
|
||||
[SerializeField] StoryPVPlayer storyPVPlayer;
|
||||
|
||||
[Header("Settings Panel")]
|
||||
[SerializeField] GameObject settingsPanel;
|
||||
@@ -35,6 +39,15 @@ namespace GameFramework
|
||||
creditsButton = FindButton("CreditsButton");
|
||||
if (quitButton == null)
|
||||
quitButton = FindButton("QuitButton");
|
||||
if (storyReplayButton == null)
|
||||
storyReplayButton = FindButton("StoryReplayButton");
|
||||
if (storyPVPlayer == null)
|
||||
{
|
||||
// 使用 FindObjectsByType 包含 inactive 对象,避免找不到隐藏的 VideoOverlay
|
||||
var players = FindObjectsByType<StoryPVPlayer>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||
if (players.Length > 0)
|
||||
storyPVPlayer = players[0];
|
||||
}
|
||||
|
||||
if (settingsPanel == null)
|
||||
settingsPanel = GameObject.Find("SettingsPanel");
|
||||
@@ -55,6 +68,12 @@ namespace GameFramework
|
||||
creditsButton.onClick.AddListener(OnCreditsClick);
|
||||
if (quitButton != null)
|
||||
quitButton.onClick.AddListener(OnQuitClick);
|
||||
if (storyReplayButton != null)
|
||||
{
|
||||
// 避免 Inspector 中已绑定的事件导致重复触发
|
||||
storyReplayButton.onClick.RemoveAllListeners();
|
||||
storyReplayButton.onClick.AddListener(OnStoryReplayClick);
|
||||
}
|
||||
|
||||
if (settingsBackButton != null)
|
||||
settingsBackButton.onClick.AddListener(OnSettingsBackClick);
|
||||
@@ -119,5 +138,18 @@ namespace GameFramework
|
||||
Application.Quit();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OnStoryReplayClick()
|
||||
{
|
||||
if (storyPVPlayer != null)
|
||||
{
|
||||
Debug.Log($"[MainMenu] 播放 PV, StoryPVPlayer on: {storyPVPlayer.gameObject.name}");
|
||||
storyPVPlayer.Play();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[MainMenu] StoryPVPlayer 未配置!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.Video;
|
||||
using System.Collections;
|
||||
|
||||
namespace GameFramework
|
||||
{
|
||||
/// <summary>
|
||||
/// 剧情 PV 播放器 —— 点击 StoryReplayButton 后弹出全屏视频覆盖层播放 PV。
|
||||
/// 播放完毕自动关闭,也可点击"跳过"按钮立即关闭。
|
||||
///
|
||||
/// 搭建:
|
||||
/// 1. 在 MainMenu Canvas 下创建 VideoOverlay(初始 SetActive(false))
|
||||
/// 2. 子物体:BG (全屏黑色 Image)、VideoDisplay (RawImage)、SkipButton (Button)
|
||||
/// 3. 挂 VideoPlayer 组件,VideoClip 赋值为 pv.mp4
|
||||
/// 4. 挂本脚本,Inspector 中关联各引用
|
||||
/// </summary>
|
||||
public class StoryPVPlayer : MonoBehaviour
|
||||
{
|
||||
[Header("覆盖层")]
|
||||
[Tooltip("视频覆盖层根节点(初始隐藏)")]
|
||||
[SerializeField] private GameObject videoOverlay;
|
||||
|
||||
[Header("视频显示")]
|
||||
[Tooltip("全屏 RawImage 用于显示视频画面")]
|
||||
[SerializeField] private RawImage videoDisplay;
|
||||
|
||||
[Header("跳过按钮")]
|
||||
[Tooltip("右下角跳过按钮")]
|
||||
[SerializeField] private Button skipButton;
|
||||
|
||||
[Header("视频播放器")]
|
||||
[Tooltip("VideoPlayer 组件(挂在 VideoOverlay 或自身上)")]
|
||||
[SerializeField] private VideoPlayer videoPlayer;
|
||||
|
||||
private RenderTexture _renderTexture;
|
||||
private bool _isPlaying = false;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 懒加载:确保所有引用都已初始化。
|
||||
/// 使用 GetComponentsInChildren(true) 递归搜索(包括 inactive 子物体)。
|
||||
/// </summary>
|
||||
private void EnsureReferences()
|
||||
{
|
||||
// videoOverlay:默认就是自身(StoryPVPlayer 挂在 VideoOverlay 上)
|
||||
if (videoOverlay == null)
|
||||
{
|
||||
// 递归搜索子物体中名为 "VideoOverlay" 的 GameObject
|
||||
var transforms = GetComponentsInChildren<Transform>(true);
|
||||
foreach (var t in transforms)
|
||||
{
|
||||
if (t.name == "VideoOverlay")
|
||||
{
|
||||
videoOverlay = t.gameObject;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 如果没找到,说明本脚本就挂在 VideoOverlay 上
|
||||
if (videoOverlay == null)
|
||||
videoOverlay = gameObject;
|
||||
}
|
||||
|
||||
// 查找 RawImage(递归搜索 videoOverlay 的子物体,包括 inactive)
|
||||
if (videoDisplay == null && videoOverlay != null)
|
||||
{
|
||||
var displays = videoOverlay.GetComponentsInChildren<RawImage>(true);
|
||||
if (displays.Length > 0)
|
||||
videoDisplay = displays[0];
|
||||
}
|
||||
|
||||
// 查找 SkipButton(递归搜索,按名称匹配避免找到其他按钮)
|
||||
if (skipButton == null && videoOverlay != null)
|
||||
{
|
||||
var buttons = videoOverlay.GetComponentsInChildren<Button>(true);
|
||||
foreach (var b in buttons)
|
||||
{
|
||||
if (b.name == "SkipButton")
|
||||
{
|
||||
skipButton = b;
|
||||
skipButton.onClick.AddListener(Stop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查找 VideoPlayer:先查自身,再查 videoOverlay
|
||||
if (videoPlayer == null)
|
||||
videoPlayer = GetComponent<VideoPlayer>();
|
||||
if (videoPlayer == null && videoOverlay != null)
|
||||
videoPlayer = videoOverlay.GetComponent<VideoPlayer>();
|
||||
|
||||
// 订阅播放完成事件(只订阅一次)
|
||||
if (videoPlayer != null)
|
||||
videoPlayer.loopPointReached -= OnVideoFinished;
|
||||
if (videoPlayer != null)
|
||||
videoPlayer.loopPointReached += OnVideoFinished;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放 PV:先激活覆盖层 → 初始化引用 → 创建 RenderTexture → 等一帧 → 开始播放。
|
||||
/// </summary>
|
||||
public void Play()
|
||||
{
|
||||
Debug.Log($"[StoryPVPlayer] Play() called, _isPlaying={_isPlaying}, gameObject.activeSelf={gameObject.activeSelf}");
|
||||
|
||||
if (_isPlaying)
|
||||
{
|
||||
Debug.Log("[StoryPVPlayer] 已在播放中,忽略本次点击");
|
||||
return;
|
||||
}
|
||||
|
||||
// 关键:本脚本所在的 GameObject 可能初始为 inactive,必须先激活才能启动协程。
|
||||
// 这里先把 gameObject.active 设为 true,让 MonoBehaviour 能正常工作。
|
||||
gameObject.SetActive(true);
|
||||
|
||||
// 确保所有引用已初始化(可能在 Inspector 中未赋值,通过递归搜索查找)
|
||||
EnsureReferences();
|
||||
|
||||
// 确保 videoOverlay 被赋值
|
||||
if (videoOverlay == null)
|
||||
videoOverlay = gameObject;
|
||||
|
||||
// 激活覆盖层
|
||||
if (videoOverlay != null && videoOverlay != gameObject)
|
||||
videoOverlay.SetActive(true);
|
||||
|
||||
Debug.Log($"[StoryPVPlayer] 引用检查:videoOverlay={videoOverlay != null}, videoDisplay={videoDisplay != null}, videoPlayer={videoPlayer != null}");
|
||||
|
||||
if (videoPlayer == null || videoDisplay == null || videoOverlay == null)
|
||||
{
|
||||
Debug.LogError($"[StoryPVPlayer] 引用未配置!videoOverlay={videoOverlay != null}, videoDisplay={videoDisplay != null}, videoPlayer={videoPlayer != null}。请检查 Inspector。");
|
||||
if (videoOverlay != null) videoOverlay.SetActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查视频文件
|
||||
if (videoPlayer.clip == null)
|
||||
{
|
||||
Debug.LogWarning("[StoryPVPlayer] VideoClip 未设置,尝试从 Resources 加载...");
|
||||
videoPlayer.clip = Resources.Load<VideoClip>("pv");
|
||||
if (videoPlayer.clip == null)
|
||||
{
|
||||
Debug.LogError("[StoryPVPlayer] 无法加载 pv 视频文件!请将 pv.mp4 放入 Resources 文件夹,或在 Inspector 中赋值 VideoClip。");
|
||||
videoOverlay.SetActive(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建 RenderTexture(匹配视频分辨率,如果未知则用屏幕分辨率)
|
||||
int width = videoPlayer.clip != null ? (int)videoPlayer.clip.width : Screen.width;
|
||||
int height = videoPlayer.clip != null ? (int)videoPlayer.clip.height : Screen.height;
|
||||
|
||||
if (_renderTexture != null)
|
||||
{
|
||||
_renderTexture.Release();
|
||||
Destroy(_renderTexture);
|
||||
}
|
||||
|
||||
_renderTexture = new RenderTexture(width, height, 0);
|
||||
videoPlayer.targetTexture = _renderTexture;
|
||||
videoDisplay.texture = _renderTexture;
|
||||
|
||||
Debug.Log("[StoryPVPlayer] 启动协程,延迟一帧播放");
|
||||
// 启动协程,等待一帧后再播放(Unity VideoPlayer 在 SetActive 同帧内调用 Play 无效)
|
||||
StartCoroutine(PlayVideoCoroutine());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 延迟一帧后播放视频(解决 VideoPlayer 在激活同帧内调用 Play 无效的问题)。
|
||||
/// </summary>
|
||||
private IEnumerator PlayVideoCoroutine()
|
||||
{
|
||||
_isPlaying = true;
|
||||
Debug.Log($"[StoryPVPlayer] PlayVideoCoroutine started, activeSelf={gameObject.activeSelf}");
|
||||
|
||||
// 等待一帧,让 VideoPlayer 完成初始化
|
||||
yield return null;
|
||||
|
||||
Debug.Log($"[StoryPVPlayer] 协程等待一帧后:videoPlayer={videoPlayer != null}, isActive={gameObject.activeSelf}");
|
||||
|
||||
if (videoPlayer == null)
|
||||
{
|
||||
Debug.LogError("[StoryPVPlayer] 协程中 videoPlayer 为 null,无法播放");
|
||||
_isPlaying = false;
|
||||
yield break;
|
||||
}
|
||||
|
||||
// 播放
|
||||
videoPlayer.Play();
|
||||
Debug.Log($"[StoryPVPlayer] 已调用 videoPlayer.Play(), isPlaying={videoPlayer.isPlaying}");
|
||||
|
||||
// 暂停背景音乐
|
||||
if (AudioManager.Instance != null)
|
||||
AudioManager.Instance.PauseBGM();
|
||||
|
||||
Debug.Log("[StoryPVPlayer] 开始播放 PV");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止播放:停止 VideoPlayer → 释放 RenderTexture → 隐藏覆盖层。
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
_isPlaying = false;
|
||||
|
||||
if (videoPlayer != null)
|
||||
videoPlayer.Stop();
|
||||
|
||||
if (_renderTexture != null)
|
||||
{
|
||||
_renderTexture.Release();
|
||||
Destroy(_renderTexture);
|
||||
_renderTexture = null;
|
||||
}
|
||||
|
||||
if (videoDisplay != null)
|
||||
videoDisplay.texture = null;
|
||||
|
||||
if (videoOverlay != null)
|
||||
videoOverlay.SetActive(false);
|
||||
|
||||
// 恢复背景音乐
|
||||
if (AudioManager.Instance != null)
|
||||
AudioManager.Instance.UnpauseBGM();
|
||||
|
||||
Debug.Log("[StoryPVPlayer] PV 播放已停止");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 视频播放完毕回调(自动关闭覆盖层)。
|
||||
/// </summary>
|
||||
private void OnVideoFinished(VideoPlayer source)
|
||||
{
|
||||
Debug.Log("[StoryPVPlayer] PV 播放完毕");
|
||||
Stop();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (videoPlayer != null)
|
||||
videoPlayer.loopPointReached -= OnVideoFinished;
|
||||
|
||||
if (_renderTexture != null)
|
||||
{
|
||||
_renderTexture.Release();
|
||||
Destroy(_renderTexture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: W38e4CukU3yODrWFvYfwfZp4T6XgBfapY8kwwHspsVk3JNoS/3ZuzTY=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user