20210113のUnityに関する記事は8件です。

Unityを始める - Unityちゃんライブステージの構造理解 -

はじめに

以前、「 Unityを始める - Unityインストールからプロジェクト作成 - 」で、Unityを触ったことがないという人でも気軽に始められるように、Unityで開発を始める場合に必要な事をまとめました。

公開されているUnityちゃんライブステージのプロジェクトを見て、Unityのプロジェクトを学んだので備忘録としてまとめました。

maxresdefault.jpg
ユニティちゃんライブステージ! -Candy Rock Star-

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

参考にした本

動作確認

  1. ユニティちゃんライブステージのダウンロード
    下記のページからダウンロードしてください。
    https://unity-chan.com/download/releaseNote.php?id=CandyRockStar

  2. ダウンロードしたプロジェクトをUnityで開く
    フォルダを展開後、 unitychan-crs-master/Assets/Scenes/Main.unity を開くとライブシーンを確認する事ができます。

実装

Unityちゃんライブステージの構造理解するために以下の手順で一から実装してみました。

  • 新しいシーンの作成
  • ステージの作成
  • ミュージックの作成
  • ユニティちゃんを配置する
  • バックスクリーンの作成
  • カメラワークの設定

参考にしたサイト

まとめ

今回のUnityのプロジェクトの改修が初回ということもあり、うまくいかないことなどの課題は多くありましたが、このサンプルを知る事で、簡単に学習して進む事が出来ます。

ライブの面白さはアーティストとの距離が近いことだけではありません。アーティストや他のファンと同じ場を共有し、一体感を高めることで盛り上がるところにあります。バーチャルアイドルのライブも、実際のライブの中継もその一体感をいかに高めるかがポイントになってくるでしょう。 Oculus Quest 2 を持っている人はぜひ試して、どの程度の一体感が得られるように出来るのか試してみてはいかでしょうか。

今後、本格的なVR向けの開発に今後取り組む予定です。

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

Nreal Light でキャラクター表示

はじめに

2020年12月1日に発売されたスマートグラス、NrealLight(エンリアルライト)
今回はそこにUnityを使用してキャラクターを表示するまでのことを書いていきます。
↓NrealLight越しに見たままキャプチャーする方法が分からないので、ちょっと見え方が違う…

NrealLightとは?


詳しくはこちらを見て頂けると…
https://news.kddi.com/kddi/corporate/newsrelease/2020/11/10/4778.html

大雑把に言うと、スマートフォンに接続してXR技術を楽しめるスマートグラスです。
ミラーリングモードを使えばYoutubeなどを空間上に映し出して見ることも可能なので電車などで周りを気にせず動画を楽しむことも出来ます。
ケーブルがあるのでちょっとあやしい感じに見えますが…(笑)
※ 対応しているスマートフォンには注意が必要です。

開発環境

・Windows10
・Unity2019.4.16f1
・Android SDK 8.0(APIレベル26)
・NRSDKForUnity_1.4.8
・NrealLight(Developer Kitではないものです)
・Xperia 5 II SOG02(Nreal Lightを使用するには、ビルド番号58.0.C.11.142以降が必須)

流れ

NrealLight DeveloperKitとUnityでの開発手順は公式サイトに書いてありますので
それを参考にしながら進めていきます。
https://developer.nreal.ai/develop/unity/android-quickstart

基本的な下準備

Android開発は初めてという人向けの簡単な説明になります。(Unityはある程度使える前提)
Unityのインストール、Android SDKのインストール、UnityのAndroidSDKパス設定、Android Debug Bridge(adb)コマンドのパスを設定、Androidの開発モード設定 に触れています。
そんなの必要ないという人も多いと思うので別記事での説明になります。
https://qiita.com/yambowcto/private/918f6eefd103d09f5e1b

Unityプロジェクト設定

・テンプレートを3Dにしてプロジェクトを新規作成します。
・メニューのFile > Build Settings で Build Settings ウィンドウを開きます。
・Platform で Android を選択し Switch Platform で変更します。
・同ウィンドウ内の Player Settings ボタンを押して Project Settings ウィンドウを開きます。
・Project Settings ウィンドウ内の Player 設定を変更します。

 ※ Target API Level は level 26~29 で設定する必要があり、Android 11は現時点で非対応。
   また、Android 10の場合はAndroidManifest.xmlを変更する必要があると書かれてます。
   キャプチャー機能等使わなければ変更しなくても大丈夫かな?

・Project Settings ウィンドウ内の Quality 設定を変更します。

 ※ Skin Weights はデータ次第ですが、大抵は4 Bonesにしてあげないと破綻します。

NRSDKForUnityパッケージのインポート

公式サイトからNRSDKForUnity_1.4.8をダウンロードして、ダブルクリックするなりしてインポートします。
https://developer.nreal.ai/download

ユニティーちゃんのインポート

Unity公式サイトからユニティーちゃんのモデルデータをダウンロードしてインポートします。
https://unity-chan.com/contents/guideline

もし、error CS0234: The type or namespace name 'Policy' does not exist in the namespace 'System.Security' (are you missing an assembly reference?)のようなエラーが出ていたら AutoBlink.csを開いて using System.Security.Policy;を削除するか(使用してないので)、Project Settings > Player > Other Settings > Api Compatibility Level* を .Net 4.xに変更すれば大丈夫です。

シーンの作成

メニューのFile > New Scene で新規シーンを作ります。
ProjectウィンドウのAssets > NRSDK > Prefabs にある NRCameraRigNRInput を Hierarchyに追加します。
Assets > UnityChan > Prefabs にある unitychan も Hierarchyに追加します。
Hierarchyにある Main Camera は必要ないので削除しましょう。
unitychanの座標は Position Y:-0.94 Z:2.56 Rotation Y:180としておきます。
名前を付けてシーンを保存しておきます。

ビルド準備

さて、実際に作ったアプリをスマートフォンにインストールする簡単な方法としては、スマートフォンをUSBケーブルでPCに繋いで Build And Run を実行することですが、今回はNreal Lightをスマートフォンに接続しないと動かないアプリなので作業のたびにケーブルの付け替えを行う必要が出てしまいます。なので、Wi-Fi経由でインストールできる準備をしましょう。

下準備のadbコマンドのためのパス設定をしていることを前提に進めます。
まずはandroidスマートフォンをUSBケーブルでPCに繋ぎます。
※ 開発者向けオプションのUSBデバッグをONにするのを忘れずに!
  また、スマートフォンに対して何かしようとすると許可の確認が結構でるので
  移行の作業で出た場合には内容を確認しつつ基本的にOKします。

コマンドプロンプトを起動し、adb tcpip 5555 を打ち込みEnterを押します。
スマートフォンのIPアドレスを確認(自分のだと、設定 > デバイス情報で見れます)してから
adb connect IPアドレスを打ち込みEnterを押します。
自分の場合Enter押した後に失敗と出ますが、スマートフォン側に許可の確認が出てるので許可をすると大丈夫です。
最後にスマートフォンとPCの接続を外して adb devicesと打ち、認識できていればOK。
2021-01-13_11h28_32.png

スマートフォンにNebulaをインストール

NrealLightアプリを動かすためにはNebulaのインストールが必要です。(違ったらゴメンナサイ)
なので、Google PlayからNebulaをインストールします。(↓のやつです、同名のアプリに注意)

nebula nreal で検索すると上の方にくると思います。
スマートフォンのソフトウェアのバージョンが条件を満たしていないとNreal Lightを接続したときのNebulaの挙動がおかしくなってしまうので注意してください。(自分はそこで詰まりました…)

Nebulaを起動してセットアップを行います。
言われるがまま許可をしながら進めていきます。すると最後にグラスを付けた時のチュートリアルに進みます。

ビルド

いよいよビルドです。
メニューのFile > BuildSettings でビルド設定を開きます。
Wi-Fi接続が上手くいっていれば、Run DeviceにIP名が入ったデバイスがあるはずです。なければRefreshで情報を更新します。
あとはBuild And Runボタンを押すとビルドが開始され、続いてインストールされます。
※ Nebulaが起動してなければPlease connect your Nreal Light Glasses.と出ます。
インストールしたアプリはNebulaのメニューから起動することができます。
これでキャラクターを表示することが出来ます。
操作方法はAPPボタン長押しでポインターリセット、ホームボタン長押しで終了になります。
以上、お疲れさまでした。

おまけ

ここからはシーンにもうちょっとだけ機能を加えてみたいと思います。
まず、HierarchyにCreateEmptyでGameObjectを作成し、その子供にunitychanを移動。
この時、GameObjectのTransformの値はリセットしておきます。
DemoにあるTargetModelDisplayCtrlを改良した以下のスクリプトをAddComponentでGameObjectに追加します。

TargetModelDisplayCtrlEx.cs
using NRKernal;
using UnityEngine;

namespace Test
{
    public class TargetModelDisplayCtrlEx : MonoBehaviour
    {
        public Transform modelTarget;

        private Vector3 m_AroundLocalAxis = Vector3.down;
        private float m_TouchScrollSpeed = 10000f;
        private float correctZ = 0.01f;
        private Vector2 m_PreviousPos;

        void Start()
        {
            ResetModel();
        }

        private void Update()
        {
            if (NRInput.GetButtonDown(ControllerButton.TRIGGER))
            {
                m_PreviousPos = NRInput.GetTouch();
            }
            else if (NRInput.GetButton(ControllerButton.TRIGGER))
            {
                UpdateScroll();
            }
            else if (NRInput.GetButtonUp(ControllerButton.TRIGGER))
            {
                m_PreviousPos = Vector2.zero;
            }
        }

        private void UpdateScroll()
        {
            if (m_PreviousPos == Vector2.zero)
                return;
            Vector2 deltaMove = NRInput.GetTouch() - m_PreviousPos;
            m_PreviousPos = NRInput.GetTouch();
            float x = Mathf.Abs(deltaMove.x);
            float y = Mathf.Abs(deltaMove.y);
            if (x > y)
                modelTarget.Rotate(m_AroundLocalAxis, deltaMove.x * m_TouchScrollSpeed * Time.deltaTime, Space.Self);
            else
            {
                Vector3 v = modelTarget.localPosition;
                v.z += deltaMove.y * m_TouchScrollSpeed * Time.deltaTime * correctZ;
                modelTarget.localPosition = v;
            }
        }

        public void ResetModel()
        {
            modelTarget.localRotation = Quaternion.Euler(0.0f, 180.0f, 0.0f);
        }
    }
}

InspectorでそのコンポーネントのModel Targetにunitychanを設定します。
これにより、アプリ中の操作で上下にスライドするとキャラクターが前後に移動、左右にスライドするとY軸回転を行うようになります。

おわりに

説明があまり上手くないかもしれませんが、いかがでしたか?
物凄く大雑把に言うと、動作環境を満たすNebulaを入れる適切なプロジェクト設定NRSDKForUnityを使う というところだけ注意すればOKだと思います。

さて、2021年に入って新たなスマートグラスの情報なども出てきています。
値段がもっとお手軽になって、街中でスマートグラスをしてても違和感がない時代が来るといいですね。

ユニティちゃんライセンスについて

ユニティちゃんライセンス
この作品はユニティちゃんライセンス条項の元に提供されています
ユニティちゃんを使用する際は、上記ライセンスをよく読んで使用しましょう。

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

Nreal Lightでキャラクター表示(準備編)

はじめに

ここではNrealLightでキャラクターを表示する下準備として、Unityを使ったAndroid開発のためのセットアップについて説明を行います。Unityに関してはある程度は使える前提で進めます。
本編はこちらです。
https://qiita.com/yambowcto/private/a973252e25cb9e5d44d7

Unityのインストール

Nreal Developer の説明にあるように Unity2019.4 LTS をインストールします。
ここでは2019.4.16f1をインストールしました。
また、インストール時には Android Build Support にチェックを入れて下さい。
Android SDK & NDK Tools と OpenJDK にもチェックが入っていると次に説明するAndroid SDK をインストールしなくても自動的にインストールされパスも設定してくれます。
ただし、指定したいTarget API LevelのSDKバージョンが入っていない場合は別途必要になるので、次に説明するAndroid Studioを入れた方が自分的には分かりやすいと思ってます。

Android SDKをインストール

まずは公式サイトからAndroid Studioをダウンロードします。
https://developer.android.com/studio?hl=ja
基本的にはデフォルトのままで進めていけばOKですが、Android SDKの場所がちょっと深いので分かりやすい場所にした方がいいかもしれません。(ここではデフォルトのままでの説明となります)

Android Studioのインストールが終わったら起動して、右下辺りにある Configure > SDK Manager を選択します。
Android SDK の必要なバージョンにチェックを入れてインストールします。
※ NrealLightの開発にはAndroid SDK 8.0(APIレベル26)以上が必要です。

UnityのAndroidSDKパス設定

メニューのEditからPreferencesを選択、ExternalToolsのAndroid SDK Tools Installed with Unityのパスを変更します。

Android Debug Bridge(adb)コマンドのパスを設定

adbコマンドを使用するためのパスの設定を行います。
これは必須というわけではないですが、NrealLightをスマートフォンに接続したままUnityで作成したapkファイルをWi-fiでインストール出来るので便利です。

まず、コントロールパネルのシステムを開きます。(ショートカット:Windowsキー + Pause)
次にシステムの詳細設定を開いて環境変数ウィンドウを表示します。

Pathを選択し、編集、新規でパスを追加します。
追加するパスはAndroidSDKのパスにplatform-toolsを付け加えたものになります。

コマンドプロンプトからadbと入力して決定、エラーが出なければOKです。
コマンドプロンプトはWindows10の場合は検索ボックスでcmdと打つと出てきます。あるいは、スタートメニュー > Windowsシステムツール からでも見つけられます。よく使うのでタスクバーにピン止めしておきましょう。

Android端末の設定

Android端末の開発向けオプションを有効にします。
この設定を表示するには、端末やOSのバージョン等によって違いがあると思うので分からない場合は検索してみて下さい。
Xperia 5ⅡSOG02 (Android 10)の場合だと、設定 > デバイス情報 > ビルド番号を7回タップすると表示されます。
表示される場所は、設定 > システム > 詳細設定 > 開発者向けオプション となりますので、その設定内のUSBデバッグを有効にしてください。許可するか表示されるのでOKを押します。
※ USBデバッグを有効にしたままにすると動作しないアプリなどありますので、必要ない場合は無効にしましょう。

おわりに

一部手かなり抜きな説明ですが、以上が基本的なUnityでAndroidアプリを開発するための下準備になります。

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

Oculus Integration/Unity2020

環境

Unity 2020.2.0f1
VS2019 free
OS windows10 pro

ビルド

[Unity]
Vulkanの削除
Android6.0
ネットワークパーミッション

Oculus Integrationのインストール
→リップシンクとAvatorは不要で重いのでチェックを外す

Swich Platform (10分くらいかかる)

ビルド2

XR Plugin Management
→ コンフリクトが発生する
image.png
けど、無視してAndroidのapkを作る事はできる。

Unity2019との違い

Oculus XRとOculus Android は明示的にインストールしなくてもよい

XRインタラクションツールキット/XR Interaction Toolkit

https://note.com/npaka/n/n7b99fac6b5fa

Unity2019.4
パッケージマネージャ横のpreview版を表示するにチェックを入れる。
image.png

Unity2020.2.0f1
image.png

ご参考

この記事群はわりと役に立つ
https://note.com/npaka/n/nc18d61e25c85

Oculus Developer Hub/Android Logcat/Controler/Locamotionなどの解説がある。

「Oculus Developer Hub」と「Android Logcat」同時には使えません。
image.png

★「Not starting debugger since process cannot load the jdwp agent.」
がでるんやが...
あとコントローラー認識できない。OculusIntegration必要?

OculusIntegrationインストールの後に、「Build And Run」の後に本体を頭にかぶるとOK

Unity + Oculus Quest 2 開発メモ(framesynthesis

https://framesynthesis.jp/tech/unity/oculusquest/

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

ゲーミングtoio - toio SDK for UnityでLEDを操る

概要

toio SDK for UnityでLEDを操ります。フルカラーLEDといえばレインボー(ゲーミングPC風) :rainbow:

GitHub repository

https://github.com/zurachu/toio-rainbow-led/

WebGL sample

toioキューブが必要です。
https://zurachu.github.io/toio-rainbow-led/

動画(Twitter)

技術情報

UnityEngine.Colorをtoioランプ制御で使うカラー値に変換するUtility

前回のかるたでもそうでしたが、キューブの接続順(どのキューブがプレイヤー1か?2か?)を認識するために画面表示とLEDの色を合わせるのは、良くある手な気がします。

ToioLedUtility.cs
using UnityEngine;
using toio;

public static class ToioLedUtility
{
    public static void TurnLedOn(Cube cube, Color color, int durationMs, Cube.ORDER_TYPE order = Cube.ORDER_TYPE.Strong)
    {
        cube.TurnLedOn(ColorByteValue(color.r), ColorByteValue(color.g), ColorByteValue(color.b), durationMs, order);
    }

    public static Cube.LightOperation LightOperationOf(Color color, int durationMs)
    {
        return new Cube.LightOperation(durationMs, ColorByteValue(color.r), ColorByteValue(color.g), ColorByteValue(color.b));
    }

    public static byte ColorByteValue(float value)
    {
        return (byte)Mathf.Clamp(value * 255, 0, 255);
    }
}

レインボーグラデーション

#ff0000#ffff00#00ff00#00ffff#0000ff#ff00ff#ff0000のグラデーションするUnityEngine.Color配列を作ってから、先ほどのCube.LightOperationに変換して、キューブに送信します。
Cube.TurnOnLightWithScenarioに一度に送れるLightOperationの上限が29
https://toio.github.io/toio-spec/docs/ble_light#operation-%E3%81%AE%E6%95%B0
なので、4<(29÷6)<5より、1色のグラデーションを4段階かけて行います。

SampleScene.cs
    public async void OnClickConnect()
    {
        var cube = await cubeManager.SingleConnect();
        if (cube == null)
        {
            return;
        }

        var colors = new List<Color>();
        colors.AddRange(Gradation(Color.red, Color.yellow, 4));
        colors.AddRange(Gradation(Color.yellow, Color.green, 4));
        colors.AddRange(Gradation(Color.green, Color.cyan, 4));
        colors.AddRange(Gradation(Color.cyan, Color.blue, 4));
        colors.AddRange(Gradation(Color.blue, Color.magenta, 4));
        colors.AddRange(Gradation(Color.magenta, Color.red, 4));

        cube.TurnOnLightWithScenario(0, colors.ConvertAll(_color => ToioLedUtility.LightOperationOf(_color, 100)).ToArray());
    }

    private List<Color> Gradation(Color fromColor, Color toColor, int division)
    {
        var colors = new List<Color>();
        for (var i = 0; i < division; i++)
        {
            colors.Add(Color.Lerp(fromColor, toColor, (float)i / division));
        }

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

【Unity シェーダー】グラデーションに巨大な値を足すと諧調が落ちる話

はじめに

巨大な値を使った際にグラデーションの諧調が落ちるという現象に遭遇したので、まとめたいと思います。

本記事は、簡潔さを重視した内容となっているため、情報の正確さに欠ける面があるかもしれません。

環境

Unity 2020.2.0f1
Universal RP 10.2.2

事の発端

シェーダーグラフにて、巨大な数を使うとグラデーションの諧調が減るという不可解な現象に遭遇しました。
image.png

今回はfloat(浮動小数点数)の視点から、階調が減る現象について検証していきたいと思います。
検証ではShaderLabを使用します。

検証 その1 : UV.xを画面に出す

ShaderLabでUVのxだけを返すようなフラグメントシェーダーを書いてみます。

fixed4 frag (v2f i) : SV_Target
{
    return i.uv.x;
}

結果

滑らかな0 ~ 1 のグラデーションが画面に表示されます。

検証 その2 : UV.xに巨大な整数を足す

次に、0.001 という小数を足して、frac で小数部分だけを返すようにしてみます。

fixed4 frag (v2f i) : SV_Target
{
    return frac(i.uv.x + 0.001);
}

結果

同じく滑らかなグラデーションが表示されます。
ここまでは、問題ないかと思います。

検証 その3 : 10^6を足してみる

10^6 という巨大な数を足してから、frac で小数部分だけを返すようにしてみます。

fixed4 frag (v2f i) : SV_Target
{
    return frac(i.uv.x + 1e6 + 0.001);
}

結果

整数を足しているだけだから、滑らかなグラデーションが表示されるのでは?と思ってしまいそうですが、
結果は以下のようなカクカクしたグラデーションになります。

この現象は、float(浮動小数点数) が関係していると考えられます。

floatの内部表現

シェーダーのfloat は 32bit float(単精度浮動小数点数) というもので、
0.3や0.7といった実数を32ケタの2進数で表現します。

float.png

ビット列から実数を計算する

符号部を $sign$ (0か1) 、指数部を $e$ (0 ~ 255の整数)、仮数部のビット列を $b_1, b_2, b_3, ... , b_{23} $ と置きます。
この時、floatが表現する実数 $value$ は以下の計算式で求めることができます。(IEEE754規格)

value = (-1)^{sign} \cdot (1 + \sum_{i=1}^{23}b_{i}2^{-i} ) \cdot 2^{e - 127}

クイズ

以下のようなビット列が表す実数 $value$ はどんな値になるでしょうか?

-02-float.png

答え

符号部 : $sign = 0$
指数部 : 8桁目のビット列が1になっているので、$e = 2^7 = 128$
仮数部のビット列 : $b_1 = 1, b_4 = 1, b_5 = 1$

ビット列が表す値$value$は以下のようになります。

\begin{align}
value &= (-1)^0 \cdot (1 + 2^{-1} + 2^{-4} + 2^{-5}) \cdot 2^{128- 127} \\
\\
&=
(1 + 0.5 + 0.0625 + 0.03125) \cdot 2
\\
\\
&= 3.1875
\end{align}

本題

さて、ここからが本題です。
以下のようなフラグメントシェーダーを書いた場合、グラデーションがカクカクしてしまいます。

float4 frag (v2f i) : SV_Target
{
    return frac(i.uv.x + 1e6 + 0.001);
}

カクカクしてしまうのは、i.uv.x + 1e6 + 0.001 という計算に原因があります。
桁数が大きく異なる数を足し合わせると、小さい方の数の情報が落ちてしまうためです。

1e6のビット列

10^6 のビット列は以下のようになります。
06-1e6.png

i.uv.x + 1e6 の計算

次に、i.uv.x + 1e6を計算します。

uv.x = 0.3の場合

i.uv.x = 0.3 の時、ビット列は以下のようになります。
04-value0.3.png

そして、0.3 + 10^6 のビット列は以下のようになります。
05-1e6+0.3.png

10^6 の仮数はそのままですが、0.3 の仮数はだいぶ下の方にズレてしまいました。
0.3 のビット列の大部分が欠損しています。

このような現象は情報落ちと呼ばれます。

uv.x = 0.9の場合

i.uv.x = 0.9 の場合を考えてみます。
0.9 + 10^6 のビット列は以下のようになります。
-06-1e6+0.9.png
仮数の下4ケタを0.9の仮数(だったもの)が埋めています。

uv.x = 1.0 の場合

i.uv.x = 1.0の場合を考えてみます。
1.0 + 10^6 のビット列は以下のようになります。
-06-1e6+1.0.png

仮数の下5ケタを1.0 の仮数(だったもの)が埋めています。

i.uv.x + 1e6 のUVデータ制度

i.uv.x + 1e6 を計算した場合、グラデーションの表現に使える領域は 4 ~ 5ビット程度になりそうです。

カクカクしたグラデーションを見てみると、グラデーションの諧調は16段階になっていますね

おまけ : さらに大きい数を足してみる

これまでは1e6を足していましたが、2倍の2e6を足してみます。

float4 frag (v2f i) : SV_Target
{
    return frac(i.uv.x + 2e6 + 0.001);
}

結果

諧調数が半分の8になります。

おまけ2 : 0.001を外す

0.001を外して以下のようなフラグメントシェーダーを書くと、グラデーションがなめらかになります。

float4 frag (v2f i) : SV_Target
{
    return frac(i.uv.x + 2e6);
}

知識不足で理由がよくわからないのですが、シェーダーコンパイラが良い感じに最適化してくれているのかもしれません。

関連

単精度浮動小数点数
https://www.wikiwand.com/ja/%E5%8D%98%E7%B2%BE%E5%BA%A6%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%95%B0#

【Unity】【シェーダ】小数点の精度と型の使い分けについて(float / half / fixedの話)
https://light11.hatenadiary.com/entry/2018/06/01/001008

地味にヤバい、シェーダ変数の精度について
https://techblog.kayac.com/unity-shader-parameters-precision

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

Unity 2020.1から、自動実装プロパティのバッキングフィールドにSerializeField属性をつけた時のEditor上の表示が良くなったが、シリアライズ名的に使ってはいけない

前提

  • Unityのフィールドに付与するSerializeField属性がわかる
  • C#のプロパティ、自動実装プロパティ、バッキングフィールドがわかる
  • C# 7.3から、自動実装プロパティに属性をつけられるようになったことを知っている

以上の内容は、自分の『C# 7.3から自動実装プロパティのバッキングフィールドに属性をつけられるようになった。UnityでSerializeFieldとそれをいい感じに使いたかった』で説明してます。

本題

次のようなコードを、自動実装プロパティのバッキングフィールドにSerializeField属性をつけて、シリアライズを試みます。

using UnityEngine;

public class Player : MonoBehaviour
{
    [field: SerializeField]
    public int Level { get; private set; }
}

Unity 2019.4までで、自動実装プロパティのバッキングフィールドにSerializeField属性をつけた時のEditor上の表示はこんな感じ。

スクリーンショット 2021-01-13 2.19.29.png

そして.unityファイル中(YAMLファイル)などでのシリアライズはこんな感じ。

スクリーンショット 2021-01-13 2.20.01.png

<Level>k__BackingField」!

これは自動実装プロパティのバッキングフィールドの名前です。ILとしては有効でも、C#のフィールド名としては不正な名前です。

この名前でEditor上で表示され、そしてシリアライズされます。

ここまではUnity 2019.4までの話。


ここからはUnity 2020.1以降の話。

Unity 2020.1以降では、先のコンポーネントのUnity Editor上の表示はこんな感じになります。

スクリーンショット 2021-01-13 2.23.59.png

なんと表示が変わっています。「Level」という表示になりました。断定はできませんが内部実装でUnityEditor.ObjectNames.NicifyVariableNameを使っている可能性があります。

// Levelと表示された
Debug.Log(ObjectNames.NicifyVariableName("<Level>k__BackingField"));

さぁ、ではUnity 2020.1では、どんな名前でシリアライズされるのでしょうか?

スクリーンショット 2021-01-13 2.34.51.png

残念ながら以前と変わらず、ILとしては有効でも、C#のフィールド名としては不正な「<Level>k__BackingField」っていう名前でシリアライズされるようです。


自動実装プロパティのバッキングフィールドに対してSerializeFieldをつけた時の、Unity 2019.4 => Unity 2020.1の仕様変更は・・・

  • Unity Editor上の表示は変更(プロパティ名になり、簡潔になった。)
  • .unityや.prefabなどでシリアライズされている名前は変わらず

となります。


さて、タイトルが自分の主張なのですが、「Unity 2020.1から、自動実装プロパティのバッキングフィールドに、SerializeField属性をつけた時のEditor上の表示は良くなったが、シリアライズ名的に使ってはいけない」です。

ぱっと見のUnity Editor上の表示は良い感じに見えます。しかし、シリアライズは「<Level>k__BackingField」という名称で行われます。そのため、自動実装プロパティのバッキングフィールドに、SerializeField属性を単純につけることは、避けることを強くおすすめします。

自分は「シリアライズされる名前」・「シリアライズされたり、永続化される際のデータフォーマット」というのは非常に重要と考えています。コードを変更するコストに比べて、シリアライズ・永続化されたデータのフォーマットを変更するというのは、影響範囲が広く、過去・未来のことを考慮する必要があり、非常に面倒だからです。

<Level>k__BackingFieldという自動実装プロパティのバッキングフィールドの名前は

  • 冗長
  • 扱いにくい
  • C#のフィールド名として不正
  • C#の自動実装プロパティのバッキングフィールドの命名が仕様化されていない

という欠点があります。そのため、自分は「シリアライズされる名前」・「シリアライズされたり、永続化される際のデータフォーマット」として不適切だと考えています。

多少冗長になりますが、こんな感じで書くことをおすすめします。

using UnityEngine;

public class Player : MonoBehaviour
{
    [SerializeField] private int level;
    public int Level => level;
}

こんなことができればよかったのに

2017年から主張しているけれど、「Unityでシリアライズ名を指定する機能が欲しい」。

SerializeFieldが引数をとって、シリアライズ名を指定できればよかったのに・・・

public class Player : MonoBehaviour
{
    // こんなことができればよかったのに 
    [field: SerializeField("field")]
    public int Level { get; private set; }
}

FormerlySerializedAsAttributeってのがあるけれど、残念ながらこれはダメ。「以前シリアライズされていた名前を指定するもの」だから。


ちなみにもし、「自動実装プロパティのバッキングフィールドにSerializeFieldをつけた時の名前が、勝手に良い感じになれば良いのにー」って思った人がいたら、それはNot Goodだと思います。「SerializeFieldをつけた時のシリアライズされる名前はフィールド名で」っていう原則が崩れちゃうので。(自動実装プロパティのバッキングフィールドもフィールドだから)

やっぱり必要なのは明示的にシリアライズする名前を指定する方法。(Unity 2020.1やUnity 2020.2の時点でそんな方法はない、ですよね?あったら教えてください。)

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

Unityでオブジェクトの複製と動的削除

 クリックすると、Cubeが生成され、積み上がるサンプルです。Cubeはprefab化し、Asset内に保持してあり、クリックによりInstantiate命令が実行され、シーンに追加されます。下方には、こぼれ落ちたCubeを削除するための平面とスクリプトが設置されています。
スクリーンショット 2021-01-13 2.01.14.png

0、Edit>ProjectSetting>PhysicsのGravityのyを-50にする
1、sceneにCubeを作成する
2、Cubeにrigidbodyを追加する
3、AssetsにResourcesフォルダを作成する
4、CubeをResourcesフォルダにドラッグ&ドロップし、prefab化する
5、sceneのCubeは削除してしまう
6、Planeで地面groundを作成する
7、新規にスクリプトaddBox.csを作成し、groundに追加する
8、groundからこぼれるCubeを消すため、Planeでdelを作成する
9、新規にスクリプトdelObj.csを作成し、delに追加する

addBox.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class addBox : MonoBehaviour
{
    void Update(){
        if (Input.GetMouseButtonDown(0)){
            Shot();
        }
    }
    void Shot () {
        GameObject dup = (GameObject)Resources.Load ("Cube");
        Vector3 pos = new Vector3(
            Random.Range(-1f,1f),
            5,
            Random.Range(-1f,1f));
        Instantiate (dup, pos, Random.rotation);
    }
}
delObj.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class delObj : MonoBehaviour
{
    void OnCollisionEnter(Collision c) {
        Destroy(c.gameObject);
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む