2014年12月31日水曜日

【Unity】 Shaderはじめました。その3

こんにちは。はじまりました、その3。

Unity公式の頂点プログラムへ頂点データ流し込み / Providing vertex data to vertex programs

ここにあるサンプルを使っていきます。
トーラスノットモデル?そんなものうちにはないよ。


【VertexInputSimple】

画像の真ん中にあるキューブが無回転のもの

Shader VertexInputSimple {
    SubShader {
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            
            struct v2f {
                float4 pos : SV_POSITION;
                fixed4 color : COLOR;
            };

            v2f vert (appdata_base v){
                v2f o;
                o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
                o.color.xyz = v.normal * 0.5 + 0.5;
                o.color.w = 1.0;
                return o;
            }

            fixed4 frag (v2f i) : COLOR0 { return i.color; }

            ENDCG
        }
    } 
}

回転のかかっているものは少し色が変わっているのがわかるでしょうか?
これは o.color.xyz = v.normal * 0.5 + 0.5; の v.normal が変わっているからですね。
この v.normal は3次元のベクトルであり、Global座標の値なので、オブジェクトが回転すると変化します。
無回転のキューブの場合、各面の法線ベクトルと o.color.xyz の値(RGB 0~255 換算)は下図のようになります。

+0.5 が0~255換算だと +128 にあたります。

おまけ:今作ってるピラミッドにこのシェーダを適用してみました。


ピラミッドの端の部分、色の変わり目の色の変化がなだらかですね。(そのせいで三角がたくさん見えます)
本当はもっとソリッドにキッチリキッカリ変化させたいんですけどね…これは今後の自分の課題です。
ちなみにこれ、メッシュはちゃんと直線のみで構成されています。ピクセル単位でみるとsmoothingされてしまうのか多少変わってしまうようです。


【UV 1】


Shader !Debug/UV 1 {
    SubShader {
        Pass {
            Fog { Mode Off }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // vertex input: position, UV

            struct appdata {
                float4 vertex : POSITION;
                float4 texcoord : TEXCOORD0;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float4 uv : TEXCOORD0;
            };

            v2f vert (appdata v) {
                v2f o;
                o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
                o.uv = float4( v.texcoord.xy, 0, 0 );
                return o;
            }

            half4 frag( v2f i ) : COLOR {
                half4 c = frac( i.uv );
                if (any(saturate(i.uv) - i.uv))
                c.b = 0.5;
                return c;
            }

            ENDCG
        }
    }
}

画像だけみると少し前にやったシェーダにとても似ていますね。
各面、各ピクセルの色がUV座標(texcoord)の値によって変化しています。
UV座標は黒赤緑黄で示され、UV座標外(0~1以外)の値に対しては青色が適用されるようです。
UV座標のデバッグに使える便利なシェーダらしいです。

そういえば texcoord : TEXCOORD0 は float4 なんですね。使っているのは xy だけですが zw には何が入っているんでしょう?
あ、0入ってましたね。すみません。この値いじると何か変わるんですかね?


■ Fog { Mode Off }
霧(フォグ)の指定

■ v2f
このネーミング、Vertex to Fragment の略みたいですね。

half4
ベクトル型、16bit浮動小数点ベクトル。
ちなみに float4 は32bit浮動小数点ベクトル、int4 は32bit符号付き整数ベクトル。

frac()
入力された値の小数部を返す。入力は0以上1未満の値。
それ以外の値は if(any(saturate~~))で処理するのか?

any()
入力された値のいずれかの成分が0以外である場合は True, それ以外の場合は False を返す。
any(saturate(i.uv) - i.uv) の値は i.uv の値が0~1の場合0になるので False を返します。それ以外の場合 True を返し、if文が実行されます。

saturate()
入力された値を0~1の範囲にクランプする。
clamp()と同じ、clamp()は範囲の最大と最小を設定できる。

■ c.b
c.b = 0.5; この .b はなんでしょう。普通に考えれば blue の b なんですが…
もしくは half4 の上から順に a, b, c, d で…そうすると c になるから違いますね。
とりあえずここで例外(0~1以外)を青く染めます。


【UV 2】


手前が UV 2 です。多少違いますね。逆側から見るとこうなります。


Shader !Debug/UV 2 {
    SubShader {
        Pass {
            Fog { Mode Off }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // vertex input: position, second UV
            struct appdata {
                float4 vertex : POSITION;
                float4 texcoord1 : TEXCOORD1;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float4 uv : TEXCOORD0;
            };

            v2f vert (appdata v) {
                v2f o;
                o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
                o.uv = float4( v.texcoord1.xy, 0, 0 );
                return o;
            }

            half4 frag( v2f i ) : COLOR {
                half4 c = frac( i.uv );
                if (any(saturate(i.uv) - i.uv))
                c.b = 0.5;
                return c;
            }
            ENDCG
        }
    }
}

マルチテクスチャとかやる時に使うんですかね。 Unityでは1つのオブジェクトに2つのテクスチャが使えるらしいです。 逆に言えば2つしか使えない。3つ以上使う方法もあるにはあるらしいですが僕はわかりません。

このコード… UV 1 と全く同じです。違うのは TEXCOORD0 が TEXCOORD1 になっているだけです。
この 0 と 1 で2つのテクスチャを管理しているんですね。


【Vertex color】


Shader !Debug/Vertex color {
    SubShader {
        Pass {
            Fog { Mode Off }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // vertex input: position, color
            struct appdata {
                float4 vertex : POSITION;
                fixed4 color : COLOR;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                fixed4 color : COLOR;
            };

            v2f vert (appdata v) {
                v2f o;
                o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
                o.color = v.color;
                return o;
            }

            fixed4 frag (v2f i) : COLOR0 { return i.color; }
            ENDCG
        }
    }
}

頂点色を表示するシェーダになります。キューブもシリンダーも何に適用しても真っ白になりました。
頂点位置と頂点色を struct appdata に持たせそれを最後まで継承し、使っています。
Unityで生成できるオブジェクト(キューブとかスフィアとか)の頂点色、デフォルトが白ってことなんですかね。


【Normals】
一番最初にやった VertexInputSimple と同じです。


【Tangents】

.normal を .tangent に変えてるだけです。出てくる色が変わります。
接線ベクトルの値で色づけしています。
真ん中のキューブの一番手前の面と上の面、同じ色ですね。
濃水色なんですがRGBの値は255換算で (0, 128, 128) でした。
接線ベクトルに直すと(-1, 0, 0)になります。
これで法線ベクトルと接線ベクトルの値がわかったので各面の従法線ベクトルの値も、それによってつく色も予測できますね。実際にやってみましょう。


【Binormals】


Shader !Debug/Binormals {
    SubShader {
        Pass {
            Fog { Mode Off }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // 頂点入力: position, normal, tangent
            struct appdata {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float4 color : COLOR;
            };

            v2f vert (appdata v) {
                v2f o;
                o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
                // 従法線を計算
                float3 binormal = cross( v.normal, v.tangent.xyz ) * v.tangent.w;
                o.color.xyz = binormal * 0.5 + 0.5;
                o.color.w = 1.0;
                return o;
            }

            fixed4 frag (v2f i) : COLOR0 { return i.color; }

            ENDCG
        }
    }
}

従法線ベクトルの値で色づけしています。
フレミングの左手の法則を覚えていますか?電・磁・力 ですね。それと同じです。
法線・接線・従法線になります。Normal Tangent Binormal、 NTB、呼びづらい。

やってることは今までの Normals とか Tangents とかと同じです。
vert内の float3 binormal = cross( v.normal, v.tangent.xyz ) * v.tangent.w; で従法線の計算をしているだけなので今までのものがわかっていれば問題ないと思います。

cross()
2つの浮動小数点3Dベクトルの外積を返す。



終了です。実は前回前々回のその1その2の方が内容的には難しかったみたいです。
どちらかといえばこちらが導入的な位置付けだったのかな?今更なんですが。仕方ありませんね。ごめんなさい。

2014年12月23日火曜日

【Unity】 近況


わらわら…

最近少し困ったことを列挙しておこう。


■ ある地点まで等速で動き、その地点で止まる

古いRPGとかによくあった動き方です。
1マス分動いたら止まる。これが意外と難しかったのです。
Lerp使えば簡単に2点間を移動できるんですが動きが等速じゃない!
ということで Lerp と timer の組合せで等速を実現します。

    void Update () 
    {
        if(_timer > 1.0f) Turning();

        transform.position = Vector3.Lerp(_currentPos, _distPos,  _timer);
        _timer += _speed * Time.deltaTime;
    }

    void Turning()
    {
         _currentPos = _distPos;

        //新しい移動地点の設定
        //ここで適当に _distPos を書き換える

        _timer = 0.0f;
    }

こんな感じで。


■ ランダムに方向を決める

上下左右ランダムに動くオブジェクトをつくりたい。

float _random = Random.value;

すると _random には 0.0 から 1.0 の値がランダムに入ります。
あとは条件分岐で上下左右振り分ければいいだけです。


■ Instantiate で生成したクローンの名前に (Clone) を付けないようにする

(Clone) はまぁ便利なんですが…というほど便利さを感じているかというと微妙ですね。

Enemy が EnemyPrefab を Instantiate で生成すると Enemy(Clone) になります。
Enemy(Clone) が生成すると Enemy(Clone)(Clone) になります。
といったようにEnemyを分裂させる処理の場合 Enemy(Clone)(Clone)(Clone)… と (Clone) だらけになるので (Clone) 消しましょう。

    GameObject _obj = Instantiate (_prefab, transform.position, Quaternion.identity) as GameObject;
    _obj.name = "Enemy";

こうすると(Clone)はなくなります。


■ Instantiate で生成したクローンに値を持たせる

参考:【Unity】 Instantiateで生成したGameObjectのScriptに引数を渡す方法

    void Deplicate()
    {
        _life -= 1000;
        GameObject _obj = Instantiate (_deplicate, transform.position, Quaternion.identity) as GameObject;
        _obj.name = "Enemy";
        EnemyMovement _objScript = _obj.GetComponent<EnemyMovement>();
        _objScript._currentNo = _currentNo;
        _objScript._nextNo = _currentNo;
    }

やろうとしてることはライフが2000になったら1000減らして分裂をつくる~みたいな感じです。
左辺の _objScript._currentNo がクローンの持つ値になり、右辺がコピー元の _currentNo です。同じスクリプトを持っているのです少しわかりにくいですね。

2014年12月21日日曜日

【Unity】 Shaderはじめました。その2

それじゃ続きをやっていきましょう。
Syntax Highlighter を導入してみたからコードがちょっとは見やすくなってるかな?

ここのVignettingからスタートします。









Shader "Custom/WindowCoordinates/Vignetting" {
    SubShader {
        Pass {
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0
            #include "UnityCG.cginc"
            float4 vert(appdata_base v) : POSITION {
                return mul (UNITY_MATRIX_MVP, v.vertex);
            }
            float4 frag(float4 sp:WPOS) : COLOR {
                float2 wcoord = sp.xy/_ScreenParams.xy;
                float vig = clamp(3.0*length(wcoord-0.5),0.0,1.0);
                return lerp (float4(wcoord,0.0,1.0),float4(0.3,0.3,0.3,1.0),vig);
            }
            ENDCG
        }
    }
}


どでかい Plane にシェーダをはっつけて配置してみました。下の画像は Plane 2枚。
スクリーンの真ん中らへんが色づきます。Vertexシェーダについては特別なことは何一つしてませんね。 : WPOS あたりがわからない場合前回の記事を見てください。


■ clamp(3.0*length(wcoord-0.5),0.0,1.0)

clamp (float value, float min, float max)
value を入力し、その値が min より小さければ min を、max より大きければ max を返す。

vig = 1.0 でピクセルはグレーになります。lerp() の効果ですね。


■ lerp(x, y, s)

ここ。 x が始点、y が終点、 s がどれだけ近づけるか。
lerpはUnityのc#でも頻繁に使いますね。



なんだかいまいちわかりにくいですねぇ。なぜ真ん中に色がつくのか。

float vig = clamp(3.0*length(wcoord-0.5),0.0,1.0);

この -0.5 を変えてみましょう。


-0.0




-0.25




-0.75





なんとなーくわかってきたでしょうか?
実際に数値を入れて考えてみるとわかりやすいかもしれません。
length(wcoord-hoge) の値がゼロに近ければ近いほど色づくんです。

length(wcoord-0.5) でスクリーンど真ん中 wcoord = (0.5, 0.5) の場合、vig はゼロになります。length(0.0, 0.0) はゼロです。
すると return lerp (float4(0.5, 0.5, 0.0, 1.0), float4(0.3, 0.3, 0.3, 1.0), 0.0) となり、画面中央の色にはfloat4(0.5, 0.5, 0.0, 1.0)が適用され暗い黄色になるわけです。
wcoord = (0.0, 0.0) 左上端のピクセルについては length(-0.5, -0.5)
となってしまい3倍すれば vig は 1.0 を軽く超えます。なのでグレーになってしまうのです。

同じように length(wcoord-0.75) で最も明るい部分の wcoord は (0.75, 0.75) ですね。発色は float4(0.75, 0.75, 0.0, 1.0) となりますので先ほどより明るい黄色になります。

それじゃ次いってみましょう。







Shader "Custom/WindowCoordinates/CirclesMask" {
    Properties {
        _CirclesX ("Circles in X", Float) = 20
        _CirclesY ("Circles in Y", Float) = 10
        _Fade ("Fade", Range (0.1,1.0)) = 0.5
    }

    SubShader {
        Pass {

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0

            #include "UnityCG.cginc"

            uniform float _CirclesX;
            uniform float _CirclesY;
            uniform float _Fade;

            float4 vert(appdata_base v) : POSITION {
                return mul (UNITY_MATRIX_MVP, v.vertex);
            }

            float4 frag(float4 sp:WPOS) : COLOR {
                float2 wcoord = sp.xy/_ScreenParams.xy;
                float4 color;
                if (length(fmod(float2(_CirclesX*wcoord.x,_CirclesY*wcoord.y),2.0)-1.0)<_Fade) {
                    color = float4(sp.xy/_ScreenParams.xy,0.0,1.0);
                } else {
                    color = float4(0.3,0.3,0.3,1.0);
                }
                return color;
            }
            ENDCG
        }
    }
}


■ Properties

ここにある変数は Mono側ではなく Unity側の Inspector でいじれるようになります。
ここで宣言したものはもう一度 Pass 内で宣言する必要があります。



うーん…特に目新しいことがないです。

if (length(fmod(float2(_CirclesX*wcoord.x,_CirclesY*wcoord.y),2.0)-1.0)<_Fade)

こういう条件式使えばパンチングできますよ。ってだけですね。やってることは Bars とほとんど同じです。
Inspectorでいろいろいじってみてください。_Fade が数値入力じゃないのが少し面白いかな。次。







Shader "Custom/TextureCoordinates/Base" {
    SubShader {
        Pass {
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0
            #include "UnityCG.cginc"

            struct vertexInput {
                float4 vertex : POSITION;
                float4 texcoord0 : TEXCOORD0;
            };

            struct fragmentInput{
                float4 position : SV_POSITION;
                float4 texcoord0 : TEXCOORD0;
            };

            fragmentInput vert(vertexInput i){
                fragmentInput o;
                o.position = mul (UNITY_MATRIX_MVP, i.vertex);
                o.texcoord0 = i.texcoord0;
                return o;
            }

            float4 frag(fragmentInput i) : COLOR {
                return float4(i.texcoord0.xy,0.0,1.0);
            }

            ENDCG
        }
    }
}


またグラデーションです。ただ今回はテクスチャ座標に : WPOS など使っていないので Plane 一枚で完結します。Cube にこのシェーダを適用しても似たようなかんじになります。
左下だけ比較用に CirclesMask を適用しています。

説明することがない…
struct vertexInput を Vertexシェーダ vert の入力に使っていて、vert が吐き出す fragmentInput o をFragmentシェーダの入力に使ってますねー texcoord0はそのまま流用してますねー ってかんじです。




これ以降のものですが…

ChessOpt は RGBa の値に bool値を入れられるということを示しています。
最後の return の RGBa を適当に p, q で置き換えるとちょっといいかんじなものが作れたりします。

Mandelbrot Fractal についてはやる気にならないので(やたら細かい計算をしているだけなので)やりません。


とりあえず Vertex/Fragmentシェーダの基本の『き』ぐらいはできたのかな?できてればいいんですが。

【Unity】 Shaderはじめました。

シェーダはじめました。タイトル通りなんですが。

・ Fixed Function
・ Surface
・ Vertex/Fragment

ご存知の通り3種類のシェーダがあるわけなんですが Vertex/Fragmentシェーダについてやっていきたいと思います。
頂点を動かしたり色をガンガン変えていけるということで一番楽しそうだからです。

ガチ初心者なんでとりあえず公式サンプルからやってきます。

どっからどう見ても真っ赤になるシェーダ。
ライティングしてない。影とかない。超基本。







Shader "Custom/SolidColor" {
SubShader {
Pass {
CGPROGRAM

#pragma vertex vert
#pragma fragment frag

float4 vert(float4 v:POSITION) : SV_POSITION {
return mul (UNITY_MATRIX_MVP, v);
}

fixed4 frag() : COLOR {
return fixed4(1.0,0.0,0.0,1.0);
}

ENDCG
}
}
}


とりあえず最低限として…

SubShader { Pass { ここに書く } }
vert (頂点シェーダ)と frag (フラグメントシェーダ)はなきゃいけない、原則 vert が先
(vert と frag の名前は #pragma vertex hoge, #pragma fragment hoge の部分で変えられるかも)
vert は全ての頂点に対して座標変換を行う
frag は全てのピクセルに対して計算を行い、見え方を決める

大きく2つにわかれています。細かいとこ見てきます↓


■ float4

x, y, z, w の4変数で頂点の位置を示している。4は変数の数。


■ : POSITION

セマンティクス(接尾辞)
その変数の役割を示すタグのようなもの。全ての関数、引数に対してこちらが役割を示す必要がある。(頂点シェーダのINOUTで2つ、フラグメントシェーダのINOUTで2つの計4つ)
グラフィックパイプラインの各ステージ間で伝達する変数が何なのかを伝える。

: SV_POSITIONとの違いは…Direct3D 10以降だとこれ…?ラスタライザーステージで解釈されることに利点があるとか…?関数の方にはSVつけて引数の方にはつけないとか…?わからんち。

参考:セマンティクス(DirectX HLSL)


追記: どうも Fragment Shader の Input が SV_POSITION で、Vertex Shader の Input が POSITION のようだ。それから : POSITION が空間中の座標で、 : SV_POSITION がプロジェクション後の座標らしい。ちょっと納得。


■float4 vert(float4 v:POSITION) : SV_POSITION

v も vert もこの一行で宣言されている。
float4 v:POSITION で 『 v は x,y,z,w の4変数を持つ POSITION (位置座標)です』と言っている。

v は頂点座標で vert() の引数で役割的には Input のはずなんだけど( Output が mul (UNITY_MATRIX_MVP, v) だからね)それについてなんにも明記されてないんだよなぁ。書く必要ないのかな?


■mul

掛け算、4×4行列(float4x4)と4行(float4)を掛けて4列(float4)を得ている。
v に行列を掛けて v の位置を移動していると解釈すればいい。

ちなみに、 return mul (UNITY_MATRIX_MVP, v); で返しているのは float4 です。
関数 vert の宣言は、 float4 vert(float4 v:POSITION) : SV_POSITION です。
赤で示した2つの型は一致する必要があります、おそらく。


■ UNITY_MATRIX_MVP

Unityが提供している組み込み値の行列。
UNITY _ MATRIX _ Model View Projection
頂点座標にこの行列を掛けることで2次元のディスプレイのどの位置に頂点が見えるかを計算している。
頂点座標そのままじゃどこに映せばいいのかわからなくてダメってことらしい。

参考:ShaderLab built-in values

似たような行列がたくさん提供されていてどんな時にどれを使えばいいのやら…
ここらへんの細かいところは考えはじめるとドツボに嵌まるのでまたの機会にしましょう。


■ fixed4

固定小数点型fixed、浮動小数点型floatに対して名付けられている。
ある桁数のうちのある場所に小数点が固定されているものとして扱う方式。


■fixed4(1.0,0.0,0.0,1.0)

左から Red, Green, Blue, Alpha(透明度)


次いきましょう。画面の位置で色が変わるシェーダ。すごい、シェーダっぽい。







Shader "Custom/WindowCoordinates/Base" {
SubShader {
Pass {
CGPROGRAM

#pragma vertex vert
#pragma fragment frag
#pragma target 3.0

#include "UnityCG.cginc"

float4 vert(appdata_base v) : POSITION {
return mul (UNITY_MATRIX_MVP, v.vertex);
}

fixed4 frag(float4 sp:WPOS) : COLOR {
return fixed4(sp.xy/_ScreenParams.xy,0.0,1.0);
}

ENDCG
}
}
}


■ #pragma target 3.0

Shader Model を 3.0 にする。
: WPOS を使うために必要。


■ #include "UnityCG.cginc"

ヘルパー関数、構造、定義を提供する。
appdata_base を使うために必要。


■ appdata_base

位置、法線、テクスチャ座標の頂点シェーダ入力。
vertex, normal, texcoordの3種類のメンバを持っている。それぞれ : POSITION, : NORMAL, : TEXCOORD0 のセマンティクスを与えられている。

参考:ビルトイン シェーダ includeファイル/Built-in shader include files


■ : WPOS

フラグメントシェーダでスクリーン座標を参照することができる変数。今扱っているピクセルの座標(スクリーン中の)。


■ _ScreenParams.xy

組み込み値、ここの下のほうにありました。
描画しようとしているスクリーンの大きさ。 x が幅で y が高さ。

.xy をつけることで x と y の2つの値を同時に参照しているみたいなので、.x とか .xyz とかもできるのかな。


■ fixed4(sp.xy/_ScreenParams.xy,0.0,1.0)

Red, Green の値については扱っているピクセルのスクリーン座標をスクリーンの大きさで割っています。
sp.xy の値はオブジェクトのスクリーン上での位置によって変化します。 _ScreenParams.xy の値は変化しません。
sp.xy の最大値は _ScreenParams.xy の値と一致します。よって sp.xy/_ScreenParams.xy の最大値は (1.0, 1.0) です。以上から

画面右上が fixed4(1.0, 0.0, 0.0, 1.0) で赤
画面左下が fixed4(0.0, 1.0, 0.0, 1.0) で緑
画面右下が fixed4(1.0, 1.0, 0.0, 1.0) で黄

になることがわかると思います。RGBaです。

次、縞模様のやつ。これはちょっと難しいです。









Shader "Custom/WindowCoordinates/Bars" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

struct vertOut {
float4 pos:SV_POSITION;
float4 scrPos;
};

vertOut vert(appdata_base v) {
vertOut o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.scrPos = ComputeScreenPos(o.pos);
return o;
}

fixed4 frag(vertOut i) : COLOR0 {
float2 wcoord = (i.scrPos.xy/i.scrPos.w);
fixed4 color;

if (fmod(20.0*wcoord.x,2.0)<1.0) {
color = fixed4(wcoord.xy,0.0,1.0);
} else {
color = fixed4(0.3,0.3,0.3,1.0);
}
return color;
}

ENDCG
}
}
}


これがコードなんですがmonoにコピペしてUnityに移るとエラー吐いててダメ。オブジェクトがどピンクになります。エラーコードは↓

Shader error in 'Custom/WindowCoordinates/Bars': 'vert': function return value missing semantics at line 15

関数vertはセマンティクスを失った値を返しています。このように出てしまうので補完しましょう。



Shader "Custom/WindowCoordinates/Bars" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

struct vertOut {
float4 pos:SV_POSITION;
float4 scrPos : TEXCOORD;
};

vertOut vert(appdata_base v) {
vertOut o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.scrPos = ComputeScreenPos(o.pos);
return o;
}

fixed4 frag(vertOut i) : COLOR0 {
float2 wcoord = (i.scrPos.xy/i.scrPos.w);
fixed4 color;

if (fmod(20.0*wcoord.x,2.0)<1.0) {
color = fixed4(wcoord.xy,0.0,1.0);
} else {
color = fixed4(0.3,0.3,0.3,1.0);
}
return color;
}

ENDCG
}
}
}


これでなんとかなったはずです。


■ struct vertOut

struct : 構造体。好きなように作れる型みたいなもの。
今後 vertOut型の変数を宣言すればその変数はvertOut構造体の持っている変数をすべて使うことができる。

vertOut vert(appdata_base v)


■ ComputeScreenPos()

UnityCG.cgincファイルで定義されている関数。引数で渡した座標位置をスクリーンの座標位置に変換してくれるものらしいです。
本当に?試しに o.scrPos = ComputeScreenPos(o.pos); の一行を抜いてみましょう。





一応グラデーションは機能してるようです。しかしCubeの面1つにつき1色になってしまっています。(Fragmentシェーダが機能しなくなった??)
縞模様になるはずのグレーが出ている面もありますね。それから黒の占める範囲が大きいのも気になります。何が起こっているのでしょう。

黒になるの範囲が大きいことからテクスチャ座標 scrPos の位置がスクリーンの位置と合っていないということがまずわかります。
それから面を構成する4つの頂点のうちの1つの座標位置で面の色が決定していることもわかりました。(これは動かしてみた実感ですが)
各ピクセルがスクリーン位置を読みにいけていないということです。

ComputeScreenPos() は引数で渡した座標位置をスクリーンの座標位置に変換すること以外にも何か別のことをしているような気がします…
そもそも渡す座標位置というのは元々どこにあるのでしょうか。各面で完結しているのでしょうか。


■ fixed4 frag(vertOut i) : COLOR0

Input として vertOut型の変数 i を宣言しています。
この i はさっきまで vert() の中でいじっていた o です。
以降 pos は使っていません。テクスチャ座標 scrPos だけ使うようです。


■ float2 wcoord = (i.scrPos.xy/i.scrPos.w);

wcoord は RGBa の RG の2値です。つまり wcoord = (0.0, 0.0) ~ (1.0, 1.0) となるわけです。
しかしこの w値で割る という操作が謎ですね。
謎なので割るのをやめてみましょう。
float2 wcoord = (i.scrPos.xy/i.scrPos.w); を float2 wcoord = (i.scrPos.xy); に変えてみましょう。すると





うーん…。。縞は出てますね、面で完結してますけど、しかもすごい模様になってる。
それから color = fixed4(1.0, 1.0, 0.0 ,1.0) になる点(黄色になる点)がとても左下の方に来ています。本来は画面の左上端に来るべき点なのですが…


■ fmod()

除算して余りを返します。2で割って余りが1以上ならグレーにする分岐にしています。




正直わからないことだらけでした。

ただ TEXCOORD を Vertexシェーダ内で ComputeScreenPos() することと Fragmentシェーダ内でw割りすることはセットとして考えた方がいいみたいです。

この先のサンプルについてですがやります。
ただ長くなってきたので一度切ります。

これはこうなんだよ!ってコメント頂けると本当に助かります。よろしくお願いします。



2014年12月6日土曜日

【Unity】 ランゲー

ランニングゲームを継続して作ってます。
組み込んだのはフィールドの自動生成、スコアマネージャー、サウンドまわり、ゲームオーバー時の挙動。あとはGUI周りいじればほとんどシステムは完成かなー。

以下いつもの。


■Instantiateの第二引数について

あるオブジェクトA(Field)がInstantiateで新しいオブジェクトB(next Field)を生成する時に起きた問題。
Instantiateで生成すると子オブジェクトになってしまうということが原因。

Instantiate (_newField, Vector3.zero, Quaternion.identity);

こいつで生成すると親オブジェクトAと同じ位置にできてしまう。globalな原点(0, 0, 0)に生成したいのに。

Instantiateの第二引数にオブジェクトAの移動分を考慮してやればいいだけなので、すぐに原点に生成することはできた。

しかし生成するオブジェクトの座標がどんどん大きくなってしまう。(ランニングゲームなので)
別に動作に支障はないが気に入らない。

ということでGameController(Empty)(tag: GameController)を原点に作ってそいつにField Makingを任せることで解決した。

ただGameControllerに生成させればGameControllerの子になるのかと思ったらそうじゃなかった。GameControllerと同じ階層に生成されたのだ。この部分は非常に不可解。わかったら追記する。


■Animation Event が動作する時としない時がある

ジャンプを終えたらColliderのenableをtrueに戻そうとかそういう処理に使えるAnimation Eventが
普段はちゃんと動いていたのに急に動作しなくなってしまった。正しくは動作する時としない時があるような状態になってしまった。(Unityのversionは4.6beta)
スクリプトをいじってないのに急にそうなったのでわけがわからなかったんですが、イベントキーの位置をアニメーションの終わりから少しズラしたら解決しました。こんなかんじに↓














ただこうすることで違う問題が出てしまった。
このEvent実行とアニメーション終了の間の時間にジャンプの入力をするとColliderが元に戻らずいわゆる無敵状態になってしまった。

jump true, Collider off, Animation開始 >> Event実行, Collider on >> jump true, Collider off >> Animation終了

とまぁこんな風にColliderがonにならずにアニメーションを抜けてしまう。

こりゃーAnimation Eventは使わない方がいいのかもなぁ。


~追記~

もうちょっと検証してみた。
Event Keyをアニメーション終了ぴったりに合わせて、その上でEventが起こる瞬間にジャンプの入力を狙った。1フレあるかないかのタイミングだけど不可能じゃない。

そうしたらアニメーションはジャンプだけどColliderはジャンプしてないという現象も確認できた。
やはりAnimation Eventは使わない方がいい。少なくともシビアな入力が必要とされるものには使うべきでない。

最初からInvoke使えばよかったんや…

2014年12月3日水曜日

【Unity】 Running Game 製作中

ランニングゲーム?ランアクションゲーム?ラン&アクションゲーム?ランゲーム?
ランゲーの正しい名称とは一体…参考文献探すのに非常に困る。

というわけでタイトルのとおり、某テンプルランみたいな Running Game (Running Action Gameかも)を作っているわけだが。
せっかくパンダくんができたことだし彼に活躍してもらいたいという想いからこうなったわけだがはたして。


以下最近詰まったこと↓


■オブジェクトAとオブジェクトBを一緒に移動させたい

追従させたい場合。シューティングゲームの子機とかプレイヤーを追うカメラとか。
子したくない場合。いろいろやり方あると思うけどそのうちのひとつ。

追従させるオブジェクトBにスクリプトをアタッチして

transform.position.z = _objectA.transform.position.z;

とかやってみてもなぜか回らない。
左辺float右辺floatで問題ないじゃんと思うけど問題あるらしい。
xyzには直接値を代入できない。正しくは

float posx = transform.position.x;
float posy = transform.position.y;
float posz = _objectA.transform.position.z;
transform.position = new Vector3 (posx, posy , posz) ;

みたいなカンジにしなきゃならない。Vector使えってことらしい。
↑はオブジェクトBのZの値だけ変わります。Z軸に沿って一緒に移動するってことね。


■Error CS0428

あるオブジェクトからゲームコントローラのスピードを参照しようとした時に起きた。エラコ↓

error CS0428: Cannot convert method group `GetComponent' to non-delegate type `GameControllerScript'. Consider using parentheses to invoke the method

書いてたスクリプト(回らない)は↓

public class MoveField : MonoBehaviour {

GameObject _gameController;
GameControllerScript _gameControllerScript;

void Awake()
{
_gameController = GameObject.FindGameObjectWithTag("GameController");
_gameControllerScript = _gameController.GetComponent<GameControllerScript>;

}

// Use this for initialization
void Start ()
{
Debug.Log (_gameControllerScript._gameSpeed);
}
}

これだけ。なぜこんなエラーが…と思って必死で調べたけどGetComponentの後ろの括弧がないだけだった。

GetComponent<GameControllerScript>();

あー、これはひどい。。


■あるオブジェクトから他のオブジェクトにアタッチされてるスクリプトの数値、関数を利用したい

多分出てくるエラーメッセージはこんなんじゃないかな。

NullReferenceException: Object reference not set to an instance of an object
MoveField.Start () (at Assets/Script2/MoveField.cs:19)

参照がうまくいってないんだね。上のコードみたいに参照すればいい。他のオブジェクトを

FindGameObjectWithTag("hogeObj")

で参照してから

hogeObj.GetComponent<hogeScript>()

でそのオブジェクトにアタッチされてるスクリプトを参照。
後はhogeScript.hogeValueでも何でも好きに使える。


■一定時間待ってから実行

Invoke(), InvokeRepeating(), Time.deltaTimeを使ったカウントを用いるなどいろいろやりようはある。
参考にさせて頂いたのは、ゲームは初心者にやさしく様の『必見!Unity初心者が学ぶ「未来事象の正しい文法」』
その上でのメモ。

Time.deltaTimeはUpdate()で使う。Fixedだと多すぎる。
deltaTimeは前フレームとの差分の秒数。





…最近思ったこととか...

大事なのはMonoDevelopの予測にちゃんと出てくること。正しい選択肢として挙がるってことね。
予測に出てこないって事は間違ってるってこと。怒り狂う前に冷静になった方がいい。

物理エンジンいらない場面って意外と多い。ランニングゲームならGravity切ってもなんの問題もないし衝突後の挙動もエンジンに頼ってると相対速度に差があるからオブジェクトすり抜けちゃう。ほんと不便。OnTriggerまわりだけで十分すぎる。

htmlは</br>がうざいなぁ。<tr>とか<td>とかよくわからん。(なきゃ)いかんのか?

2014年12月2日火曜日

【Unity】 Blenderとの連携

Blenderでモデルを作ってUnityで動かしたい!わたしです(^o^)

というわけで進めてました。進捗、悪くないです。まだ4.6βで頑張ってます。正規版、出たらしいですね。

参考にしたサイトはこちら

日本VTR実験室様の『初心者のための!作って学ぶBlender基礎』①から⑥!

いやーこれは本当に助かった。Blender未経験&Illustrator未経験でもなんとかなった。ちゃんとUnityで動くところまで持ってけたのでマジおすすめ。

以下いつもの詰まった所特集。


■選択したオブジェクト(カメラ)を違うレイヤーに移動したらカメラを表示できなくなった

Blender未経験者、超初心者だから起こるトラブルだと思います。

オブジェクト(カメラ)選択 >> M >> 左上以外のボックス をクリック

これでカメラは移動されて消えました。でもなぜかそのボックスをクリックしてもカメラが出てこない。
↓ここをクリックしてもでてこない。


そりゃでてきません。▼Move to Layer って書いてありますやん。
M押して出てきたやつと同じですよこれは。レイヤー切り替えはココ。




カメラがちゃんと出てきたはずです。次。


■Illustratorなんて持ってない

そんなものはない。Photoshop Elements5.0は眠っているペンタブのおまけで持ってたけどどうせならベクター系のソフトできれいな線を引きたい。

で調べてたらいろいろ良さそうなフリーソフトもあるんだけどなんとAdobe CreativeSuiteファミリー(CS2)が無料でダウンロードできるとのこと。当然飛びついた。

参考にしたのはここ
http://netbe.jp/adobemuryou/

Illustratorも未経験だったので非常に苦労したけどここには書かない。書くことはないかもしれない。要望があれば書きます。

・曲線が上手く引けない
・線Aの端と線Bの端を結びたい
・塗りつぶしができない、エラー吐く
・ガイド(元画像)のレイヤーを見えなくして書き出すと画像のサイズが変わってしまう(元画像は1024×1024)

こんなところだったか。完成したパンダくん。最初に吐き出したやつ。






















ぶっさ、コミュ抜けるわ。この後修正しました。Illustratorで.png塗り替えるだけだから楽だね。
ちなみに格子はガイドレイヤーの上に白塗りレイヤーをつくれば消えます。


■床のマテリアルがおかしい

対角線を境に床の模様が変わる。






















テクスチャのマッピングがUVなのでGeneratedに変えれば直る。質問スレPart23 >>163さんに感謝。
ちなみにUnityに取り込むときは部屋は消します。違うレイヤーに移してもUnityで出てしまうので消します。
法線について確認しておきたい場合一度部屋ごと取り込むことをおすすめします。


■Unityに取り込んだBlenderのモデルがぐちゃぐちゃになる

動かすとぐちゃぐちゃ、動かす前からぐっちゃぐちゃ。
.blendファイルで読み込むと前者、.fbxファイルで読み込むと後者になった。Blenderの中ではちゃんと動いてたじゃないか!
主に手と肩が壊れてるパンダくん。














取り込むとUnityのコンソールに警告が出る。警告文は↓

ImportFBX Warnings:
Mesh 'Plane' has 35 (out of 7626) vertices with no weight and bone assigned (they will be assigned to bone #0 with weight 1). The list of vertices: 2124, 2125, 2126, 2127, 2128, 2129, 2130, 4255, 4256, 4257 and so on...

UnityEditorInternal.InternalEditorUtility:ProjectWindowDrag(HierarchyProperty, Boolean)
UnityEditor.DockArea:OnGUI()

頂点群がどうたら、ノーウェイトとボーンってことでWeightPaintモードを起動して全ての頂点に対しweightを設定、boneに関連付ける。
簡単に言えば全体をうっすら塗って腰骨あたりに関連付け。操作的には

pandaオブジェクト 選択 >> Weight Paintモード(Ctrl+Tab) >> spine2 選択 >> 全体をBrushで塗る

デフォルトではWeightは1.000に設定されていてパンダは塗れば赤くなる。Weightを落としてRadi(塗り範囲)を大きくして全体をまんべんなく塗る。色は青とほとんど変わらなくていい。
それから取り込み。もう.blendファイルだけでいいや。同期してくれるし。.fbx吐き出すのめんどい。
取り込んだらまたエラー出ました。

ImportFBX Warnings:
Mesh 'Plane' has 2 (out of 7626) vertices with no weight and bone assigned (they will be assigned to bone #0 with weight 1). The list of vertices: 7298, 7551

UnityEditorInternal.InternalEditorUtility:ProjectWindowDrag(HierarchyProperty, Boolean)
UnityEditor.DockArea:OnGUI()

頂点群が35から2に減った。ご丁寧に頂点の番号まで書いてくれてるけどどこかわからないしそういうエラーだってことがわかっただけで良しとする。
ぐちゃぐちゃ度合が少しましになった。ほんと少し。相変わらず手の先がヤバイ。
35から2に減ったにしては効果が薄いと思ったので違うところに原因があるんじゃないかと思い別のところを探す。

ウェイトペイントモードでいろいろ変えながらUnityで確認を繰り返す。
footにウェイト乗ってなかったから赤く塗る。そしたら足先がぐちゃった。
ということでfootとhandのWeightをSubtractで消す
Unity側で正常に、ぐちゃらず動く。

ボーンがパンダのメッシュから飛び出てるのが原因だったのか?なんにせよ直ったので良しとしよう。

そういえばUnityに取り込むときはY軸とZ軸の関係が逆だからモデルを後ろ向きにしなきゃいけないはずだったんだけど実際やってみたら後ろ向きにする必要なかった。仕様が変わったのかな。


■MeshColliderが機能しない

先に言っとく。まだ解決してない。Mesh ColliderはあきらめてCapsule Collider入れた。回転拘束つけて。

MeshColliderコンポーネントのConvexにチェックを入れればMeshCollider同士衝突できるようになり(重くなるが)、Mesh以外ならConvexは必要ない。

MeshColliderが機能する頂点数は255が上限でそれ以上になると突き抜けるから頂点を削る。
Blender側で滑らかにしたことを覚えているだろうか。その逆をやる。

















両方とも0まで落としたが依然突き抜ける。無念。














突き抜けるパンダ。どうすればいいんじゃ。誰か教えてくれい。


■Unity側で取り込んだアニメーションがループしない

UnityでAnimatorタブで矢印引っ張ってプログラム書いてさぁ動かした!動く、ループしない。Animator上ではバーが動いていて繰り返しているはずなのに…

取り込んだPrefabを選択してここをいじる。























■Weight Paintモードでボーンを個別に選択できなくなった

モデルぐちゃぐちゃ事件で全塗りしようとした時に出てきた問題。
WeightPaintモードでなぜかboneを選択できなくなる。選択するとArmatureまるごと選択されてしまう。こんな風に↓






















こりゃおかしい。ってことで解決策。

Armature を選択 >> Pose Mode に移行
Plane(panda) を選択 >> Weight Paint Mode に移行 >> bone選択

これだけ。どうもArmatureがオブジェクトモードだとこうなるみたい。イラ壁事案でした。




今回はこんなもんかな。以下私的なメモ

UpdateとInput.GetKeyは相性が悪い
FixedUpdateとInput.GetKeyDownは相性が悪い
LookRotationにInput.GetAxisで得た値を直接いれるとまずい。入れるならifで分岐させろ
Bloggerの使い方がいまいちわからない。レイアウトのいじり方とか画像で改行とかやりたいことができてない。エラーだけ灰色Rectで囲むとか。見づらくてごめん。
ちょっと前にやってたロボットくんはだいぶパワーアップしました。


2014年11月21日金曜日

【Unity】 Sample Assets (beta), robotの改造

引き続き2D Character.unityのrobotくんを改造している。実装したものを以下に記す。


■2段ジャンプ(多段ジャンプ、空中ジャンプ)

適当にフラグをぶちこんであげればいい。
接地(grounded = true)で立てて、2段ジャンプした瞬間に折る。そういうフラグを。
↓こんな杜撰なものでもできる。



■ローリングによる一瞬の加速

ローリングする瞬間にAddForce。
FixedUpdateとInput.GetKeyの組み合わせでローリングアクションを再現していたため加速しっぱなしになってしまうという問題が出た。
ローリング押しっぱなしで高速移動。こりゃまずい。ダッシュなら許せる。

前回の教訓からUpdateとInput.GetKeyDownの組み合わせで一瞬のrolling = trueが確実にやれるようになったのでそいつを適用。


しかし問題が残った。PlatformerCharacter2D.csのMaxSpeedが邪魔をする。減速してしまうのだ。
回っている間は加速させたい。


■壁張り付き、壁キック

壁に張り付いてゆっくりと下降する仕様。そっから壁キックによるジャンプもできるようにする。

まずロボットくんに当たり判定をもう1つ作る。壁に当たってるかどうかを判定する。
2D Characterの子にGroundCheck(地面)とCeilingCheck(天井)がいるからWallCheckとでも名付ける。

空中にいる状態(grounded = false)かつ、壁にあたっている状態(bool wallCheck = true)でアクションをつけ、重力と逆向きの力を少し加えればいい。


■弾の射出


【Unity】 FixedUpdate と Input.GetKeyDown

■キーを押しているのに入力されない、反応しない、反応する時としない時がある


Sample Assets 2D Character.unityのrobotにローリングアクションを組み込んでいた時に起きた問題。

スプライトシートからRoll.animを作成しAnimatorに配置、bool rollingでRunからつなぐ。

Platformer2DUserControl.cs の中の FixedUpdate に
bool rolling = Input.GetKeyDown(KeyCode.LeftShift) ; を追加、Moveの第4引数としてrollingを渡す。
あとは渡された方でanim.SetBoolだのanim.GetBoolだの加えりゃできる。
実際できた。robotは転がったのだ。
ただLeftShiftキーを押したときに転がったり転がらなかったりで非常に不安定。
ゲームをしていて一番イラつくのは『押したのに反応しなかった』ってことで間違いないので直すことにする。

Debug.Logを使って調べたらやはり反応しない時があるようなので
どういうことだと思って調べてみるとどうもこういうことらしい。

http://gamesonytablet.blogspot.jp/2012/12/unityinput.html

Updateの方ならInputを確実に拾ってくれるみたいだ。

ってことで入力のスクリプトを書き換えた。でもやっぱりたまに拾えない。キーが反応しない。
拾えない頻度は…どうかな。あんまり変わらないような気もするが。

結局FixedUpdateの方にInput.GetKeyを使って解決した。
キー押しっぱで転がりっぱになったがこれはどうにでもなる。確実に反応することより大事なことはない。

追記
PC再起動してUpdateの方にInput.GetKeyDownを書き込んだら取りこぼしがなくなった。なぜ取りこぼしが発生していたのか。謎。直ったのでよしとする。



まとめ

FixedUpdate と Input.GetKeyDown は相性が悪い



最近Unityが不安定。
Runするとスクリプトのアタッチが外れたりAnimatorの矢印がなくなってたりする。あとMain Cameraのスクリプトが動作しなくなったり、これは頻繁に起こる。
ほとんどの症状はUnity再起動で直るあたりイラ壁。
とりあえずアセットをReImport All。初期状態に戻すことにする。

2014年11月19日水曜日

【Unity】 Unity Remote4 の設定, Xperia A2 の場合 【Android】

初心者がUnity Remote4 を使って作りながら実機でデバッグするぜーって意気込んで躓いた所の覚書。
通常設定については他の方がわかりやすく説明してくれているので是非そちらで。

とりあえずスマホ側の設定とUnityの Build Setting 及び Project Setting をいじってから出た問題。
スマホは Xperia A2 (SO-04F)、Unityのバージョンは4.6βを使用。OSはWindows7でやってます。


■「スマホが見つからないよ」と言われる

Build & Run の途中で止まる。
can't find any device みたいなエラーメッセージだったと思う。失念した。

Editor Settings >> Unity Remote >> Device >> Any Android Device  にすることで動いた。

本当にスマホ上で動いた。すごい!
File >> Build & Run はとりあえずできるようになった。なったけど…

スマホの中にアプリができていて、あれ?これってなんか想像してたのと違くね?それとBuild遅くね?ってことで継続。
そもそも「Unity Remote4起動しといてUnity側で再生ボタン押せばスマホ側とUnity側で同じものが動くんじゃねーのかよ?ってことでやっぱり継続。


■ADBドライバがインストールできない&されてない

Xperiaの場合、Sony PC Companion をPCにインストールすれば勝手にやってくれるらしいということでインストール。自動でやってくれることはやってもらった。
でもやっぱりUnity Remote4は待機画面から何も変わりませんでした。おわり。

そもそもADBドライバはインストールされてんのかよ?って思ったのでデバイスマネージャー開いて調べてみるとない。
ADB Interface みたいなのがあるはずなんだよ、本当は。

ってことでADBドライバをPCにインストールしなきゃいけないわけなんだがいろいろ方法はあるらしい。
とりあえず公式ページ行ってXperia_A2_SO-04F_driver落としてみる。
.infとか.catとか知らないものが多すぎて面倒だなと思っていると違う方法を発見。

スマホの電源を落としてボリュームボタン(-)を押しながらUSB接続する

おぉ?こんな方法が…と思ってやったら、本当にできた。
なんかPC側でドライバインストール始まってデバイスマネージャーにADB Interface が追加された。







これこれ。コイツが欲しかったんだよね。
ってことでUnity Remote4 起動、Unity側で再生、やったぜ同時起動、第三部完。以下おまけ。



■スマホ側でUSBデバッグが見つからない

設定 >> 開発者向けオプション >> USBデバッグ  にチェックを入れたい。

入れなければいけないと思うんだけどそれが見つからない人向け。俺は見つからなくてイライラしました。

新しめのAndroidにはロックがかかっているので解除する必要がある。

設定 >> 端末情報 >> ビルド番号  を7回タップすれば出てくる。

"あなたは今開発者になりました!" うるせえ!