2015年4月23日木曜日

BlenderのFBXエクスポートが難しすぎる

・Blender 2.7.3 バイナリ出力
一部アニメーション(ジャンプなど宙に浮くアニメ)が
上下逆さ(X軸180度回転)したものになる。
しかし!出力時にそれらアニメを開いている状態だと逆さにならない!
不思議!わからん!でも解決するならいっか、
と思ったが他の平常だったアニメでXY平面にズレが生じたりする!わーい!


・Blender 2.7.4 バイナリ出力
全てのアニメが前後逆に。後ろ歩き!クール!
じゃあエクスポート時の前後を変えればいいんだろう!変えた!アニメ変わんない!後ろ歩き!クール!


・Blender 2.7.4 ASCII出力
マテリアル名が「マテリアル名__テクスチャ(?)名」になる。
このテクスチャ名ってのも各マテリアルそれぞれで使用しているものでなく、
直近で編集したのだか開いていた画像名になるので安定しない。
そして非表示になっているオブジェクトのマテリアルはこの影響を受けないっぽい?
うむ、さっぱりわからん。


とりあえず
・Blender 2.7.4 バイナリ出力
でrotation 180度回転させるのが安定した選択に思える。

と色々試して選択した結果を適用しようとしたところ、
ゲーム上でアニメが動かなくなった。
アニメーション単体の動作確認はできるのだが、animatorのゲージも動いてる、遷移している。
しかし、モデルがさっぱり動かない。

…ハハハッ!

追記:animatorのstatesのWrite Defaultsをチェック外して入れ直したらまた動くようになった

Mesh.CombineMeshesでエラー BlenderでUV頂点を数える

メッシュの結合を行うMesh.CombineMeshesでエラーが起きた。
エラー文は count <= std::numeric_limits<UInt16>::max()

原因はこちらを参考にした。(対策案も書かれているがここではその手法について考えない)
http://rotorz.com/tilesystem/guide?ref=6b89bd45-1d5c-4221-aaa4-38073a6aab37

要するに一つのMeshで扱える頂点数がUInt16(65535)を超えてしまった、ということであった。
メッシュの頂点数を疑ったが、メッシュにくまなくUVを貼っている場合、
・メッシュ頂点数 ≦ UV頂点数
となる点に注意したい。
実際私の場合、頂点数を確認したところUV頂点数がオーバーしていた。
(なお、UV頂点数はmesh.uv.lengthで出力できる。)

ここでは対策として、Blenderを使用し、UV頂点数の削減を行うことにした。
しかし、削減の指標となるUV頂点数がBlenderでは標準で表示されない。
そのため、UV頂点数を確認する手段を設けることにした。


〇BlenderでUV頂点を数えるpythonコード

import bpy
import bmesh
bpy.ops.object.mode_set(mode='OBJECT')
mesh = bpy.context.object.data
bm = bmesh.new()
bm.from_mesh(mesh)
uv_layer = bm.loops.layers.uv.active
num = 0
for face in bm.faces:
    for vert in face.loops:
        num+=1
        print(vert[uv_layer].uv)
print(num)

・参考
http://blender.stackexchange.com/questions/19724/uv-vertex-using-python



こうして作成したこちらのコードを動かしてみたが、得られた頂点数はUnity上とは異なる数であった。
この方法で得られるUV頂点は 三角面の数x3 + 四角面の数x4 というわかりやすい数で、
どうやらBlender上では重複するUV頂点をまとめていないようだ。

Unityで使用するためにエクスポートしたFBXでは
重複UV頂点をまとめている(?)からかUV頂点数は少なくなっている。
(実際はよくわかってないです。想定以上に減らなかったりして、
まとめているにしても全てをまとめているわけではなさそう)


結局のところ、Blender上でFBX出力時の正確なUV頂点数は得られない。というところに落ち着いた。
とはいえ、おおよその指標として上記pythonコードは使用できる。

2015年4月12日日曜日

Unity C#のプロパティなどをインスペクターに表示するエディタ拡張「VFW」の使い方

Unityのエディタ拡張「VFW」を紹介します。
(※ビデオコーデックのVFWではありません!)

 説明の前に、まずは一例

〇 サンプル


サンプルコード
 
カスタム属性[Show]を設定した読み取り専用のプロパティmaxHPを宣言。

インスペクター上で数値を変更 
 
Levelの増減に合わせてMaxHPも増減するのがわかります。
また、読み取り専用なのでMaxHPを直接変更することはできないようになっています。
このようにして[SerializedField]属性では対応できない読み取り専用プロパティを
インスペクターに表示できることが確認できました。
 
サンプルで示したmaxHPのような他の変数に依存する変数は、
プロパティ(getアクセサ)とVFWを用いることで
インスペクター上で数値を確認しながら調整を行うことができるようになります。
 

VFWには他にも色々なエディタ拡張の機能を備えています。
ここでは、VFWのダウンロードからプロパティをインスペクターに表示するまでを説明します。

2015年4月8日水曜日

Unity 1つのGameobject、ビルボードでダメージ表示

GUIでダメージなどの表示をすると重なった場合にドローコールが増える、らしい。
なので、ローポリ同士でバッチされてDrawCallの増加を1に抑えることを狙い、
板ポリにテクスチャを張り付け、ビルボードとして数値を表示する。

そのとき一桁ずつGameObjectを生成し、破棄するのはコストが高い
(1つのGameObjectで実現するよりも、多分)
と考え、1つのGameObjectで実現する。

…ふわふわとした走り出しだ!

2015年4月7日火曜日

unity Instantiate後、スクリプトから登録したparentが使えるタイミングの確認

スクリプトからプレハブの実体を生成し、子に設定した際、
子から親のポジションなど変数が利用できるタイミングを確認した。


〇 親にアタッチしたスクリプト

public class TestParent : MonoBehaviour {

    public GameObject child_prefab;    //インスペクターから子プレハブを登録

 void Update () {
        Debug.Log(Time.frameCount + " " + name + " : Update Start ");
        if (Input.GetKeyDown("space"))
        {
            Debug.Log(Time.frameCount + " " + name + " : Instatiate ");
            GameObject go = Instantiate(child_prefab);
            Debug.Log(Time.frameCount + " " + name + " : set Parent ");
            go.transform.parent = transform;
        }
        Debug.Log(Time.frameCount + " " + name + " : Update End ");
 }
}


〇 子プレハブにアタッチしたスクリプト

public class TestChild : MonoBehaviour {

    void Awake()
    {
        Debug.Log(Time.frameCount + " " + name + " : Awake ");
        if (transform.parent != null) Debug.Log(Time.frameCount + " : Parent is not null ********************");
    }

 void Start () {
        Debug.Log(Time.frameCount + " " + name + " : Start ");
        if (transform.parent != null) Debug.Log(Time.frameCount + " : Parent is not null ********************");
    }

 void Update () {
        Debug.Log(Time.frameCount + " " + name + " : Update ");
        if (transform.parent != null) Debug.Log(Time.frameCount + " : Parent is not null ********************");
    }
}


〇 結果



154フレーム目にスペースキーが押された。
154フレームの流れは

・親のUpdateが開始し、
 Instantiateで子を生成
・子のAwakeが起動
・親が子にParentをセットし、
 親のUpdateが終了
・子のStartが起動、ここでparentが有効

となっており、子のStart時にはparentを使用できることが確認できた。



*を連続で並べたのはログから見つけ易くするため。
Insta"n"tiate 脱字です。

2015年4月3日金曜日

unity Gameobjectのlayerがスクリプトから変更できないとき

コリダーとの衝突時に条件に応じて、
レイヤーを変更する処理を行ったところ、エラーが出て成功しない。


ここから前置きのエラー説明。


gameObject.layer = LayerMask.NameToLayer("Enemy");
だとか
gameObject.layer = 15;
といったコードでは
layer numbers must be between 0 and 31
とエラーメッセージが出る。エラー発生元を明かさず凄い勢いで。
LayerMask.NameToLayer("Enemy")を
ウォッチ式で見ても値は15で、本来はこれで動くはず。

試しに15ビット目に1を立ててみる。
gameObject.layer = 1 << 15;
では
A game object can only be in one layer.
The layer needs to be in the range [0...31]
とエラーメッセージが出る。
2^15=32768なのでこれはエラーの通り。


ここまで前置き。


で、エラーを吐き出しているところを調べるために
各コンポーネントのON/OFFを切り替えていたところ、
対称にアタッチしてあるCharacterControllerが吐き出していたことがわかった。
そして、OFF/ONを行うことでエラーが起きなくなることもわかった。

CharacterController hoge = GetComponent<CharacterController>();
gameObject.layer = LayerMask.NameToLayer("Enemy");
hoge.enabled = false;
hoge.enabled = true;

以上のようにすることで、エラーは無くなった。

また、細かい検証はしていないが、
Rigidbody + BoxColliderでは同様のエラーは出なかった。

他にも細かい原因が絡んでいるかもしれないが、
レイヤー変更後のキャラクターコントローラーは一度再起動させる
のが良い、かもしれないとあやふやに終わる


2015/04/29 追記---------------------------------------------------
改めてやるとダメっぽいので確認
そんなことなかった
enabledが抜けてるところがあっただけだった