
UnityのVideoPlayerでオープニングムービーを再生する
Unity5.5までは公式の機能でiOS,Androidで動画を再生するにはHandheld.PlayFullScreenMovieを使うしかなく、
よくあるオープニングムービーを再生する場合も、動画の前面にUnityのUIを表示することもできませんでした。
lc_title_movie from Vimeo.
Unity5.6から登場したVideoPlayerを使うと、このように動画の前面にUIを出すことも簡単に実現できます。
このVideoPlayerを利用して、オープニングムービー再生を試してみたので、その実装方法を紹介します。
VideoPlayerの基本的な使い方については探せばいくつか記事がでてくるので割愛します。
サンプルにある動画は以下のゲームのものです。よければダウンロードしてください。
サンプルの仕様
- 全画面の動画用に利用する
- UI等を動画の前面に表示する可能性があることを考慮する
- 複数の動画を続けて再生する
- コードだけで再生可能にする
Usage
video = sample.VideoPlay.Create( camera );
video.Preload( Application.streamingAssetsPath + "ファイルパス" );
( 完了待ち : video.State == sample.VideoPlay.PlayState.Loaded) )
video.OnEnd = ( 再生停止時の次の処理 );
video.OnErrorReceived = ( エラー発生時の処理 );
video.OnStarted = ( 再生開始時の処理、インジケータ非表示等 );
video.PlayPrepared( false(ループしない) , true(タップによってスキップする) );
簡単に使用例を書くと上記のような形になります。詳細はサンプルプロジェクトの「Sample」クラスを見てもらえればと思います。
では、サンプルプロジェクトの「Art.Sample.VideoPlay」の実装について説明していきます。
インスタンスの生成
/// <summary>インスタンスの生成</summary>
public static VideoPlay Create(Camera camera = null)
{
// シーンを横断する場合はDontDestroyOnLoadにする
GameObject movieGameObject = new GameObject(kMovieGameObjectName);
VideoPlay movieBehaviour = movieGameObject.AddComponent<VideoPlay>();
movieBehaviour.Init(movieGameObject, camera);
return movieBehaviour;
}
VideoPlayerのインスタンス1つで、clipやurlを再度指定して Play し、2つの動画を連続で再生することはできましたが、つなぎ目のところでゴミ(前の動画の1コマ)が表示されてしまいました。
そのため、動画ファイル1つ毎にVideoPlayerのインスタンスを1つ生成します。
設定
private void Init(GameObject gameObject, Camera camera)
{
_movieGameObject = gameObject;
_audioSource = gameObject.AddComponent<AudioSource>();
_videoPlayer = gameObject.AddComponent<VideoPlayer>();
// 表示設定
_videoPlayer.renderMode = VideoRenderMode.CameraNearPlane;
_videoPlayer.targetCamera = camera;
_videoPlayer.aspectRatio = VideoAspectRatio.FitInside;
_videoPlayer.playOnAwake = false;
// サウンド設定
_audioSource.playOnAwake = false;
_videoPlayer.audioOutputMode = VideoAudioOutputMode.AudioSource;
_videoPlayer.EnableAudioTrack (0, true);
_videoPlayer.SetTargetAudioSource(0, _audioSource);
// イベント設定
_videoPlayer.errorReceived += ErrorReceived;
_videoPlayer.prepareCompleted += PrepareCompleted;
_videoPlayer.started += PlayStart;
_videoPlayer.loopPointReached += PlayEnd;
}
表示設定
- VideoRenderModeにはCameraNearPlaneを設定
カメラの一番手前に表示しますが、例えばOverlayに設定されたUI等はその全面に表示されます。
- VideoAspectRatioはFitInsideを設定
動画が画面外にはみでないような設定にします。
- playOnAwakeはfalseに
Prepareで事前にロードしたり、再生タイミングをコントロールするためにplayOnAwakeは無効にします。
サウンド設定
iOS,AndroidだとVideoAudioOutputModeはAudioSourceにしないと再生ができないようです。
また、エディタだとUnity2017.2.1f1で音が鳴らなくなって困ってます…。
イベント設定
- errorReceived
何かしらエラーが発生した場合に通知され、その場合停止通知がこないのでこの通知を受け取ったら停止と判断します。
- prepareCompleted
事前にローディングし、そのタイミングをハンドリングできるように通知を受け取ります。
- started
再生が開始されるまでにインジゲータ表示等を行うため、通知を受け取ります。
- loopPointReached
再生停止のタイミングを判断するために通知を受け取ります。
private void ErrorReceived(VideoPlayer player, string message)
{
if (OnErrorReceived != null)
{
OnErrorReceived(message);
}
}
private void PrepareCompleted(VideoPlayer player)
{
_playState = PlayState.Loaded;
if (OnPrepareCompleted != null)
{
OnPrepareCompleted();
}
}
private void PlayStart(VideoPlayer player)
{
if (OnStarted != null)
{
OnStarted();
}
}
private void PlayEnd(VideoPlayer player)
{
if (_playState == PlayState.Stoped)
{
return;
}
_playState = PlayState.Stoped;
if (OnEnd != null)
{
OnEnd();
}
}
事前ロード
/// <summary>事前ローディング</summary>
public void Preload(string filePath)
{
_playState = PlayState.Loading;
#if WITH_DEVELOP
_movieGameObject.name = kMovieGameObjectName + System.IO.Path.GetFileName(filePath);
#endif
_videoPlayer.url = filePath;
_videoPlayer.Prepare();
}
ファイルパスを指定して事前ロードを行います。
後述しますが、プラットフォームによってはアプリのバックグラウンドに遷移した際の挙動が不安定なので自前で状態管理しています。
またデバッグ時はヒエラルキー上で認識しやすくするためにGameObject名を変更しています。
再生開始
/// <summary>事前ロードした動画の再生開始</summary>
public void PlayPrepared(bool loop = false, bool tapSkip = true)
{
if (_playState != PlayState.Loaded)
{
const string message = "not Prepared";
ErrorReceived(_videoPlayer, message);
return;
}
_tapSkip = tapSkip;
_videoPlayer.isLooping = loop;
_playState = PlayState.Playing;
_videoPlayer.Play();
}
/// <summary>動画の再生開始(パス指定)</summary>
public void Play(string filePath, bool loop = false, bool tapSkip = true)
{
if (_playState == PlayState.Playing || _playState == PlayState.Paused)
{
const string message = "already playing";
ErrorReceived(_videoPlayer, message);
return;
}
if (_playState == PlayState.Loading || _playState == PlayState.Loaded)
{
const string message = "already Prepared";
ErrorReceived(_videoPlayer, message);
return;
}
#if WITH_DEVELOP
_movieGameObject.name = kMovieGameObjectName + System.IO.Path.GetFileName(filePath);
#endif
_tapSkip = tapSkip;
_videoPlayer.url = filePath;
_videoPlayer.isLooping = loop;
_playState = PlayState.Playing;
_videoPlayer.Play();
}
ループ再生と画面タップによるスキップの設定を行った後、再生を開始します。
一応用意したものの、オープニングムービーでループ再生することはまずなさそうですが。
タップによるスキップ(再生停止)は以下のようにしています。
void Update ()
{
if (_playState != PlayState.Playing || !_tapSkip)
{
return;
}
// タップ確認
if ( Input.GetMouseButtonUp(0) )
{
Stop();
}
}
問答無用でスキップしてしまうので、ダイアログ表示とかを挟む場合は改良が必要です。
再生停止と一時停止
/// <summary>再生停止</summary>
public void Stop()
{
if (_videoPlayer.isPlaying)
{
_videoPlayer.Stop();
}
PlayEnd(_videoPlayer);
}
/// <summary>再生一時停止</summary>
public void Pause()
{
if (_playState != PlayState.Playing)
{
return;
}
_playState = PlayState.Paused;
_videoPlayer.Pause();
}
/// <summary>再生一時停止再開</summary>
public void Resume()
{
if (_playState != PlayState.Paused)
{
return;
}
_playState = PlayState.Playing;
_videoPlayer.Play();
}
/// <summary>表示の有効無効設定</summary>
public void SetEnabled(bool enabled)
{
_videoPlayer.enabled = enabled;
}
VideoPlayerのインスタンスに対してStopとPauseを呼び出します。
一時停止再開は再度Playを呼び出します。
また、再生停止をした場合もインスタンスが有効な間は最後のフレームが表示されるので、enabledをfalseにすることで非表示にできます。
その他
不具合っぽいものなど
- バックグラウンドに遷移し、戻ってきた際に再生が継続しないケースがある
- 原因不明で、暫定対策としてバックグラウンド遷移時に一時停止、フォアグラウンドの際に一時停止再開すると発生が軽減することを確認
- エディタ(Mac)で動作させた場合、OnApplicationPauseで判定するバックグラウンド遷移時に再生中にもかかわらずVideoPlayer.isPlayingがfalse判定なってしまう
- state管理にisPlayingを利用しないことで解決
- エディタ(Mac)だとUnity2017.2.1f1で音が鳴らなくなった
- 外部のhttp URL指定で再生するとiOS,Androidの実機で再生できない
- Androidでヒドいコマ落ちが発生。Unityのバージョンアップか何かのタイミングで気がついたら再現しなくなった…
この記事へのコメントはありません。