Editor拡張ではSerializedPropertyを使おう。

UnityのEditor拡張は便利だが、よく紹介されているEditorGUILayout.FloatValue()などの直接値を返すものを使用すると、Prefabモードでは値が反映されない。 その場合は、SerializedProperty経由のEditorGUILayout.PropertyFieldを使うと、Prefabモードでも正しく値が反映される。

以下のようなEditor拡張はサンプルとしてもよく見ると思う。

using UnityEngine;
using UnityEditor;

public class EditorSample1 : MonoBehaviour
{
    public float a;
    public GameObject b;

#if UNITY_EDITOR
    [CustomEditor(typeof(EditorSample1))]
    class InternalEditor : Editor
    {
        EditorSample1 _self;

        private void OnEnable()
        {
            _self = target as EditorSample1;
        }

        public override void OnInspectorGUI()
        {
            _self.a = EditorGUILayout.FloatField(_self.a);
            _self.b = EditorGUILayout.ObjectField(_self.b, typeof(GameObject), false) as GameObject;
        }
    }
#endif
}

このコンポーネントをアタッチすると、Projectビューから直接選択した場合には問題なく編集することができる。

しかしUnity2018.3から追加されたPrefabモードでは値を変更することができない。

代わりにSerializedObjectからSerializedPropertyを取得して、EditorGUILayout.PropertyField()を使用するように変更する。 (Editorクラスのみ抜粋)

[CustomEditor(typeof(EditorSample1))]
class InternalEditor : Editor
{
    EditorSample1 _self;
    SerializedObject _sObj;

    private void OnEnable()
    {
        _self = target as EditorSample1;
        _sObj = serializedObject;
    }

    public override void OnInspectorGUI()
    {
        var propA = _sObj.FindProperty("a");
        var propB = _sObj.FindProperty("b");
            
        EditorGUILayout.PropertyField(propA);
        EditorGUILayout.PropertyField(propB);

        if(_sObj.hasModifiedProperties)
        {
            _sObj.ApplyModifiedProperties();
        }
    }
}

これによりPrefabモードでも値を変更することができる。

内積はクランプしたほうがいい

結論

内積を関数の入力にする場合はクランプした方がいい。

理由

正規化済みの内積であっても、計算誤差によっては絶対値が1を超える場合があるのでクランプしたほうが良いです。

例えば内積からAcosを取る場合

// a, bは正規化済みのベクトルとする
Vector3 a, b;
float dot = Vector3.Dot(a, b);
float rad = Mathf.Acos(dot);

Mathf.Acos()は1より大きく、-1より小さい値が入力された場合はNaNを返すので、結果がNaNになる場合があります。

なので、Clampで-1から1の範囲にしたほうが良い。

// a, bは正規化済みのベクトルとする
Vector3 a, b;
float dot = Mathf.Clamp(Vector3.Dot(a, b), -1f, 1f);
float rad = Mathf.Acos(dot);

なんかNaNになるなと思ったら、こんなところで引っかかってましたとさ。

PlayCanvasチートシート Unityと比べてみよう エディタ拡張編 その2

前回の記事で触れてなかったところなどを追加したバージョン

前回の記事
seiroise.hatenablog.com

Unityを使ったことのある人がPlayCanvasに手を出したとき「Unityのアレをやりたいんだけど・・・」という状況が結構あった。
今回はその中でもPlayCanvas版のエディタ拡張的要素であるスクリプト属性についてまとめる。


参考

スクリプト属性についてのリファレンス
Script Attributes | Learn PlayCanvas


注意

PlayCanvasではスクリプトを書いた後にEditorのParseボタンを押さないと更新されないので注意しよう。
また、エディタ上の値をいじったあと、入力を確定させないとLaunch後に反映されないので必ず確定させるよう注意しよう。


目次

数値

Unity

public int hp = 80;


PlayCanvas

ScriptAttribute.attributes.add("hp", {
    type: "number",
    default: 80
});

console.log(this.hp); //80

f:id:seiroise:20170509201333g:plain
typeオプションに"number"を指定して数値に、defaultオプションでデフォルトの値を決めてる。


ちなみに日本語でもいける。でもソースコード中に文字列以外で日本語があると精神上よろしくない。
なのでtitleオプションで変更する方が心臓に負担がかからないかも。(あとタブがずれる)

ScriptAttribute.attributes.add("お金", {
    type: "number",
    default: 100
});

console.log(this.お金); //100


titleオプションを使ったバージョン

ScriptAttribute.attributes.add("money", {
    type: "number",
    title: "お金",
    default: 100
});

console.log(this.money); //100

単位の表示

Unityだとエディタ拡張しないとできないので割愛


PlayCanvas

NumberAttribute.attributes.add("weight", {
    type: "number",
    placeholder: "kg",
    default: 60
});

console.log(this.weight); //150(kgは出力されない)

f:id:seiroise:20170509201553g:plain
(薄い

スライダー

Unity

[Range(10, 200)]
public float height = 60;


PlayCanvas

NumberAttribute.attributes.add("height", {
    type: "number",
    default: 175.8,
    min: 100,
    max: 250
});

console.log(this.height); //175.8

f:id:seiroise:20170508194339g:plain

minとmaxの二つのオプションを必ず指定すること。

文字列

Unity

public string id;


PlayCanvas

StringAttribute.attributes.add("id", {
    type: "string",
});

console.log(this.id); //

f:id:seiroise:20170509201844g:plain


defaultオプションで初期値の指定もできる

StringAttribute.attributes.add("name", {
    type: "string",
    default: "太郎"
});

console.log(this.name); //"太郎"


UnityのTextArea属性みたいに複数行入力を可能にするオプションはないみたい

真偽値

Unity

public bool isHeal;


PlayCanvas

BooleanAttribute.attributes.add("isHeal", {
    type: "boolean"
});
console.log(this.isHeal); //false

f:id:seiroise:20170509202123g:plain


defaultオプションで初期値の指定もできる

BooleanAttribute.attributes.add("isTrigger", {
    type: "boolean",
    default: true
});
console.log(this.isTrigger); //true

配列

Unity

public string[] characters;

PlayCanvas

Test.attributes.add("characters", {
    type: "string",
    array: true,
});

f:id:seiroise:20170508194848g:plain
重複は許されない(gifだと"太郎")

列挙

Unity

public enum Value {
    valueOne = 1,
    valueTwo = 2,
    valueThree = 3
}

public Value value;

PlayCanvas

Test.attributes.add("value", {
    type: "number",
    enum: [
        {"valueOne": 1},
        {"valueTwo": 2},
        {"valueThree": 3}
    ]
});

f:id:seiroise:20170508195512g:plain

ベクトル

Vec2

Unity

public Vector2 direction;


PlayCanvas

VectorAttribute.attributes.add("direction", {
    type: "vec2"
});
console.log(this.direction); //[0, 0]

f:id:seiroise:20170509202054g:plain


defaultオプションに配列を指定することで初期値の指定もできる。
Vec3とVec4も同様に初期値の指定ができる。

VectorAttribute.attributes.add("direction", {
    type: "vec2",
    default: [1, 2]
});
console.log(this.direction); //[1, 2]


さらにplaceholderオプションに配列を指定することで要素ごとの単位の表示もできる。
これもVec3とVec4も同様に指定できる。

VectorAttribute.attributes.add("direction", {
    type: "vec2",
    default: [1, 2]
    placeholder: ["cm", "cm"]
});

Vec3

Unity

public Vector3 position;


PlayCanvas

VectorAttribute.attributes.add("position", {
    type: "vec3"
});
console.log(this.position); //[0, 0, 0]

Vec4

Unity

public Vector4 rotation;


PlayCanvas

VectorAttribute.attributes.add("rotation", {
    type: "vec4"
});
console.log(this.rotation); //[0, 0, 0, 0]

rgb

たしかUnityだとエディタ拡張しないとできないので割愛。(できたっけ?)


PlayCanvas

ColorAttribute.attributes.add("backgroundColor", {
    type: "rgb"
});

console.log(this.backgroundColor); //[1, 1, 1]

f:id:seiroise:20170508193922g:plain


defaultオプションに配列を指定することで初期値の指定もできる。
このとき有効なのは0~1のみで0~255でも"#FF5588"などもだめ。
rgbaも同様に指定できる。

ColorAttribute.attributes.add("mainColor", {
    type: "rgb",
    default: [0.2, 0.5, 1]
});

console.log(this.mainColor); //[0.2, 0.5, 1]

rgba

Unity

public Color vertexColor;


PlayCanvas

ColorAttribute.attributes.add("vertexColor", {
    type: "rgba",
});

曲線

Curve

Unity
おそらく一番近いのはAnimationCurve

public AnimationCurve wave;


PlayCanvas

CurveAttribute.attributes.add("wave", {
    type: "curve"
});

f:id:seiroise:20170508195950g:plain

CurveSet

多分Unityにはない?かな
curvesオプションに値を指定することでCurveSetになる。

CurveAttribute.attributes.add("xyzCurve", {
    type: "curve",
    curves: ["x", "y", "z"]
});

f:id:seiroise:20170509194613g:plain

GradientCurve

Unity

public Gradient gradient;


PlayCanvas

CurveAttribute.attributes.add("gradient", {
    type: "curve",
    color: "rgba"
});

f:id:seiroise:20170509195229g:plain


colorオプションに"r"などを指定することでその色だけの曲線を設定することもできる

ツールチップ

Unity

[Tooltip("握力")]
public int grip;


PlayCanvas

NumberAttribute.attributes.add("grip", {
    type: "number",
    description: "握力",
    default: 30
});

f:id:seiroise:20170509202151g:plain

PlayCanvasチートシート Unityと比べてみよう エディタ拡張編 その1

上位互換
seiroise.hatenablog.com

PlayCanvasのエディタ拡張的な要素である「スクリプト属性」について
あんまり日本語の解説がなかったのでUnityと比較してまとめてみる。

参考

pc.ScriptAttribute

Unity

public Color color;

PlayCanvas

Attribute.attributes.add("color", {
    type: "rgba"
});

こんな感じになる
f:id:seiroise:20170508181803p:plain
f:id:seiroise:20170508193922g:plain

スライダー

Unity

[Range(100f, 250f)]
public float height = 175.8f;

PlayCanvas

Test.attributes.add("height", {
    type: "number",
    placeholder: "cm",
    default: 175.8,
    min: 100,
    max: 250
});

こんな感じになる
f:id:seiroise:20170508194339g:plain

配列

Unity

public string[] characters;

PlayCanvas

Test.attributes.add("characters", {
    type: "string",
    array: true,
});

こんな感じになる
f:id:seiroise:20170508194848g:plain
ちなみに重複は許されない(gifだと"太郎"のところ)

列挙

Unity

public enum Value {
    valueOne = 1,
    valueTwo = 2,
    valueThree = 3
}

public Value value;

PlayCanvas

Test.attributes.add("value", {
    type: "number",
    enum: [
        {"valueOne": 1},
        {"valueTwo": 2},
        {"valueThree": 3}
    ]
});

こんな感じになる
f:id:seiroise:20170508195512g:plain

曲線

Unity

public AnimationCurve wave;

PlayCanvas

Attribute.attributes.add("wave", {
    type: "curve"
});

こんな感じになる
f:id:seiroise:20170508195950g:plain

備考

Unityにはない使い方ができるものもあるので、 それはまた今度

【Unity】かっちょいいUIを作る!解説編【UI】

今朝に引き続きUIのお話です。

前回はこちら
seiroise.hatenablog.com

今回は前回紹介したこいつ↓について解説します。(主に忘れっぽい自分へ)

f:id:seiroise:20160919122549g:plain:w500

ソースコード

まず初めに全体のソースコードへのリンクをはっておきます。
説明なんていらん!という人はこっちを見てください。
github.com

円の描画

Unityで円を描画させるにはいくつかあるんですが、 今回は円形のMeshを作って描画するようにしました。 ソースコードでいうと

といった役割になっています。
円の形状定義と描画部分を分けてモジュールっぽさを上げてます。
描画するための形状のデータはEasyMeshとかいう、簡易的な頂点などの集合を介して行うようにしています。

メインの部分は円の形状を定義しているCircleFragment.csですかね。こいつに何度(start)から何度(end)まで、内径(inner)と外径(outer)といった円の形状を指定して、あとはSetIndicate()でどんな表示の仕方をするかを決めてCircleFragment.Update()を呼ぶことで形状をいじくってます。

コライダーへのポインタイベントの送信

今回の方法では既存のEventSystemは使えないので、こちらで都合の良いものを用意します。

ほぼほぼいうことはないと思いますがCollisionEventSystemはマウスの位置からRaycastを行って当たったものに対してICollisionEventHandlerが確認できたらそいつにイベントを送るといった感じです。

Radial Menu の制御

最後にRadialMenuの管理ですがその名の通りRadialMenu.cs君が全て行っています。
UniCoLib/RadialMenu.cs at master · seiroise/UniCoLib · GitHub

表示命令がきたらそのオブジェクトの子のUICircleFragmentを探しそれらに対して位置や範囲を指定して表示するようにしています。
親子関係はTreeではなくStackで擬似的に管理しています。
今回のRadialMenuでは別の子が同時に二つ以上表示されることがないのでStackでおけっていうことですね。

こいつの問題点

今更ですが、ここでこのRadialMenuの問題点を言っておこうと思います。 なんとなく察しているとは思いますが。こいつはワールド空間に配置されているので一緒にテキストを表示するのがちとめんどくさいです。

こいつに関しては今の所良い方法が思い浮かばないので保留です。
何か良い方法があったらコメントください・・・

おわり

かなり端折りましたが解説はこんな感じです。
ここわからんという方はコメント欄とかでよろしくです。

そんでは次の記事では使い方を説明していきたいと思います。
明日の朝には書けると良いなぁ・・・

ではではー

余談

すーぱー被ってるAssetを発見。
事前調査してなかった・・・
assetsale.hateblo.jp

https://www.assetstore.unity3d.com/jp/#!/content/15857

【Unity】かっちょいいUIを作った!みんなの憧れRadial Menu【UI】

お久しぶりです。せいろです。

近況報告みたいな感じです。

ゲームを作りたい欲求を我慢していたらなぜかUIを作ってしまいました。

だってこういうUIかっこいいじゃん・・・ f:id:seiroise:20160919121514j:plain

とりあえず作ったものの見た目はこんな感じです。 f:id:seiroise:20160919122549g:plain

親子関係を持ったRadial Menuを作ることができます。(見たまんま)
あとはボタンっぽい挙動のカスタマイズだったり、
表示方法とか結構いじくれます。

表示方法その1 内側から外側
f:id:seiroise:20160919123138g:plain

その2 端から端
f:id:seiroise:20160919123613g:plain

その3 外から中
f:id:seiroise:20160919123655g:plain

とりあえずこんな感じです。

UGUIを使うと当たり判定が矩形でしか行えないのでワールド座標に置いたメッシュをそれっぽく扱っています。

実際の詳しいことは次回で解説する予定です。今回は紹介のみです。
今日の夜ぐらいには書けたら良いなぁ・・・

ではっでは。

【Unity】ビルド後に使えるお手軽パラメータ調節機能【OnGUI】

最近,Unityのフロントのアルバイトをしているのですが,
ゲームデザイナの方がテスト用にビルドしたアプリのパラメータを調節したいとのことだったので,ビルド後もお手軽にパラメータを調節出来る何かを作りました。

お手軽にということで,C#のインタフェースとUnityのOnGUIを使って実装します。
OnGUIは小回りが利くのでお手軽にやるときには結構使える子だったりします。

今回は直線移動するプログラム(Move.cs)を調節できるようにします.

インタフェース(IParameterIndicator.cs)はこんな感じです.

このインタフェースをMove.csにインプリメンツして表示用のコードを書くとこんな感じです.
GUILayoutを使ってるのは下の方まで読めば分かるはずです.

そんで重要な関数を読んであげる人です

全てのインタフェースを取得するプログラムはここを参考にしてあります.
http://forum.unity3d.com/threads/how-to-get-all-components-on-an-object-that-implement-an-interface.101028/forum.unity3d.com

そんなこんなでそれぞれをUnity上に配置して実行するとこんな感じです.
f:id:seiroise:20160629124514g:plain

メリットとしては

  1. お手軽
  2. 表示側からスクリプトへの参照がないのでシンプル
  3. インタフェースなので後から付け加えるとか出来る

デメリットとしては

  1. あんまり複雑なのは表示に向いてない
  2. 表示するものが多すぎると重たい

ってことが挙げられますね.
やってることは単純なんですけどそこそこ便利だったりします.

ではでは~