Unityを使って、小さな状態から拡大し、動画を再生して、再生終了後に小さくなって消えるような動画再生機能を実装する方法について解説します。

紹介する実装方法は、Unityのチュートリアルレベルの知識があることが前提にしています。すべての設定項目等を細かく伝えているチュートリアル的な記事ではない点をご留意下さい。
前提条件
このチュートリアルでは、Unityプロジェクトに以下のライブラリや設定が導入されていることを前提とします。
- Unity 2020以降
DOTweenライブラリの導入VideoPlayerコンポーネントの設定RenderTextureの設定
DOTweenの導入
DOTweenの導入方法について以下の記事で紹介しています。

Hierarchyの設定
以下のようなオブジェクト構成で作成します。

Raw Imageを生成するキャンバスの設定
HierarchyでUI→Canvasで動画を映すRaw Imageを生成するためのキャンバスを用意します。


VideoControllerとvideoPlayerの設定
空のオブジェクトを生成してVideoControllerとします。ここにはあとで、作成するスクリプトをコンポーネントに追加します。
また、このコントローラーの子に空のオブジェクトを生成して名前をvideoPlayerHalfとします。このvideoPlayerHalfにコンポーネントの追加からVideo Playerを追加します。

MainManager.csの概要と役割
MainManager.csは、Unityプロジェクトにおける中心的なマネージャークラスであり、他のコンポーネントやスクリプトとのやり取りを管理する役割を果たします。
このスクリプトは、シングルトンパターンを使用してインスタンスを一元管理し、プロジェクト全体のメイン制御を担います。
using UnityEngine;
public class MainManager : MonoBehaviour
{
#region public 変数
/// <summary>
/// メインマネージャーのインスタンス
/// </summary>
public static MainManager instance;
/// <summary>
/// 動画コントローラー
/// </summary>
public GameObject videoController;
#endregion
#region private 変数
private VideoController videoC;
#endregion
#region unityのイベント処理
void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Destroy(gameObject);
}
}
private void Start()
{
//各コンポーネントの取得
videoC = videoController.GetComponent<VideoController>();
}
#endregion
#region デバッグ処理
public void debug_video_btn()
{
//ビデオ生成
StartCoroutine(videoC.PlayVideo(VideoController.VideoClipName.haiku));
}
#endregion
}シングルトンパターンの実装
public static MainManager instance;
MainManagerは、シングルトンパターンを採用しており、プロジェクト内で唯一のインスタンスとして機能します。Awake()メソッドで、既にインスタンスが存在する場合は新しいインスタンスを破棄し、存在しない場合は現在のオブジェクトをインスタンスとして保持します。
void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Destroy(gameObject);
}
}
この実装により、複数のインスタンスが生成されるのを防ぎ、プロジェクト全体で一貫した状態管理を実現しています。
動画コントローラーとの連携
MainManagerは、VideoControllerを通じて動画再生機能を管理しています。
Start()メソッドで、VideoControllerコンポーネントを取得し、videoC変数に格納します。
private void Start()
{
videoC = videoController.GetComponent<VideoController>();
}このようにして、MainManagerからVideoControllerの機能を直接制御できるようにしています。
デバッグ用のメソッド
MainManagerには、デバッグ用のメソッドが含まれており、動画再生機能を簡単にテストできるようになっています。debug_video_btn()メソッドでは、指定された動画クリップを再生するコルーチンを呼び出します。
public void debug_video_btn()
{
StartCoroutine(videoC.PlayVideo(VideoController.VideoClipName.haiku));
}
このメソッドを使用することで、開発者はUIボタンやその他のインタラクションを介して動画再生機能をテストできます。
MainManager.csは、プロジェクト全体の中核をなすスクリプトであり、他のコンポーネントやスクリプトをまとめて管理する重要な役割を果たしています。このスクリプトを通じて、プロジェクト全体の整合性と一貫性を保ちつつ、柔軟な制御を行うことが可能です。
VideoController.csの概要と役割
VideoController.csは、Unityでの動画再生機能を詳細に制御するためのクラスです。
このスクリプトは、特定の条件下で動画を再生し、その再生過程やアニメーション効果を制御する役割を担っています。
動画の拡大・縮小アニメーションや、複数の動画クリップの管理と再生を行うための重要な機能が詰まっています。
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;
public class VideoController : MonoBehaviour
{
/// <summary>
/// ビデオクリップ種類
/// </summary>
public enum VideoClipName
{
haiku,
mikopoppo,
slot_atari01,
slot_atari02,
slot_atari03,
slot_atari04,
slot_atari05,
sanpai,
hikouki,
taiyaki,
bonus_keizoku,
bonus_mae,
bonus_ato,
semifinal,
soredemo,
daidaidai,
daidaidai_pre,
daidaidai_after,
ending,
mikosama,
none //noneはビデオ再生なし 一番最後に記述する
}
public Canvas overlayCanvas; // 元々の再生機能用Canvas
public VideoPlayer videoPlayerHalf;
public List<VideoClip> videoClips;
public GameObject topLeftImagePrefab; // 左上画像のプレファブ (UI Image)
public GameObject bottomRightImagePrefab; // 右下画像のプレファブ (UI Image)
public GameObject videoFramePrefab; // ビデオフレームのプレファブ (UI RawImage)
public GameObject backgroundPrefab; // 背景画像のプレファブ (UI Image)
public RenderTexture renderTexture; // ビデオ表示用のRenderTexture
public Vector2 startScale; // 初期スケール
public Vector2 targetScale; // ターゲットスケール
public float expandDuration = 1f; // フレーム拡張の時間
public float initialWaitDuration = 0.01f; // 拡大前の待機時間
public Vector2 topLeftOffset; // 左上画像のオフセット
public Vector2 bottomRightOffset ; // 右下画像のオフセット
public float fadeDuration = 0.1f; // フェードアウトの時間
private GameObject topLeftImageInstance; // 左上画像のインスタンス
private GameObject bottomRightImageInstance; // 右下画像のインスタンス
private GameObject videoFrameInstance; // ビデオフレームのインスタンス
private GameObject backgroundInstance; // 背景画像のインスタンス
private RawImage videoFrameRawImage; // ビデオフレームのRawImage
private RectTransform videoFrameRectTransform; // ビデオフレームのRectTransform
private RectTransform backgroundRectTransform; // 背景画像のRectTransform
private float backgroundScaleFactor = 1.1f; // 背景画像のスケールファクター
// VideoClipを列挙型で管理するための辞書
private Dictionary<VideoClipName, VideoClip> videoClipDict;
/// <summary>
/// 起動時の初期化
/// </summary>
void Start()
{
// VideoClip辞書の初期化
videoClipDict = new Dictionary<VideoClipName, VideoClip>
{
{ VideoClipName.haiku, videoClips[0] },
{ VideoClipName.mikopoppo, videoClips[1] },
{ VideoClipName.slot_atari01, videoClips[2] },
{ VideoClipName.slot_atari02, videoClips[3] },
{ VideoClipName.slot_atari03, videoClips[4] },
{ VideoClipName.slot_atari04, videoClips[5] },
{ VideoClipName.slot_atari05, videoClips[6] },
{ VideoClipName.sanpai, videoClips[7] },
{ VideoClipName.hikouki, videoClips[8] },
{ VideoClipName.taiyaki, videoClips[9] },
{ VideoClipName.bonus_keizoku, videoClips[10] },
{ VideoClipName.bonus_mae, videoClips[11] },
{ VideoClipName.bonus_ato, videoClips[12] },
{ VideoClipName.semifinal, videoClips[13] },
{ VideoClipName.soredemo, videoClips[14] },
{ VideoClipName.daidaidai, videoClips[15] },
{ VideoClipName.daidaidai_pre, videoClips[16] },
{ VideoClipName.daidaidai_after, videoClips[17] },
{ VideoClipName.ending, videoClips[18] },
{ VideoClipName.mikosama, videoClips[19] },
};
videoPlayerHalf.playOnAwake = false;
//音量を最初から設定しておく
videoPlayerHalf.SetDirectAudioVolume(0, 0.25f);
// VideoPlayerの準備が完了したらフレームを表示する
videoPlayerHalf.prepareCompleted += DisplayFirstFrame;
}
/// <summary>
/// ビデオを再生する
/// </summary>
/// <param name="clipName"></param>
/// <returns></returns>
public IEnumerator PlayVideo(VideoClipName clipName)
{
if (videoClipDict.TryGetValue(clipName, out VideoClip clip))
{
videoPlayerHalf.clip = clip;
yield return StartCoroutine(CreateVideo());
}
else
{
Debug.LogError("Specified video clip not found.");
}
}
/// <summary>
/// ビデオを生成する
/// </summary>
/// <returns></returns>
private IEnumerator CreateVideo()
{
// プレファブをインスタンス化
backgroundInstance = Instantiate(backgroundPrefab, transform);
topLeftImageInstance = Instantiate(topLeftImagePrefab, transform);
bottomRightImageInstance = Instantiate(bottomRightImagePrefab, transform);
videoFrameInstance = Instantiate(videoFramePrefab, transform);
// インスタンスをCanvasの子オブジェクトに設定
backgroundInstance.transform.SetParent(overlayCanvas.transform, false);
topLeftImageInstance.transform.SetParent(overlayCanvas.transform, false);
bottomRightImageInstance.transform.SetParent(overlayCanvas.transform, false);
videoFrameInstance.transform.SetParent(overlayCanvas.transform, false);
// 初期スケールを設定
videoFrameInstance.transform.localScale = new Vector3(startScale.x, startScale.y, 1);
backgroundInstance.transform.localScale = new Vector3(startScale.x * backgroundScaleFactor, startScale.y * backgroundScaleFactor, 1);
// 枠画像のスケールを2倍に設定
topLeftImageInstance.transform.localScale = new Vector3(2.5f, 2.5f, 1);
bottomRightImageInstance.transform.localScale = new Vector3(2.5f, 2.5f, 1);
// CanvasGroupコンポーネントを追加してフェードアウト可能にする
AddCanvasGroup(backgroundInstance);
AddCanvasGroup(videoFrameInstance);
AddCanvasGroup(topLeftImageInstance);
AddCanvasGroup(bottomRightImageInstance);
// VideoPlayerのターゲットテクスチャを設定
videoPlayerHalf.targetTexture = renderTexture;
// ビデオフレームのRawImageを取得し、RenderTextureを適用
videoFrameRawImage = videoFrameInstance.GetComponent<RawImage>();
if (videoFrameRawImage == null)
{
Debug.LogError("ビデオフレームプレハブには RawImage コンポーネントがない。");
//return;
}
videoFrameRawImage.texture = renderTexture;
// ビデオフレームと背景のRectTransformを取得
videoFrameRectTransform = videoFrameInstance.GetComponent<RectTransform>();
backgroundRectTransform = backgroundInstance.GetComponent<RectTransform>();
// ビデオフレームと背景を画面中央に配置
PositionBackgroundAndVideoFrame();
// 画像を動画の左上と右下の端に配置
PositionImages();
// 画像を動画の前面に配置
topLeftImageInstance.transform.SetAsLastSibling();
bottomRightImageInstance.transform.SetAsLastSibling();
// 描画順序を設定
backgroundInstance.transform.SetAsFirstSibling();
videoFrameInstance.transform.SetAsLastSibling();
topLeftImageInstance.transform.SetAsLastSibling();
bottomRightImageInstance.transform.SetAsLastSibling();
// 拡大前に待機
yield return StartCoroutine(WaitAndExpand());
}
/// <summary>
/// ビデオを再生して最初のフレームを表示
/// </summary>
/// <returns></returns>
IEnumerator WaitAndExpand()
{
// ビデオを再生して最初のフレームを表示
videoPlayerHalf.Prepare();
yield return new WaitUntil(() => videoPlayerHalf.isPrepared);
videoPlayerHalf.Play();
yield return new WaitForEndOfFrame(); // フレームを待つ
videoPlayerHalf.Pause();
videoPlayerHalf.frame = 0; // 0フレーム目に設定
videoPlayerHalf.Play(); // フレームを更新するために一瞬再生
videoPlayerHalf.Pause(); // 再度一時停止
// 拡大前に指定した時間だけ待機
yield return new WaitForSeconds(initialWaitDuration);
// フレーム拡張を開始
Sequence sequence = DOTween.Sequence();
sequence.Append(videoFrameInstance.transform.DOScale(new Vector3(targetScale.x, targetScale.y, 1), 0.5f));
sequence.Join(backgroundInstance.transform.DOScale(new Vector3(targetScale.x * backgroundScaleFactor, targetScale.y * backgroundScaleFactor, 1), 0.5f)
.OnUpdate(PositionImages));
// シーケンスの完了を待つ
yield return sequence.WaitForCompletion();
// フレームが拡張された後の処理
yield return StartCoroutine(OnFrameExpanded());
}
/// <summary>
/// セットフレームと表示
/// </summary>
/// <param name="vp"></param>
void DisplayFirstFrame(VideoPlayer vp)
{
StartCoroutine(SetFrameAndDisplay(vp));
}
/// <summary>
/// 1フレーム目を表示
/// </summary>
/// <param name="vp"></param>
/// <returns></returns>
IEnumerator SetFrameAndDisplay(VideoPlayer vp)
{
vp.Play();
vp.Pause();
yield return new WaitForSeconds(0.1f); // 短い遅延を追加
vp.frame = 0; // 0フレーム目に設定
vp.StepForward(); // 0フレーム目を強制的に表示
yield return null;
}
/// <summary>
/// 背景とビデオフレームを画面中央に配置
/// </summary>
void PositionBackgroundAndVideoFrame()
{
// 背景とビデオフレームを画面中央に配置
backgroundRectTransform.anchorMin = new Vector2(0.5f, 0.5f);
backgroundRectTransform.anchorMax = new Vector2(0.5f, 0.5f);
backgroundRectTransform.pivot = new Vector2(0.5f, 0.5f);
backgroundRectTransform.anchoredPosition = Vector2.zero;
videoFrameRectTransform.anchorMin = new Vector2(0.5f, 0.5f);
videoFrameRectTransform.anchorMax = new Vector2(0.5f, 0.5f);
videoFrameRectTransform.pivot = new Vector2(0.5f, 0.5f);
videoFrameRectTransform.anchoredPosition = Vector2.zero;
}
/// <summary>
/// 画像ポジションを設定
/// </summary>
void PositionImages()
{
var topLeftRectTransform = topLeftImageInstance.GetComponent<RectTransform>();
var bottomRightRectTransform = bottomRightImageInstance.GetComponent<RectTransform>();
// 背景画像のサイズとスケールを取得
float backgroundWidth = backgroundRectTransform.rect.width * backgroundRectTransform.localScale.x;
float backgroundHeight = backgroundRectTransform.rect.height * backgroundRectTransform.localScale.y;
// 左上画像の位置を設定
topLeftRectTransform.anchorMin = new Vector2(0, 1f);
topLeftRectTransform.anchorMax = new Vector2(0, 1f);
topLeftRectTransform.pivot = new Vector2(0, 1f);
topLeftRectTransform.position = backgroundRectTransform.position + new Vector3(-backgroundWidth / 2, backgroundHeight / 2, 0) + (Vector3)topLeftOffset;
//右下画像の位置を設定
bottomRightRectTransform.anchorMin = new Vector2(1f, 0f);
bottomRightRectTransform.anchorMax = new Vector2(1f, 0f);
bottomRightRectTransform.pivot = new Vector2(1f, 0f);
bottomRightRectTransform.position = backgroundRectTransform.position + new Vector3(backgroundWidth / 2, -backgroundHeight / 2, 0) + (Vector3)bottomRightOffset;
}
/// <summary>
/// ビデオを再生してフレームをリセットしてオブジェクトを削除
/// </summary>
/// <returns></returns>
IEnumerator OnFrameExpanded()
{
// ビデオを再生
videoPlayerHalf.Play();
// ビデオが終了した後にフレームをリセットし、オブジェクトを削除
yield return StartCoroutine(ResetAndDestroyAfterVideo());
}
/// <summary>
/// ビデオが終了した後にフレームをリセットし、オブジェクトを削除
/// </summary>
/// <returns></returns>
IEnumerator ResetAndDestroyAfterVideo()
{
// ビデオが終了するのを待つ
yield return new WaitForSeconds((float)videoPlayerHalf.length);
// フレームスケールをリセット
Sequence sequence = DOTween.Sequence();
sequence.Append(videoFrameInstance.transform.DOScale(new Vector3(startScale.x, startScale.y, 1), expandDuration));
sequence.Join(backgroundInstance.transform.DOScale(new Vector3(startScale.x * 1.25f, startScale.y * 1.25f, 1), expandDuration)
.OnUpdate(PositionImages));
sequence.OnComplete(() =>
{
Sequence sequence2 = DOTween.Sequence();
sequence2.Append(backgroundInstance.GetComponent<CanvasGroup>().DOFade(0, fadeDuration));
sequence2.Join(videoFrameInstance.GetComponent<CanvasGroup>().DOFade(0, fadeDuration));
sequence2.Join(topLeftImageInstance.GetComponent<CanvasGroup>().DOFade(0, fadeDuration));
sequence2.Join(bottomRightImageInstance.GetComponent<CanvasGroup>().DOFade(0, fadeDuration));
sequence2.OnComplete(() =>
{
// フレームがリセットされた後にオブジェクトを削除
Destroy(topLeftImageInstance);
Destroy(bottomRightImageInstance);
Destroy(videoFrameInstance);
Destroy(backgroundInstance);
});
});
}
/// <summary>
/// フェードアウト準備
/// 指定したGameObjectにCanvasGroupコンポーネントがアタッチされていない場合、アタッチする
/// </summary>
/// <param name="obj"></param>
void AddCanvasGroup(GameObject obj)
{
if (obj.GetComponent<CanvasGroup>() == null)
{
obj.AddComponent<CanvasGroup>();
}
}
}動画クリップの管理
VideoControllerでは、複数の動画クリップをenumとDictionaryを使って管理しています。このアプローチにより、動画クリップを簡単に識別し、効率的に再生することが可能です。
public enum VideoClipName
{
haiku, mikopoppo, slot_atari01, ...
none // 再生なしのためのプレースホルダー
}
private Dictionary<VideoClipName, VideoClip> videoClipDict;Start()メソッド内で、これらのクリップを辞書にマッピングして管理しています。この方法により、クリップ名で動画を簡単に取得できるようになります。
再生動画はmp4で”Assets/Resources/Videos” でまとめて保存し、以下のようにVideo Clipsに動画ファイルをアタッチすることで設定しています。

動画の生成と再生
VideoControllerは、PlayVideo()メソッドを使用して、指定された動画クリップを再生するためのコルーチンを開始します。
このメソッドは、与えられたVideoClipNameに対応する動画を検索し、CreateVideo()メソッドを呼び出して動画再生の準備をします。
public IEnumerator PlayVideo(VideoClipName clipName)
{
if (videoClipDict.TryGetValue(clipName, out VideoClip clip))
{
videoPlayerHalf.clip = clip;
yield return StartCoroutine(CreateVideo());
}
else
{
Debug.LogError("指定されたビデオクリップが見つかりません。");
}
}
CreateVideo()メソッドでは、動画再生の準備として、ビデオフレームや背景、他のUI要素をインスタンス化し、適切な場所に配置します。その後、動画の拡大アニメーションを実行し、動画を再生します。
動画のアニメーション効果
動画再生時に、フレームの拡大・縮小やフェードアウトといったアニメーションを実現するために、DOTweenライブラリを利用しています。
Sequence sequence = DOTween.Sequence();
sequence.Append(videoFrameInstance.transform.DOScale(new Vector3(targetScale.x, targetScale.y, 1), 0.5f));
sequence.Join(backgroundInstance.transform.DOScale(new Vector3(targetScale.x * backgroundScaleFactor, targetScale.y * backgroundScaleFactor, 1), 0.5f)
.OnUpdate(PositionImages));
このように、シーケンスを使って一連のアニメーションを定義し、拡大後に動画を再生し、再生後に縮小して消滅する一連の流れを作成します。
フレームのリセットと破棄
動画が終了した後には、ResetAndDestroyAfterVideo()メソッドを使用して、ビデオフレームや背景のリセットとオブジェクトの破棄を行います。このメソッドにより、再生が終わった後に不要になったオブジェクトが適切に処理され、メモリリークや表示の残留を防ぎます。
IEnumerator ResetAndDestroyAfterVideo()
{
// フレームの縮小とフェードアウト
Sequence sequence = DOTween.Sequence();
sequence.Append(videoFrameInstance.transform.DOScale(new Vector3(startScale.x, startScale.y, 1), expandDuration));
sequence.OnComplete(() =>
{
Sequence fadeSequence = DOTween.Sequence();
fadeSequence.Append(videoFrameInstance.GetComponent<CanvasGroup>().DOFade(0, fadeDuration));
fadeSequence.OnComplete(() =>
{
Destroy(videoFrameInstance);
});
});
yield return sequence.WaitForCompletion();
}
動画を連続再生する際のフレーム残り対策 Prepare()とPause()
VideoController.csで使用されているPrepare()とPause()の組み合わせは、UnityのVideoPlayerを使用する際に、動画の再生時に最初のフレームが正しく表示されるようにするための重要な手法です。
この手法を理解するために、それぞれのメソッドの役割と、その組み合わせの意図について詳しく説明します。
Prepare()メソッドの役割
Prepare()メソッドは、VideoPlayerが指定された動画クリップの再生準備を行うために使用されます。このメソッドを呼び出すと、VideoPlayerはバックグラウンドで動画をデコードし、再生のために準備を整えます。
Prepare()は非同期で動作し、準備が完了するまで時間がかかるため、isPreparedプロパティを使って準備が完了したかどうかを確認できます。
videoPlayerHalf.Prepare();
yield return new WaitUntil(() => videoPlayerHalf.isPrepared);
このコードでは、Prepare()を呼び出した後、isPreparedがtrueになるまで待機しています。これにより、動画が再生可能な状態になるまで次の処理を遅延させ、準備が整った段階で次のステップに進むことができます。
Pause()メソッドの役割
Pause()メソッドは、再生中の動画を一時停止するために使用されます。このメソッドを使うことで、動画の再生を一時的に停止し、特定のフレームを表示したままにすることができます。
Prepare()とPause()を組み合わせる理由
動画をシームレスに再生するためには、VideoPlayerが最初に表示するフレームを正確に制御することが重要です。特に、連続して動画を再生する場合、前回再生された動画の最後のフレームが一瞬表示されてしまう問題が発生することがあります。これを防ぐために、以下のようにPrepare()とPause()を組み合わせています。
※試行錯誤の結果、最終的に以下のような記述になっていますが、おそらくもっとシンプルに記述する方法があると思います。
// ビデオを再生して最初のフレームを表示
videoPlayerHalf.Prepare();
yield return new WaitUntil(() => videoPlayerHalf.isPrepared);
videoPlayerHalf.Play();
yield return new WaitForEndOfFrame(); // フレームを待つ
videoPlayerHalf.Pause();
videoPlayerHalf.frame = 0; // 0フレーム目に設定
videoPlayerHalf.Play(); // フレームを更新するために一瞬再生
videoPlayerHalf.Pause(); // 再度一時停止VideoController.csの設定
VideoController.csの設定は以下のようになっています。Video Clipsには必要分の動画をアタッチします。

Top Left Image Prefabには左上の枠画像をプレハブ化したものを設定。
Bottom Right Image Prefabには右下の枠画像をプレハブ化したものを設定。
両方、UI=>Imageで作成しています。


Background Prefabには動画の後ろに置く背景をプレハブ化したものを設定。ここでは、作成したImageに色を設定して、少し透明度をつけています。

Videoの映るテクスチャの設定とRaw Imageのプレハブを作成
video Frame PrefabにはUI=>Raw Imageを作成し、Videoを映すことのできるテクスチャーを設定したものをプレハブ化してそれを設定しています。

Videoの映るテクスチャの作成
Assets内でCreate=>Render Textureでテクスチャを作成します。名前をVideoRenderTextureとします。
再生したい動画のサイズに合わせてSizeを設定します。

HierarchyでUI=>Raw Imageでオブジェクトを作成して名前をrawVideoとし、テクスチャに先ほど作成したVideoRenderTextureをセットし、HierarchyからAssetsにドラッグアンドドロップしてプレハブ化します。
残りの設定
残りの設定は以下の通りです。お好みで変更して下さい。
- Start Scale:拡大前の初期サイズの設定
- Tartget Scale:拡大後の大きさの設定
- Expand Dration:リセットする際の小さくなるまでの時間
- Initial Wait Dration:拡大前に指定した時間だけ待機
- Top Left Offset:左上枠画像のオフセット値
- Bottom Right Offset:右下枠画像のオフセット値
- Fade Duration:フェードアウト時の時間

まとめ
MainManager.csはプロジェクト全体の管理を担い、VideoController.csは具体的な動画再生の制御を行います。
VideoController.csではPrepare()とPause()を組み合わせることで、連続する動画再生時に不自然な表示が発生しないように細かい制御を行っています。
さらに、DOTweenを利用したアニメーション効果を活用することで、動画の拡大・縮小やフェードアウトといった視覚効果も簡単に実装できます。これにより、単なる動画再生にとどまらず、視覚的に魅力的でインタラクティブなコンテンツを作成することが可能です。
この記事が拡大・縮小が可能なビデオプレイヤーの作成の参考になれば幸いです。
