VideoPlayer でハマったポイントの対処法(時間・シーク・終了イベント)

こないだ、UnityのVideoPlayerの機能を使っていたのですが、何か所かハマったポイントがあったのでメモがてら整理しておきます。難しい内容ではなく、知っているかどうかがメインかと思います。では、早速!

URL形式でビデオクリップの時間を取得する

URL形式でビデオを再生した際に「VideoPlayer.clip」がどうも(null)になってしまうことに気付きました。ビデオクリップが(null)だとLengthが取れず、ビデオの時間/尺が分からないぞ…ってなります。

じゃあ、どうするか? 自分で計算します。

下記の内容はURL形式でも分かるので、そのデータを元に算出できます。計算式は単純に割ってください。

  • VideoPlayer.frameCount: ビデオ全体のフレーム総数
  • VideoPlayer.frameRate: ビデオのフレームレート

var videoDuration = (float)VideoPlayer.frameCount / VideoPlayer.frameRate;

注意点としてはまず、frameCountの方のデータ型がulongなのでキャストしてあげましょう。次に、ビデオがロードされきる前に上記の変数を読み込むと値が入っていないことがあったので、ロードが終わった後に計算するようにした方が良いです。ロードが終わった後に処理させるには下記の感じになります。

VideoPlayer.prepareCompleted += StartProcess;

VideoPlayer.Prepare();

メソッドとして入れ込んでいる StartProcess ですが、引数にVideoPlayerを設定してあげる必要がありそうでした。ちなみに、StartProcessは自身で用意するメソッドなので名称は何でも大丈夫です。

(Sliderとかを使って)シークさせる

シークに関しては、YouTubeなどでも分かるように再生ポイントをSliderや指定の位置をクリックして移動させる方法のことです(よね…?)

細かいことはさておき、VideoPlayerでシークさせるには上記のフレーム情報を使うと簡単にできます。とりあえず、ここではSliderでやる前提で考えますが、基本的には以下のことが出来れば問題ないかと思います。

  • VideoPlayer.frameCount を、SliderのMaxValue として設定する(初期処理)
  • VideoPlayer.frame から現在再生中のフレームを取得する/移動させるフレームを設定する(SliderのValue値として扱う)

といった形で、ごにょごにょ出来るかと思います。ただ毎フレーム並みにフレームを移動させたりすると負荷が高そうなのでプレビューしながらとかは難しいかなとは考えています。

終了時にイベント処理を発生させる

単純に考えたら、再生時間を待ってあげれば良いですが、シークなどが発生するとその限りではないので、終了時にイベントを仕込みたくなります。ので、仕込んでみましょう。

VideoPlayer.isLooping = true;

VideoPlayer.loopPointReached += EndProcess;

基本的にはビデオプレイヤーのループ機能を使うといった具合になります。ループをする際にループポイント(=ビデオの終端)に到達したことが分かるので、それを利用しています。

注意点としては、ループ機能をオンにしていないと呼ばれない噂?(未検証です…)があったので、一応ループにしておいた方が良いかと思います。その影響で、再生終了時にStopをかけたり、ループをオフにするといったフォローも入れておくと良いです。あとは、先ほどと同じようにEndProcessのメソッドにVideoPlayerの引数は必要だったかと。

VideoPlayerに関してはまだ奥が深そうではありますが、個人的には使いやすい感がありますし、仕様的にも上記のことが出来たて一通りこなせたので便利だなーって思っています。

Foveで取得できるEyeClosedをInputとして使用してみた

こないだFoveを使うことがあって、Foveでは目の開閉を取れることを知ったのでそれをInputとして使おうという話が出ました。ただ、残念なことにInput.GetKeyDownといったような使い方が用意されているわけではなく、目の開閉を取得した時のステータス(両目/片目の開閉の計4パターン)が返ってくるAPIがある程度でした。 ※2017年12月当時

とはいえ、そのステータスを単純に比較してあげればやりたいことは出来たのでよかったのですが、どうせなら汎用的にしてみようと躍起になってInput.GetKeyDownみたいなのを作ってみました。

作ってみたコードはこんな形になります。では、少しだけ解説を。

使い方としては、

  • InputEyeClosed.GetClose~~() で対象となるステータスの入力を取ります。
  • InputEyeClosedUpdateを空のGameObjectにアタッチしてPrefab化してResourcesフォルダの直下に置きます。

実際にやっていることとしては、基本的に目の開閉のステータスを毎フレーム更新して変更があったときにキー・ダウン・アップの状態も更新されるようにしています。本当は全部staticのクラスでやりたかったんですが、毎フレーム処理するのは出来ない?と思い、Update関数に頼っているといった感じです。

その他に注意点としては、どうも瞬きでもステータスが変わってしまうので、ダウン・アップ系は使いづらいなという点と、常に何かしらステータスはオンになっているという点ぐらいかなと。

まぁ自分が今後もFoveを使うことがあるのかは微妙なところではありますが、何かの参考になれば幸いです。

[C#] アプリの起動時に指定されたコマンドライン引数の取得

今回は前に仕事で少しハマった内容についてのメモを簡単に残しておきます。内容はというと、Unity製のアプリを起動するときに引数としてトークンが渡されるのでWWWリクエストを投げて起動チェックをかける、といった要件でした。

それで、WWWリクエストは大丈夫だったのですが(まぁこれも少しハマりましたが…)、引数はどうやって取得するんだろうとなりまして。。結論から言えば、C#に用意されているメソッドを利用しましょうということでした。知っていれば何てことないやつでした。

string[] args = System.Environment.GetCommandLineArgs();

このメソッドを呼んであげると指定された引数を全て取得できるので、あとはよしなに。ちなみに、このメソッドは例えばUnityのどのシーンで呼ぼうが、アプリを起動した際の引数を保持している(していたはず)なので、必要な場面で引数を使用することも出来ます。

要件として引数が渡されるようなケース以外だと、デバッグコマンドのように指定した引数があるとデバッグモードで起動するといった使い方も出来るのかなといった感じでしょうか。後者は割かし思い付きなのであれですが、実際にデバッグとして使っているような人がいたり、それ以外の使い方をしている人がいたら話を聞いてみたいです。

AnimatorからAnimationClipのLengthを取得する

UnityのAnimatorで設定されたAnimationClipの時間(Length)の取得方法について知ったので復習がてらメモ用に簡単に記しておきます。

では、さっそく。

public float GetAnimationClipLength(Animator animator, string clipName) 
{
    float clipLength = 0f;

    var rac = animator.runtimeAnimatorController;
    var clips = rac.animationClips.Where (x => x.name == clipName);
    foreach (var clip in clips) {
        clipLength = clip.length;
    }
    return clipLength;
}

補足ですが、対象となるAnimationClipを探すためにLinqを使用しているので、「System.Linq」をusingしておく必要があります。また、AnimationClipの名前はユニークであることを想定しているため、foreach内では特に判定処理などは行っていないです。

ゲームプレイをショートカットで操作する

UnityのEditor上でゲームプレイしているときに、ポーズやコマ送りさせたい場面は結構あるかと思います。

ただ、ポーズさせるのにカーソルを移動させてボタンクリックといった手順はシンプルなのですが、場合によっては面倒だったりします。例えば、コマ送りさせるためにマウスクリックの連打するとか。連打するのであればキーボードの方がよっぽど楽なので、ゲームプレイに関わるショートカットを覚えて使い分けていければ良いのかな、ということで整理しておきます。

ゲームを実行する/終了する : Ctrl + P

ゲームをポーズする/解除する : Ctrl + Shift + P

コマ送りする : Ctrl + Alt + P

※ ゲームがポーズ状態でない場合は、ポーズされます

基本的には、Pボタンがキーになることを覚えておけば良さそうです。ゲームの実行とポーズに関してはトグル(OffならOn、OnならOff)となっています。

徒歩1秒の在宅勤務/リモート開発をしてみた感想

いつまで続けるかや続けられるのかは分かりませんが、最近は在宅勤務(リモート開発)をメインでしているのでそのメリットとデメリットを自分なりに整理してみようと思います。

では、まず自分の在宅勤務がどんな形なのかやその経緯を整理していきます。

  • 立ち上げて間もないベンチャーに所属
  • 在宅勤務のきっかけは通勤が面倒、開発に集中したい、など
  • 打ち合わせや対面での作業が必要な時もあるのでたまには行く
  • 在宅勤務を始めてから大体二ヶ月ほど経過

いざ挙げてみると少ないというか、シンプルな感じになりました。でも、まぁこんなもんですよね、きっと。二番目のところはこの後に詳しく掘り下げていければと考えているので、何かフワッとしているなと思っても気にしないでください。(とは言っても、そこまで言及はしていないですがw)

そしたら、本題の方に入っていきます。

メリット

通勤が楽(というより、無い)

これはもう言葉の通りです。楽というより通勤そのものが無いので、そういう概念が生まれませんね。あえて言うならば、「徒歩1秒」です、職場という家の机まで。

睡眠時間が増える

通勤時間が「徒歩1秒」、つまり裏を返せばギリギリまで寝ていても余裕で間に合います。睡眠時間が少ないと効率が悪くなるので、これは良いメリットと言えるでしょう。

身だしなみに気を使わなくても良くなる

在宅勤務をしているとあまり外に出ることは無くなってきます。そうなると、人にも合わなくなるので寝間着姿かつ、男性なら髭を剃らない・女性ならメイクしないみたいなことでも済むようになるので気が楽にはなります。もちろん、これに慣れ過ぎてしまうと人として何かを失っていくことにはなるかと思います。

仕事に集中して取り組める

実際の職場では集中が妨げられることが割とあったりするかと思います。雑談はもちろん、仕事の確認やら相談などでも。雑談に関しては個人差はあると思いますが、自分が入っていなくても大きな話し声などが気になったりすることもあるかと。そうなると、ほぼ自分で環境を整備できる在宅勤務は集中して作業できるような場作りがしやすいメリットがあります。

家事的なことにも融通が利く

基本的に家にいるので、家事的なことに融通が利く場面は結構多いです。例えば、宅配便の受け取りだったり、洗濯物を干したりなど。ランチも自炊すれば大分お得です。子供がいる家庭では育児も出来ますし、住む場所もあまり気にしなくてすむようになります。

デメリット

運動不足になる

何だかんだで通勤していると、それなりに歩くので軽い運動にはなっていたと思うのですが、在宅勤務では下手するとほとんど歩くこともなく過ごせることになります。結果、余計に運動不足になりがちです。意識的に体を動かさないと結構やばいことになってしまうかと思われます。

人と会わなくなる

段々とメリットの裏返し的な雰囲気になってきました。これは、一人暮らしだと特にですが、三日間まともに会話してないとかざらにあります。対人能力も落ちますし、精神的にもあまり良くないです。家族だったり彼氏彼女がいたり、よく遊びに出かけている人なら大丈夫だと思いますけど、そうではないのに一人で居続けるのが苦手な人には在宅勤務は向いてない気がします。

自己管理能力が求められる

在宅勤務は会社に行っていない分、成果はアウトプットでしか示すことが出来ません。元来、仕事はそういうものだと思いますが、過程の部分を誰かが知ることも難しいので、そこのバイアスは効かないですし、むしろマイナスに見られがちだと思います。それこそ、フリーランスのような働き方に近いものがあるのかなと。それに在宅だと別の誘惑もたくさんあるので、そこを断ち切る努力も必要です。実際に家ではなく、どこかのカフェだったりコワーキングスペースを使って集中できる環境を作っていくことが大事なのかと思います。

結果的に仕事時間が長くなることもある

上記の自己管理能力にも関連しますが、作業で何かトラブルが起きたりするとその日のタスクを完遂することが難しくなってきます。ですが、それでも何とか間に合わせないという気持ちにもなるので、結果的に夜遅くまで作業することも。

チームを感じにくい、会社の情報に疎くなる

在宅勤務は作業に集中できるのは良いことではありますが、どうしてもソロ活動のようになりがちです。相対的に、相談なども気軽には出来なくなります。その結果、チームワークといったモノが感じにくくなったり、会社の情報にも疎くなったりするのでモチベーションを維持する方法を持つことが結構大事になってくるかと思います。

まとめ

哲学的な話になってしまいますが、メリットはデメリットの、デメリットはメリットの裏返しになってくるので、在宅勤務をする際は結局のところ何を優先するかを明確にしておかないとあまり良い結果を生まないことには注意が必要です。実際、メリットとデメリットは会社や環境、その人の性格などにも影響されるので、在宅勤務を始めるならきちんと整理してからの方が良いでしょう。

個人的には、在宅勤務は全然アリですが、最善というほどメリットを感じているわけではなかったりします。もちろん、自分なりのメリットは感じているのでどちらかと言えば在宅勤務を始めて良かった気持ちですけど、チームを感じにくい部分のデメリットはそれなりに痛いなといった感想です。

もしかしたら、週3でオフィス・週2で在宅みたいなハイブリッド的な働き方も割と今の時代に合っているかもなと思ったところで終わりたいと思います。

外部データからParse変換にTryParseを使ってハンドリングする

今回はC#でのStringからIntやFloat型に変換するParse処理について。

システム内部の話であれば、基本的にデータ型は一致しているわけでそもそもParse処理を必要しない、もしくは(データの中身が)自明のため変換時のエラーは発生しないことが多いので安心して使える思います。

ただ、外部(サーバー経由や、エクセルからのデータ変換などの)データを受け取って処理する場合は少し面倒です。というのも、そこのデータを例えば手作業で編集しているとかだと入力ミスや予期しないデータが入ってくることもあります。そういう不確定要素が来そうなところはParseではなくTryParseを使った方が良いケースになります。

では、TryParseを使い方を見てみましょう。(例のごとく、Unityで使えるクラスになってます)

public class TryParse : MonoBehaviour 
{
    public bool isFalseCheck = false;
    public bool isOverflowCheck = false;

    private int result = -1;
    private bool isParse = false;
    private string msg = "";

    readonly private string trueInt = "12";
    readonly private string falseInt = "12a";

    private string overflowInt = "";

    void Start () {

        long overflowValue = (long)int.MaxValue + 1;
        overflowInt = overflowValue.ToString();
        Debug.Log(overflowInt);  // 2147483648

        result = int.Parse(trueInt);
        WriteConsole("Result 1");  // 12

        if (isFalseCheck) result = int.Parse(falseInt);  // FormatException 
        if (isOverflowCheck) result = int.Parse(overflowInt);  // OverflowException

        TryParseCheck(trueInt, "Result 1 True", "Result 1 false");  // True : 12
        TryParseCheck(falseInt, "Result 2 True", "Result 2 false"); // False : 0
        TryParseCheck(overflowInt, "Result 3 True", "Result 3 false"); // False : 0
    }

    void TryParseCheck(string data, string msg1, string msg2)
    {
        isParse = int.TryParse(data, out result);
        if (isParse) WriteConsole(msg1);
        else WriteConsole(msg2);
    }

    void WriteConsole(string msg)
    {
        Debug.LogFormat("{0} : {1}", msg, result);
        result = -1;
    }
}

まぁ何でこの記事を書いたかを簡単に説明すると、自分が単純にParse処理を書いてエラーしたからなんですけどね。Parse処理に失敗するとExceptionが発生するので止まりますし、エラーの理由は直ぐに予測と原因にたどり着いて修正できるんですがそれが面倒という・・・ね。

なもんで、自分用のメモとTryParseの復習って感じでした。例外処理は大事ですね!もちろん、TryParseを使わなくても別の手法で例外処理をしてもOKです。