20200326のUnityに関する記事は6件です。

rotationでプレイヤーがランダムに傾く方法

勝手に倒れるオブジェクト

rotationを利用して、オブジェクトがランダムに傾くのをキーで操作する方法を紹介します。

dqz4d-ebqdc.gif

z軸を使って回転します。
UnityではQuaternionで回転数を決めているので、先に宣言。
new Vector3のzの値にRandom.Rangeを使って1-4までの乱数を返します。
これにより、ランダムで傾きます。

スクリーンショット 2020-03-26 19.52.13.png

この傾きをキーを使って戻したり、するためには下記のコードを追加。
zの値は乱数の最大値よりも大きな値を設定します。

スクリーンショット 2020-03-26 18.36.58.png

下重心にする

しかし、上記のスクリプトをただオブジェクトにアタッチした場合、こうなります。
movie1.gif

なので、今回は重心を下にしてplaneにCapsulの面が触れながら傾きたかったので、
空のGameObjectを作成します。
そしてpositionを重心を置きたい位置に合わせます。
今回はここ。
スクリーンショット 2020-03-26 19.01.26.png
そして、

空のGameObjectを親にして、Capsuleを子にします。

スクリプトをアタッチするのは空のGameObjectの方です。
スクリーンショット 2020-03-26 18.56.12.png

すると、このように傾きます。
人間を傾ける時なんかは足がついていないといけないので、この方法が利用できるかなと思います。
dqz4d-ebqdc.gif

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

InputFieldで日本語を入力する際にOnValueChangedが一気に呼ばれる問題をUniRxで対応してみた

環境

Unity2019.3.3f1

どんなことが起こったか

InputFieldに日本語を打つと以下のgifの様にOnValueChangedが呼ばれてしまう。
oneFrame.gif

対策

同一フレームに発火されたイベントの最後だけを通したいのでThrottleFrameを使用すれば想定通りの挙動になった。

m_InputField.OnValueChangedAsObservable().ThrottleFrame(1)

fix.gif

参考

https://qiita.com/toRisouP/items/3cf1c9be3c37e7609a2f

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

東京オリンピックのエンブレムを遊びつくす!(2)〜エンブレムに隠された法則〜

すでに多くの方がエンブレムの作られ方の解説をされていますが、ここではもう一度自分なりにエンブレムの作られ方について整理したいと思います。

emblem.tif

エンブレムはどうやってできている?

東京オリンピックのエンブレムは幾何学的なエンブレムですよね?となると何か幾何学的な仕掛けがあるのではと考えられます。エンブレムを眺めると、まずどちらのエンブレムも3種類の長方形を組み合わせて作られているということがわかります。
fig2_2-02.tif
取り敢えず三種類の長方形だけを使ってエンブレムチックなものを作ろうとすると、頂点が重ならなかったり、キレイな図形にならなかったりとなかなかうまくいきません。
fig2_3.tif

何か手がかりが足りない…。そもそもなぜこれではうまくいかないのか考えてみると、図形の頂点を重ねて敷き詰めようとしているからです。頂点で考えると図形をどれくらい傾けるのかも考えなければならず、自由度が大きすぎて制御しきれません。図形を敷き詰めるときはタイルのように辺が重なるように敷き詰めるのがふつうです。
というわけで発想を変えて長方形が頂点が重なるように敷き詰められたのではなく、長方形を格納した図形がタイルのように敷き詰められて作られたと考えてみましょう。
そして色々試行錯誤してみるとエンブレムはひし形が敷き詰められて作られたということがわかります。そして各ひし形の中点を結んだものが私たちが見ている長方形の正体なのです。
fig2_4-01.tif

ひし形の敷き詰め方

エンブレムがひし形を敷き詰めて作られたという事はわかりましたが、それだけではまだ遊びつくせなさそうです。まだどのように敷き詰めるかの手がかりがないからです。
ここで2つのエンブレムを見比べてみると、オリンピックは大きい正十二角形の外枠の中に小さい正十二角形の穴が空いている構造になっています。一方パラリンピックは正十二角形の上がポッカリ開いたような構造になっています。それぞれの穴がなければどちらも正十二角形をひし形で充填させた形ということで理解できそうです。
実際にやってみるとこの穴は同様のひし形で埋めることができます。
fig2_5-05-05.tif

エンブレムの正体

というわけでエンブレムの正体は
・正十二角形を三種類のひし形で充填し、
・それぞれ何個かのひし形を取り除き、
・各ひし形の中点を結んでできた長方形
という事になります。
fig2_6-04.tif

エンブレムの正体が分かったところで今回はここまで!
次回はひし形を充填するという事についてさらに詳しく書いていきたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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専用のテンプレートがある。(見つかりませんでした)

新規プロジェクト作成

image.png

image.png

設定

いつものなので詳細省きます

image.png

PlayerSettings...
CompanyName修正、Valkan消す、MinimumAPIを6.0以上
XRSettings VirtualRealitySupported + Oculus, Single Pass

Switch Platform

Oculus Integrationインポート

VR,Platformだけチェック付けてインポート

materialがピンクになっているので選択して
image.png

Edit > Render Pipeline > Universal Render Pipeline > Upgrade Selected Materials to UniversalRP Materials
でURP用のマテリアルに変換する

image.png

Post-processing

今までのPost-processingは重く、OculusQuestに対応するには難しかったのですが、
URPは後処理になることで高速に処理できるようなので使ってみます。

Camera設定

OVRCameraRig 配置
Rendering > Post Processing にチェックを入れ、
Anti-aliasing をFXAAにする

image.png

Global Volume追加

HierarchyでVolume > Global Volumeを追加

image.png

追加したGlobal VolumeのVolumeでProfileを新しく作るのでNewボタンをクリック

image.png

Add OverrideでPost-processingを追加していく

image.png

モバイルで使いやすいのは下記エフェクト

  • Bloom
  • Color Grading
  • Vignette

早い動きだと酔いやすくなるのでVRでは効果を小さくした方がいいもの

  • Chromatic Aberration
  • Lens Distortion
  • Motion Blur

Bloom

明るい光を表現する

image.png

Threshold しきい値
Intensity 強度
Scatter 散布
Tint 色合い
Clamp 計算に使用する最大強度
High Quality Filtering パフォーマンスに影響を与えるのでVRでは使わない

Color Adjustments

色を調整する

image.png

Post Exposure 露出後
Contrast コントラスト
Color Filter カラーフィルター
Hue Shift 色相シフト
Saturation 飽和(すべての色の強度)

Vignette

端を暗くする

image.png

Color 色
Center 中心(0.5、0.5)
Intensity 強度
Smoothness 滑らかさ
Rounded 丸み

ビルド

調整難しい

image.png

おまけ

Oculus Questは無理ですが、Unityで試せるURPを使ったBoatAttackをインストールしてみます。

https://github.com/Verasl/BoatAttack

  1. GitLFSインストール
  2. リポジトリクローン(時間かかる)
  3. UnityHubでリストに追加して起動

めっちゃキレイ!レースができて、気持ちいいスピードが楽しい!
image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

非同期処理にtry-catchを何度も書きたくない...!UniTaskに対する例外処理のアイデア

はじめに

こんにちは、Unityエンジニアのイワケンです。Qiitaの閲覧数のタグの割合が公開されるようになりましたね。私はUnityが1位でした。やっぱりなという感じです。Qiitaはこの機能を今後このまま残すのか、色んな意味で楽しみですね (追記:現在非表示になりましたね)

さて、今回の記事は、プロジェクト内のUniTaskのあらゆる非同期処理に対して共通の例外処理を行いたい時に役立つかもしれない記事です。

非同期処理中に例外が起きたらエラーダイアログを表示したい!

非同期処理の例としてResources.LoadAsync(cubeName);でPrefabを読み込み生成、cubeNameがResoucesフォルダに存在しない場合はエラーダイアログを出す実装について考えてみます。

Cubeが正しく生成された場合
image.png

CubeNameを間違ってしまったら例外発生→エラーのダイアログを表示
image.png

こんなエラーダイアログを、あらゆる非同期処理 (例えばHTTPClientでAPIを叩く時など)に対して適用したいのです。
やりたくないのは、全ての非同期処理に対して、例外処理(try-catch文)を書くこと。共通処理はまとめたいですよね。

まずシンプルに非同期処理を書いてみる

ResoucesフォルダからCubeNameの名前のPrefabを非同期に読み込み、Instantiateで生成する処理です。
Forget();をくっつけることで、awaitを書かなくてもエラーがでなくなる (asyncでない関数内で実行できる)ようになります。

CubeGenerator
void 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してエラーダイアログをだす処理を書きます。(例外の思想として正しいかどうかは今回置いておきます。)

CubeGenerator
    async 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.cs
using 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();
    }
}

すると先程のコードは次のように書けます。

CubeGenerator
    void 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.cs
using 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.cs
using 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.cs
    void Start()
    {
        CallApi().RunTaskHandlingError();
    }
    async UniTask CallApi() {
        var json = await ApiClient.GetAsync("www.example.com");
        // 続き処理を書く
    }

おまけ

UniTaskExtensionsに次のように書くと、UniTask<T>にも対応します。

UniTaskExtensions.cs
    public 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);
        // 続き処理を書く
    }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity(C#)】MaterialPropertyBlock使って一つのマテリアルを使いまわす

MaterialPropertyBlock

下記記事で作成していたVRお絵描きアプリに
色の変更機能を設けようとした際にMaterialPropertyBlockを活用しました。

【参考リンク】:【Unity(C#)】ハンドトラッキングで簡易版VRお絵かきアプリ

描いた線ごとに色を変更したかったのですが、
描く予定の線の分だけマテリアルを用意するのは
あまりにも手間ですし、その都度Instantiateするのも
負荷的にどうなの?と感じたので
MaterialPropertyBlockの使い方を学ぶことにしました。

デモ

リアルタイムに色違いのキューブがランダムに生成されるデモです。

MaterialPropertyBlock.gif

マテリアルは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型)
という処理を行わずに済み、負荷が軽くなります。

参考リンク

【Unity】【シェーダ】MaterialPropertyBlockの使い方

【Unity】MaterialのPropertyIDについて

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む