- 投稿日:2020-01-04T23:25:12+09:00
Oculus Link 時に FMOD failed to initialize the output device エラーが出たときの対応方法
Oculus Link しようとした時に、
FMOD failed to initialize the output device
エラーが出たときの対応方法です。このエラーが出ると、AVPro ではファイルがありませんというエラーが出るようになり、知らないと迷走するなと思いました。(実際迷走した)前提
- Windows10
- Unity 2019.2.02f
- Oculus Integration 12.0
- AVPro
- 元々開発中のプロジェクトで、本エラーが発生する前は Unity Editor で正常に動作していた
- ビルドして、adb install すれば、Oculus Questでも正常に動作していた
再現手順
- Oculusの管理アプリケーションをインストール
- Unity から Editor 実行
エラー
Unity Edtior から実行すると、
FMOD failed to initialize the output device.: "Error initializing output device. " (60)とエラーが発生した後に、AVPro からも以下エラーが発生した。
[AVProVideo] Error: Loading failed. File not found, codec not supported, video resolution too high or insufficient system resources. UnityEngine.Debug:LogError(Object) RenderHeads.Media.AVProVideo.MediaPlayer:UpdateErrors() (at Assets/AVProVideo/Scripts/Components/MediaPlayer.cs:1783) RenderHeads.Media.AVProVideo.MediaPlayer:Update() (at Assets/AVProVideo/Scripts/Components/MediaPlayer.cs:1107)解決方法
Oculus の管理アプリケーションをインストールすると、音声の箇所が下記の通り、
ヘッドホン(Oculus Virtual Audio Device)
となるようです。この設定値を変更するだけで解消されます。
- 投稿日:2020-01-04T21:36:18+09:00
NLogを使ってAzure Table Storageにログを書きこむ
NuGetを検索するとAzure Table Storage用のTargetをいくつか見かけますが、.NET Coreへの対応状況や依存ライブラリの最新化状況などの制約があって微妙です。
また、Azure Table StorageまわりのSDKはCosmosDBに切り出されたりしてややこしいので、たかがEntityの追加のみで依存関係を増やしたくありません。
そんなわけで、NLog標準のWebServiceTarget
を使った書き込みを試したら結構ハマりどころがあったので共有します。事前準備
コンソールアプリのプロジェクトを作成
ターゲットフレームワークはNLogがサポートするものであれば何でも良いですが、.NET Core 2.0〜3.0までで動作確認をしました。
NLogのインストール
NuGetからインストールします。この記事では4.6.8の利用を前提としていますが、多少前後しても問題ないと思います。
Azure StorageのSAS URLの作成
SASとはShared Access Signatureの略で、あらかじめリソースやそれに対するアクション(参照、書き込みなど)、利用可能期間を制限したトークンです。これを含むURLを使うことで、リクエスト都度署名をするといった手間も省けるためWebServiceTargetで利用する上では好都合というわけです。
作成は簡単で、Azure Portalで対象ストレージアカウントを選択し、メニューの「Shared Access Signature」に移動します。対象リソースなどを選んで「SASと接続文字列を作成する」ボタンを押下後、表示された「Table service の SAS URL」を控えておいてください。
NLogの設定
まずは以下の通り
WebServiceTarget
を設定します。名前は何でもいいですが、ここではAZTBLTarget
としてみました。NLog.config<?xml version="1.0" encoding="utf-8" ?> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" throwExceptions="true"> <targets> <target name="AZTBLTarget" xsi:type="WebService" protocol="JsonPost" url="<SAS URL>"> <parameter name="PartitionKey" layout="${callsite}" /> <parameter name="RowKey" layout="${ticks}.${sequenceid:padCharacter=0:padding=6}" /> <parameter name="LocalTimestamp" layout="${date:format=yyyy-MM-ddTHH\:mm\:ss.fffffff}" /> <parameter name="Level" layout="${level:uppercase=true}" /> <parameter name="Callsite" layout="${callsite}" /> <parameter name="Message" layout="${message}" /> <parameter name="Error" layout="${exception:format=Message, ToString:separator=\n}" /> </target> </targets> <rules> <logger name="*" minlevel="Trace" writeTo="AZTBLTarget" /> </rules> </nlog>簡単に説明すると、ターゲットの種類として
WebService
のものを定義していて、その通信手続きとしてJSONデータのPOST、そのJSONのパラメータとしてPartitionKey
やRowKey
を含む7項目を送信するようにしています。なおPartitionKey
とRowKey
はTable Storageとして必須の項目です。さて、説明の中で
url
アトリビュートの説明を飛ばしていましたが、ここがハマりポイントです。コピペしてurl
に貼り付けるだけではうまくいきません。先の手順で取得したSAS URLを、以下の通り修正する必要があります。生成されたものhttps://accountname.table.core.windows.net/?&sv=2019-02-02&ss=t&srt=sco&sp=wau&se=2030-01-04T08:21:42Z&st=2020-01-04T00:21:42Z&spr=https&sig=xxxxxxxxx%3D修正後https://accountname.table.core.windows.net/nlogtable?$format=application/json&sv=2019-02-02&ss=t&srt=sco&sp=wau&se=2030-01-04T08:21:42Z&st=2020-01-04T00:21:42Z&spr=https&sig=xxxxxxxxx%3D修正内容は以下の通り。
- URIの末尾に
/<テーブル名>
をつける。当たり前ですが、コピペでは動かず一瞬「ウッ」てなります。修正後の例は、テーブル名をnlogtable
のケースです。&
を&
に置換。これもXMLに一般的な話だと思いますが、エラーになるので小心者の私は「えっえっ」てなりました。$format=application/json
をつける。Insert Entityの仕様を見る限りAccept
ヘッダーは任意なのですが、なぜかこれを渡さないと415
エラーとなります。しかしながらNLogのWebServiceTargetではなぜかHTTPヘッダーにAccept
を設定することができませんでした。諦めかけていたところ、Payload format for Table service operationsを読んだら$format
で指定できることを発見したため、これをURLに含んでやります。ログの書き込み
それでは実際に書き込んでみましょう。本記事のポイントは設定方法ですので、書き込み自体は一般的な手順に従えばOKかと思います。ちょっとハマるかもしれないポイントとしては、最終行の
LogManager.Shutdown();
を実行しないと、ログ送信前にプロセスが終了してしまいTable Storgeに書き込まれない場合があるところでしょうか。using NLog; namespace NLogToAzureTable { class Program { static void Main(string[] args) { // NLog.configを出力ディレクトリにコピーするように設定してください。 // もしくは、以下の通り明示的にパスを指定することもできます。2つめの引数falseはエラーを無視するか否かの設定です(false=無視しない) // LogManager.Configuration = new XmlLoggingConfiguration("/path/to/NLog.config", false); // ロガーの初期化・取得 var logger = LogManager.GetCurrentClassLogger(); // ログの書き込み logger.Info("test message for info"); logger.Warn("test message for warn"); // ロガーの終了。プロセスを終了する前にログを送信するために必要 LogManager.Shutdown(); } } }非同期書き込みの利用
NLogには便利な非同期処理の仕組み
AsyncWrapper
ターゲットがあります。ログメッセージをキューに貯めてバッチ的に別スレッドで処理してくれるといった便利なものです。
使い方は超簡単で、targets
のasync
アトリビュートにtrue
を設定するだけで全てのターゲットへの書き込みが非同期に行われます。NLog.config:中略 <targets async="true"> :中略一部の書き込みのみを非同期にしたい場合は、ラッパーの名の通り非同期にしたいターゲットを
AsyncWrapper
で囲みます。このとき、rule
に設定するname
もラッパーのものにすることに注意です。NLog.config<targets> <target name="AZTBLTargetAsync" xsi:type="AsyncWrapper"> <target name="AZTBLTarget" xsi:type="WebService" protocol="JsonPost" url="<SAS URL>"> <parameter name="PartitionKey" layout="${callsite}" /> :中略 </target> </target> </targets> <rules> <logger name="*" minlevel="Trace" writeTo="AZTBLTargetAsync" /> </rules>詳細は公式Wikiを読んでみてください。
https://github.com/nlog/NLog/wiki/AsyncWrapper-target補足
PartitionKey
に設定する値。記事では便宜上出力元メソッドの名前を指定していますが、ユーザーとかデバイスとかを一意に特定できるものを設定した方が調査しやすいと思います・- パフォーマンスや負荷は未知数。
AsyncWrapper
を使うことでメイン処理への影響はないと思いますが、書き込み速度や負荷は計測していません。WebServiceTargetに一般的な議論かと思います。詳しい方アドバイスください!!- 出力レベル。そもそもリモートでログを取る時点で常にデバッグ情報が欲しいわけではないと思いますので、
minlevel
をエラーに設定するなど最適化が必要です。- Logging Frameworkについて。実装ライブラリを自由に変更できるようにDIなフレームワークが.NET Coreでは用意されていますが、今回は未考慮です。
おまけ:UnityでNLogを使ってAzure Table Storageに書き込み
そもそも何でAzure Table Storageにログを書き込もうと思ったかというと、Gateboxというデバイス向けのアプリ開発をはじめたことがきっかけです。実機デバッグする際にコンソール出力やファイルファイルを選択できないことから、リモートにログ出力をする方法を探ることにしました。ログのためにサーバーを用意するのもアレなのでAzureに・・・という経緯です。
UnityのConsoleへのログ出力用ターゲットも同梱したファクトリーぽいものを作りましたのでよかったら使ってみてください。
NLogFactoryForUnity
https://github.com/uezo/NLogFactoryForUnity
- 投稿日:2020-01-04T18:28:16+09:00
UnityでHaskellのコードを実行する手法の模索
概要
Unityでゲーム制作をするためにはC#を使う。しかし、場合によっては他の言語を用いたいことがあり、Unityではネイティブプラグインという方法で、外部で生成されたDLLを利用することが可能である。
筆者は、ダンジョン生成のプログラムをHaskellで作成し、それをUnity内で利用したいという経緯があったので、今回、HaskellのシンプルなコードをCabalとStackを利用してDLLを作成し、それをUnityから利用する効率的な方法を模索した。環境はWindows 10 64bit
1. 準備
Stackで新しいプロジェクトを作る。
stack new haskell-foreign-library-test一度ビルド
stack build
して、package.yamlからcabalファイル(haskell-foreign-library-test.cabal)を生成する(バックでhpackが動いている)。後述するが、ライブラリ作成にはCabalの機能
foreign-library
を使う。しかし、現在(2019/01/04)、cabalファイルを生成するhpackは、これに未対応なようので、泣く泣くpackage.yamlは削除しておく。hpackのforeign-library対応は、議論されているようだ。
ついでに.gitignoreのcabalの行を削除して、cabalファイルをバージョン管理に含めておきたいところ。
2. HaskellプログラムのDLL作成
Haskell側の全てのコードはここ(masatoko/haskell-foreign-library-test)で見られる。
2.1. Haskellのコード
Haskellのコードは非常にシンプルなものにした。受け取った整数に1を足して返すだけの関数plus_oneだ。
これをforeign export
で出力しておく。src/Lib.hsmodule Lib where foreign export ccall plus_one :: Int -> IO Int plus_one :: Int -> IO Int plus_one x = return $ x + 12.2. hs_init関数のラッパーをC言語で作成
外部からplus_oneを利用するには、使う前にGHCのランタイムシステムを初期化し、使ってから終了させなければならない。
hs_init() -> plus_one() -> hs_exit() という流れ。
しかし、hs_init()は引数をとり、外部(C#)から利用する際、引数を渡すのが面倒になるので、hs_init()のラッパー関数hs_init_wrapper()を作成しておく。crsc/mylib.h#pragma once extern "C" { void hs_init_wrapper(void); }csrc/mylib.cpp#include "HsFFI.h" #include "mylib.h" #include "Lib_stub.h" // 今回は不要。ここでplus_one()を使うことができる。 void hs_init_wrapper(void) { int argc = 2; char *argv[] = {(char *)"+RTS", (char *)"-A32m", NULL}; char **pargv = argv; hs_init(&argc, &pargv); }mylib.cppでインクルードされているLib_stub.hは自動生成される。
(.stack-work/dist/*/build/mylib/mylib-tmp/Lib_stub.hに出力されていた。)
中身を見てみよう。Lib_stub.h#include "HsFFI.h" #ifdef __cplusplus extern "C" { #endif extern HsInt plus_one(HsInt a1); #ifdef __cplusplus } #endifplus_oneがエクスポートされている。
外部からより使いやすくするために、関数を加工して、また別の関数として出力しておくのもいいと思う。2.3. ビルド
stack build
の1コマンドでDLLを生成してくれたら嬉しい。その方法を調べた。
Cabalはv2.0.0.2(2017年7月)からforeign-library
でDLL(macの場合はdylib)を作成できるようになったらしい。これを使いたい。公式の説明haskell-foreign-library-test.caballibrary hs-source-dirs: src other-modules: Lib build-depends: base >=4.7 && <5 default-language: Haskell2010 foreign-library mylib type: native-shared if os(Windows) options: standalone other-modules: Lib build-depends: base >=4.7 && <5 hs-source-dirs: src c-sources: csrc/mylib.cpp default-language: Haskell2010これでビルドすればDLLが作成される。
stack build2.4. 生成されたDLLを確認
生成されたDLLのパスは、
stack build
のログの最後のほうに表示されている。
今回は、.stack-work/install/426b9cbc/lib/mylib.dll に生成されていた。nmコマンドで中身を確認。
nm mylib.dll以下の行を発見し、関数が出力されていることを確認した。
0000000067881620 T hs_init_wrapper 0000000067886c20 T hs_exit 0000000067881580 T plus_one3. UnityでのDLLの利用
生成したDLLをUnityで使ってみた。
開発時のEditorの挙動で少し厄介なことがあったので、それの対策が必要だった。3.1. DLLの配置
Assetsフォルダ配下にPluginsフォルダを作成し、そこにDLLを配置すればUnityが認識してくれる。
今回は素直にAssets/Plugins/mylib.dllに配置した。3.2. 各種設定を64ビットにする
生成されたDLLは64ビットのバイナリなので、Platform settings > Windows > x86_x64 だけにチェックを入れる。
また、Build SettingsのArchitectureをx86_64にしておく。
3.3. 利用するコードの記述
以下は適当な変数counterを1秒ごとに1足して、画面上のテキストに反映させるだけのプログラムである。
しかし、1点だけ注意することがある。MylibWrapper.csusing System.Runtime.InteropServices; using UnityEngine; using UnityEngine.UI; public class MylibWrapper : MonoBehaviour { [DllImport("mylib")] public static extern void hs_init_wrapper(); [DllImport("mylib")] public static extern void hs_exit(); [DllImport("mylib")] public static extern int plus_one(int x); public Text text; // counterを確認するためのText int counter = 0; private void Awake() { hs_init_wrapper(); } private void Update() { counter = plus_one(counter); print(counter); text.text = $"{counter}"; } void OnApplicationQuit() { // エディター上でhs_exit()すると次回のhs_init()でエラー。 // エディター上ではhs_exit()を実行させないのを一時的な解決方法とする。 #if UNITY_EDITOR #else hs_exit(); #endif } }エディター上での2度目の起動時に落ちる問題の解決
ghcのランタイムシステムは、hs_exit()した後にhs_init()できないようになっている。
しかし、Unityでは、エディター上でロードされたDLLはUnityを終了するまでアンロードされないため、
エディター上でゲームを開始して、hs_init_wrapper()し、ゲームを停止した時にhs_exit()し、次回のゲーム開始時に再びhs_init_wrapper()を実行するような仕組みにすると、hs_init_ghc: reinitializing the RTS after shutdown is not currently supported
というエラーが発生して落ちてしまう。ソースコードを見ると、
rts_shutdown
フラグがこれを拒んでいる。それであれば、エディター上ではhs_exit()を実行しないという方法しか残されていない。
ソースコードを見ると、hs_init()を複数回実行しても、hs_init_count
で呼び出し回数をカウントされるものの、2回目以降は何の処理もしておらず、問題ないだろう。(本当に?)
よって、エディター上ではゲーム開始のたびにhs_init_wrapper()が呼ばれるが、ゲーム終了時にはhs_exit()を呼ばないという方針とした。#if UNITY_EDITOR
~#else
~#endif
の部分。4. 実行
エディター上でも、ビルドした実行ファイルでも、うまく動作した。
5. まとめ
64bitという制約付きであるものの、HaskellのコードをUnity上で動作させることができた。
しかし、いくつか懸念もある。
- 【懸念1】 AndroidやiOSで動作するアプリを作れるのか?
Mobile Haskellというプロジェクトがあるので、これを使ってDLLを作れるかどうかにかかっていそう。検証は苦しそうなので今はしない。
- 【懸念2】 SwitchやPS4で動作するゲームを作れるのか?
それぞれのハードウェアで動作するバイナリ(DLL)が作れるのかどうか。ハードウェアの仕様がわからないので、これも未知数。
Windows上でゲームを公開するだけならば不安は無いが、将来的にモバイル端末やコンシューマー機で公開する可能性がある場合は、これが原因で断念せざるをえなくなる可能性があるので注意が必要。
- 活用方法の検討
- Haskellでダンジョン生成をしてUnityに取り込む。
- C#で書きたくない複雑な関数をHaskellで記述してUnityで使う。
- Haskellでゲームロジックを書いて、全ての描画はUnityに任せる。
- 投稿日:2020-01-04T18:28:16+09:00
UnityでHaskellのコードを実行する方法の模索
概要
Unityでのゲーム制作には基本的にはC#を使う。しかし、場合によっては他の言語を用いたいことがあり、Unityではネイティブプラグインという方法で、外部で生成されたDLLを利用することが可能である。
筆者は、ダンジョン生成のプログラムをHaskellで作成し、それをUnity内で利用したいという経緯があったので、今回、HaskellのシンプルなコードをCabalとStackを利用してDLLを作成し、それをUnityから利用する効率的な方法を模索した。環境はWindows 10 64bit
1. 準備
Stackで新しいプロジェクトを作る。
stack new haskell-foreign-library-test一度ビルド
stack build
して、package.yamlからcabalファイル(haskell-foreign-library-test.cabal)を生成する(バックでhpackが動いている)。後述するが、ライブラリ作成にはCabalの機能
foreign-library
を使う。しかし、現在(2019/01/04)、cabalファイルを生成するhpackは、これに未対応なようので、泣く泣くpackage.yamlは削除しておく。hpackのforeign-library対応は、議論されているようだ。
ついでに.gitignoreのcabalの行を削除して、cabalファイルをバージョン管理に含めておきたいところ。
2. HaskellプログラムのDLL作成
Haskell側の全てのコードはここ(masatoko/haskell-foreign-library-test)で見られる。
2.1. Haskellのコード
Haskellのコードは非常にシンプルなものにした。受け取った整数に1を足して返すだけの関数plus_oneだ。
これをforeign export
で出力しておく。src/Lib.hsmodule Lib where foreign export ccall plus_one :: Int -> IO Int plus_one :: Int -> IO Int plus_one x = return $ x + 12.2. hs_init関数のラッパーをC言語で作成
外部からplus_oneを利用するには、使う前にGHCのランタイムシステムを初期化し、使ってから終了させなければならない。
hs_init() -> plus_one() -> hs_exit() という流れ。
しかし、hs_init()は引数をとり、外部(C#)から利用する際、引数を渡すのが面倒になるので、hs_init()のラッパー関数hs_init_wrapper()を作成しておく。crsc/mylib.h#pragma once extern "C" { void hs_init_wrapper(void); }csrc/mylib.cpp#include "HsFFI.h" #include "mylib.h" #include "Lib_stub.h" // 今回は不要。ここでplus_one()を使うことができる。 void hs_init_wrapper(void) { int argc = 2; char *argv[] = {(char *)"+RTS", (char *)"-A32m", NULL}; char **pargv = argv; hs_init(&argc, &pargv); }mylib.cppでインクルードされているLib_stub.hは自動生成される。
(.stack-work/dist/*/build/mylib/mylib-tmp/Lib_stub.hに出力されていた。)
中身を見てみよう。Lib_stub.h#include "HsFFI.h" #ifdef __cplusplus extern "C" { #endif extern HsInt plus_one(HsInt a1); #ifdef __cplusplus } #endifplus_oneがエクスポートされている。
外部からより使いやすくするために、関数を加工して、また別の関数として出力しておくのもいいと思う。2.3. ビルド
stack build
の1コマンドでDLLを生成してくれたら嬉しい。その方法を調べた。
Cabalはv2.0.0.2(2017年7月)からforeign-library
でDLL(macの場合はdylib)を作成できるようになったらしい。これを使いたい。公式の説明haskell-foreign-library-test.caballibrary hs-source-dirs: src other-modules: Lib build-depends: base >=4.7 && <5 default-language: Haskell2010 foreign-library mylib type: native-shared if os(Windows) options: standalone other-modules: Lib build-depends: base >=4.7 && <5 hs-source-dirs: src c-sources: csrc/mylib.cpp default-language: Haskell2010これでビルドすればDLLが作成される。
stack build2.4. 生成されたDLLを確認
生成されたDLLのパスは、
stack build
のログの最後のほうに表示されている。
今回は、.stack-work/install/426b9cbc/lib/mylib.dll に生成されていた。nmコマンドで中身を確認。
nm mylib.dll以下の行を発見し、関数が出力されていることを確認した。
0000000067881620 T hs_init_wrapper 0000000067886c20 T hs_exit 0000000067881580 T plus_one3. UnityでのDLLの利用
生成したDLLをUnityで使ってみた。
開発時のEditorの挙動で少し厄介なことがあったので、それの対策が必要だった。3.1. DLLの配置
Assetsフォルダ配下にPluginsフォルダを作成し、そこにDLLを配置すればUnityが認識してくれる。
今回は素直にAssets/Plugins/mylib.dllに配置した。3.2. 各種設定を64ビットにする
生成されたDLLは64ビットのバイナリなので、Platform settings > Windows > x86_x64 だけにチェックを入れる。
また、Build SettingsのArchitectureをx86_64にしておく。
3.3. 利用するコードの記述
以下は適当な変数counterを1秒ごとに1足して、画面上のテキストに反映させるだけのプログラムである。
しかし、1点だけ注意することがある。MylibWrapper.csusing System.Runtime.InteropServices; using UnityEngine; using UnityEngine.UI; public class MylibWrapper : MonoBehaviour { [DllImport("mylib")] public static extern void hs_init_wrapper(); [DllImport("mylib")] public static extern void hs_exit(); [DllImport("mylib")] public static extern int plus_one(int x); public Text text; // counterを確認するためのText int counter = 0; private void Awake() { hs_init_wrapper(); } private void Update() { counter = plus_one(counter); print(counter); text.text = $"{counter}"; } void OnApplicationQuit() { // エディター上でhs_exit()すると次回のhs_init()でエラー。 // エディター上ではhs_exit()を実行させないのを一時的な解決方法とする。 #if UNITY_EDITOR #else hs_exit(); #endif } }エディター上での2度目の起動時に落ちる問題の解決
ghcのランタイムシステムは、hs_exit()した後にhs_init()できないようになっている。
しかし、Unityでは、エディター上でロードされたDLLはUnityを終了するまでアンロードされないため、
エディター上でゲームを開始して、hs_init_wrapper()し、ゲームを停止した時にhs_exit()し、次回のゲーム開始時に再びhs_init_wrapper()を実行するような仕組みにすると、hs_init_ghc: reinitializing the RTS after shutdown is not currently supported
というエラーが発生して落ちてしまう。ソースコードを見ると、
rts_shutdown
フラグがこれを拒んでいる。それであれば、エディター上ではhs_exit()を実行しないという方法しか残されていない。
ソースコードを見ると、hs_init()を複数回実行しても、hs_init_count
で呼び出し回数をカウントされるものの、2回目以降は何の処理もしておらず、問題ないだろう。(本当に?)
よって、エディター上ではゲーム開始のたびにhs_init_wrapper()が呼ばれるが、ゲーム終了時にはhs_exit()を呼ばないという方針とした。#if UNITY_EDITOR
~#else
~#endif
の部分。4. 実行
エディター上でも、ビルドした実行ファイルでも、うまく動作した。
5. まとめ
64bitという制約付きであるものの、HaskellのコードをUnity上で動作させることができた。
しかし、いくつか懸念もある。
- 【懸念1】 AndroidやiOSで動作するアプリを作れるのか?
Mobile Haskellというプロジェクトがあるので、これを使ってDLLを作れるかどうかにかかっていそう。検証は苦しそうなので今はしない。
- 【懸念2】 SwitchやPS4で動作するゲームを作れるのか?
それぞれのハードウェアで動作するバイナリ(DLL)が作れるのかどうか。ハードウェアの仕様がわからないので、これも未知数。
Windows上でゲームを公開するだけならば不安は無いが、将来的にモバイル端末やコンシューマー機で公開する可能性がある場合は、これが原因で断念せざるをえなくなる可能性があるので注意が必要。
活用方法の検討
- Haskellでダンジョン生成をしてUnityに取り込む。
- C#で書きたくない複雑な関数をHaskellで記述してUnityで使う。
- Haskellでゲームロジックを書いて、全ての描画はUnityに任せる。
追記1-Haskell関数からStringを返す(2020/01/05)
戻り値をStringにする方法はこのブログ記事が参考になりました。
- 追記2-エディターが重くなる問題について(2020/01/06)
本記事のplus_oneのようなシンプルな関数ではなくて、プロシージャルなダンジョンを生成するような、少し複雑な関数を実行したところ、関数の実行後にエディタがかくかくしてしまい、とても開発できるような状況でなくなってしまう問題が発生することがわかりました。原因不明であり、対処方法が判明するまではこの方法はやめておくべきかもしれません。
GHCのランタイムの処理がバックグラウンドで残っていて、Unityの処理を邪魔しているんですかね?どんなコードが問題を発生させるのかを調べたいですが、それが分かったとしても、問題を避けるようなコーディングをせねばならず、そんな不自由なプログラミングはしたくはありませんが。。
ひとまずはオフラインでダンジョンをファイルに書き出しておいて、それをUnityで読み込むという平凡な仕組みにしましたが、筆者はHaskell活用の夢をまだ諦めてはいないぞ。
- 投稿日:2020-01-04T16:02:17+09:00
【Unity】子のAnimatorを親から操作してみるテスト
概要
子要素が三つあって、動きは同じなんだけど、開始のタイミングだけずらしたい。
動きは同じなんだけど、Animationのキーは、三倍打たなきゃいけない・・・面倒だなぁ。Animationで、タイミングだけ指定できないかな?やってみよう!
SerializeFieldはAnimationでキーが打てる
そもそもMonoBehaviourスクリプトのSerializeFieldは、Animationで操作できます。ColorをSerializeFieldに出して、Animationで色を変える・・・なんてのは、よくやるテクニックです。
今回は、子要素のAnimatorのパラメーターを、SerializeFieldに用意して、親のアニメーションのキーから操作するようにします。
Animatorの構造
子供のAnimatroはこんな感じ。
シンプルな構造で、Any StateからBoolパラメーターで、即遷移するようにしました。
※TriggerではなくBoolの方が、アニメーションから制御しやすい
子要素は3つありますが、全部同じAnimatorを使っています。
アニメーションの更新があった場合でも、一つのAnimatorを更新すれば良いのでラクチンです。親のAnimatorはこんな感じです。
用途にあわせて自由に作りましょう。
今回は、in Triggerで入って、out Triggerではけるような動きを作っています。スクリプト
childにアタッチするスクリプトはこんな感じです。
子要素のAnimatorのパラメーターと同じSerializeFieldを用意し、UpdateでSetBoolするだけです。SerializeAnim.csusing UnityEngine; public class SerializeAnim : MonoBehaviour { Animator mAnimator; [SerializeField] public bool BoolWait; [SerializeField] public bool BoolStay; [SerializeField] public bool BoolIn; [SerializeField] public bool BoolOut; [SerializeField] public bool BoolRotate; void Awake() { mAnimator = GetComponent<Animator>(); } void Update() { mAnimator.SetBool("wait", BoolWait); mAnimator.SetBool("stay", BoolStay); mAnimator.SetBool("in", BoolIn); mAnimator.SetBool("out", BoolOut); mAnimator.SetBool("rotate", BoolRotate); } }root_in.animにキーを打つ
root_in.animに、キーを打ってみます。
Add Propertyを選択すると、子要素のSerializeFieldが追加できます。
1秒ごとにズラして、child1のBool in、child2のBool in、child3のBool inのキーを打ちます。
他のAnimationも同じように準備します。さて、実行だ!
メリット/デメリット
- 長所
- 子供の動きを作るのが一つだけでいい
- 更新があった場合、一つだけ更新すれば良いので、修正が少なくて済む
- 短所
- Animationウインドウのプレビューで動きを見ることができない
サンプル
- 投稿日:2020-01-04T14:34:44+09:00
[Unity] invalid build pathでAndroidビルドが出来ない
事象
Invalid build path: c:/develop/unity/test UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr) (at C:/buildslave/unity/build/Modules/IMGUI/GUIUtility.cs:179)
Unity 2019.2.13f1
でAndroidビルドをしようとすると、.apk
ファイルの保存ができなかった。ビルドボタン選択時、表示されるダイアログで、(保存先の?)フォルダ選択しかできず。
いつもなら、保存する.apk
ファイルの名前を指定して保存する手順のはず。。。とりあえず、プロジェクトフォルダを選択してみると、このエラー。
いや、なんで?????
解決方法
プロジェクトフォルダ以外のフォルダを指定する。
ただし、プロジェクトフォルダ配下のフォルダはいくつか試してみてダメだったので、それより手前のフォルダを指定する必要があるかも。
今回は適当にフォルダを掘って、
C:\new
を指定してみました。
するとビルドが始まり、success後、指定したフォルダに.apkファイルが出来た。。。いや、なんで?????
ちなみに
環境
Windows10
Unity 2019.2.13f1
AndroidSDK 26.1.1UnityでOculusQuestのビルドを試していたところ発生。
AndroidSDKのバージョンと、Unityのバージョンに互換性がないと動かないらしいけど、2週間ほど前にビルド実績があるのでおそらく関係なし。
Questビルド用に、プロジェクトの設定を色々変えていたのが原因?
分かり次第、追記したい。。