面白い方へ転がっていくブログ

TouchDesignerとかUnityとか色々使って面白そうな方向へ転がっていきます。

【Operator Snippets】Timer CHOP編 その1

最近ようやくTouchDesignerを触り始めたので、ちょっと記事を書いてみたいと思います。

Operator Snippetsって何?

いきなりですが「Operator Snippets」ってご存知でしょうか?

・・・私は知りませんでした!(ドーン)

「Operator Snippets」は、TouchDesignerアプリ内でオペレータの簡単な使い方などを解説してくれているオペレータの説明書みたいなものです。

f:id:gesyutapo:20180713012259p:plain

「Help → Operator Snippets」で見る事が出来ます。 下の画像がOperator Snippetsのウィンドウです。 全てのオペレータがあるわけではないようなのですが、かなり多くのオペレータについての簡単なサンプルがあります。

f:id:gesyutapo:20180713012658p:plain

今回はこの中からTimer CHOPについてその中身を見てみたいと思います。

なぜTimer CHOP?

仕事上かなり使いこなす必要がありそうなのと、「ちゃんと使いこなせるとすごい役に立つけど、影が薄いCHOP三銃士」のうちの一人だからです!

ちなみに、Timer CHOPのSnippetsは以下のものがあります。

  1. why timer
  2. basic timer
  3. output channels
  4. delay, cycle, done
  5. segments - serial
  6. segments begin
  7. segment interpolate
  8. interpolate external
  9. segments parallel
  10. text per segment
  11. callbacks
  12. initialize actions
  13. cycle end alert
  14. channel history
  15. time scrubber go to
  16. timers overlapping
  17. state machine game
  18. start hold loop end
  19. sequence animation
  20. jump to segment
  21. timer back-to-back
  22. movie sequencer
  23. sequence channels

全部で23個もあります。 他のCHOPを見てもこれだけ多いSnippetがあるのはなかなかありません。 ここからどれだけTimer CHOPが高機能で、色々と出来るかが伝わるかと思います。

以下、出来るだけこの23個を全部紹介しようと思いますが、かなり多いので記事をいくつかに分けます。 また解説の原文は英語なので、雰囲気の翻訳文(ほぼGoogle翻訳)も載せています。私、英語は全然わからないので、ご参考レベルでどうぞ。(ツッコミあったらお願いします!)

(1) Timer CHOP:why timer

f:id:gesyutapo:20180713015415p:plain

説明(原文)

The Timer CHOP was created because this was too hard:

Ring a bell after 5 seconds.

... too many OPs, too many parameters, click Reset on the Speed CHOP... too much thinking to get right.

説明(翻訳)

Timer CHOPを作ったのは、それがとても難しいからです:

5秒後にRing a bellを表示する

... とても多くのオペレータ、とても多くのパラメータ、Speed CHOP上のResetをクリック... きちんと動作させるめに、とても多くの事を考慮しています。

解説

何故Timer CHOPを作ったのか?それはタイマー的な動作と同じような事をするのが大変だからさ!という事のようです。

サンプルのノードは、Timer CHOPを使用せずに5秒経過すると「Ring the bell.」とダイアログを表示しているようです。これは非常に単純な例なので、このくらいで済んでますが、確かにタイマーに欲しい機能を入れていくととんでもないものになりそうだなぁという事がわかると思います。


(2) Timer CHOP:basic timer

f:id:gesyutapo:20180713020646p:plain

説明(原文)

This is the basic timer with Length that you can set to any number of seconds.

Click Initialize to get ready, then Start to  begin counting.

Or just press Start at any time to initialize and begin counting right away.

You can set Length to seconds, frames or samples using the menu on the right of Length. 

説明(翻訳)

任意の秒数を設定する事が出来る、基本的なタイマーです。

Initializeをクリックすると準備ができ、それからStartをクリックするとカウントを開始します。

あるいはすぐにStartを押して、初期化とカウント開始をしてください。

メニューのLengthの右側で、秒数、フレーム数、サンプル数でカウントの長さを設定できます。

解説

タイマーといえばこれ。指定した秒数までカウントする本当に基本的なタイマーのようです。

パラメータの赤く囲んだところで6秒を指定しています。ちなみに「S」のところはドロップダウンメニューになっていて、ドロップダウンメニューを開くと「フレーム数」「サンプル数」に単位を変更できるようです。

f:id:gesyutapo:20180713021443p:plain

とりあえず超基本的なところで、今回はおしまい。 また次回!!(ちゃんと続くのか!?)

Unityちゃんトゥーンシェーダー2.0のアウトラインが、HoloLensのMixedRealityCaptureで透ける問題

問題

Unityちゃんトゥーンシェーダー2.0を適用したモデルが、HoloLens実機で見るとアウトラインが透けずにちゃんと見えているのに、MixedRealityCaptureを通してみるとアウトラインが透けてしまうという問題に遭遇。(画像左がHoloLensをスマホカメラで直撮り、右がMixedRealityCaptureで録画)

f:id:gesyutapo:20180324115527j:plain

どうしたものかとTweetしたのが以下のもの。

この時はトゥーンシェーダーが問題と思っていなくて、アウトラインがほぼ黒(0x0A0A0AFF)だったのが原因なのかな?と思っていたのですが、

デコ・シさん(@Ash_Yin)から教えてもらった、凹みさんの記事を読んでみるとなるほど、と。 実機では黒が透けるけど、録画の場合は黒ではなく純粋にアルファで抜かれるっぽい。

仮説1

ここで最初に立てた仮説は、アウトラインは法線を反転させて表現している(ノーマル反転押出法式)ため、法線が反転すなわちポリゴンの裏側という事で、バックフェイスカリングが効いてなんやかんやで最終的なグラフィックスプレーンにはアルファゼロが書き込まれてしまっているのではないか?と。

そこで、シェーダー設定の「Cull Mode」を「BACK」から「OFF」に変更してみたけど、解決せず。

仮説2

こうなるともうシェーダーの中身を見るしかない。 シェーダー全然わからないけど、きっとアルファの計算とかしてるところがあるだろうから、そこを強制的にイジればなんとかなるだろう。くらいの気持ちでシェーダーコードを見てみる。

なお、アウトライン関連の処理は「UCTS_Outline.cginc」に分離されているので、めっちゃ見やすい!ありがたい! コメントなどを含めても60行弱のコードなので、シェーダーがまったくわからない俺でもなんとかかんとか見る事ができそうです。

頑張って読んでいると、フラグメントシェーダーの最後に気になる記述が。

return fixed4(Set_Outline_Color,0);

あれ?fixed4()の最後の引数ってアルファ?これがゼロで固定されて・・・る? 試しにこれを以下のように書き換えてみる。

return fixed4(Set_Outline_Color,1.0);

これで実行してみると、

f:id:gesyutapo:20180324121709j:plain

キ、キター!!!

と、いうわけで、何とか無事にUnityちゃんトゥーンシェーダー2.0を適用したモデルをMixedRealityCaptureで見ても、アウトラインが透けなくなりました。 でも、これ勝手にアルファいじちゃって良いんだろうかー・・・他の機能に何か影響してるんだろうか。何か知っている方いましたら、教えてください!!

ユニティちゃんライセンス

この作品はユニティちゃんライセンス条項の元に提供されています

【小ネタ】HoloLensでShift-JISを扱う方法

知らなくても全然困らないけれど、知っているとどこかで役に立つかもしれないシリーズ。

今回はShift-JISをHoloLens(UWP)で扱う方法を書きたいと思います。
最近ではほとんどUnicodeエンコーディングされているので使う機会は少ないのですが、QRコードの標準的なテキストエンコードがShift-JISなので、日本語が含まれるQRコードなんかを読み込む時に役にたつかもしれません。*1

まず初めに、通常文字エンコードの変換などを行う際に Encoding.GetEncoding() を使うかと思うのですが、UWPの場合そのままではShift-JISは使えません。

#if UNITY_WSA && !UNITY_EDITOR
Encoding Shift_JIS = Encoding.GetEncoding("shift_jis");    // ← NG
#endif

という風に書いて、使用しようとすると実行時に以下のExceptionが発生します。

ArgumentException: 'shift_jis' is not a supported encoding name. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method.

というワケで、うまく動きません。 そこでおまじない的に次に示すEncoding.RegisterProvider(CodePagesEncodingProvider.Instance);Encoding.GetEncoding("shift_jis");の前に呼び出してあげる必要があります。

#if UNITY_WSA && !UNITY_EDITOR
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);    // ←おまじない的にコレが必要
Encoding Shift_JIS = Encoding.GetEncoding("shift_jis");            // ← OK
#endif

正直、何故このおまじないが必要なのかはよくわかっていないのですが、UWPでは標準で「Unicode」しか対応しておらず、このおまじないを呼び出す事でWindowsシステム内のEncodingを取得できるようになるようです。 つまりデバイスに搭載されているWindowsがShift-JISをサポートしていなければ、この方法でもダメなようです。 幸いにもHoloLensではうまく動きます。

おまけにShift-JISで取得した文字列をUTF8へ変換するには以下のようなコードを書きます。

#if UNITY_WSA && !UNITY_EDITOR
var utf8bytes = Encoding.Convert(Shift_JIS, Encoding.UTF8, Shift_JIS.GetBytes(shiftjisStr));
var utf8Str = Encoding.UTF8.GetString(utf8bytes);
#endif

これで通常通りUTF-8の文字列として処理できるようになるはずです。 ちなみにこれら、例によってUWPでのみ使用可能なので、#ifディレクティブでUnityEditorでは動かないようにしましょう。


*1:QRコードをバイナリモードにしてUTF-8なんかの文字を埋め込む事も可能なようです

お手軽簡単ノーコーディングでシェアリング機能を実現する

今回はシェアリング機能を持つアプリを作る時に、ベースとなるシンプルなやつを手軽にパパッと用意したい!という自分自身の要望によりこの記事を書いています。

ちなみにMixedRealityToolkitの中にあるSharingTestも十分シンプルなのですが、IPアドレスの設定がマニュアルで出来るようになっていたりと個人的に余計なものがくっついているので、こいつよりもシンプルを目指します。

仕様

  1. 起動後、自動的にSharingService.exeへ接続しにいく
  2. お互いの頭の位置と回転を共有する

用意するもの

  1. MixedRealityToolkit-Unity ver1.2017.2.0
  2. Unity 5.6.2f1

手順

  1. HoloToolkitとHoloToolkit-Examplesをインポートする
  2. Sharing関連のコンポーネントを配置する
  3. SharingServiceに接続するための設定を行う

なんとたったこれだけの手順で、上記仕様を満たすシェアリング機能を使ったアプリのベースが出来てしまいます。 なおこの手順の中でスクリプトをコーディングする事は一切ありません。

1. HoloToolkitとHoloToolkit-Examplesをインポートする

f:id:gesyutapo:20171226204658p:plain

https://github.com/Microsoft/MixedRealityToolkit-Unity/releases/tag/v1.2017.2.0へアクセスします。

  • HoloToolkit-Unity-Examples-v1.2017.2.0.unitypackage
  • HoloToolkit-Unity-v1.2017.2.0.unitypackage

の2つをダウンロードしてきて、Unityのプロジェクトへインポートします。

2. Sharing関連のコンポーネントを配置する

まずHierarchyパネルから[Create]-[CreateEmpty]を選択して、適当に空のGameObjectを作成します。 これ以降の手順でアタッチするコンポーネントは全て、この新たに作成したGameObjectへアタッチしていきます。

HoloToolkitから必要なコンポーネントをアタッチします

f:id:gesyutapo:20171226204920p:plain

Assets/HoloToolkit/Sharing/Scripts/からそれぞれ以下の3つのスクリプトをアタッチします。

  • SharingStage
  • Utilities/SharingWorldAnchorManager
  • Utilities/AutoJoinSessionAndRoom

HoloToolkit-Examplesから必要なコンポーネントをアタッチします

f:id:gesyutapo:20171226205734p:plain

Assets/HoloToolkit-Examples/Sharing/SharingService/Scripts/からそれぞれ以下の2つのスクリプトをアタッチします。

  • CustomMessages
  • RemoteHeadManager

3. SharingServiceに接続するための設定を行う

ここまでの手順で、シェアリング機能を実現するためのコードは全て揃いました。 ここでは、シェアリング機能を実現するための設定を行います。

SharingService.exeを動かすPCのIPアドレスを設定する

f:id:gesyutapo:20171226210610p:plain

SharingStageコンポーネントの設定項目「Server Address」に、サーバーとなるPCのIPアドレスを設定してください。

インターネットアクセスを許可する

f:id:gesyutapo:20171226211203p:plain

Unityのメニューバーから、[Edit]-[Project Settings]-[Player]を選択して、Inspectorパネルで「Publishing Settings」を選択します。 その中のCapabilitiesから以下の2つの項目のチェックをいれます。

  • InternetClient
  • InternetClientServer

SharingServiceを動かす

最後にシェリング機能を実現するためのSharingServiceをサーバーとなるPCで実行します。

f:id:gesyutapo:20171226211653p:plain

Unityのメニューバーから、[Mixed Reality Toolkit]-[Sharing Service]-[Launch Sharing Service]を選択します。 すると以下のようなコマンドプロンプトウィンドウが起動します。

f:id:gesyutapo:20171226212032p:plain

上記までの手順を行ってからHoloLensへデプロイすれば、シェアリング機能は実現できます。もちろんこれだけではアプリケーションとしては不十分なので、これをベースにシェアリング機能の実装をしていくと、理解がなかなか難しいシェアリング機能も理解しやすいのではないでしょうか。

実行画面

youtu.be

これはUnity上で実行したものと、Hololensで実行したものとでシェアリングしている様子をキャプチャしたものです。Unityで実行されたものとシェアリング出来るので、HoloLensが1台あれば一応の動作確認が出来ます。

HoloLensでAssetBundleDemoを動かす

皆さんお馴染みのAssetBundle。
AssetBundleはちゃんと使おうと思ったら色々と闇が深そうですが、とりあえずUnity Technologiesさんが公開しているAssetBundleDemoをHoloLensで動かしてみたいと思います。

なおAssetBundleDemoは以下にあります。

bitbucket.org

AssetBundleDemoの中でも、今回は「AssetBundleLoader」シーンを動かしてみたいと思います。AssetBundleLoaderシーンを動かすための手順は大きく以下の3つです。

  1. AssetBundleをビルドする
  2. AssetBundleServerを起動する
  3. デプロイ & 実行する

この手順自体はHoloLensであっても、他のプラットフォームであってもそう変わらないと思います。 ですが、UWP/HoloLens向けに多少ソースコードを修正しないと実行できないので、手順を追いながら適宜HoloLensで実行できるように変更していきたいと思います。

手順1:AssetBundleをビルドする

まずはAssetBundleとしてサーバーへ配置するファイル群を生成します。

ビルドターゲットをUWPへ変更する

f:id:gesyutapo:20171120170234p:plain
BuildSettingsのPlatformをUWPへ変更

Unityのメニューバーから[File]-[Build Settings]を選択すると、上記のダイアログが表示されます。 この画像の通り、Platformから「Windows Store」を選択し、「Switch Platform」ボタンを押下します。 UnityのロゴマークWindows Storeのところに表示されたら、変更完了です。

AssetBundleビルドする

Unityのメニューバーから[Assets]-[AssetBundles]-[Build AssetBundles]を選択します。

f:id:gesyutapo:20171220185756p:plain

これでUnityのプロジェクトディレクトリに「AssetBundles」ディレクトリが生成され、その中にアセットバンドルファイルが生成されて・・・いません

どうやらビルドに失敗して、UnityのConsoleに以下のエラーが表示されてしまうようです。

ArgumentNullException: Argument cannot be null.
Parameter name: path2
System.IO.Path.Combine (System.String path1, System.String path2) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.IO/Path.cs:115)
AssetBundles.BuildScript.CreateAssetBundleDirectory () (at Assets/AssetBundleManager/Editor/BuildScript.cs:19)

【修正1】UWP向けのプラットフォーム名を返すようにする

エラーメッセージを見ると、「Assets/AssetBundleManager/Editor/BuildScript.cs」の19行目、System.IO.Path.Combine()を呼び出しているところの引数path2がnullのようです。 ここをソースコードで確認すると以下のような感じになっています。

static public string CreateAssetBundleDirectory()
{
    // Choose the output path according to the build target.
    string outputPath = Path.Combine(Utility.AssetBundlesOutputPath, Utility.GetPlatformName());
    if (!Directory.Exists(outputPath))
        Directory.CreateDirectory(outputPath);

    return outputPath;
}

Path.Combine()の引数path2の部分は恐らく Utility.GetPlatformName() の部分だと思われます。 このUtilityクラスは「Assets/AssetBundleManager/Utility.cs」にあるらしいので、そちらを見てみます。

public static string GetPlatformName()
{
#if UNITY_EDITOR
    return GetPlatformForAssetBundles(EditorUserBuildSettings.activeBuildTarget);
#else
    return GetPlatformForAssetBundles(Application.platform);
#endif
}

このGetPlatformForAssetBundlesがnullになっているために、エラーが発生していそうです。 このメソッドはビルドターゲットやアプリケーションのプラットフォームに応じて、そのプラットフォームに対応する文字列を返してくれるっぽいです。

つまり先に答えを言ってしまうと、このメソッドの中では各プラットフォーム別にswitchで対応文字列を返しているのですが、UWP向けの条件がないためにnullが返ってきています。 と、いうワケで以下のコード中のコメントで // Add と書いている部分を4行ほど追加してUWPへ対応させてみましょう。

private static string GetPlatformForAssetBundles(BuildTarget target)
{
    switch (target)
    {
        case BuildTarget.Android:
            return "Android";
#if UNITY_TVOS
        case BuildTarget.tvOS:
            return "tvOS";
#endif
        case BuildTarget.iOS:
            return "iOS";
        case BuildTarget.WebGL:
            return "WebGL";
        case BuildTarget.WebPlayer:
            return "WebPlayer";
        case BuildTarget.StandaloneWindows:
        case BuildTarget.StandaloneWindows64:
        case BuildTarget.WSAPlayer:                  // Add
            return "Windows";
        case BuildTarget.StandaloneOSXIntel:
        case BuildTarget.StandaloneOSXIntel64:
        case BuildTarget.StandaloneOSXUniversal:
            return "OSX";
        // Add more build targets for your own.
        // If you add more targets, don't forget to add the same platforms to GetPlatformForAssetBundles(RuntimePlatform) function.
        default:
            return null;
    }
}
#endif

private static string GetPlatformForAssetBundles(RuntimePlatform platform)
{
    switch (platform)
    {
        case RuntimePlatform.Android:
            return "Android";
        case RuntimePlatform.IPhonePlayer:
            return "iOS";
#if UNITY_TVOS
        case RuntimePlatform.tvOS:
            return "tvOS";
#endif
        case RuntimePlatform.WebGLPlayer:
            return "WebGL";
        case RuntimePlatform.OSXWebPlayer:
        case RuntimePlatform.WindowsWebPlayer:
            return "WebPlayer";
        case RuntimePlatform.WindowsPlayer:
        case RuntimePlatform.WSAPlayerARM:         // Add
        case RuntimePlatform.WSAPlayerX64:         // Add
        case RuntimePlatform.WSAPlayerX86:         // Add
            return "Windows";
        case RuntimePlatform.OSXPlayer:
            return "OSX";
        // Add more build targets for your own.
        // If you add more targets, don't forget to add the same platforms to GetPlatformForAssetBundles(RuntimePlatform) function.
        default:
            return null;
    }
}

【修正2】GetStreamingAssetsPath()で適当なパスを返すようにする

上記を修正しても、今度は以下のようなエラーが表示されてAssetBundleのビルドに失敗します。

Assets\AssetBundleManager\AssetBundleManager.cs(162,56): error CS0117: 'Environment' does not contain a definition for 'CurrentDirectory'

問題となるコードはAssets/AssetBundleManager/AssetBundleManager.csのようです。

private static string GetStreamingAssetsPath()
{
    if (Application.isEditor)
        return "file://" +  System.Environment.CurrentDirectory.Replace("\\", "/"); // Use the build output folder directly.
    else if (Application.isWebPlayer)
        return System.IO.Path.GetDirectoryName(Application.absoluteURL).Replace("\\", "/") + "/StreamingAssets";
    else if (Application.isMobilePlatform || Application.isConsolePlatform)
        return Application.streamingAssetsPath;
    else // For standalone player.
        return "file://" + Application.streamingAssetsPath;
}

上記コードの中の

return "file://" +  System.Environment.CurrentDirectory.Replace("\\", "/"); // Use the build output folder directly.

のところで、System.Environment.CurrentDirectoryが存在しない定義だと怒られているようです。

これはUnityがHoloLens用にビルドする際の.NetCore1.1ではSystem.Environment.CurrentDirectoryプロパティが存在しないためにエラーになっているようです。 .NetCoreのドキュメントを見ると、確かに.NetCore2.0からSystem.Environment.CurrentDirectoryプロパティが追加されたようなので、.NetCore1.1では存在しないようです。

しかしここのコード、実は何にしても影響ないので、先ほどのコードを適当なものへ変更します。

private static string GetStreamingAssetsPath()
{
    if (Application.isEditor)
        return string.Empty;      // ← 適当に変更
    else if (Application.isWebPlayer)
        return System.IO.Path.GetDirectoryName(Application.absoluteURL).Replace("\\", "/") + "/StreamingAssets";
    else if (Application.isMobilePlatform || Application.isConsolePlatform)
        return Application.streamingAssetsPath;
    else // For standalone player.
        return "file://" + Application.streamingAssetsPath;
}

はい、適当にstring.Emptyにしちゃいました。 真面目にやるならSystem.Environment.CurrentDirectoryに代わる、.NetCore1.1で使用可能なカレントディレクトリを取得する方法を用いて設定してあげましょう。

改めてAssetBundleビルドを行う

上記修正が終わったら、改めてAssetBundleのビルドを行います。 再びUnityのメニューバーから[Assets]-[AssetBundles]-[Build AssetBundles]を選択します。

これでUnityのプロジェクトディレクトリに「AssetBundles」ディレクトリが生成され、その中にアセットバンドルファイルが生成されていると思います。

f:id:gesyutapo:20171220190958p:plain

手順2:AssetBundleServerを起動する

Unityのメニューバーの[Assets]-[AssetBundles]-[Local AssetBundle Server]を選択します。

f:id:gesyutapo:20171220191428p:plain

これで先ほどの「AssetBundles」ディレクトリの中身が公開されるようになります。

ServerとなるPCのIPアドレスを指定する

上記のLocal AssetBundle Serverを動かしているPCのIPアドレスを設定して、HoloLensからアクセス出来るようにします。

Assets/AssetBundleSample/Scripts/LoadAssets.csに指定します。

// Initialize the downloading URL.
// eg. Development server / iOS ODR / web URL
void InitializeSourceURL()
{
    // If ODR is available and enabled, then use it and let Xcode handle download requests.
    #if ENABLE_IOS_ON_DEMAND_RESOURCES
    if (UnityEngine.iOS.OnDemandResources.enabled)
    {
        AssetBundleManager.SetSourceAssetBundleURL("odr://");
        return;
    }
    #endif
    #if DEVELOPMENT_BUILD || UNITY_EDITOR
    // With this code, when in-editor or using a development builds: Always use the AssetBundle Server
    // (This is very dependent on the production workflow of the project.
    //      Another approach would be to make this configurable in the standalone player.)
    AssetBundleManager.SetDevelopmentAssetBundleServer();
    return;
    #else
    // Use the following code if AssetBundles are embedded in the project for example via StreamingAssets folder etc:
    AssetBundleManager.SetSourceAssetBundleURL(Application.dataPath + "/");
    // Or customize the URL based on your deployment or configuration
    //AssetBundleManager.SetSourceAssetBundleURL("http://www.MyWebsite/MyAssetBundles");
    return;
    #endif
}

上記コードの19行目あたりから、以下のように変更します(抜粋)。

#else
// Use the following code if AssetBundles are embedded in the project for example via StreamingAssets folder etc:
//AssetBundleManager.SetSourceAssetBundleURL(Application.dataPath + "/");      // ←コメントアウト
// Or customize the URL based on your deployment or configuration
AssetBundleManager.SetSourceAssetBundleURL("http://(ServerPCのIPアドレス):7888/");   // ←コメントアウトを外してIPアドレスを指定
return;
#endif

接続先は

http://(ServerPCのIPアドレス):7888/

となります。

UWPのインターネット受信許可を与える

UWPでは、セキュリティの関係でそのままではインターネットアクセスなどが行えません。これはAndroidiOSなんかと一緒で、必要な機能の許可設定をしてあげる必要があります。 今回はAssetBundleServerとなるPCへの接続を行うため、「InternetClient」の許可を行います。

まずはUnityのメニュバーで[Edit]-[Project Settings]-[Player]を選択します。

f:id:gesyutapo:20171220201600p:plain

選択するとInspectorパネルにPlayerSettingsが表示されるので、「Publishing Settings」を選択して「InternetClient」を選択してチェックを入れます。

f:id:gesyutapo:20171220202057p:plain

手順3:デプロイ&実行する

これで一通りの準備が整いました。 HoloLensへデプロイして、実行してみましょう。

f:id:gesyutapo:20171220200737j:plain

これでひとまずHoloLensでAssetBundleManagerを利用したAssetBundle機能を使うことが出来ました。


参考URL

mtaulty.com

【小ネタ】HoloLensのデバイス名をスクリプトから取得する方法

知らなくても全然困らないけれど、知っているとどこかで役に立つかもしれないシリーズ。

HoloLensのDevicePortal画面で表示されている「Device name」をUnityのC#スクリプト上で取得する方法です。

f:id:gesyutapo:20171212134320j:plain

コードは以下の通りです。

#if UNITY_WSA && !UNITY_EDITOR
        var deviceInfo = new Windows.Security.ExchangeActiveSyncProvisioning.EasClientDeviceInformation();
        string deviceName = deviceInfo.FriendlyName;
#endif

Windowsネームスペース以下はUWPでしか使えないので、Unityエディタ上では動かないように #if ディレクティブで区切りましょう。

ちなみにこの EasClientDeviceInformation クラス、他にも色々とプロパティありますが、HoloLensの場合は他はあまり有用な情報は取れ無さそうでした。

新卒から12年勤務していた会社を辞めました

この記事は 退職者 Advent Calendar 2017 - Adventar の11日目の記事です。

adventar.org

というわけでタイトルの通り、新卒で入社して12年間勤務していた会社を2017年9月末をもって退職しました。 10月から現職で働き始めており、在籍期間がまだ2か月くらいと短いため、現職の事についてはあまり深い話は出来ません。

TL;DR

なんか調子にのって書いていたら長くなっちゃったので、要約すると

OculusRiftかぶったらもうVRの事しか考えられないし、HoloLensもやべぇ!
メディアアートとかもやりたいぜぇぇぇぇえ!!
うぉおおおお!!転職しちゃおー!!

です。

自己紹介

3歳になる娘がいます。
めちゃくちゃかわいいです。
最近ホッペにチューするのを嫌がるようになったけど、その断り方が「お化粧しているから、チューするとお化粧パパについちゃうし、とれちゃう」という大人な返しをしてきます。 おもちゃのお化粧です。

あ、これ自己紹介じゃなくて、娘紹介でしたね。

前職について

前職は某家電メーカーのソフトウェア専門子会社でGUI関連の開発を中心としたエンジニアをやっていました。 家電メーカー子会社ということもあり組み込み向けの仕事が多いところでしたので、私も組み込み向けの仕事をしていた事もありましたが、私の場合はちょっと特殊でWindowsアプリやAndroidアプリ層の開発を行っていた時期の方が長かったかもしれません。

前職に入るまで

もう12年も前の話なので今の新卒の人には全然参考にならないかもしれませんが、せっかくなので当時前職に入るまでの話、すなわち新卒の就職活動を振り返ってみたいと思います。 2004年入社なので、就職活動をしていた2003年当時は確か就職氷河期の最悪期から、ちょっと立ち直り始めているかな?くらいの時期で、最悪とまでは言わないけれど大変な時だったと思います。

応募するキッカケは就職活動が始まるくらいに自宅に届いた新卒募集のダイレクトメールです。当時使っていたVHSビデオデッキがそのメーカーのものでリモコンが使いづらくてしょうがなかったので、ちょっと文句言うついでに応募してみるか、くらいの軽い気持ちで応募しました。とはいえ親でも知っているメーカー系という事で、第一志望でもありました。

そんな軽い気持ちで応募したものだから、選考の日程もかなり早いやつを適当に選んでしまい、就職活動を始めてから最初のSPI試験と面接(両方とも同じ日に行われた)が第一志望の前職だった、という状況でした。 会社説明会自体は大手/中小SI、ゲームメーカー等々に参加はしていましたが、選考までは進んでいませんでした。

前職の面接が初めての面接だったのですが、私の数少ない新卒面接の経験の中では一番楽しかったです。 もちろん日頃のリモコンに対するうっぷんを晴らせたというスッキリ感もあったのですが、一番覚えている楽しかった質問が

「ここにあなたを含めて100人いるとして、あなたは何番目くらいだと思いますか?」

というもの。当時ちょっと流行っていた答えのない質問ってやつなんですかね。

私は中途半端な「51番目」と答えました。 理由としては自分よりも能力が上の人はいっぱいいるし、同様に下の人もいっぱいいる。 だから恐らく半分くらいだろうと。しかしまったく真ん中の「50」では、そこで安定してしまって上を目指さなくなるかもしれない。だからちょっと下の「51番目」で上を向いて行きたい。

みたいな事を言ったような気がします。 個人的には今同じ質問が突然きてもそんな回答は出来ないよってくらい良い回答しました(自画自賛)。 きっと、リモコンのうっぷんが晴れたスッキリ感のおかげでしょう。

他にも履歴書の趣味に絵を描く事だとか書いていたので、面接の最後に当時の人事部長の似顔絵を描いたりもしました(※好きなだけで下手です)。 人事部長と社長との2対1の面接で似顔絵を描きながら雑談をするという謎の空間が出来上がっていましたが、楽しかったです。

そんなこんなあって、前職に入ることになりました。

前職に在籍していた12年間は、大きく3つに分けられます。

  1. 社内向けGUI開発ツールの開発(約5年)
  2. カーナビGUI開発(約6年)
  3. Windowsアプリ開発(約1年)

社内向けGUI開発ツールの開発

リモコンの操作性の悪さについてしゃべり過ぎたせいか、最初に配属された先はユーザービリティ研究を行っているところでした。
場所は横須賀の方で当時「研究所」と呼ばれていたところです。 多くの人は製品開発の方(横浜)へ配属され、研究所は同期50名くらい居た中で私を含めて4名だけでした。

このユーザービリティ研究部門は大きく2つのグループがあり、一つは本当にユーザーに機器を使ってもらってアンケートを収集して分析したり、ペルソナを使ったUXの向上とかそういった事を研究しているグループ、もうひとつは自社製品のGUI開発を効率化することを目的とした、GUIを開発するためのWindowsアプリケーションの開発および、そのGUIの実行ランタイムエンジンの開発を行っているグループでした。

私は後者の方の開発に携わることになりましたが、当時私が入るまでこのGUI開発ツールの開発はグループといっても1人で、しかも開発している人は立場的にはめちゃくちゃ偉い人だし、なのにめちゃくちゃコーディングするし、書くコードも無駄がないし、新卒のちょっと授業でCとかJavaやりましたレベルの私にとっては「なんかヤベェところにきちまった・・・」という感想だったのを覚えています。

それでもなんとか2年くらい頑張っていたら、後輩が2人同じ部署に配属されてきました。 この後輩2人が2人ともこれまた相当にヤバい人達で、当時もしも新卒面接の時の質問「ここにあなたを含めて100人いるとして、あなたは何番目くらいだと思いますか?」という質問をされていたら、100%ぶっちぎりで100番!!!!と答えていたことでしょう。この世界には遥か上のヤバい人たちしか居ない!

当時は周りについていくために必死でしたが、振り返ってみるとヤバい人たちしか周りに居なかったあの環境は恵まれてたなー!!最高だったなー!!と思います。

カーナビGUI開発

会社の状況がどんどこ悪くなる中で、研究開発が縮小方向へと向かい、GUI開発ツールも開発中断となりました。 開発中断となる2年くらい前に、子会社である我々がこのGUI開発ツールを全て開発・サポートするという事で全てを引き取ってハンドリングする立場にありました。 しかし時代と共に家電の性能も上がりGUIに求められるクオリティが向上する一方、従来型のあまりリッチではないGUIを必要とする機器も存在し、その格差がどんどん広がっていく中で、その全てをカバーしようと手を広げ過ぎた事によって「帯に短し襷に長し」な中途半端な立ち位置のツールにしてしまったのが中止となった原因でもあります。 特に少人数の開発でしたので、ターゲットを絞って開発していたら、もっと違った結果になったかもしれません。

前置きが長くなりましたが、そういうわけで研究開発から製品開発であるカーナビ開発へ転属となりました。
場所は群馬県前橋市。未開の地「グンマー」として恐れられている地です。そこに運転免許も持たずに転勤することになりました。(転勤後すぐに運転免許とりました)

入社6~7年目、真剣に転職を考えた最初のタイミングです。

しかし、前橋で出会ったナビ開発を主導していた偉い人がこれまたとんでもないヤバい人で、製品のコンセプトに対する信念が強くて、その信念を守るためなら他は何を犠牲にしても良い!という明確なビジョンを持って開発を主導していました。直前にコンセプト不在で社内向けGUI開発ツールの開発中断を経験していたために、その力強さに惚れ込んで結局転職するのを辞めました。

ナビ開発で担当していたのは、GUI開発のためのオレオレフレームワークの開発、メンテ等々。他にも技術的困難な機能のものを部品として提供するための先行開発的な事をしていました。ナビ開発の忙しさは時期にもよりますが、プラットフォームを全面的に変更する時なんかは毎日のように終電ダッシュ(終電に間に合うように会社から駅までダッシュ)したり、昼休みはパンを食べながらずっと仕事してました。3~4ヵ月くらい昼休みに休んだ記憶がありません。が、めちゃくちゃやりがいはありましたし、良いもの作ってる感覚があって、個人的にはそこまで辛みは無かったです。むしろ楽しかったです。ちなみに前橋は最初の1年だけで、そのあとはナビ開発拠点ごと八王子へ移転したので、ナビ開発2年目からは八王子へ引っ越してます(前橋は車/自転車通勤が大多数で電車の終電がないため、終電ダッシュが存在しない)。

頑張ったかいもあって(?)ナビの売り上げは順調に上がっていって、気が付けば会社を支える事業に育っていました。私がナビ開発に配属された当時は社内でもあまり存在感のない部署だった気がします。そうやって成長してくると製品ラインナップが異常に増えてきて、これまでの尖った何を犠牲にしてもコレ!というものを追求するという姿勢よりは、既存のものを使いまわしたり見た目だけを変えたりするような形となり、先行開発的な事は減っていき、大量の人を使って大量のものを安く一定品質で作り続ける事が重要となってきました。つまり、どちらかというと技術を使うことよりも、人を使うことの方が多くなっていったのです。会社的には儲かっているので良いのですが、個人的には興味を無くしていき、当時リーダーという立場でしたが、何だかよくわからない雑務で時間ばっかりとられるし、おもしろくないなぁと思い自らリーダーを辞め、空いた時間で何か面白いことしたいなぁと模索していた時期がありました。

そこで出会ったのがOculusRift DK2やGoogle CardboardといったVRの世界でした。

これはヤバい!!!と思いましたが、当時は高スペックなPCも持っていなかったので、Google Cardboard互換のタオバイザーなんかを買って一人すげぇぇええ!すげぇええ!!と唸ってました。

ここで一気にVR業界に転職かっ!?と思いましたが、なんとほぼ同時期に結婚&出産というイベントが発生し、まだ海のものとも山のものともつかない世界に飛び込むワケにもいかず(私自身スキルもあるわけではなかったですし)、とりあえず安定した生活費を稼ぐため会社に居続ける事になったのでした。

Windowsアプリ開発

なんやかんやあって2016年にナビ開発から抜け、Windowsアプリ開発(C#)を中心とした部署へ異動になります。かなり最近の事なのであまり詳細は書けませんが、個人的に製造業の常識をサービス業に近いソリューションビジネスに持ち込むのは辞めた方がいいんじゃないかなぁと思うようなところでした。ただ、ここでも良い出会いがあり、上司でもあり一緒にアプリを開発していた方がなかなかヤバい人で、もうしばらくこの会社に居てもいいかなぁと思っていました。

とはいえ家ではVR ReadyなPCを嫁に無理言って買わせてもらい、OculusRift CV1 + OculusTouchも買い、子供を寝かしつけた後に(一緒に寝てしまわなければ)、シコシコとVRアプリを作って転職の機をうかがっていました。

そして2017年春に一緒に開発していた上司の方が退職されたのをキッカケに本気で転職活動を始め、現職に至ります。

転職活動について

転職活動にはWantedlyを使いました。
ポートフォリオというポートフォリオが無い状態でしたので、正直不安しかありませんでした。
唯一、UnityでOculusRift向けに作っていたものが以下の動画の「ホタル観賞VR(仮)」のみです。


Unity5.6 ホタル観賞VR(仮) テスト03

Unityを勉強しながら作っている未完成のこれだけで雇ってくれた現職には感謝しかありません!

ちなみに、転職活動も今のところが転職活動を始めてから初めての選考で、しかも一番良いなぁと思っていたところでした。 ほんと奇跡的としか言いようがないです。

現職はどんなところ?

ヤバい人たちしか居ません!

TouchDesignerめちゃくちゃ使ってます!(私はまだ勉強中ですが・・・)

HoloLens案件めちゃくちゃあります!

VR ReadyなノートPC支給されます!

Webサイトは↓です!
THINK & SENSE

転職して良かったか?

私は良かったと思ってます。
正直、年収とか色々な面を考えると減った部分はあります。 しかし、面白い方へ転がろうと思えば、いくらでも転がらせてくれそうな雰囲気がありますし、実際個人的にはすごい面白いです。 (※会社的には(悪い意味で)ヤベェやつ採ってしまった・・・となっているかもしれませんが・・・すみません!)

前職では昼休みとかに隠れるようにしてUnityを触っていたのが、毎日堂々とUnityを起動することができますし、HoloLensがすぐ手の届くところにある!毎日かぶれる!(仕事だから当たり前ですが)

まだまだ私は中途採用の割には価値が全然出せていないので、これから頑張って価値出していきたいです。

最後に

新卒からの12年を改めて振り返ってみると、ほんと凄い人たちとの出会いがターニングポイントごとにあったなぁと思います。そういった人達に出会う度に成長させてもらいました。本当に感謝しています。そして今も絶賛凄い人たちに囲まれてますが、その中でも存在感出していけるように頑張りたいと思います。

さらに「面白いことやりてぇ!」で転職してしまうような奴を支えてくれている妻と娘にも感謝です。 二人のためにももっと頑張っていかないとな、と思えます。

そして最後にお約束ですが、現職(ティーアンドエス)ではエンジニア&デザイナーを募集しています! Wantedlyではエンジニアの募集は終了していますが、まだまだエンジニアも募集していますし、デザイナーさんも絶賛募集中です!! 是非お願いします!

www.wantedly.com

www.wantedly.com