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

[Unity]shaderコード備忘録

はじめに

shader書いてて「あーこれいいなー」とか「たまたま出来た」みたいなやつを忘れないようにするために
随時ここに吐き出していこうと思います.(要はちらうら)

fragmentシェーダ系

うねうねスライム

元々は全然違うshaderで円入れたいなーと思って色々やってたらできた副産物(ほんとは円がsin(uv.y)でゆらゆら上昇するのを考えながら作ってた)
output-palette.gif

fixed4 frag (v2f i) : SV_Target
            {
                //球の生成
                float r = 0.1;
                float2 pivot = float2(0.5,0.0);
                pivot.x += 0.2*sin(i.uv.y * 10);
                pivot.y += (fmod(_Time.x*3, 1) / 1);
                float dist = distance(i.uv, pivot);
                float isSphere = step(r,dist);

                fixed4 col = float4(1.0,1.0,1.0,1.0) * isSphere + float4(sin(i.uv.x + 0.3), sin(i.uv.x+ _Time.x *200),sin(i.uv.x + _Time.x *100), 1.0) * (1.0 - isSphere);
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }

とりあえずこれ一個

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

【Unity(C#)】ファイルの保存上限を設けて日付の古いファイルから順に消去する実装

はじめに

【参考リンク】:【Unity(C#)】テクスチャを画像データに変換して端末に保存、読み込み
↑前回の応用です。

やりたい事としては下記の画像のように
ファイルの保存上限を設けて、
上限を超えた場合は最も古いファイルから削除するという実装です。
SaveLimitImage.png

デモ

早速ですがデモです。
保存するファイルは画像ファイルとしました。
Paint2DForQiita5.gif

PC内の保存領域を確認したところ、
しっかりと最後の3枚が保存されていました。

SaveImageCheckSS.PNG

コード

下記今回の処理の全文です。

適当なオブジェクトにアタッチ
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UniRx;
using UniRx.Triggers;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// テクスチャー ⇔ Png画像 の変換と保存と読み込み
/// </summary>
public class TexturePngConverter : MonoBehaviour
{
    [SerializeField] private Button _saveButton;
    [SerializeField] private Button _loadButton;
    [SerializeField] private Image _paintImage;
    [SerializeField] private Painter _painter;
    [SerializeField] private Transform _loadImagesParentTransform;

    private const string IMAGE_SAVE_FOLDER = "Image";
    private const string PNG = ".png";

    /// <summary>
    /// 限界保存枚数
    /// </summary>
    private const int UPPER_LIMIT_SAVE_PICTURE = 3;

    private void Start()
    {
        //セーブボタン
        _saveButton.OnPointerClickAsObservable()
            .Subscribe(_ =>
            {
                //保存
                ConvertToPngAndSave(GetSaveDirectoryPath(IMAGE_SAVE_FOLDER), GetSaveFilePath(IMAGE_SAVE_FOLDER, PNG));
                //リセット
                _painter.ResetTexture();
            }).AddTo(this);

        //ロードボタン
        _loadButton.OnPointerClickAsObservable()
            .Subscribe(_ => ConvertToTextureAndLoad(GetSaveDirectoryPath(IMAGE_SAVE_FOLDER))).AddTo(this);
    }

    /// <summary>
    /// 保存先のファイルのパス取得
    /// </summary>
    /// <param name="folderName">区切りのフォルダ名</param>
    /// <param name="fileType">拡張子</param>
    /// <returns>保存先のパス</returns>
    private string GetSaveFilePath(string folderName, string fileType)
    {
        return GetSaveDirectoryPath(folderName) + DateTime.Now.ToString("yyyyMMddHHmmss") + fileType;
    }

    /// 保存先のディレクトリのパス取得
    /// </summary>
    /// <param name="folderName">区切りのフォルダ名</param>
    /// <returns>保存先のパス</returns>
    private string GetSaveDirectoryPath(string folderName)
    {
        string directoryPath = Application.persistentDataPath + "/" + folderName + "/";

        if (!Directory.Exists(directoryPath))
        {
            //まだ存在してなかったら作成
            Directory.CreateDirectory(directoryPath);
            return directoryPath;
        }

        return directoryPath;
    }

    /// <summary>
    /// "ディレクトリ配下のファイル"が全て入ったリストを返す
    /// 最も古いファイルが[0]番目
    /// </summary>
    /// <param name="directoryName">取得したいファイル群の親ディレクトリ</param>
    /// <returns>指定したディレクトリ配下のファイルが全て入ったリスト</returns>
    private List<string> GetAllFileFromDirectory(string directoryName)
    {
        //古いものが先頭にくるようにファイルをソート
        List<string> imageFilePathList = Directory
            //Imageディレクトリ内の全ファイルを取得
            .GetFiles(directoryName, "*", SearchOption.AllDirectories)
            //.DS_Storeは除く
            .Where(filePath => Path.GetFileName(filePath) != ".DS_Store")
            //日付順に降順でソート
            .OrderBy(filePath => File.GetLastWriteTime(filePath).Date)
            //同じ日付内で時刻順に降順でソート
            .ThenBy(filePath => File.GetLastWriteTime(filePath).TimeOfDay)
            .ToList();

        return imageFilePathList;
    }

    /// <summary>
    /// 画像に変換&保存
    /// 上限保存数をチェック
    /// </summary>
    /// <param name="directoryPath">保存数をチェックするディレクトリ</param>
    /// <param name="fileSavePath">保存先のパス</param>
    private void ConvertToPngAndSave(string directoryPath, string fileSavePath)
    {
        //指定したディレクトリー配下のファイルが全て入ったリストを取得
        List<string> imageFilePaths = GetAllFileFromDirectory(directoryPath);

        //ファイル数の上限をチェック
        if (imageFilePaths.Count >= UPPER_LIMIT_SAVE_PICTURE)
        {
            //上限に達していた場合、最も古いファイルを削除
            File.Delete(imageFilePaths[0]);
        }

        //Pngに変換
        byte[] bytes = _paintImage.sprite.texture.EncodeToPNG();
        //保存
        File.WriteAllBytes(fileSavePath, bytes);
    }

    /// <summary>
    /// テクスチャに変換&読み込み
    /// </summary>
    /// <param name="directoryPath">ロードしたいファイル群の親ディレクトリ</param>
    private void ConvertToTextureAndLoad(string directoryPath)
    {
        List<Image> imageList = new List<Image>();

        //ロード後、複数枚表示するためのImageリスト作成
        foreach (Transform child in _loadImagesParentTransform)
        {
            Image childImage = child.gameObject.GetComponent<Image>();

            if (childImage != null)
            {
                imageList.Add(childImage);
            }
        }

        //指定したディレクトリー配下のファイルが全て入ったリストを取得
        List<string> imageFilePaths = GetAllFileFromDirectory(directoryPath);

        //インデックス用カウンター
        int count = 0;

        //ファイルのリストから古い順にロードしてImageに適用
        foreach (string imageFilePath in imageFilePaths)
        {
            Debug.Log(imageFilePath);
            //読み込み
            byte[] bytes = File.ReadAllBytes(imageFilePath);
            //画像をテクスチャに変換
            Texture2D loadTexture = new Texture2D(2, 2);
            loadTexture.LoadImage(bytes);
            //テクスチャをスプライトに変換
            imageList[count].sprite = Sprite.Create(loadTexture, new Rect(0, 0, loadTexture.width, loadTexture.height), Vector2.zero);
            //インデックス用カウンターを進める
            count++;
        }
    }
}

ソート

今回は古いフォルダの概念を保存した日時(秒単位)にしています。
そのため、ディレクトリ内のファイルの中から
最も古い保存日時のファイルを取得する必要がありました。

そのために一度全ファイルを取得して
それぞれの保存先のパスを保存順にソートしています。

ロード時も同様に全ファイルのパスを取得し
File.ReadAllBytesの引数に渡しています。

下記リンクを参考にLINQを使って簡単に書くことができました。

【参考リンク】:タイムスタンプ(作成日、変更日、最後に開いた日)を基準にファイルを古い順や新しい順にソート【C#】【LINQ】

DateTime.Now

DateTime.Nowを使えば現在の日付と秒単位までの時刻を取得することができます。

今回の実装においては、
この日付と秒単位までの時刻をファイル名とすることで
ユニークなパスを生成しています。

ただ、そのままファイル名として与えた際に少し厄介なことがありました。

Image/2020/09/21 17:12:43.pngのようにディレクトリの区切りとして解釈される
/(スラッシュ)が含まれてしまいます。

このまま保存処理を行おうとすると
そんなディレクトリは存在しませんと怒られます。

ですので、下記のように引数に"yyyyMMddHHmmss"を渡しています。

DateTime.Now.ToString("yyyyMMddHHmmss")

こうすることでImage/20200921171710.pngとなり
秒数まで全て数字で取得することが可能となります。

【参考リンク】:日付や時刻を文字列に変換するには?


2020/09/21 追記

GetFiles(directoryName, "*", SearchOption.AllDirectories)
の箇所を"*""*.png"等にした方が
想定外のファイルを除外することができるとコメント頂いたので
メモしときます (ありがとうございます!)

おわりに

モバイル端末で一気に5,60枚読み込んでも処理落ちは見られませんでした。
枚数が何百、何千となるといろいろと工夫が必要そうです。

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

VGO 2.0 のデータ スキーマの解説

2. データ スキーマ

このページではVGO 2.0 の データ スキーマの詳細を説明します。

目次

2. データ スキーマ仕様

VGO 2.0 では 一部のデータをJSONまたはBSONとして格納しています。

対象のデータは以下の通りです。

名前 補足
アセット情報
レイアウト情報
リソース アクセサー
リソース チャンクタイプがREPJまたはREPBの場合のみ

2-1. アセット情報 (vgo.assetInfo)

VGOのアセット情報を格納しています。

定義名 説明 データ タイプ 必須
generator このVGOモデルの生成ツールの情報 vgo.generatorInfo
right このVGOのコピーライト。 vgo.right
extensions 拡張定義。 object
extensionsUsed このアセットで使用されている拡張定義の名前(レイアウトを含む) string[]

ジェネレーター情報 (vgo.generatorInfo)

定義名 説明 データ タイプ 必須
name 生成ツールの名前。 string true UniVGO
version 生成ツールのバージョン。 string true 2.0.0

ジェネレーターがVGOファイルをエクスポートする際に設定します。

権利情報 (vgo.right)

このVGOモデルに対するコピーライト©です。

定義名 説明 データ タイプ 必須 補足
title このワークのタイトル。 string
author クリエイターの名前。 string
organization クリエイターの所属する組織。 string
createdDate このワークの作成日。 string 指定のフォーマットはありません。
updatedDate このワークの更新日。 string 指定のフォーマットはありません。
version このワークのバージョン。 string 指定のフォーマットはありません。
distributionUrl 配布URL。 string URLフォーマット
licenseUrl ライセンスを記載したURL。 string URLフォーマット

アセット情報(例)

assetInfo.json
{
    "generator": {
        "name": "UniVGO",
        "version": "2.0.0"
    },
    "right": {
        "title": "Sample 3D Model",
        "author": "Izayoi Jiichan",
        "organization": "",
        "createdDate": "2020-08-20",
        "updatedDate": "2020-08-20",
        "version": "1.0.0",
        "distributionUrl": "",
        "licenseUrl": ""
    },
    "extensions": [],
    "extensionsUsed": []
}

2-2. レイアウト

VGOのレイアウト情報です。

定義名 説明 データ タイプ 必須 補足
nodes ノードの一覧です。 layout.node[] true ノードの先頭にはルートを含めます。
skins スキンの一覧です。 layout.skin[]
meshes メッシュの一覧です。 layout.mesh[]
materials マテリアルの一覧です。 layout.material[]
textures テクスチャーの一覧です。 layout.texture[]
particles パーティクルの一覧です。 layout.particle[]
extensions 拡張定義。 object

ノード (layout.node)

ノード ヒエラルキーのノードです。

定義名 説明 データ タイプ 必須 設定値 既定値
name このノードの名前です。 string true
isRoot ノードがルートかどうか。 bool true / false false
isActive ノードがアクティブ(有効)かどうか。 bool true / false true
isStatic ノードがスタティック(静的)かどうか。 bool true / false false
tag ノードに付与されたタグ。 string Untagged
layer ノードが位置するレイヤー。 int [0, 31] 0
animator アニメーター。 node.animator
rigidbody 剛体。 node.rigidbody
colliders コライダーの一覧。 node.collider[]
skybox スカイボックス。 vgo.skybox
light 光源。 vgo.light
right このノードのコピーライト。 vgo.right
particle パーティクルのインデックス。 int
mesh メッシュのインデックス。 int
skin スキンのインデックス。 int
children このノードの子ノードの一覧。 int[]

アニメーター (node.animator)

定義名 説明 データ タイプ 必須 設定値 既定値
name オブジェクトの名前です。 string
enabled アニメーターが有効かどうか。 bool true / false true
humanAvatar ヒューマン アバター。 vgo.humanAvatar
applyRootMotion ルートモーションを適用するべきかどうか。 bool true / false false
updateMode アニメーターのアップデート モード。 enum 0: Normal
1: AnimatePhysics
2: UnscaledTime
0
cullingMode アニメーターのカリング モード enum 0: AlwaysAnimate
1: CullUpdateTransforms
2: CullCompletely
0

ヒューマン アバター (vgo.humanAvatar)

人型アバターを使用する場合に設定します。

定義名 説明 データ タイプ 必須 設定値 既定値
name このヒューマン アバターの名前。 string
humanBones ヒューマンボーンの一覧。 vgo.HumanBone[] true

ヒューマン ボーン (vgo.humanBone)

定義名 説明 データ タイプ 必須 設定値 既定値
humanBodyBone ヒューマン ボディ ボーン enum true [0, 55]
nodeIndex ノードのインデックス。 int true

剛体 (node.rigidbody)

ノードを物理特性によって制御する場合に設定します。

定義名 説明 データ タイプ 必須 設定値 既定値
mass 物体の質量。(kg単位) float [0.0000001, 1000000000]
drag 力によって動く際に、オブジェクトに影響する空気抵抗の大きさ。 float [0.0, infinity]
angularDrag トルクによって回転する際に、オブジェクトに影響する空気抵抗の大きさ。 float [0.0, infinity]
useGravity オブジェクトが重力の影響を受けるかどうか。 bool true / false true
isKinematic 物理エンジンでなくTransformによって操作するかどうか。 bool true / false false
interpolation 補完タイプ。 enum 0: None
1: Interpolate
2: Extrapolate
0
collisionDetectionMode 衝突検知モード。 enum 0: Discrete
1: Continuous
2: ContinuousDynamic
3: ContinuousSpeculative
0
constraints 剛体の動きに関する制限。 int FreesePositionX(2) \
FreesePositionY(4) \

コライダー (node.collider)

オブジェクトが衝突判定を必要とする場合に設定します。

定義名 説明 データ タイプ 必須 設定値 既定値 Box Capsule Sphere
enabled コライダーが有効かどうか。 bool true / false true * * *
type コライダーのタイプ。 enum 0: Box
1: Capsule
2: Sphere
0 * * *
isTrigger コライダーがトリガーかどうか。 bool true / false false * * *
center オブジェクトのローカル空間でのコライダーの位置。(m単位) float[3] x, y, z 0.0, 0.0, 0.0 * * *
size コライダーの大きさ。(m単位) float[3] x, y, z 1.0, 1.0, 1.0 * - -
radius コライダーのローカル座標系における半径。(m単位) float [0, infinity] - * *
height コライダーの高さ。 float [0, infinity] - * -
direction オブジェクトのローカル座標系における長辺方向の軸の向き。 int 0: X
1: Y
2: Z
- * -
physicMaterial 使用する物理特性マテリアル。 vgo.PhysicMaterial * * *

物理特性マテリアル (vgo.physicMaterial)

衝突するオブジェクトの摩擦や跳ね返り効果を調整する場合に設定します。

定義名 説明 データ タイプ 必須 設定値 既定値
dynamicFriction 移動している物体に対する摩擦。 float [0.0, 1.0]
staticFriction 面上で静止しているオブジェクトに使用される摩擦。 float [0.0, 1.0]
bounciness 表面にどれだけ弾性があるか。 float [0.0, 1.0]
frictionCombine 衝突するオブジェクト間の摩擦タイプ。 enum 0: Average
1: Multiply
2: Minimum
3: Maximum
0
bounceCombine 衝突するオブジェクト間の跳ね返しタイプ。 enum 0: Average
1: Multiply
2: Minimum
3: Maximum
0

スカイボックス (vgo.skybox)

スカイボックスを使用したい場合に設定します。

定義名 説明 データ タイプ 必須 設定値 既定値
materialIndex マテリアルのインデックス。 int

スカイボックスは通常は最大で1個まで設定できます。

光源 (vgo.light)

definition name description type required setting value default value Spot Directional Point Rectangle Disc
enabled ライトが有効かどうか。 bool true / false true * * * * *
type ライトのタイプ。 enum 0: Spot
1: Directional
2: Point
3: Rectangle
4: Disc
0 * * * * *
shape ライトの形状。 enum 0: Cone
1: Pyramid
2: Box
0 * - - - -
range ライトの到達距離。 float [0, infinity] * - * * *
spotAngle スポットライトの円錐形の底面の角度。(度単位) float [0, infinity] * - - - -
areaSize エリアライトのサイズ。 float[2] x, y - - - * -
areaRadius エリアライトの半径。 float [0, infinity] - - - - *
color 光の色。 float[4] r, g, b, a * * * * *
lightmapBakeType ライトのベイク タイプ。 enum 1: Mixed
2: Baked
4: Realtime
* * * * *
intensity ライトの輝度。 float [0, infinity] * * * * *
bounceIntensity バウンス照明の強度を定義する乗数。 float [0, infinity] * * * * *
shadows この光がどのように影を落とすか。 enum 0: None
1: Hard
2: Soft
0 * * * * *
shadowRadius 影の範囲。 float [0, infinity] * - * - -
shadowAngle 影の角度。 float [0, infinity] - * - - -
shadowStrength 影の強度。 float [0, infinity] - * * - -
shadowResolution シャドウマップの解像度。 enum -1: FromQualitySettings
0: Low
1: Medium
2: High
3: VeryHigh
-1 - * * - -
shadowBias 影が現れなくなるライトからの距離。 float [0, infinity] - * * - -
shadowNormalBias 影を落す面が、面の法線に沿って縮小されるようになる距離。 float [0, infinity] - * * - -
shadowNearPlane シャドウ錐台に使用する平面に近い値。 float [0, infinity] - * * - -
renderMode ライトのレンダリング方法。 enum 0: Auto
1: ForcePixel
2: ForceVertex
0 * * * * *
cullingMask カリング マスク。 int [-1, infinity] -1 (Everything) * * * * *

Cookie、Flare、Halo はサポートされていません。

JSON example (layout.nodes)

layout.json(nodes)
{
    "nodes": [
        {
            "name": "Node1",
            "isRoot": true,
            "tag": "Player",
            "animator": {
                "humanAvatar": {
                    "name": "",
                    "humanBones": []
                }
            },
            "children": [
                1,
                2,
                3,
                4,
                5
            ]
        },
        {
            "name": "Node2",
            "layer": 1,
            "rigidbody": {
                "mass": 1,
                "drag": 0,
                "angularDrag": 0.05,
                "useGravity": true,
                "isKinematic": false,
                "interpolation": 0,
                "collisionDetectionMode": 0,
                "constraints": 0
            },
            "colliders": [
                {
                    "type": 0,
                    "center": [ 0, 0, 0 ],
                    "size": [ 1, 1, 1 ]
                },
                {
                    "enabled": false,
                    "type": 1,
                    "radius": 0.49999997,
                    "height": 1,
                    "direction": 1,
                    "physicMaterial": {
                        "dynamicFriction": 0.6,
                        "staticFriction": 0.6,
                        "bounciness": 0,
                        "frictionCombine": 0,
                        "bounceCombine": 0
                    }
                }
            ],
            "mesh": 0
        }
    ]
}

スキン (layout.skin)

スキンを定義するジョイントとマトリックス。

定義名 説明 データ タイプ 必須 設定値 既定値
inverseBindMatrices 浮動小数点の4x4逆バインド行列を含むアクセサーのインデックス。 int
skeleton スケルトン ルートとして使用されるノードのインデックス。 int
joints このスキンのジョイントとして使用されるスケルトン ノードのインデックス。 int[] true

メッシュ (layout.mesh)

レンダリングされるプリミティブのセット。

定義名 説明 データ タイプ 必須 設定値 既定値
name このメッシュの名前。 string true
attributes 辞書マッピング属性。 mesh.primitive.attributes true
subMeshes サブメッシュ インデックスを含むアクセサーのインデックス。 int[]
materials レンダリング時にこのプリミティブに適用するマテリアルのインデックスリスト。 int[]
blendShapeKind ブレンドシェイプの種類。 enum 0: None
1: Face
2: Face_2
3: Kind_3
4: Kind_4
5: Kind_5
0
blendShapes ブレンドシェイプのリスト。 mesh.blendshape[]
blendShapePesets ブレンドシェイプ プリセットのリスト。 mesh.blendshape.preset[]

メッシュ プリミティブ属性 (mesh.primitive.attributes)

辞書オブジェクト。

データ タイプ キー
Dictionary POSITION
NORMAL
TANGENT
COLOR_0
TEXCOORD_0
JOINTS_0
WEIGHTS_0
アクセサー インデックス
キー アクセサー データ タイプ
POSITION Vector3 (float)
NORMAL Vector3 (float)
TANGENT Vector4 (float)
COLOR_0 Color4 (ubyte)
Color4 (float)
TEXCOORD_0 Vector2 (float)
JOINTS_0 Vector4 (ubyte)
Vector4 (ushort)
WEIGHTS_0 Vector4 (float)

ブレンドシェイプ (mesh.blendshape)

メッシュ ブレンドシェイプ。

定義名 説明 データ タイプ 必須 設定値 既定値
name ブレンドシェイプの名前。 string true
attributes 辞書マッピング属性。
POSITION,NORMAL,TANGENTのみ含めることができます。
mesh.primitive.attributes true
facePartsType 顔パーツのタイプ。 enum 0: None
10: Forehead
11: Eyebrow
12: Eyelashes
13: Eyelid
14: Pupil
20: Ear
30: Nose
40: Cheek
50: Mouth
51: Teeth
52: Tongue
60: Hair
61: BackHair
62: SideHair
63: Frizz
64: EarHair
65: NoseHair
66: Mustache
67: Beard
70: Mole
0
blinkType まばたきのタイプ。 enum 0: None
1: Left
2: Right
3: Both
0
visemeType 口形素のタイプ。 enum -1: None
0: Silence
1: PP
2: FF
3: TH
4: DD
5: kk
6: CH
7: SS
8: nn
9: RR
10: A
11: E
12: I
13: O
14: U
-1

ブレンドシェイプ プリセット (mesh.blendshape.preset)

ブレンドシェイプのプリセット。

定義名 説明 データ タイプ 必須 設定値 既定値
name プリセットの名前。 string
type プリセットのタイプ。 enum true 0: Custom
1: Neutral
2: Joy
3: Angry
4: Sorrow
5: Fun
6: Confuse
7: Nervous
8: Sleepy
9: Surprise
10: WinkL
11: WinkR
12: Preset_12
13: Preset_13
14: Preset_14
15: Preset_15
bindings バインディングのリスト。 mesh.blendshape.binding[] true

ブレンドシェイプ バインディング (mesh.blendshape.binding)

プリセットのブレンドシェイプ バインディング。

定義名 説明 データ タイプ 必須 設定値 既定値
index ブレンドシェイプのインデックス。 int true
weight このブレンドシェイプのウェイト。 float [0.0, 100.0] 0

JSON example (layout.meshes)

layout.json(meshes)
{
    "meshes": [
        {
            "name": "face",
            "attributes": {
                "POSITION": 59,
                "NORMAL": 60,
                "TEXCOORD_0": 61,
                "JOINTS_0": 62,
                "WEIGHTS_0": 63
            },
            "subMeshes": [
                64,
                65,
                66
            ],
            "materials": [
                4,
                5,
                6
            ],
            "blendShapeKind": 1,
            "blendShapes": [
                {
                    "name": "face.mouth_a",
                    "attributes": {
                        "POSITION": 67,
                        "NORMAL": 68,
                        "TANGENT": -1
                    },
                    "facePartsType": 50,
                    "blinkType": 0,
                    "visemeType": 10
                }
            ],
            "blendShapePesets": [
                {
                    "name": "Joy",
                    "type": 2,
                    "bindings": [
                        {
                            "index": 24,
                            "weight": 100.0
                        },
                        {
                            "index": 36,
                            "weight": 100.0
                        },
                        {
                            "index": 44,
                            "weight": 100.0
                        }
                    ]
                }
            ]
        }
    ]
}

マテリアル (layout.material)

プリミティブのマテリアルの外観。

定義名 説明 データ タイプ 必須 設定値 既定値
name マテリアルの名前。 string true
shaderName シェーダー名。 string true
renderQueue レンダー キュー。 int
isUnlit マテリアルがライトの影響を受けないかどうか。 bool true / false false
intProperties キーはプロパティ名です。 Dictionary
floatProperties キーはプロパティ名です。 Dictionary
colorProperties キーはプロパティ名です。
値は color[3] または color[4] です。
Dictionary
vectorProperties キーはプロパティ名です。
値は vector です。
Dictionary
matrixProperties キーはプロパティ名です。
値は matrix です。
Dictionary
textureOffsetProperties キーはプロパティ名です。
値は vector2 です。
Dictionary
textureScaleProperties キーはプロパティ名です。
値は vector2 です。
Dictionary
textureIndexProperties キーはプロパティ名です。 Dictionary
keywordMap キーはキーワードです。 Dictionary
tagMap キーはタグ名です。 Dictionary
extensions 拡張定義。 object

JSON example (layout.materials)

layout.json(materials)
{
    "materials": [
        {
            "name": "StandardMaterial",
            "shaderName": "Standard",
            "renderQueue": 2000,
            "intProperties": {
                "_Mode": 0,
                "_SmoothnessTextureChannel": 0,
                "_SpecularHighlights": 1,
                "_GlossyReflections": 1,
                "_UVSec": 0
            },
            "floatProperties": {
                "_Cutoff": 0.5,
                "_Metallic": 0,
                "_Glossiness": 0.5,
                "_GlossMapScale": 1,
                "_BumpScale": 1,
                "_Parallax": 0.02,
                "_OcclusionStrength": 1,
                "_DetailNormalMapScale": 1
            },
            "colorProperties": {
                "_Color": [
                    0.743349731,
                    0.4882486,
                    0.181164235,
                    1
                ],
                "_EmissionColor": [
                    0,
                    0,
                    0
                ]
            },
            "textureScaleProperties": {
                "_MainTex": [
                    5,
                    5
                ]
            },
            "textureIndexProperties": {
                "_MainTex": 1
            },
            "keywordMap": {
                "_NORMALMAP": false,
                "_PARALLAXMAP": false,
                "_EMISSION": false,
                "_DETAIL_MULX2": false,
                "_METALLICGLOSSMAP": false
            }
        }
    ]
}

テクスチャー (layout.texture)

テクスチャー。

定義名 説明 データ タイプ 必須 設定値 既定値
name テクスチャーの名前。 string true
source イメージを格納したアクセサーのインデックス。 int
dimensionType テクスチャーの次元タイプ。 enum -1: Unknown
0: None
1: Any
2: Tex2D
3: Tex3D
4: Cube
5: Tex2DArray
6: CubeArray
0
mapType テクスチャー マップのタイプ。 enum -1: Unknown
0: Default
1: NormalMap
2: HeightMap
3: OcclusionMap
4: EmissionMap
5: MetallicRoughnessMap
6: SpecularGlossinessMap
7: CubeMap
0
colorSpace イメージ カラー スペース。 enum 0: sRGB
1: Linear
0
mimeType 画像のMIMEタイプ。 string image/jpeg
image/png
filterMode テクスチャーのフィルタリング モード。 enum 0: Point
1: Bilinear
2: Trilinear
0
wrapMode テクスチャー座標ラッピング モード。 enum 0: Repeat
1: Clamp
2: Mirror
3: MirrorOnce
0
wrapModeU テクスチャーU座標ラッピング モード。 enum 0: Repeat
1: Clamp
2: Mirror
3: MirrorOnce
0
wrapModeV テクスチャーV座標ラッピング モード。 enum 0: Repeat
1: Clamp
2: Mirror
3: MirrorOnce
0
metallicRoughness マテリアルの金属の粗さ。 float [0.0, 1.0]
extensions 拡張定義。 object

JSON example (layout.textures)

layout.json(textures)
{
    "textures": [
        {
            "name": "MetallicTexture",
            "source": 1,
            "dimensionType": 2,
            "mapType": 5,
            "colorSpace": 1,
            "mimeType": "image/png",
            "filterMode": 1,
            "wrapMode": 0,
            "wrapModeU": 0,
            "wrapModeV": 0,
            "metallicRoughness": 0.5
        }
    ]
}

目次

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

Unity 2019.4.10f1の起動時のエラー「Could not establish a connection with the Unity Package Manager local server process.」の解決方法

あらすじ

 著者は、Unity 2019.4.10f1をインストールしてからというもの、毎回決まって出てくるエラーに苛立っていた。ところが、ファイアウォール等のウイルス対策ソフトの除外設定をすると、Unityを起動させることに成功した。今回は、それの備忘録を綴っておきたい。

環境

  • OS:Windos 10 Home
  • システムの種類:64ビット オペレーティングシステム、x64 ベース プロセッサ
  • プロセッサ:Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz 2.70 GHz
  • 実装RAM:8.00 GB
  • グラフィックボード:Intel(R) Graphics 620
  • GPU:Intel(R) Graphics Family
  • Unity 2019.4.10f1
  • Unity 2018.4.27f1
  • Unity Hub 2.4.0

問題

 Unity 2019.4.10f1(以下、Unity)を上手くインストールできた。よーし、早速Unity Hubからプロジェクトを作って開こう——―とした時に起こったエラーがこちら。

キャプチャ.PNG

 今回の記事の趣旨は、このエラーを解消し、Unityの編集画面を開くことです。それでは見ていきましょう。

試した方法(失敗)

 今回試した解決方法は、全て参考文献の1番の記事を参考にさせていただいております。

1. ログインし直し

 このエラーを有識者の方に見せたところ、「これログイン出来ていないぽくない?」と言われました。
 ということで、一度Unity Hubにログインはしていたのですが、再度ログインし直してみることに。

 しかし、失敗。
 何回か繰り返したり、Googleからのログインも試してみましたが、同じエラーが出続けてしまいました……。

2. 特定のフォルダの削除

次は、参考文献の2番に書いてあったこと。以下、そのページからの引用です。

以下のフォルダを削除して起動すると、起動に成功する場合があります。

C:\Users\admin\AppData\Roaming\Unity

C:\Users\ユーザ名\AppData\Roaming\Unity

 ちなみに気を付ける点は、「普通にファイルのエクスプローラーを開いただけでは、このファイルを開けないこと」。というより、表示されないようです。なので、コマンド プロンプトからの操作をお勧めします。実際、私もそうしました。

 実のところ、このフォルダが何なのか分かっていないのですが……。まあ、良いでしょう。コマンド プロンプトからこのフォルダを削除、っと。よし、起動。

 ……出来ませんでした。失敗です。

解決方法

 3個目の方法でやっと上手く行きました……。それは、「セキュリティソフト全部に例外(UnityPackageManagerとUnity.exe両方)を設定すること」。もう既に知ってるしやったよ、という方もいらっしゃるかとは思いますが、ご了承ください……。

 ちなみに、今回私が例外を設定したセキュリティソフトは次の通りです。
* Windows Defender
* Windows ファイアウォール
* ウイルスバスター コーポレートエディション

 これらのセキュリティソフトそれぞれに例外を設定してあげると、上手く行きました。なお、Windows Defenderの例外の設定方法は参考文献の3番、Windows ファイアウォールに関しては参考文献の4番に記載の方法で行いました。

 例外に設定したファイルは、以下の通りです。
* C:\Program Files\Unity\2019.4.10f1\Editor\Unity.exe
* C:\Program Files\Unity\2019.4.10f1\Editor
\Data\Resources\PackageManager\Server

 ちなみに、私の場合はUnity 2018.4.27f1もインストールしていたので、今回はそちらにも同じように例外設定をかけています。私のようにUnity 2019.4.10f1と2018.4.27f1、両方ともUnity Hubからインストールした場合は、両方のファイルのアドレスの"2019.4.10f1"が"2018.4.27f1"に変化するだけだと思います(インストールする場所の設定により変化します)。

ウイルスバスター コーポレートエディションの例外設定

 参考までに、ウイルスバスター コーポレートエディションの例外の設定方法を載せておきます。
ウイルスバスター設定画面1.PNG
① 鍵マークのボタンを押して、ロックを解除する。
②歯車マークのボタンを押して、設定画面を開く。

ウイルスバスター設定画面2.png
③[設定]→[保護]から、ドロップダウンリストを選択。
④「信頼済みプログラム」を選択。

ウイルスバスター設定画面3.png
⑤「参照…」をクリックして、例外設定をするファイルを選択。
⑥「信頼済みプログラムリストに追加」をクリック。
⑦全ての設定が終わった後、「OK」をクリックする。

 これで設定完了です。

参考文献

  1. @velo. "Unityエラー 「could not establish a connection with the unity package manager local server process. this is most likely due to a proxy or firewall configuration. 」". Qiita. 2020-05-19. https://qiita.com/velo/items/b08a6a315e18d45c307a, (参照 2020-09-21).
  2. 黒河優介. "インストールがうまくいかない場合". ユニティ・テクノロジーズ・ジャパン合同会社. 2019-11-20. https://helpdesk.unity3d.co.jp/hc/ja/articles/219377968-%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%8C%E3%81%86%E3%81%BE%E3%81%8F%E3%81%84%E3%81%8B%E3%81%AA%E3%81%84%E5%A0%B4%E5%90%88, (参照 2020-09-21).
  3. "Windows 10のWindows Defenderで特定のファイルやフォルダーをスキャンしないように設定する方法". NEC LAVIE公式サイト. 2020-04-16. https://faq.nec-lavie.jp/qasearch/1007/app/servlet/relatedqa?QID=018507, (参照 2020-09-21).
  4. "Windowsファイアウォールの例外にアプリケーションを追加する方法". バッファロー. 2019-05-24. https://www.buffalo.jp/support/faq/detail/792.html, (参照 2020-09-21).
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】Timelineを拡張してポストプロセスのカスタムトラックを作成する

概要

Timelineでポストプロセス専用のカスタムトラックを作成する実装例をご紹介します。
TimelinePostProcessing.gif

環境

  • Unity2019.4.1f1
  • UniversalRP 7.3.1

Timelineでカスタムトラックを作成して連携

TimelineのクリップごとにProfileを指定します。
2020-09-13_19h59_15.png

ポストエフェクトのかかり具合はWeight Curveで調整するようにしました。
WeightCurveが1ならよくかかる、0ならかかりません。
2020-09-13_19h59_46.png

コード

4つのC#ファイルを作成します。

クラス 説明
PostProcessBehaviour.cs クリップごとの振る舞いを表します
PostProcessMixerBehaviour.cs トラック内で複数のクリップがある場合の振る舞いを表します。inputWeightが正のものが現在のクリップです
PostProcessTrack.cs トラックを表します
PostProcessClip.cs クリップを表します。クリップのパラメータが変更されるたびにCreatePlayableが呼ばれます。
PostProcessBehaviour.cs
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Rendering;
using System;

[Serializable]
public class PostProcessBehaviour : PlayableBehaviour
{
    [HideInInspector]
    public Volume volume;
    public VolumeProfile profile;
    public int layer;
    public AnimationCurve weightCurve = AnimationCurve.Linear(0f, 0f, 1f, 1f);

    public override void OnBehaviourPlay(Playable playable, FrameData info)
    {
        if (profile != null)
        {
            QuickVolume();
        }
    }

    public override void OnBehaviourPause(Playable playable, FrameData info)
    {
        if (volume != null)
        {
            GameObject.DestroyImmediate(volume.gameObject);
        }
    }

    void QuickVolume()
    {
        if (volume == null)
        {
            volume = new GameObject().AddComponent<Volume>();
            volume.gameObject.layer = layer;
            volume.gameObject.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
            volume.gameObject.name = $"PostProcessBehaviourVolume [Profile {profile.name}]";
            volume.weight = 0;
            volume.priority = 1;
            volume.isGlobal = true;
            volume.profile = profile;
        }
    }

    public void ChangeWeight(float time)
    {
        if (volume == null) { return; }
        volume.weight = weightCurve.Evaluate(time);
    }
}
PostProcessMixerBehaviour.cs
using System;
using UnityEngine;
using UnityEngine.Playables;

[Serializable]
public class PostProcessMixerBehaviour : PlayableBehaviour
{
    public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
        int inputCount = playable.GetInputCount();
        for (int i = 0; i < inputCount; i++)
        {
            var playableInput = (ScriptPlayable<PostProcessBehaviour>)playable.GetInput(i);
            PostProcessBehaviour input = playableInput.GetBehaviour();
            float inputWeight = playable.GetInputWeight(i);
            if (Mathf.Approximately(inputWeight, 0f))
            {
                continue;
            }
            float normalizedTime = (float)(playableInput.GetTime() / playableInput.GetDuration());
            input.ChangeWeight(normalizedTime);
        }
    }
}
PostProcessTack.cs
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;

[TrackColor(0.98f, 0.27f, 0.42f)]
[TrackClipType(typeof(PostProcessClip))]
public class PostProcessTrack : TrackAsset
{
    public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
    {
        var scriptPlayable = ScriptPlayable<PostProcessMixerBehaviour>.Create(graph, inputCount);
        return scriptPlayable;
    }
}

PostProcessClip.cs
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using System;
#if UNITY_EDITOR
using UnityEditor;
#endif

[Serializable]
public class PostProcessClip : PlayableAsset, ITimelineClipAsset
{
    public PostProcessBehaviour template = new PostProcessBehaviour();

    public ClipCaps clipCaps
    {
        get { return ClipCaps.Extrapolation | ClipCaps.Blending; }
    }

    public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
    {
        var playable = ScriptPlayable<PostProcessBehaviour>.Create(graph, template);
        PostProcessBehaviour clone = playable.GetBehaviour();
        return playable;
    }
}

#if UNITY_EDITOR

[CustomEditor(typeof(PostProcessClip))]
public class PostProcessClipEditor : Editor
{
    PostProcessClip postProcessClip;
    Editor profileEditor;
    SerializedProperty profileProperty;
    SerializedProperty curveProperty;

    void OnEnable()
    {
        postProcessClip = target as PostProcessClip;
        profileEditor = Editor.CreateEditor(postProcessClip.template.profile);
        profileProperty = serializedObject.FindProperty("template.profile");
        curveProperty = serializedObject.FindProperty("template.weightCurve");
    }

    void OnDisable()
    {
        DestroyImmediate(profileEditor);
    }

    public override void OnInspectorGUI()
    {
        postProcessClip.template.layer = EditorGUILayout.LayerField("Layer", postProcessClip.template.layer);
        serializedObject.Update();
        EditorGUILayout.PropertyField(profileProperty);
        EditorGUILayout.PropertyField(curveProperty);
        serializedObject.ApplyModifiedProperties();

        profileEditor?.OnInspectorGUI();
    }
}
#endif

プロジェクトデータ

以前の記事と同じ場所にサンプルプロジェクトデータをGitHubアップしましたので
ご自由にお使いください。

参考サイト様

まとめ

  • 以前の記事ではコンポーネントを作ってアニメーターと連携して パラメータをいじってました。
  • 今回Timelineを拡張してポストプロセス用のカスタムトラックを作って設定できるようにしました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity(C#)】Lerpで滑らかな線を描く

はじめに

平面上に線を滑らかに描く方法を調べました。

VR空間でもぬるぬる動いている例を参考にしました(↓すごい)

【参考リンク】:ホワイトボードで線がヌルヌル描ける仕組み

描く実装

"描く部分"のコードは下記リンクからまるまる拝借しました。

【参考リンク】:Unityでテクスチャにお絵描きしよう

デモ

まずは補間することなくそのまま動かしてみたサンプルです。
Paint2DForQiita1.gif

線がかすれてしまって不格好です。


次にLerpによる補間を施したものです。
Paint2DForQiita2.gif

しっかりと線の間を補間してくれました。

コード

下記全文です。

using UnityEngine;

//使い方↓
//https://nn-hokuson.hatenablog.com/entry/2016/12/08/200133
public class SmoothPaint : MonoBehaviour
{
    Texture2D drawTexture;
    Color[] buffer;

    private Vector2 _prevPosition;

    void Start()
    {
        Texture2D mainTexture = (Texture2D) GetComponent<Renderer>().material.mainTexture;
        Color[] pixels = mainTexture.GetPixels();

        buffer = new Color[pixels.Length];
        pixels.CopyTo(buffer, 0);

        drawTexture = new Texture2D(mainTexture.width, mainTexture.height, TextureFormat.RGBA32, false);
        drawTexture.filterMode = FilterMode.Point;
    }

    public void Draw(Vector2 p)
    {
        for (int x = 0; x < 256; x++)
        {
            for (int y = 0; y < 256; y++)
            {
                if ((p - new Vector2(x, y)).magnitude < 5)
                {
                    buffer.SetValue(Color.black, x + 256 * y);
                }
            }
        }
    }

    void Update()
    {
        if (Input.GetMouseButton(0))
        {
            //前回値がまだないなら現在の値を前回値として扱う
            if (_prevPosition == Vector2.zero)
            {
                _prevPosition = Input.mousePosition;
            }

            //線形補間に使う入力の終点座標
            Vector2 endPosition = Input.mousePosition;
            //1フレームの線の距離
            float lineLength = Vector2.Distance(_prevPosition, endPosition);
            //線の長さに応じて変わる補間値 CeilToIntは小数点以下を切り上げ
            int lerpCountAdjustNum = 5;
            int lerpCount = Mathf.CeilToInt(lineLength / lerpCountAdjustNum);

            for (int i = 1; i <= lerpCount; i++)
            {
                //Lerpの割合値を "現在の回数/合計回数" で出す
                float lerpWeight = (float) i / lerpCount;

                //前回の入力座標、現在の入力座標、割合を渡して補間する座標を算出
                Vector3 lerpPosition = Vector2.Lerp(_prevPosition, Input.mousePosition, lerpWeight);

                Ray ray = Camera.main.ScreenPointToRay(lerpPosition);
                RaycastHit hit;
                if (Physics.Raycast(ray, out hit, 100.0f))
                {
                    Draw(hit.textureCoord * 256);
                }

                drawTexture.SetPixels(buffer);
                drawTexture.Apply();
                GetComponent<Renderer>().material.mainTexture = drawTexture;
            }

            //前回の入力座標を記録
            _prevPosition = Input.mousePosition;
        }
        else
        {
            //前回の入力座標をリセット
            _prevPosition = Vector2.zero;
        }
    }
}

ロジックとしては下記です
①前フレームのマウスの入力座標を保持
②次フレームにて①と現在の入力座標の距離を算出
③距離に応じて補間値(補間回数)を算出
④補間値を利用しLerpで補間

線が繋がる

実装していてそこそこ時間を使ってしまったのが、
線が繋がってしまう という現象でした。

下記GIFのように新しく描き始めた箇所と
前回の終了地点が繋がってしまっていました。

Paint2DForQiita3.gif

これは描き終えた際の値を前回値として保持したまま
新しい入力箇所での補間を行っていることが原因でした。

ですので、下記箇所で入力が無い状態になった際に前回値のリセットを行っています。
さらに、入力値のリセットにより前回値が存在しないフレーム、
すなわち描き始めのフレームにおいては補間を行う必要が無いので
前回の値=現在の値として扱うようにしています。

    if(Input.GetMouseButton(0))
    { 
        //前回値がまだないなら現在の値を前回値として扱う
        if (_prevPosition == Vector2.zero)
        {
            _prevPosition = Input.mousePosition;
        }
    }
    else
    {
        //前回の入力座標をリセット
        _prevPosition = Vector2.zero;
    }

これにより描き始めた箇所に線を新しく描画することができました。
Paint2DForQiita4.gif

おわりに

線の太さ(大きさ)も考慮した補間値を算出すれば
もっと最適な補間ができるみたいです。

そのうち書けたら書きます。

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