タイムラインで動画に字幕を付けるでは、エディタの裏で作業をしたりして負荷をかけると、動画と字幕のタイミングがずれてしまうときがあります。そこで、タイムラインを使わずに動画に字幕を付けてみました。
VideoPlayerを作る
まず、ヒエラルキーウィンドウでVideo Playerオブジェクトを作りました。
このVideo Playerコンポーネントでは、ビデオクリップとメインカメラをアタッチします。「Play On Awake」はオンにして、シーンの開始時に再生が始まるようにしました。
スクリプトを作る
空のゲームオブジェクトを作って、字幕を表示するためのスクリプトを追加しました。
using UnityEngine;
using UnityEngine.Video;
using UnityEngine.UI;
namespace Test1
{
public class DisplaySubtitles : MonoBehaviour
{
[SerializeField] VideoPlayer videoPlayer;
[SerializeField] Text text;
[SerializeField] Subtitle[] subtitles; // 字幕の配列
int index; // インデックス
bool? isDisplaying = null; // 表示中かどうか
// Start is called before the first frame update
void Start()
{
isDisplaying = false;
}
// Update is called once per frame
void Update()
{
// 表示中のとき
if (isDisplaying == true)
{
// 字幕の終了時間をすぎると
if (videoPlayer.time >= subtitles[index].GetEndTime())
{
// 字幕を削除
subtitles[index].Delete(text);
// 表示中でない状態にする
isDisplaying = false;
// インデックスを進める
index++;
// 最後の字幕が終わったとき
if (index >= subtitles.Length)
{
// 終了
isDisplaying = null;
}
}
}
// 表示中でないとき
else if (isDisplaying == false)
{
// 字幕の開始時間をすぎると
if (videoPlayer.time >= subtitles[index].GetStartTime())
{
// 字幕を表示する
subtitles[index].Show(text);
// 表示中にする
isDisplaying = true;
}
}
}
}
[System.Serializable]
public class Subtitle
{
// 字幕の開始時間
[SerializeField] double startTime;
public double GetStartTime()
{
return startTime;
}
// 終了時間
[SerializeField] double endTime;
public double GetEndTime()
{
return endTime;
}
[SerializeField] string subtitle; // 字幕文字列
[SerializeField] Color color; // 字幕の色
[SerializeField] int fontSize; // フォントサイズ
[SerializeField] Vector3 position; // 表示位置
// 字幕を表示する
public void Show(Text text)
{
text.text = subtitle;
text.color = color;
text.fontSize = fontSize;
text.rectTransform.localPosition = position;
}
// 字幕を消す
public void Delete(Text text)
{
text.text = "";
}
}
}
スクリプトでは、まず字幕クラスを作って、字幕の開始/終了時間や表示する文字列、色、大きさなどを設定できるようにしました。
MonoBehaviourを継承するクラスでは、この字幕クラスのインスタンスを入れる配列を宣言しました。
[SerializeField] Subtitle[] subtitles; // 字幕の配列
Updateメソッドでは、現在のインデックスを使って、配列から開始時間と終了時間を取り出し、再生中の動画の時間と比較します。
// 表示中のとき
if (isDisplaying == true)
{
// 字幕の終了時間をすぎると
if (videoPlayer.time >= subtitles[index].GetEndTime())
{
// 字幕を削除
subtitles[index].Delete(text);
// 表示中でない状態にする
isDisplaying = false;
// インデックスを進める
index++;
// 最後の字幕が終わったとき
if (index >= subtitles.Length)
{
// 終了
isDisplaying = null;
}
}
}
字幕を表示中のときに、動画の時間の値が現在の字幕の終了時間の値を上回ると、字幕を削除するメソッドを呼びます。このメソッドでは、渡されたテキストの文字列を空にします。
// 字幕を消す
public void Delete(Text text)
{
text.text = "";
}
Transformを渡すときと同様に、元のテキストの値が変わります。字幕を消した後は、表示していない状態にします。表示していない状態では、動画の時間と字幕の開始時間を比較します。
// 表示中でないとき
else if (isDisplaying == false)
{
// 字幕の開始時間をすぎると
if (videoPlayer.time >= subtitles[index].GetStartTime())
{
// 字幕を表示する
subtitles[index].Show(text);
// 表示中にする
isDisplaying = true;
}
}
字幕を表示するメソッドでは、文字列や色などを変更します。
// 字幕を表示する
public void Show(Text text)
{
text.text = subtitle;
text.color = color;
text.fontSize = fontSize;
text.rectTransform.localPosition = position;
}
字幕を消した後に、表示していない状態にして、インデックスに1を足すので、次のフレームからは、配列の次の字幕の開始時間と動画の時間を比較するようになります。
もし消した字幕が最後の字幕なら、表示中かどうかの状態を示すnull許容型の変数にnullを入れます。時間の比較はこれがnull以外のときに行われます。
字幕を設定する
このスクリプトにビデオプレイヤーとテキストをアタッチして、字幕配列の「Size」に字幕の数を入れます。
各要素を開くと、文字列や色を設定できます。
字幕クラスに[System.Serializable]属性を付けているので、このクラスのフィールドの値をインスペクタで設定できます。
これでタイムラインを使わずに動画に字幕を付けられました。