- 投稿日:2020-03-26T23:42:34+09:00
rotationでプレイヤーがランダムに傾く方法
勝手に倒れるオブジェクト
rotationを利用して、オブジェクトがランダムに傾くのをキーで操作する方法を紹介します。
z軸を使って回転します。
UnityではQuaternionで回転数を決めているので、先に宣言。
new Vector3のzの値にRandom.Rangeを使って1-4までの乱数を返します。
これにより、ランダムで傾きます。この傾きをキーを使って戻したり、するためには下記のコードを追加。
zの値は乱数の最大値よりも大きな値を設定します。下重心にする
しかし、上記のスクリプトをただオブジェクトにアタッチした場合、こうなります。
なので、今回は重心を下にしてplaneにCapsulの面が触れながら傾きたかったので、
空のGameObjectを作成します。
そしてpositionを重心を置きたい位置に合わせます。
今回はここ。
そして、空のGameObjectを親にして、Capsuleを子にします。
スクリプトをアタッチするのは空のGameObjectの方です。
すると、このように傾きます。
人間を傾ける時なんかは足がついていないといけないので、この方法が利用できるかなと思います。
- 投稿日:2020-03-26T22:51:57+09:00
InputFieldで日本語を入力する際にOnValueChangedが一気に呼ばれる問題をUniRxで対応してみた
- 投稿日:2020-03-26T14:15:24+09:00
東京オリンピックのエンブレムを遊びつくす!(2)〜エンブレムに隠された法則〜
すでに多くの方がエンブレムの作られ方の解説をされていますが、ここではもう一度自分なりにエンブレムの作られ方について整理したいと思います。
エンブレムはどうやってできている?
東京オリンピックのエンブレムは幾何学的なエンブレムですよね?となると何か幾何学的な仕掛けがあるのではと考えられます。エンブレムを眺めると、まずどちらのエンブレムも3種類の長方形を組み合わせて作られているということがわかります。
取り敢えず三種類の長方形だけを使ってエンブレムチックなものを作ろうとすると、頂点が重ならなかったり、キレイな図形にならなかったりとなかなかうまくいきません。
何か手がかりが足りない…。そもそもなぜこれではうまくいかないのか考えてみると、図形の頂点を重ねて敷き詰めようとしているからです。頂点で考えると図形をどれくらい傾けるのかも考えなければならず、自由度が大きすぎて制御しきれません。図形を敷き詰めるときはタイルのように辺が重なるように敷き詰めるのがふつうです。
というわけで発想を変えて長方形が頂点が重なるように敷き詰められたのではなく、長方形を格納した図形がタイルのように敷き詰められて作られたと考えてみましょう。
そして色々試行錯誤してみるとエンブレムはひし形が敷き詰められて作られたということがわかります。そして各ひし形の中点を結んだものが私たちが見ている長方形の正体なのです。
ひし形の敷き詰め方
エンブレムがひし形を敷き詰めて作られたという事はわかりましたが、それだけではまだ遊びつくせなさそうです。まだどのように敷き詰めるかの手がかりがないからです。
ここで2つのエンブレムを見比べてみると、オリンピックは大きい正十二角形の外枠の中に小さい正十二角形の穴が空いている構造になっています。一方パラリンピックは正十二角形の上がポッカリ開いたような構造になっています。それぞれの穴がなければどちらも正十二角形をひし形で充填させた形ということで理解できそうです。
実際にやってみるとこの穴は同様のひし形で埋めることができます。
エンブレムの正体
というわけでエンブレムの正体は
・正十二角形を三種類のひし形で充填し、
・それぞれ何個かのひし形を取り除き、
・各ひし形の中点を結んでできた長方形
という事になります。
エンブレムの正体が分かったところで今回はここまで!
次回はひし形を充填するという事についてさらに詳しく書いていきたいと思います。
- 投稿日:2020-03-26T10:18:41+09:00
UnityのURP(Universal Render Pipeline)を使ってOculus Questに対応してみる
はじめに
※Unity初心者です。
Oculus Questで快適にキレイな海を表示したくて、URPにたどり着きました。
試したときのメモです。Universal Render Pipelineについて
https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@7.0/manual/index.html
シングルパス対応しているので、いろんなプラットフォームに向いている。(現在すべてのVRにも!)
UniversalRP-VRっていうVR専用のテンプレートがある。(見つかりませんでした)新規プロジェクト作成
設定
いつものなので詳細省きます
PlayerSettings...
CompanyName修正、Valkan消す、MinimumAPIを6.0以上
XRSettings VirtualRealitySupported + Oculus, Single PassSwitch Platform
Oculus Integrationインポート
VR,Platformだけチェック付けてインポート
Edit > Render Pipeline > Universal Render Pipeline > Upgrade Selected Materials to UniversalRP Materials
でURP用のマテリアルに変換するPost-processing
今までのPost-processingは重く、OculusQuestに対応するには難しかったのですが、
URPは後処理になることで高速に処理できるようなので使ってみます。Camera設定
OVRCameraRig 配置
Rendering > Post Processing にチェックを入れ、
Anti-aliasing をFXAAにするGlobal Volume追加
HierarchyでVolume > Global Volumeを追加
追加したGlobal VolumeのVolumeでProfileを新しく作るのでNewボタンをクリック
Add OverrideでPost-processingを追加していく
モバイルで使いやすいのは下記エフェクト
- Bloom
- Color Grading
- Vignette
早い動きだと酔いやすくなるのでVRでは効果を小さくした方がいいもの
- Chromatic Aberration
- Lens Distortion
- Motion Blur
Bloom
明るい光を表現する
Threshold しきい値
Intensity 強度
Scatter 散布
Tint 色合い
Clamp 計算に使用する最大強度
High Quality Filtering パフォーマンスに影響を与えるのでVRでは使わないColor Adjustments
色を調整する
Post Exposure 露出後
Contrast コントラスト
Color Filter カラーフィルター
Hue Shift 色相シフト
Saturation 飽和(すべての色の強度)Vignette
端を暗くする
Color 色
Center 中心(0.5、0.5)
Intensity 強度
Smoothness 滑らかさ
Rounded 丸みビルド
調整難しい
おまけ
Oculus Questは無理ですが、Unityで試せるURPを使ったBoatAttackをインストールしてみます。
https://github.com/Verasl/BoatAttack
- GitLFSインストール
- リポジトリクローン(時間かかる)
- UnityHubでリストに追加して起動
- 投稿日:2020-03-26T04:32:46+09:00
非同期処理にtry-catchを何度も書きたくない...!UniTaskに対する例外処理のアイデア
はじめに
こんにちは、Unityエンジニアのイワケンです。Qiitaの閲覧数のタグの割合が公開されるようになりましたね。私はUnityが1位でした。やっぱりなという感じです。
Qiitaはこの機能を今後このまま残すのか、色んな意味で楽しみですね(追記:現在非表示になりましたね)さて、今回の記事は、プロジェクト内のUniTaskのあらゆる非同期処理に対して共通の例外処理を行いたい時に役立つかもしれない記事です。
非同期処理中に例外が起きたらエラーダイアログを表示したい!
非同期処理の例として
Resources.LoadAsync(cubeName);
でPrefabを読み込み生成、cubeNameがResoucesフォルダに存在しない場合はエラーダイアログを出す実装について考えてみます。CubeNameを間違ってしまったら例外発生→エラーのダイアログを表示
こんなエラーダイアログを、あらゆる非同期処理 (例えばHTTPClientでAPIを叩く時など)に対して適用したいのです。
やりたくないのは、全ての非同期処理に対して、例外処理(try-catch文)を書くこと。共通処理はまとめたいですよね。まずシンプルに非同期処理を書いてみる
ResoucesフォルダからCubeNameの名前のPrefabを非同期に読み込み、Instantiateで生成する処理です。
Forget();
をくっつけることで、awaitを書かなくてもエラーがでなくなる (asyncでない関数内で実行できる)ようになります。CubeGeneratorvoid Start(){ InstantiateCubeAsync("Cube").Forget(); } async UniTask InstantiateCubeAsync(string cubeName){ GameObject cube = await Resources.LoadAsync(cubeName) as GameObject; Instantiate(cube,new Vector3(0,1,0),Quaternion.identity); }試しに例外処理をそのまま書いてみる[ダメ例]
こちらは極力避けたい例です。
cubeNameがResoucesフォルダに存在しない場合、例外を吐き出します。例外をcatchしてエラーダイアログをだす処理を書きます。(例外の思想として正しいかどうかは今回置いておきます。)CubeGeneratorasync UniTask InstantiateCubeAsync(string cubeName) { try { var a = Resources.LoadAsync(cubeName); GameObject cube = await Resources.LoadAsync(CubeName) as GameObject; Instantiate(cube,new Vector3(0,1,0),Quaternion.identity); } catch(Exception e) { Debug.Log(e.Message); Debug.Log($"{CubeName}はResoucesフォルダに存在しない名前です。"); ErrorDialogController.Instance.SetActive(true); //エラーダイアログ表示 } }1つ,2つこのような処理を書くのはいいのですが、10個100個非同期処理のメソッドがあり、それぞれにtry-catchを書き、例外時の処理を書くのはしんどいのですよね...
そのために、UniTaskの拡張コードを書き、例外処理を同じコードで行うようにします。
UniTaskを拡張する[今回の記事の推しパターン]
結論次のようなコードを書きます
UniTaskExtensions.csusing System; using UnityEngine; using UniRx.Async; static class UniTaskExtensions { public static async UniTask RunTaskHandlingErrorAsync(this UniTask task) { try { await task; } catch(Exception e) { Debug.Log(e.Message); ErrorDialogController.Instance.SetActive(true); } } public static void RunTaskHandlingError(this UniTask task) { RunTaskHandlingErrorAsync(task).Forget(); } }すると先程のコードは次のように書けます。
CubeGeneratorvoid Start() { InstantiateCubeAsync(cubeName).RunTaskHandlingError(); } async UniTask InstantiateCubeAsync(string CubeName) { GameObject cube = await Resources.LoadAsync(CubeName) as GameObject; Instantiate(cube,new Vector3(0,1,0),Quaternion.identity); }
Forget()
の部分がRunTaskHandlingError()
に変わっただけですが、これだけでtry-catchをいちいち書かずに、例外が発生した時にエラーダイアログを表示する処理を書くことができました。応用: 複数の例外に対応
実際に例外処理で行いたいこととしては、例外の種類によってユーザに対する反応を変えるということではないでしょうか?それを行うために、複数の例外を定義し、それぞれcatchして例外処理を行うようにします。
具体例として、RESTApiのエラーコードに応じて、ダイアログの表示を変える処理について考えてみます。順序としては
- オリジナルの例外の定義
- 例外を投げる処理の実装
- UniTaskExtensionを書き換える
- 非同期処理実行時に
RunTaskHandlingError()
を付与する。という順番になります。
オリジナルの例外の定義
まず、ダイアログの表示に対応する例外を定義してみます。今回は、401 認証エラーと404 NotFoundに対する例外処理を別々で行うとしましょう。
MyException.csusing System; using System.Runtime.Serialization; [Serializable()] //クラスがシリアル化可能であることを示す属性 public class NotFoundException : Exception { public NotFoundException() : base() { } public NotFoundException(string message) : base(message) { } public NotFoundException(string message, Exception innerException) : base(message, innerException) { } //逆シリアル化コンストラクタ。このクラスの逆シリアル化のために必須。 //アクセス修飾子をpublicにしないこと!(詳細は後述) protected NotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { } } public class UnauthenticatedException : Exception { public UnauthenticatedException() : base() { } public UnauthenticatedException(string message) : base(message) { } public UnauthenticatedException(string message, Exception innerException) : base(message, innerException) { } //逆シリアル化コンストラクタ。このクラスの逆シリアル化のために必須。 //アクセス修飾子をpublicにしないこと!(詳細は後述) protected UnauthenticatedException(SerializationInfo info, StreamingContext context) : base(info, context) { } }例外を投げる処理の実装
REST的なApiClientの実装例です。(UniTask便利や...)
ResponseCodeに応じて、投げる例外の処理を変えています。ApiClient.csusing UnityEngine.Networking; using UniRx.Async; public class ApiClient { public async static UniTask<string> GetAsync(string uri) { UnityWebRequest webRequest = UnityWebRequest.Get(uri); await webRequest.SendWebRequest(); // エラーコードが返ってきたら、例外を投げる if(webRequest.isNetworkError || webRequest.isHttpError) { int responseCode = (int)webRequest.responseCode; if(responseCode == 401) { // 認証エラー throw new UnauthenticatedException(); } if(responseCode == 404) { // Not Found throw new NotFoundException(); } // ...省略 } return webRequest.downloadHandler.text; } }UniTaskExtensionを書き換える
例外をcatchしたときの処理を書きます。
UniTaskExtensions: static class UniTaskExtensions { public static async UniTask RunTaskHandlingErrorAsync(this UniTask task) { try { await task; } catch(UnauthenticatedException e) { Debug.Log(e.Message); // 「認証されませんでした」のダイアログ表示の処理など } catch(NotFoundException e) { Debug.Log(e.Message); // 「見つかりませんでした。」のダイアログ表示の処理など } catch(Exception e) { Debug.Log(e.Message); // その他の例外.... } } public static void RunTaskHandlingError(this UniTask task) { RunTaskHandlingErrorAsync(task).Forget(); } }非同期処理実行時に
RunTaskHandlingError()
を付与する先程同じ感じで、このようにかけます。
CubeGenerator.csvoid Start() { CallApi().RunTaskHandlingError(); } async UniTask CallApi() { var json = await ApiClient.GetAsync("www.example.com"); // 続き処理を書く }おまけ
UniTaskExtensionsに次のように書くと、
UniTask<T>
にも対応します。UniTaskExtensions.cspublic static void RunTaskHandlingError<T>(this UniTask<T> task) { RunTaskHandlingErrorAsync(task).Forget(); } public static async UniTask<T> RunTaskHandlingErrorAsync<T>(this UniTask<T> task) { try { return await task; } catch(Exception e) { ErrorDialogController.Instance.SetActive(true); Debug.Log(e.Message); } return await task; //もっと良い書き方教えて下さい。 }これで、こんな書き方もできるようになります。
string型を返り値に持つ非同期メソッドUniTask<string>
にも適用できました。async UniTask CallApi() { var json = await ApiClient.GetAsync("www.example.comaaa").RunTaskHandlingErrorAsync(); Debug.Log(json); // 続き処理を書く }
- 投稿日:2020-03-26T01:47:04+09:00
【Unity(C#)】MaterialPropertyBlock使って一つのマテリアルを使いまわす
MaterialPropertyBlock
下記記事で作成していたVRお絵描きアプリに
色の変更機能を設けようとした際にMaterialPropertyBlock
を活用しました。【参考リンク】:【Unity(C#)】ハンドトラッキングで簡易版VRお絵かきアプリ
描いた線ごとに色を変更したかったのですが、
描く予定の線の分だけマテリアルを用意するのは
あまりにも手間ですし、その都度Instantiateするのも
負荷的にどうなの?と感じたので
MaterialPropertyBlock
の使い方を学ぶことにしました。デモ
リアルタイムに色違いのキューブがランダムに生成されるデモです。
マテリアルは1つしか使っていません。
コード
using System.Collections; using UnityEngine; /// <summary> /// 色違い場所違いキューブ自動生成プログラム /// </summary> public class CubeCreate : MonoBehaviour { [SerializeField] private GameObject _cube; private MaterialPropertyBlock _materialPropertyBlock; private int propertyID; private void Start() { _materialPropertyBlock = new MaterialPropertyBlock(); //プロパティーのIDを取得しておく SetColorをstringで指定しても結局intに変換してるらしく、無駄らしい propertyID = Shader.PropertyToID("_Color"); StartCoroutine(InstantiateColorCube()); } /// <summary> /// ランダムな位置にランダムな色のキューブを生成 /// </summary> private IEnumerator InstantiateColorCube() { while (true) { //ランダムな値 float randomValueA = Random.Range(-1.0f, 1.0f); float randomValueB = Random.Range(-1.0f, 1.0f); float randomValueC = Random.Range(-1.0f, 1.0f); //ランダムな値 float randomMagnification = Random.Range(0.0f, 5.0f); //ランダムな位置にキューブ生成 Vector3 randomPos = new Vector3(randomValueA, randomValueB, randomValueC); GameObject tmp = Instantiate(_cube, randomPos*randomMagnification, Quaternion.identity); //MaterialPropertyBlockで色を変更 元のマテリアルの色はそのまま MeshRenderer mr = tmp.GetComponent<MeshRenderer>(); Color randomColor = new Color(randomValueA, randomValueB, randomValueC); _materialPropertyBlock.SetColor(propertyID, randomColor*randomMagnification); mr.SetPropertyBlock(_materialPropertyBlock); yield return null; } } }SetColor , SetPropertyBlock
色をセットするために
SetColor
を使います。
第一引数にShaderで定義されている色のProperty名
を指定します。
(今回はIDで指定しています)その後、Rendererに反映させるために
SetPropertyBlock
を呼び出します。Shader.PropertyToID
Shader.PropertyToID
を使うことで
Shader内で定義されている特定のプロパティ名を
ID(int型)に変換することができます。先ほどの
SetColor
の第一引数に渡すことができます。メリットしてはStart関数内でIDを取得しているので
何度も指定したShader内プロパティの文字列
→ID(int型)
という処理を行わずに済み、負荷が軽くなります。参考リンク