FC2ブログ
ヽ|∵|ゝ(Fantom) の 開発blog? ホーム »Unity
カテゴリー「Unity」の記事一覧

【Unity】【C#】オーディオ再生でのエラー:An invalid seek position was passed to this function の対処法  

 ↓こんな感じのエラー

C:\buildslave\unity\build\Modules/Audio/Public/sound/SoundChannel.cpp(512) : Error executing channel->setPosition(position_pcm, FMOD_TIMEUNIT_PCM) (An invalid seek position was passed to this function. )

※行番号は Unityバージョンによって違うらしい

 なぜかググっても日本語記事が無かったので、備忘録として残しておく。

(※) Unity 2019.2.21f1 / Windows10(x64) で確認



 結論からと言うとこのエラーは「オーディオデータの長さより大きい再生位置へ移動(シーク)したためエラー」というものだ。

 特にシーク機能(早送り/巻き戻し)などを付けた場合、気をつけないと、AudioClip を変更したときに長さが足りず、再生でエラー出たりする。よくあるのが、AudioSource.Stop() を実行して、AudioClip (AudioSource.clip) を入れ替えて再生しようとすると、エラーが出てしまうものだ。これは AudioSource.Stop() ではシーク位置はリセットされないので、入れ替えた AudioClip の長さが、現在の再生位置より小さい場合、エラーが出てしまうからだ。

 なので、対処法はいくつかある。

●AudioClip の長さを調べてから再生する
if (audioSource.time < audioSource.clip.length)
{
audioSource.Play(); //この場合、途中から再生となる
}


●再生位置をリセットしてから Play する
audioSource.time = 0;  //先頭位置へ移動(シーク)
audioSource.Play(); //この場合、最初から再生となる

 など…。知っていればなんてことない事だが、AudioSource.Stop() では再生(シーク)位置がリセットされないため、不具合に気が付かないってことあるんだよね。エラーが出ても最初から再生されてしまうので、見た目上手く行ってるように感じてしまうしね。それでもエラーは出さない方がやっぱりシステム的にも安定すると思う。





(関連記事)
【Unity】【C#】Windows で mp3 をランタイムで再生する
【Unity】【C#】動的にオーディオファイルの読み込みと再生をする


スポンサーサイト



category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityトラブルシューティング  オーディオファイル 
tb: 0   cm: --

【Unity】【C#】Windows で mp3 をランタイムで再生する  


 なぜか Windows ではランタイムでの mp3 再生(ストリーミング)に対応してない。Unity のバージョン上がったらいつか対応するのかと思ってたけど、いっこうに対応しない(エラーとなる)。Android や Mac, iOS などは普通に再生できるのにね。

 VRM Live Viewer でも、音楽素材は wav/ogg でしか使えず、少し不便だな~といつも感じていたんだよね。

 なのでちょっと調べてみたが、実装が面倒なのが多い。色々見てみたが結局、NAudio というプラグインライブラリで「mp3 → wav に変換して再生する」のが一番簡単なようだ。

 あまり日本語での記事が見つからなかったので、せっかくなので実装例を載せておこう。VRM Live Viewer ではこれが実装されている(※実際には変換作業を非同期化[UniTask]にしている)。ここではわかりやすいように、中核となる部分を簡略化したコードを掲載しておこう。


NAudio
Microsoft Public License (Ms-PL)


(※) Unity 2019.2.21f1 / Windows10(x64) で確認



■NAudio (dll) の導入

 まずはプラグイン本体の dll をダウンロードしよう。Release ページに移動すると、掲載時点では v1.10.0 が出ていたが、「▼Assets (2)」を開くとソースコードしか無かったので、自分でコンパイルしないのなら、そのすぐ下バージョンの v1.8.4 の「▼Assets (4)」を開いて、「NAudio-Release.zip」をダウンロードしよう(※要するに zip で落とせる最新版をダウンロードする)。


(NAudio) Release ページ

 zip を解凍したら、「NAudio.dll」と「NAudio.xml」とドキュメント(txt)が入っているので、「Plugins」フォルダを作って(Windows だけで良いのなら「Plugins/x86」を作る)プロジェクトにドラッグドロップしてインポートしよう(dll と xml だけでも良い)。ちなみに「Plugins」は全てのプラットフォーム or Fallback で、以下に「Android」「iOS」「x86_64」を作れば、それぞれのプラットフォームごとになると考えて良い。ここでは Windows でだけしか使わないので、以下のように配置している。


Unityの特殊フォルダと各々の役割(追記版)

 あとは dll をクリックして、Import Settings を設定しておこう。「x86_64」にチェックを入れておけば 64bit でも使えるようになる。CPU の種類もとりあえず全てにしておこう。

●プラットフォーム


●CPU




■mp3 → wav に変換して再生する

 NAudio のプラグインを導入できたら、次にサンプルコードを作成しよう。

 先に簡単にコードの内容を解説しておくと、インスペクタで設定した mp3 のファイルパスを NAudio で一時フォルダに wav として書き出し、ストリーミング再生するサンプルになっている。

 ちにみに、音声ファイルをストリーミング再生する方法は以前に紹介した方法と基本的には同じだ。

動的にオーディオファイルの読み込みと再生をする

●mp3 → wav に変換して再生するサンプルコード
using System.Collections;
using System.IO;
using UnityEngine;
using NAudio.Wave; //← 見つからないとエラーが出たときは Import Settings を見直す

public class Mp3Play : MonoBehaviour //クラス名は任意
{
public AudioSource audioSource; //再生用 AudioSource をアタッチしておく
public string mp3Path; //再生する mp3 のパス

// Use this for initialization
private void Start()
{
if (File.Exists(mp3Path))
{
//wav 変換した一時ファイルを保存するパス
var wavPath = Application.temporaryCachePath + "/converted.wav";
ConvertMp3ToWav(mp3Path, wavPath);
StartCoroutine(PlayWav(wavPath));
}
else
{
Debug.Log($"File not found : {mp3Path}");
}
}

//mp3 → wav 変換して、保存したパスを返す
public void ConvertMp3ToWav(string mp3Path, string wavPath)
{
//ファイルを byte 配列で読み込み
var bytes = File.ReadAllBytes(mp3Path);

//wav を一時ファイルとして保存
using (var stream = new MemoryStream())
{
stream.Write(bytes, 0, bytes.Length);
stream.Position = 0;

using (var reader = new Mp3FileReader(stream))
{
WaveFileWriter.CreateWaveFile(wavPath, reader); //wav で書き出し
Debug.Log($"Convert to wav successfully : {wavPath}");
}
}
}

//ストリーミング再生する
IEnumerator PlayWav(string path)
{
using (var www = new WWW("file://" + path))
{
while (!www.isDone)
yield return null;

if (string.IsNullOrEmpty(www.error))
{
var clip = www.GetAudioClip(false, true);
audioSource.clip = clip;
audioSource.Play();
Debug.Log($"Play wav : {path}");
}
else
{
Debug.Log(www.error);
}
}
}
}

 再生するには GameOjbect にこのスクリプトと AudioSource をアタッチし、その AudioSource をインスペクタでセットしておく必要がある。

 プレイして、設定したパス(mp3)の音声が鳴ったなら成功だ。


 エラーチェックは最低限しかしてないので、必要なら try-catch などを追加しておいた方が良いだろう。

 また wav ファイルはサイズが大きいので、アプリ終了時(OnApplicationQuit)などで一時ファイルを削除しておいた方が良いかも知れない。その辺りは好きにやって欲しい。


 ちなみに、上記の例Mp3FileReader にパスを与えるオーバーロードもあるので、ConvertMp3ToWav() の中身を以下のように書くこともできる。

●Mp3FileReader(パス) のオーバーロードを使う
//mp3 → wav 変換して、保存したパスを返す
public void ConvertMp3ToWav(string mp3Path, string wavPath)
{
using (var reader = new Mp3FileReader(mp3Path))
{
WaveFileWriter.CreateWaveFile(wavPath, reader); //wav で書き出し
Debug.Log($"Convert to wav successfully : {wavPath}");
}
}

 ただコードは短くて良いのだが、私が実行速度を計測してみた所、前述のコードからすると約 100~200ms ほど遅いようだ(※2~3分の曲の場合)。また Mp3FileReader → AudioFileReader に置き換えてみたら、更に 300~400ms 遅い。
 なので、実行速度を気にしない場合であれば、こちらを使っても良いだろう。


 また、再生には WaveOut を使う方法があるが(本来は WaveOut が正しい使い方だと思うが、Unity では AudioClip として扱いたかったため、変換だけ利用している)、興味があったら、以下の資料を覗いてみると良い。

(参考)
C# プログラミング解説 NAudio





(関連記事)
【Unity】【C#】動的にオーディオファイルの読み込みと再生をする
【Unity】【C#】オーディオ再生でのエラー:An invalid seek position was passed to this function の対処法
【Unity】【C#】BMP をランタイムで読み込む
【Unity】【C#】TGAをランタイムでロードする
【Unity】【C#】ランタイム時にファイルをドラッグ&ドロップして取得する(Windows のみ)


category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityオープンソースライブラリ  Unityプラグイン  オーディオファイル 
tb: 0   cm: --

【Unity】SymGetSymFromAddr64, GetLastError: '無効なアドレスにアクセスしようとしています。' と出たら…  



●Unity 2019 LTS が公開された


 現在メインで使っている Unity2019.2.21f1 が2月以降アップデートが止まってしまったので(これもコロナの影響?)、ちょっと試しに Unity 2019 LTS (4.0) を入れてみた。

 まぁ今のところ、Unity2019.3.x 以降にするとアプリ起動時の解像度設定やゲームパッドの入力設定のダイアログが無くなってしまうので、本格的にアップグレードできないのだが(まだ公式からも代替方法が提示されてないので、自前実装する必要がある)、それ以外にも Android のネイティブ構成なども変更されているので、プラグイン関係の不具合も調査しないといけないしね。ちょっと Unity2019.3.x 以降へのアップグレードは敷居が高い。

【Unity】Unity2019.3 で Android / iOS ネイティブの構成が変わるらしい

(※) Unity 2019.4.0f1 (LTS) / Windows10(x64) で確認



 しかし、それとは別にアプリをビルドして動かしてみたら、いきなりクラッシュした。そして、エラーログを見てみると

ERROR: SymGetSymFromAddr64, GetLastError: '無効なアドレスにアクセスしようとしています。
' (Address: 00007FF8367A8E5B)
※Address は色々出る

としかない。

 ちなみに、エラーログは、
C:\Users\{UserName}\AppData\Local\Temp\{CompanyName}\{ApplicationName}\Crashes\Crash_{日時}
に出る。
※古い Unity バージョンでは「C:\Users\{UserName}\AppData\{CompanyName}\{ApplicationName}」に出る

 よくわからないのでググってみたが、結論から言うとこのエラーは、
「クラッシュハンドラーがコードがクラッシュした場所を特定できなかったことを意味します」
だそうだ。つまり「原因不明」

U5.3 - ERROR: SymGetSymFromAddr64, GetLastError: 'Attempt to access invalid address.'

 実際この時、私も原因不明のクラッシュで手の付けようが無かったのだが(フォーラムを見てみると、それぞれ全く別の原因で出ていると推測できる)、色々操作しているうちに、「あるプレファブを Instanciate するとクラッシュする」ことに気づいた。そしてそのプレファブを徹底的に調べてみたところ、「あるマテリアルを使うとクラッシュする」ところまで突き止めた。

 しかし、このマテリアルは特別なものではなく、同じマテリアルで色違いなどではクラッシュしない。また、Unity2019.3.x で試してみてもクラッシュしない(Unity2019.2.x はもとよりクラッシュしない)。他にも色々パラメタを変えたり、クラッシュしたものと値をあわせたりしてみたが、結局最後まで原因不明だった…。ちなみにエディタ上ではエラーも出ず、クラッシュもしない。アプリとしてランタイム時にだけクラッシュするので、ビルドしたバイナリがバグっているとしか思えない(少なくとも手動で直せるものではない)

 まぁ、これまでも初期のバージョンは色々とやっかいなバグを持っていることも多いので、とりあえず、Unity2019.4.0f1 は使わない方が無難だろう(特に市販アプリでは)。このときはたまたまわかりやすいクラッシュだったので良いが、使う頻度が低いプレファブだったり、潜在的に引き起こされるクラッシュだったりしたら、見つけることは超絶困難なことになる(そして対処法もない)。

 ちなみにこのクラッシュを特定した方法は、とても面倒だがやり方は簡単だ。それは「あやしいと思ったヒエラルキー内のオブジェクトから1つずつ削除して、クラッシュしなくなったら、最後のオブジェクトが原因」だ。これは本当に最後の手段(笑)。しかもそのプレファブに内包しているオブジェクトは大量にあったため、かなり大変だった…(全オブジェクトの半分ずつ消していくのも良い。二分探索みたいな考え方)。超アナログな方法だが、コードのバグも同じ方法で見つけることもできる。どうにもならないときのただの力技だけどね(笑)。





(関連記事)
【Unity】IncrementalCompiler でのエラー:Unloading broken assembly Packages/com.unity.incrementalcompiler/Editor/Plugins/Unity.PureCSharpTests.dll, this assembly can cause crashes in the runtime
【Unity】Unity2018.3.2 にアップグレードすると見た目がおかしくなることがある
【Unity】Unity2019 の StandardShader の処理が少し変わったらしいよ
【Unity】【Android】2019.2.0 でパッケージ名(Bundle Identifer)でアンダーバーが使えない
【Unity】【Android】Unity2019 へのアップグレードの問題点 メモ
【Unity】EventTrigger のコールバック引数変わった?
【Unity】Unity2018 でビルドエラー「CommandInvokationFailure: Gradle build failed.」が出る


category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityトラブルシューティング 
tb: 0   cm: --

【Unity】【C#】TGAをランタイムでロードする  


 BMPLoader に続き、これも試験的(Experimental)に VRM Live Viewer に導入しているのだが、オリジナルソースに少し手を加えた方が使い易いと思うので、ほんのちょっと改造版を載せておこう。

 今回紹介する方法は、オープンソースの TGALoader を使う方法だ。ただ、少し注意点は、「フルカラー・無圧縮」のみ対応という点だ。

 それを踏まえた改造版になる。簡単に説明すれば、「ヘッダ部分から画像データ形式を拾って、フルカラーでなければエラーを出す」というものを付け加えたものだ。あと、github にあったコメントなどの修正もついでに入れてある。


TGALoader.cs (mikezila)
TGA Loader for Unity3D (aaro4130)

(※) Unity 2019.2.21f1 / Windows10(x64) で確認



●TGALoader.cs(フルカラー・無圧縮のみ) [github のほんのちょっと改造版]
// https://gist.github.com/mikezila/10557162
// This was made by aaro4130 on the Unity forums. Thanks boss!
// https://forum.unity.com/threads/tga-loader-for-unity3d.172291/
// It's been optimized and slimmed down for the purpose of loading Quake 3 TGA textures from memory streams.

using System;
using System.IO;
using UnityEngine;

public static class TGALoader
{
public static Texture2D LoadTGA(string fileName)
{
using (var imageFile = File.OpenRead(fileName))
{
return LoadTGA(imageFile);
}
}

public static Texture2D LoadTGA(Stream TGAStream)
{

using (BinaryReader r = new BinaryReader(TGAStream))
{
r.BaseStream.Seek(2, SeekOrigin.Begin);
int type = r.ReadByte(); //(offset 2) 画像形式 (1バイト)

//フルカラー以外は非対応とする(=RLE圧縮は展開コードが必要のため)
if (type != 2)
{
throw new Exception("Only Full-color Uncompressed is supported.");
}

// Skip some header info we don't care about.
// Even if we did care, we have to move the stream seek point to the beginning,
// as the previous method in the workflow left it at the end.
r.BaseStream.Seek(9, SeekOrigin.Current); //offset 3 から +9 → 12

short width = r.ReadInt16(); //(offset 12) 画像の横幅 (2バイト)
short height = r.ReadInt16(); //(offset 14) 画像の縦幅 (2バイト)
int bitDepth = r.ReadByte(); //(offset 16) 色深度 (1バイト)

// Skip a byte of header information we don't care about.
r.BaseStream.Seek(1, SeekOrigin.Current);

Texture2D tex = new Texture2D(width, height);
Color32[] pulledColors = new Color32[width * height];

if (bitDepth == 32) //アルファあり
{
for (int i = 0; i < width * height; i++)
{
byte red = r.ReadByte();
byte green = r.ReadByte();
byte blue = r.ReadByte();
byte alpha = r.ReadByte();

pulledColors [i] = new Color32(blue, green, red, alpha);
}
} else if (bitDepth == 24) //アルファなし
{
for (int i = 0; i < width * height; i++)
{
byte red = r.ReadByte();
byte green = r.ReadByte();
byte blue = r.ReadByte();

/* (github のコメントから)
https://gist.github.com/mikezila/10557162#gistcomment-2995969
pulledColors [i] = new Color32(blue, green, red, 1); //float で 1.0 と間違えた?
1 need to be replaced by 255, in another case, texture become transparent
*/
pulledColors [i] = new Color32(blue, green, red, 255);
}
} else
{
throw new Exception("TGA texture had non 32/24 bit depth.");
}

tex.SetPixels32(pulledColors);
tex.Apply();
return tex;
}
}
}

 ファイルフォーマットに関しては以下を参考にさせて貰った。 このコードではRLE圧縮には対応してない。RLE圧縮に関しては以下の記事にも載っているので、参考にするのも良いだろう。

(参考)
TGA ファイルフォーマット (GAME PROGRAMMING UNIT)
TGA ファイルフォーマット (PROJECT ASURA)

 今回利用するヘッダ部分と画像のデータ形式だけ抜粋させて頂くと以下のような表になる。

ヘッダー (18 byte)
offsetbyte内容
01ヘッダーの後に続く、IDの長さ (ID field length)
11カラーマップの有無 (Color map type)
21データ形式 (Image type)
32カラーマップの位置 (Color map index)
52カラーマップの長さ (Color map length)
71カラーマップエントリーのビット数 (Color map size)
82X (Image origin X)
102Y (Image origin Y)
122幅 (Image width)
142高さ (Image height)
161ピクセルのビット数 (Bit per pixel)
171属性 (Discripter)

(offset 2) データ形式 (1 byte)
値(整数)内容
0イメージなし
1インデックスカラー(256色)
2フルカラー
3白黒
9インデックスカラー。RLE圧縮
10フルカラー。RLE圧縮
11白黒。RLE圧縮

 まぁ、ここでは「フルカラー・無圧縮」に限定してしまっているが、データ形式にあるように「フルカラー/インデックスカラー/白黒」と「圧縮/無圧縮」と「格納方向:縦/横」(offset 17 の属性)を組み合わせるとコードが長くなるのは容易に想像できるだろう。以下に複数の形式で TGA → BMP に変換するサンプルコードはあったので、興味があったら覗いてみるのも良いかも知れない(そしてRLE展開も作ったら公開して欲しい(笑))。

TGALoader.cs (openmetaversefoundation/libopenmetaverse)


 簡単なテストをするには以下ようなのコードで十分だろう。UI に RawImage を適当に置いて、インスペクタにセットし、読み込む画像のパスを設定してプレイすれば良い。画像が表示できたら成功だ。

●簡単な確認コード
using System;
using System.IO;
using UnityEngine;
using UnityEngine.UI;

public class TGALoaderTest : MonoBehaviour
{
public RawImage image; //画像表示用
public string path; //画像パス

// Use this for initialization
private void Start()
{
LoadTGA(path);
}

void LoadTGA(string path)
{
try
{
var tex = TGALoader.LoadTGA(path);
image.texture = tex;
}
catch (Exception e)
{
Debug.LogError(e.Message);
}
}
}

 ちなみにフルカラーのチェックをしないでRLE圧縮形式のTGAを読み込んだ場合は「Unable to read beyond the end of the stream.」というエラーが出る(たぶん圧縮してある場合は、データ長が短くなるので足りなくなり、エラーとなる)。このサンプルコードの場合はフルカラー以外では「Only Full-color Uncompressed is supported.」と出る。必要なら何か処理を入れた方が良いだろう。

 また、私が提供しているプラグインを使えば、Android でも TGA を読み込めた。実際にスマホで全天球ビューワを作ることも可能だ(これが VRM Live Viewer で実装されている)。フリーなので、自由に使って欲しい。

>>AssetStore版






(関連記事)
【Unity】【C#】BMP をランタイムで読み込む
【Unity】【C#】ランタイム時にファイルをドラッグ&ドロップして取得する(Windows のみ)
【Unity】スマホで簡易360度(パノラマ, 全天球)ビューワを作る
【Unity】Androidのトーストやダイアログ、通知、音声認識、ハード音量操作など基本的な機能を使えるプラグインを作ってみた
【Unity】AssetStore版 FantomPlugin のセットアップ


category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityオープンソースライブラリ    画像ファイル読み込み 
tb: 0   cm: --

【Unity】UniRx 7.x にアップグレードすると UniTask 系のエラーが出る  



※(AssetStore) UniRx 7.0.0 からは UniTask (UniRx.Async) は入ってない


 どうやら Unity2019.2.x (2.21f1) が2月頃からアップデートが止まっている…これもコロナウィルスのせいなのか…(会社はロスみたいだし)?
だけど、Unity2019.3.x はずっとアップデートされ続けている…。

 しかたないので、そろそろ覚悟を決めて (Android では内部実装が変わるので、ネイティブプラグインが死ぬ可能性がある) Unity2019.3.x にアップグレードしなくてはいけないかな?と思ってやってみたら、大量のエラーが出た。

【Unity】Unity2019.3 で Android / iOS ネイティブの構成が変わるらしい

(※) Unity 2019.3.13f1 / Windows10(x64) / UniRx 7.1.0 / UniTask 1.3.1 で確認



 1つは、どうやらカメラに Unity5 時代にデフォルトで付いていた「GUI Layer」が完全に廃止されたっぽい。もしかしたら旧 GUI も完全に使えなくなったのかな…?

Component GUI Layer in Main Camera for Scene Assets/xxx.unity is no longer available.
It will be removed after you edit this GameObject and save the Scene.

これは一度シーンを保存すれば、自動的に消されるらしい


 もう1つは UniRx のエラーだった。その1つは「using UnityEngine.Experimental.LowLevel;」の namespace で、どうやら Unity2019.3 では「using UnityEngine.LowLevel;」になったっぽい。
※この修正は後述の UniRx 7.x にアップグレードする場合は書き換える必要はありません。

error CS0234: The type or namespace name 'LowLevel' does not exist in the namespace 'UnityEngine.Experimental' (are you missing an assembly reference?)
error CS0246: The type or namespace name 'PlayerLoopSystem' could not be found (are you missing a using directive or an assembly reference?)


 そういえば、UniRx (6.x) は長らくアップデートしてないなぁと思ったので、AssetStore から再ダウンロード&インポート(UniRx 7.1.0)。そしたら今度は UniTask 関連のエラーが大量に出た。

UniRx - Reactive Extensions for Unity (AssetStore)

error CS0234: The type or namespace name 'Async' does not exist in the namespace 'UniRx' (are you missing an assembly reference?)
error CS0246: The type or namespace name 'UniTask' could not be found (are you missing a using directive or an assembly reference?)

 「UniRx.Async の namespace が無い」みたいなエラーだったので、しばらく原因がわからなかったのだが(6.x では普通に使えたので)、github で何か issue でも立ってないかと見ていたら、もっと単純に「UniRx.Async is separated to https://github.com/Cysharp/UniTask」([Ver 7.0.0 で] UniRx.Async のパッケージが分離しました) というコメントを見つけた。ああ、これか、と(笑)。

 なので、UniRx.Async のパッケージをDLしようかと思ったら、Ver 2.x.x-preview (プレビュー版)となっている。しかし「This version is preview, if you want to use stable version, check 1.3.1.」(安定版が使いたいなら、1.3.1 を見てね)ともあるので、今回はそちらを使うことにした。


UniRx (github)
UniTask (github)


 インポートしてみると、UniRx.Async は以前のように「Plugins」フォルダ以下ではなくなったみたい。もしかしたら「Plugins」フォルダ以下に移動した方が良いのかもだが(ビルド順が変わるとか何とか)、まぁ、私は動けば良いので、とりあえずこれでよし(笑)。

特殊フォルダーとスクリプトのコンパイル順 (Unity Manual)





 後で試しに「Assets/Plugins/UniRx」以下に「UniRx.Async」を移動してみたが、これも問題ないようだ。まぁ、コンパイル順でエラーが出たことはないが、アップグレードするときにまとめておいた方が便利なので、Plugins 以下に移動しても良いかもね。





(関連記事)
【Unity】【C#】UniRx で「1フレームごと待機して処理」してみる


category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityオープンソースライブラリ  Unityプラグイン  Unityトラブルシューティング 
tb: 0   cm: --


プロフィール

Social

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR