« 2010年6月 | トップページ | 2010年8月 »

2010年7月

2010年7月21日 (水)

通常ボーン → Implicit Bone なスクリプト。

hayase さんというお方がですね、なんだかステキなスクリプトを送ってきてくれたようでしてね。

http://homepage3.nifty.com/jjj/XSIFiles/etc/bone convert to implicit bone script for softimage2011.zip

スクリプト数本と大量の解説画像が入っています。



この hayase さんから先日の ICE-K セミナーの記事にコメントを頂きましてね、旧来の通常ボーンから Implicit Bone へ変換してくれるスクリプトのようです。ICE-K のリギングに役立つはずです。 俺が記事のコメントの中で、嘔吐デスクさんの新バージョンデモの中でサクっとスクリプトを実行しているくせにそのスクリプトは配布しねえのかよゴルァ と文句をタレてたらご親切にもスクリプトを書いて下さったようであります。


「・・・・のようです」 とか曖昧な書き方をしているのは、大変申し訳ないことに、こともあろうに俺自身、まだこのスクリプトを試せていないのであります。 ちと忙しくなってしまって・・・すいませんすいません試してもいないのにブログに載せてすいません。でもせっかくの御好意でわざわざ俺にメールで送ってきてくれて、このブログで公開させてくれると仰るし、しかも旬を逃してしまってもいけないと思い、無礼を承知で、頂いた物を、その効果もありがたみも実感しないうちにブログに載せてしまっています。すいませんすいません。

少し余裕ができたら俺もいじってみますね。いや、悪いのはあの人なんです。あんなややこしいキャラを俺に押し付けるあの人のせいなんです。今頃のんきに大陸で青島ビールでも呑んでいることでしょう。あるいはリグ担当名乗り出ろゴルァ いやなんでもありません。


ちなみにうちの WEB スペースも容量がきつくてですね、恒久的にここに置いておくことは難しそうなので、そのうち工夫してどこかに移します。重ね重ねすいませんすいません。悪いのはあいつなんです。ウソですお世話になります。いやなんでもありません。



あ、こんなわけわからないこと書いてたらいけませんね。
ともかく、hayase さんありがとうございました。
時間ができたらいじくり倒します。
ありがとうございます。





.

| | コメント (3) | トラックバック (0)

2010年7月19日 (月)

友愛その9。

いつまでやんの。



今回は Self-installing 形式な話になります。

前回とほぼ同じソースです。違うのは、冒頭の赤字の行だけ。


var oProp = ActiveSceneRoot.AddProperty ( "CustomProperty", false, "hoge" );

oProp.AddParameter2 ( "XRes",  siInt4,  1280 );
oProp.AddParameter2 ( "YRes",  siInt4,  720 );
oProp.AddParameter2 ( "Aspect",  siDouble,  1280/720 );
oProp.AddParameter2 ( "LockAspect",  siBool,  true);

var oLayout, oItem;
oLayout = oProp.PPGLayout;

oLayout.AddRow( );
    oItem = oLayout.AddItem ( "XRes", "X" );
    oItem = oLayout.AddItem ( "YRes", "Y" );
oLayout.EndRow( );

oLayout.AddRow( );
    oItem = oLayout.AddItem ( "Aspect", "Aspect Ratio" );
    oItem = oLayout.AddItem ( "LockAspect", "Lock Aspect Ratio" );
oLayout.EndRow( );

//    Logic
oLayout.Language = "JScript";
oLayout.Logic =    XRes_OnChanged.toString( )+
                     YRes_OnChanged.toString( )+
                     Aspect_OnChanged.toString( )+
                     AspectUpdate.toString( )+
                     LockAspect_OnChanged.toString( )+
                     OnInit.toString( );

function XRes_OnChanged( )
{
    if (PPG.LockAspect.value)
    {
        PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
    }
    else
    {
        AspectUpdate( );
    }
}
function YRes_OnChanged( )
{
    if (PPG.LockAspect.value)
    {
        PPG.XRes.value = PPG.YRes.value * PPG.Aspect.value;
    }
    else
    {
        AspectUpdate( );
    }
}
function Aspect_OnChanged( )
{
    PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
}
function AspectUpdate( )
{
    PPG.Aspect.value = PPG.XRes.value / PPG.YRes.value;
}
function LockAspect_OnChanged( )
{
    if (PPG.LockAspect.value)
    {
        PPG.Aspect.ReadOnly = true;
    }
    else
    {
        PPG.Aspect.ReadOnly = false;
    }
}
function OnInit( )
{
    LockAspect_OnChanged( );
}

InspectObj ( oProp );


前回のはシーンに実体を残さない Property でしたが、今回は以前にその1とかでやったように、冒頭の赤字の部分で ActiveSceneRoot に対して AddProperty の呪文を唱えているので、シーンの中に実体として Property が出現します。

Explorer で見ると実体として hoge があるのがわかります。
Ui_91

実行した結果出てくる PPG はもちろん前回と同じ。
Ui_83
X と Y の連動や、Lock のオンオフで Aspect の ReadOnly が変わるという機能も生きています。


で、この hoge プロパティは実体があるので、シーンを保存すればもちろん一緒に保存されます。

ということでシーンを保存しましょう。
次に新規シーンを実行するとかして一度クリアし、またそのシーンをロードしてみましょう。
はい、hoge が残っていますね。当たり前です。
hoge をダブルクリックするなりして PPG を開けば、PPG の見た目も、XY 連動の機能なども、そのまんま生きてます。当たり前です。


では、XSI を一度終了させましょう。
そしてまた起動しましょう。
同じシーンをロードしましょう。
hoge は残っていますね? 当たり前です。

で、再び hoge をダブルクリックとかして PPG を表示させてみると・・・・
Ui_92
てめえオラ 配置崩れてるじゃねえかよ AddRow でちゃんと並べただろがドルァ


このように、シーンを保存した時点ではちゃんとしていた配置は崩れ、パラメータ1つにつき1行、タテに順番に並んでいるだけになっちまいました。AddRow の呪文でヨコに並べた効果はもはや失われています。 すでに秩序はなく、テロリズムに支配されています。

混乱はそこだけに留まりません。 よく見てみれば、パラメータの名前がすべてスクリプトネームになってしまっています。 スクリプトネームというのは、そのパラメータの本当の名前のことです。 AddParameter の呪文で指定した「本名」です。 スクリプティング的に都合の良い本名を付けた場合、必ずしもユーザにとって分かり易いとは限らないので、AddItem の呪文で UI 上に登録する時に 「本名は "XRes" だけど UI 上は "X" と表示しろ」 などと指定していたわけです。 でも XSI を再起動した後は、この UI 上の名前はどこかに消え失せ、本名表示になってしまっています。 個人情報の漏洩です。

混乱は更に続きます。 XRes や YRes の値をいじってみてください。LockAspect のオンオフを切り替えてみてください。 あんなに苦労して Logic の function を書いて、やっとこさ連動の機能やグレー表示切替の機能を追加したのに、Logic の挙動は消え失せてしまい、値の変更を検知する仕事も、検知後に function の中身を実行する仕事も、全て放棄されています。 賃上げ要求のストライキです。


このように一度 XSI を再起動してしまうと、テロや個人情報のダダ漏れやストが起こり、PPG の社会はその秩序を完全に失います。非常事態宣言です。戒厳令です。夜間外出禁止になり、とても不便です。


ということで、PPGLayout を苦労してあれこれいじっても、それは1セッションの間だけ有効であり、再起動後はもう効果が無いんですね。 1セッションというのは、XSI を起動して終了するまでの間のことです。 もちろん再起動後もちゃんとカスタムプロパティとして機能はしますが、AddRow やら AddGroup やらの配置に関する効果、Logic などの効果を含めて PPGLayout に関する効果は全て失われるのです。そういうもんなんです。


この効果を持続させようとすると、もはや Self-installing 形式の Property にするしかありません。たぶん。

Self-installing 形式ってのは何か、と問われると説明できないんですが、いわゆるプラグイン形式のことです。 今までこの友愛シリーズでやってきたのは、スクリプトエディタから走らせるだけのただのスクリプトです。 これに対しプラグインというのは、XSI の起動と同時に XSI の機能の一部として使えるよう勝手にロードされるものです(合ってるかな?)。  「 自分自身を、XSI の起動時に XSI にインストールして、XSI の機能の一部になる 」 みたいな意味で Self-installing と言うのかなあ、などと勝手に想像していますが実のところはわかりません。 SDKガイドの該当箇所読むのめんどくさいので、誰か代わりに読んで教えて下さい。


では、hoge プロパティを、プラグイン形式にしていきましょう。


いつも俺は、空っぽのプラグイン形式プロパティを作って、後は中身を普通にコーティングしていくのですが、今回は一応、Migrate な方法を書いてみます。既存のプロパティを元にしてプラグイン形式のプロパティを作るというやり方です。

まず、既に存在している hoge の PPG を開き、ウインドウの上の方のすき間で右クリックします。 この右クリックの位置が微妙なので、ちょっと探って下さい。

Ui_93
この赤ワクのあたりを右クリックするのです。
で、Migrate to Self-installed を選びます。

あれ? "Self-installing" じゃなくて "Self-installed" か? 過去分詞が正しい? でも マニュアルとか Wiki で Self-installing と書かれている部分もあるような? よくわかんなくなった。まあいいや。とにかくプラグイン形式ということです。

するとこんなメッセージがが出て、
Ui_94
カスタムプロパティの魔法使いさんがお手伝いしまっせー
既存のプロパティにあるパラメータを、そのまんま新しいプロパティに反映させまっせー

とかなんとか言っているだけなので、さっさとOK押して進みましょう。


ちなみにですが、Migrate の元になるプロパティ hoge は、今回はスクリプトを書くことによって作ったわけですが、別にスクリプトを書かなくても XSI の GUI から作ってもいいわけですね。Animate モジュールに Create > Parameter があるじゃないですか。
Ui_94b
この機能を使ってよくカスタムパラメータ作りますよね。 そうやって作られたカスパラを、今回のように Migrate to Self-installed を使ってプラグイン形式に変換するというのが、おそらく XSI 的に想定されている使い方なんだと思います。 

ただし、GUI からカスパラを作ることはできても、その配置やLogic の挙動を定義する方法は GUI に用意されていません。そこはスクリプトを書くしかないわけです。 この友愛シリーズは配置やら Logic やらの話ですので、どのみちスクリプトを書かねばならず、なら最初からスクリプトを使ってプロパティを作ってしまえ、ということだったわけであります。


話がそれてしまった。
さて、魔法使い(SDK Wizard)が降臨して、こういうウインドウが出てきたはずです。
Ui_95
Property Name のところに、新たに作るカスタムプロパティの名前を入れます。その場限りのスクリプトとは違いプラグイン形式なので、XSI が起動されるたびに呼び込まれ、どのシーンに対してもいつでも使うことができるプロパティを作ることになります。それゆえに他のプロパティと名前がかぶることができません。なので名前はあまりにもテキトーにしない方がいいと思います。

あとは Coding Language でお好きなスクリプト言語を。 俺は JScript しか知りませんが。

あとは、Output Directory か。 プラグイン形式のカスタムプロパティを作るということは、当然そのプロパティの内容が記述されたファイルを作ることにもなるんですが、その書き出し先のことです。 デフォルトだと XSI のユーザプラグインフォルダになってますので、とりあえずそのままでいいんじゃないですか。 Workgroup プラグインとして作成したい場合は、既存の Workgroup のプラグインフォルダを指定すればいいです。

それ以外はまあ、とりあえずほっとけば。 見ればわかるだろうし、わかんなきゃマニュアル嫁。


一応、となりのタブ(Add Parameterタブ)も見てみましょう。
Ui_96
ここで、このプロパティが持つパラメータを定義しています。 上の Define Item グループ内をいじって新しいパラメータを追加してもOKですが、今回の場合は Migrate でやっているので元になるプロパティから既にパラメータをゲットしてきており、下の Pamateter タブの中で見ると、最初のスクリプト内で AddParameter2 の呪文によって定義した XRes とかのパラメータが、すでに登録されているのがわかります。 新たにパラメータを追加したいのでなければ、そのまんまほっとけばいいです。

3つ目のタブ(Layouytタブ)も見てみましょう。
Ui_97
このタブで、まさに PPGLayout に関する操作ができるようになっています。
Group も作れます。つまり AddGroup の呪文を、スクリプトのコードを書くことなく、この GUI の操作で作ることができます。 魔法使いさんが GUI で入力された情報をもとに、勝手に AddGroup の呪文をスクリプトに埋め込んでくれるというものです。 Row も同じことができますね。

しかし。
このウィザード内での Group や Row の操作は超わかりにくいというか、激使いにくいです。 こんな所で頑張らないで、スクリプト内に直接コードを書いてしまった方がずっとラクです。 なのでこのタブはまず使いません。 もっと言えば、さっきの2つ目のタブ(AddParameter タブ)も、俺は全く使いません。 Migrate でやる場合でもそうじゃない場合でも、パラメータを追加したければスクリプトの中で AddParameter2 の呪文を唱えればいいだけですから、何もこんな使いにくい GUI と格闘する必要は全くありません。 ということで俺は、ウィザードの機能は雛形の出力としてしか使いません。


で、一番下の Generate Property ボタンを押すと、実際にプロパティを作るためのスクリプトコードが生成されます。そのコードはすぐに、自動的にスクリプトエディタで開かれます。
Ui_98
こんな感じ。

よく見てみると、ちゃんと AddParameter2 の呪文などが埋め込まれているのがわかります。 スクリプトを書いて作った PPG から、リバースエンジニアリングをしてまたコードを生成したという感じになるのかな? 

ともかく、最初にスクリプトで書いていたその場限りのプロパティが、ようやくプラグイン形式のプロパティに生まれ変わりました。( ゚∀゚)


でもまだやることがあります。

まず、どうやってこのプロパティをシーンの中に出現させるかを説明します。今まではスクリプトエディタからスクリプトを実行することによってプロパティを作り出していたわけですが、もはやプラグイン形式になってしまったので、スクリプトエディタから実行できるコードではなくなっています。 スクリプトエディタで実行ボタンを押しても何も起こらないはずです。

思いつくのは2つの方法。まずは、このプラグイン形式のプロパティを降臨させるためのスクリプトを書くというもの。

ActiveSceneRoot.AddProperty( "hoge_Migrated" );


これを実行してみて下さい。シーンルートにプロパティが作られます。


シーンルートではなく選択したオブジェクトにプロパティをぶら下げたいのであれば、

Selection(0).AddProperty( "hoge_Migrated" );

こう書けばいいです。

hoge_Migrated は既にプラグイン形式のプロパティですので、XSI の中では "hoge_Migrated" という名前が与えられて、どのような場面からでも呼び出せるようになっているのです。なので AddProperty の呪文で、どこへでも召還できます。


冗長な説明になりますが、例をあげると、

ActiveSceneRoot.AddProperty( "rendermap" );

これを実行すれば、シーンルートにレンダーマップのプロパティが降臨します。レンダーマップをやる時に必要なアレです。 そして  "rendermap" という名前のプロパティは XSI の標準装備機能として最初から組み込まれているので、このように名前を呼べばどんな場面でも召還できます。 その rendermap を hoge_Migrated に置き換えただけです。 この例を見ても、hoge_Migrated が rendermap などと同じく、XSI に登録された標準装備機能として扱われているのがわかると思います。 これがプラグイン形式です。


もうひとつの方法は、Plug-in Manager からやる方法ですね。
File メニュー > Plug-in Manager を開き、
Ui_99
もしユーザプラグインとして作ったのならこのように User Root の Plug-ins フォルダを開いていくと hoge_Migrated が見つけられます。ワークグループに作ったのなら、ワークグループフォルダの下の Plug-ins です。
そして右クリックすると Create Property というのが出てくるので、これを選ぶと現在選択中のオブジェクトに hoge_Migrated プロパティが降臨します。 何も選んでいない状態でこれをやると、シーンルートに降臨します。


実際には、こんなめんどくさいことしたくないですよね。
なので、ウィザードの段階で登録先のメニューを指定すれば、例えば Get > Property 以下に出現させることができます。 今回のようにウィザードで登録しなかったとしても、後からスクリプトのコードにメニュー登録の呪文を書けば同じことができます。 これはまたそのうち説明するかもしれません。しないかも知れません。知りません。


で、こうしてプラグイン化したプロパティの PPG を表示させてみると、
Ui_99b
やっぱり配置崩れてるじゃねえかよドルァ
ロジックの挙動も消えてるよオルァ


そうなのです。配置もロジックも死んでいます。 配置に関しては、ウィザードの中で指定をしなかったのでこうなっています。配置情報は、元になっているプロパティから自動で拾ってくれないんです。 ロジックに関しては、そもそもウィザードで指定する方法がありません。配置と同じく元のプロパティから拾ってもくれないので、結局手で書き直すことになります。 まあそんなもんです。


配置から直しましょう。
プラグイン形式になったスクリプトファイルのうち、プラグイン名_DefineLayout( in_ctxt ) となっているファンクションを探します。

function hoge_Migrated_DefineLayout( in_ctxt )
{
    var oLayout,oItem;
    oLayout = in_ctxt.Source;
    oLayout.Clear();
    oLayout.AddItem("XRes");
    oLayout.AddItem("YRes");
    oLayout.AddItem("Aspect");
    oLayout.AddItem("LockAspect");
    return true;
}


ここですね。プラグイン形式のプロパティでは、この function の中で配置に関する記述をするのが基本になっています。

現在の状態は、見ての通り、AddItem の呪文があるだけで、AddRow や AddGroup の呪文はありません。なので付け加えればそれでOKです。


function hoge_Migrated_DefineLayout( in_ctxt )
{
    var oLayout,oItem;
    oLayout = in_ctxt.Source;
    oLayout.Clear();

  
oLayout.AddRow ( );
        oLayout.AddItem("XRes", "X" );
        oLayout.AddItem("YRes", "Y" );

    oLayout.EndRow ( );

   
oLayout.AddRow ( );
        oLayout.AddItem("Aspect", "Aspect Ratio" );
        oLayout.AddItem("LockAspect", "Lock Aspect Ratio" );
   
oLayout.EnddRow ( );

    return true;
}


前にやったのと全く同じように、AddRow と EndRow で囲ってやるだけです。あと、スクリプトネームのままで表示されないように、AddItem の呪文のオプションで表示名を指定しています。


修正後、スクリプトを上書きセーブします。
上書きセーブ後、PPG の上で右クリックし、Refresh してやります。
Ui_910
Migrate の時と同じエリアを右クリックです。

これにより、スクリプトに加えた変更が PPG に反映されます。
Ui_911_2
こうして、プラグイン形式になった hoge_Migrated もめでたく正しい配置になりました。パラメータの名前も、スクリプトネームではなく、指定した表示名になっています。


さあ次にロジックを直しましょう。
と言いたいところですがもう完全にめんどくさくなっているので、今回はここでやめます。
また次回にやります。やらないかも。知りません。


注意しなければならないのは、このように右クリック > Refresh で更新できるのは PPGLayout に関する情報のみ、ということです。 PPGLayout に関する情報=つまり基本的には 「配置関係」 と 「 Logic 」 です。

今回は場合は、hoge_Migrated_DefineLayout( in_ctxt ) このファンクションの中身を書き換えました。 このファンクションの中では配置のこと以外は扱っておらず、パラメータの生成やパラメータの名前決定などは何もしていません。 こういうときは、Refresh さえすれば変更が反映されます。

それに対し、例えばパラメータを追加するなど、そのプロパティの成立に根本から関わる変更を加えたときは、Refresh だけでは反映できません。 パラメータを追加したと仮定します。 しかしこの Refresh という行為はそもそも、既存の PPG をリフレッシュしているだけであります。このプロパティが最初に作られた時点ではまだ新パラメータは存在してなかったわけですから、Refresh しようとしている PPG の中には新パラメータはまだできておらず、それに大して Refresh した所で、新パラメータが現れるはずもありません。

なのでパラメータを追加したり、パラメータの名前を変えたり、パラメータの属性をいじった時などは、一度そのプロパティをシーンから捨てて、また作り直さねばなりません。新たに作り直されたプロパティは、ゼロからそのプロパティを作るので(正確にはそのプロパティのインスタンスを作るので)、変更が加えられたコードを丸ごと読み込むことになります。故にちゃんと変更が反映されます。 今回挙げたやり方で言えば、AddProperty の呪文唱え直すか、Plug-in Manager から右クリック > Create Property をし直せばいいことになります。

今回は配置の部分しかいじってないので、Refresh でOKよ、という所でやめておきます。 次回以降、パラメータに変更を加えたために Refresh だけでは反映させられなくなった時のワークフローを、説明したいと思います。 説明しないかもしれません。 知りません。



ということで、ひとまず配置は直りました。そして一番大事なポイントですが、プラグイン形式になったので、もはや XSI 再起動後でも PPG 内のパラメータの配置は保たれます。 ためしにシーンをセーブして、 XSI を再起動し、PPG を再び開いてみて下さい。 ちゃんと AddRow の呪文でヨコに並べられた配置が生きていると思います。 これがやりたかったんです。そうなんです。





ってことで、

 ・ロジックも修正しなきゃ
 ・パラメータに変更を加えた時の、反映のさせ方

この辺が宿題ということになりますか。

知りません。





ごきげんよう。




.

| | コメント (0) | トラックバック (0)

2010年7月18日 (日)

鳩ぽっぽ完結。

鳩ぽっぽの Podcast が完結しました。うーむ、長いこと楽しませてもらいました。そして勉強になった。 ルーカスさん、マジでありがとう。


本編の他、Podcast 全部、その他おまけなどが入った DVD が発売されてるようです。欲しいな。 買おうかな。




ところで割と最近アップされた Podcast #20 なんですが、キャラクタアニメーションの手法についての大まかな概論でして、まあ特段新しい話ではなかったものの、面白かったです。

以前このブログにも書いた気がするんだけど、俺の場合、アニメーションを付ける時は ポーズtoポーズ の方法ができず、いつも重心となるメインのパーツ1個か2個だけでほぼ最後までアニメーションを付けてしまい、そのメインパーツを基準にそれ以外の要素を、しかも1つ1つ足していくような方法でやっているんですね。このやり方しかできないんです。

例えば人間がジャンプして着地してキック、みたいな動きを作るときは、まず腰のコントローラだけでジャンプ~着地~キックまで全尺付けてしまいます。腰の中でも、最初はポジション、次はローテーションかな。 この時、上半身はデフォルトTポーズのままなんですが気にしない。 IK が仕込まれた足は初期位置に置き去りにされるため、ジャンプ後すぐに足がびょーーーんと伸びきってすごいことになるんですが気にしない。おかまいなしに腰だけ作っちゃいます。 
※ま、実際は、足がびょーーーんって伸びきったままだとあまりにも見づらい/やりづらいと感じることもあるので、腰と同時に足の位置もラフに動かしていくことはよくありますけどね。もちろんこの時の足の動きは完全に仮であり、後で直すことを前提にね。

これで尺の最後まで腰が出来たら、次に胸とか、あるいは足ですかね。胸とか足だけを、尺の最後まで付けてしまいます。ここら辺が終わるまでは腕なんか一切いじらない。Tポーズのまま。

胸ができたらやっと腕とか、頭とか。 


こうして1つずつ順番に、当たり前だけど主従の関係で言えば主が先で従が後という順で、作ります。 このやり方だと、腕や頭などが最終的にどうなってるのかは、作業後半にならないと見えてこないですね。


こういうやり方に対し ポーズtoポーズの方法は、まずはジャンプの前の予備動作で全身のポーズを作り、ジャンプの蹴り出しで全身のポーズを作り、ジャンプの頂点あたりでも全身のポーズを作り、ジャンプの着地とその反動でまたそれぞれ全身のポーズを作り、キックの予備動作で全身ポーズ、キックで全身ポーズ・・・・ という風に、その時間軸における全身のポーズを、経過時間に沿って1つ1つ作っていく方法ですね。 こうやってキーフレームのポーズとそのタイミングだけを先に終わらせ、その後、キーフレーム間のブレイクダウン(より細かく切ってキー間を補完する)をやるということですね。


んでね、「ポーズtoポーズ」 という呼び方はあちこちで見るのでいいのですが、俺がやっている「腰から順番に」みたいな方法の呼び方って、何かあるのかなーーと、ずっと前から思っていたんですよ。そしたらですね、この Podcast #20 で Layered って呼び方をしてたんですよ。 冒頭でも、Pose to Pose VS Layered のイントロ芝居がありますね。 なるほど、これ、Layered って呼ぶんですか。

Pose to Pose という呼び方の由来はまあそのまんまだからいいとして、Layered と呼ぶ理由を想像すると、腰の動きのように基準になるものをまず作り、その上に胸、その上に腕、みたいな、まるでレイヤを重ねるかのような、つまり下地を元に上に新たなものをかぶせるかのような方法だからなのではないでしょうかね。たぶんね。

ともかくですよ、Pose to Pose と対になる言葉が切実に欲しかったんです。誰かとアニメーションの話をする時とか、説明する時にラクですから。 ということでみなさん、今日からこの手法を  レイヤード と呼んで下さい。 あるいは、あなたの会社とかで違う呼び方をしてるなら、是非教えて下さい。



ルーカスさんがこの Potcast でしゃべっていることで、おやっ? と思ったのは、俺の聞き間違いでなければ、Layered の手法は2Dの手描きアニメータがやっている手法のCGバージョンである、と言っているところでしたね。 うーん、そうですかね? 手描き作画マンの絵の描き方って、むしろポーズtoポーズに近いと俺は認識していました。どうなんだろう。 
ま、1枚ごとにフィニッシュに近い状態まで描き込んでから次に進むのではなく、1枚1枚はラフに描いて尺の最後まで通してしまい、パラパラと動きを確認しながらまたそれぞれの絵に戻って修正していく、という作業の流れが Layered に似てると言えなくもないので、ルーカスさんはその意味で言っているのかも知れません。

話はそれますが、昔ちょこちょこ目にしていた、というか4~5年前までだと思うんだけど、ポーズtoポーズなワークフローを前提にした国産の3Dアニメーションソフトウェア、なんて名前でしたっけね? タイムラインがタテ方向だったような気がする。 フルボディ IK 的なことができたんじゃなかったっけ? トラディショナルな作画ワークフローに近い感覚でアニメーション付けが出来る、とかいうのが売り文句だったような気がします。 気になりながらも、ついに一度もいじったことがないまま、最近はあまり見かけなくなったような気がします。 ちょっといじってみたかったな。


ルーカスさんによると、どちらの手法を採るのかはショットによりけりだそうです。 1つ1つのポーズがはっきりしていて、そのポーズでしばらく止まるような動きの時は、ポーズtoポーズでやるそうです。 それに対し、キャラクタの動きが連続的に流れているような場合は Layered でやるそうです。 彼の場合、カートゥーン的なパキパキした動きの時はポーズtoポーズが上手くいき、スムーズでリアルな傾向の動きは Layered が上手くいくそうです。 どちらも試して、上手く行くほうでやるのがいいよ、でもその中間で上手く行くなら、ショットによって両方の手法で行ったり来たりできるのでそれがベストだ、という意味のことも言ってますね。 また、ポーズtoポーズな人は、ポーズtoポーズでやっておきながらも最終段階では Layered っぽいやり方で仕上げをし、逆に Layered な人は最初だけポーズtoポーズのやり方でいくつかポーズを作ってから Layered な作業に突入していくことが多いだろう、みたいなことも言っています。 聞き間違いでなければ。


まあ、要は両方できるようになっておいて、そのときの場合によって使い分けろということを言っているんですよね。やっぱそうだよなあ。偏っててはいけないですね。 うひゃー まだまだベンキョすることが多くて気が遠くなるよ。おぢさんにはつらいですほんともう。 っていうか若い頃から場当たり的にやってばかりで、系統立てて訓練してこなかったツケですねそうですね。 専門学校とかでこういうことを系統立ててやるのがいいんじゃないのかなあ、などと思ったり。

ポーズtoポーズを練習するには、海外のアニメーション習得コースとかでよく見る、二人の会話とかキャラひとりで喋っているようなショットでやるのか良さそうな気がしますが、どうですかね。 大げさなジェスチャーをいっぱい入れながら喋るようなやつね。 




その次の Podcast #21 もすんげー面白い。 どのカテゴリにも含めづらいために今までの Podcast で出てこなかった話とか、視聴者からの質問に答える形の話とかですね。 これもまた後日、じっくり見直そう。

なにせルーカスさん早口だから、気合入れて聴かないと内容がわからないんですよね。何度も何度も巻き戻しが必要。かなり疲れます。 早口というよりも、次の話題に行くときに「間」がないんですよね。だから内容を咀嚼している間に次の話になっちゃって、一時停止と巻き戻しの連続になります。疲れます。 でも疲れる価値のある Podcast です。





.

| | コメント (2) | トラックバック (0)

2010年7月17日 (土)

えびちゃん。

HDD を大整理していたら、こんなものが出てきた。


Ebi1

Ebi2

Ebi3

ずいぶん前にモデリングしたやつだな。
ずっと埋もれていた。

久我さんのデザインですね。久我さん会ってないなあ。元気ですか。彼はこんなブログ見るはずもないが。

なんか、スカルプトなツールでいじいじしたら楽しそうなキャラだなあ。
セットアップの研究にも使えそう。
このまま埋もれさせるのも、なんだかもったいねえ。



誰かテクスチャ描いて下さい Orz
誰かリグ組んで下さい Orz
誰か俺に ZBrush 買って下さい Orz



っていうかもう長いこと、モデリングしてないな。
ヤヴァい。
モデリングのみならず、色々。ヤヴァい。




.

| | コメント (0) | トラックバック (0)

2010年7月16日 (金)

マスターコース。

空調屋さんの ICE Kinematics のセミナーに行ってきたんですがね。
講演者は ICE の開発者のひとりでもあり、CAT の生みの親でもあるフィリップ・テイラーさんでした。



結論から言うと、思ったより内容が浅くてがっかり Orz
とは言え、とったメモから無理やり何かを拾い出してみようと思います。




イントロダクションの話は、

・ICE とは、ビジュアルプログラミング環境である。コードを書く代わりにノードをつなげていく。

・Unreal や Shake や Mental Mill など、これまでにもビジュアルプログラミングというものは存在しており、その意味において ICE のコンセプトはは特段新しいものではない。

・ICE では変数を使用しないというのがコードを書くのと大きく違う


とかなんとか。うーむ、軽いメモしかない。っていうかこれ、ICE の話だ。ICE-K の話じゃないよゴルァ





その後、前半はドラゴンのキャラクタを ICE-K を使ってリギングしていく実演でした。ゼロから作る。黙々と作る。くねくねと胴体を波打たせながら羽根も羽ばたく、というループモーションを ICE-K だけを使って作っていくという内容でした。

基準になるのは

  結果の値 = sin ( t + offset ) * amplitude

この式のみ。 これを間接のポジションに突っ込んでいたんだっけ? ローテーションだっけ?

t ってのは時間軸ですね。これで sin波の繰り返しの周期が決まり、offset で時間軸方向にオフセットするってことですかね。 amplitude は sin波を上下方向にどれだけ拡大縮小するのかを決めているってことですよね。

この式だけを元に、全てのパーツを動かしてました。自分でやろうと思ったら全然スラスラとはできないけど、説明聞きながら見ていれば、何をやっているのかは簡単にわかるくらいの内容でしたね。ここでつまづいたらどうしよう、とドキドキしていたので、助かりました。

なにやら、こんな感じの ICETree だったような記憶が・・・。
Kine
Tree が思い出せなくて焦った・・・・
もらった CD-ROM にはドラゴンのデータ入ってないんですよね。
あれ? 後からくれるって最後に言ってたっけ? どうだっけ。 



過去に、タコ・イカの足、クリーチャーの尻尾、イモムシ系クネクネクリーチャーの胴体など、何度かクネクネ系ループモーションなリグを組んだことがありますが、もちろん Expression と Constraint でやってました。今回のドラゴンは、まさに同じ内容を ICE に置き換えたというものでした。つまり基本的な概念はすでに分かっているつもりでして、あとは ICE に置き換えるのがスラスラできればいいわけで、ハードルはさほど高くないと思えましたね。 ま、この程度の内容ならば、という話ですけどね。

また、旧来の方法ではすんごく多くのオブジェクトに Expression を仕込まねばならなかったんですが、ICE の場合は割と少ない仕込みで同じことが実現できそうな予感がしましたが、どうでしょうかね。 実際フィリップさんも 「各オブジェクトに operator があるのではなく、1つの operator(ICE op)からたくさんのオブジェクトを制御できるのがパワフルだぜ」 という意味のことを言ってましたよね。ただし仕込む数が少なくてラクだという話ではなく、op の数が少ないので実行が速い=パワフルだ、という文脈の話だったかもしれません。


sin カーブを元にしているのはその方が扱いがラクだからだとは思いますが、ループモーションでもやはりツメタメが欲しくなる場合が多いわけでして、そういう時って sin に何かを掛けたり割ったりすればいいんでしょうかね? 算数ができない俺はこの辺がダメであります。要は Fカーブの形で考えた時に、ハンドルがブレイクした状態とか、頂点付近が平らな時間がより長くなっている状態とかにするにはどうしたらいいのだろう?
っていうか最初から sin とか使わずに、そこだけFカーブにしちまえばいいのかな。今度やってみよう。


それと今回のドラゴンの例では、ドラゴンのジオメトリがあった上でその骨を構築していくようなものではなく、骨しかないという状態でした。まあ、ICE-K の説明なんだからそれでいいと言えばいいのですが、実務で考えると骨ありきでリグを組むなんてことはあり得ず、必ずモデリングされたジオメトリありきで骨を仕込むことになりますよね。 これまでのように見た目合わせでスケルトンを描いていく方法は、直感的でラクです。それに対し、ICE でやる時は同じようにラクで直感的にできるもんだろうか?という疑問はあります。 特に ICE-K でリギングするには ICETree の中で骨の長さを与えてやらねばならないわけであって、これを ICE のノードの数値スライダ(もしくは手打ち)でやるのがどのくらい直感的なのか。たぶん直感的ではない。それに初期ポーズで骨がナナメに入るべき所はその角度も同じように ICETree の中から調整してやらねばならず、それも直感的ではなさそう。
そうなると、例えば、まずはこれまでのようにスケルトンを描き、その length や rot を自動で拾ってきて ICE のノードにブチ込む、みたいな自作ツールが必須になるのではないか、と思いましたがどうでしょうかね。 あるいはすでにそういう機能なりワークフローなりが用意されているのかな?


あとは実行速度の話として、1つの ICETree から複数のオブジェクトの Kinematics を直接操作すると重い、という話がありましたよね。なのでいきなり Kinematics に Set Data せず、カスタムアトリビュートを作って全ての計算結果を一度格納しておき、それを取り出してから Kinematics に Set Data すると良い、という話だったと思います。 単純に考えると、一度格納してそれを取り出すのだから、工程が増えてかえって重くなりそうな気がするんですがねえ。。。 まあ ICE を作った本人が言うんだから間違いはないでしょう。 「 ICE はその設計上、こういう順番でデータを計算しちゃう。だから遅くなる。その順番で計算させないためには、こうつなげばいい」 みたいな、仕様上の弱点を回避できる tips だと解釈したんですが、合ってますかねこの解釈?



にしても、前日に予習しておいて良かった! まるでテスト前の中学生のように一夜漬けなんですけどね、Users Notes を見て ICE-K をざっとやっておいたおかげで、フィリップさんの話していることは大筋ですんなり理解できました。もし予習してなかったら根本的な所で思考がつまづいてしまって、すんなり入ってこなかっただろうと思います。やっぱり予習必要ですね。



後半は、パイプラインがどうしたとか言ってましたが・・・・概念的な話をごく浅くしただけで、あまり実例を出してもらえず、正直つまんなかったですね。


サムライだか剣士だかみたいなキャラクタを見せてくれたんですが、1週間で作ったリグだそうです。 通常の人間のセットアップはもちろん、腕が伸びてくると自動で鎖骨が動くだとか、足が伸びてくると自動でカカトが上がるだとか、髪の毛が体とコリジョンしながらひらひら動くだとか、その他の揺れ物もやはり体とコリジョンして食い込まないようになっており、かつ移動量に制限を設けて暴れ過ぎないようにしてあるなど、とても良く出来てそうなリグでした。1週間でこれを作るって、かなりすごいんじゃないかな。

このリグは、CAT のリグにとてもよく似ていると言ってましたね。しかも、CAT リグの不具合を修正して仕込んでいるため、実際は CAT より良いリグになっているとか言ってました。ふーん。

でもねえ、まあ、あのリグの細かい解説をされても理解できなかったかも知れないけど、このプレゼンテーションはリグのデモンストレーションを見せてもらったというだけで、アレを見て特に ICE-K の理解が進んだとかアイデアが出てきたというわけではないんですよね。そこが残念でしたね。もうちょっとこう、「お、これなら俺にもできる!」 とか 「アレに使えそうだ!」 みたいな予感を味わわせてもらいたかったんだが、まあこれは俺のレベルがまだそこまで達してないからということになりますかね。


「ビヘイビア」というコトバをキーワードに何度も使っていましたが、これも今ひとつピンと来ていません。 ICE は再利用がラクだ、だが本当に再利用したいものはボーンとかではなく、ボーンがどう動くかという挙動のアルゴリズム=ビヘイビアだ、ビヘイビアは実際のボーン構造とは独立したものであるから、ボーンはキャラごとに再構築し、ただしビヘイビアは再利用する、それが ICE でリギングする利点だ、という主旨の話だと思ったんですが、違いますかね? この辺、あまりに概念的過ぎて、なんだかよくわかりませんでした。眠かった。


また、1つのリグから他のリグへアニメーションを転送するような ICETree を構築できる、つまり ICE-K はアニメーションのリターゲッティングに使える、という話がありましたね。でも実例は見せてもらえなかったような気が。


それと、ICE-K はモーションキャプチャデータのプロセッシング(キャプチャデータをリグの動きとして流し込む)にも使えるから、もはや Motion Builder は要らない! すでにそういうパイプラインを作った、みたいな話をしてましたね。 でも最後の質疑応答でどなたかから鋭い突っ込みを受けて、実はまだ実験・開発中レベルであり、XSI 7 の SDK のバグだかなんだかのために開発はストップしており、実務では実用化されていないということをゲロしてましたねw  ま、そのアイデア自体はとても有効なんでしょうね。なんとか実用化して売り出したらいいんじゃないでしょうかね。


などなど、あまり実例は出てこなくて、ICE-K の活用法の可能性を概念的にお話しました、という印象でありました。




その後は tips 集のような感じ。それほどびっくりするような tips があったわけでもなく、むしろ疑問に思える箇所もあったんですが、とても面白かったですよ。


ICETree を後から自分で見たとき、あるいは他人が見たときに内容がわかるように、ICETree を徹底的に整理しよう! という話をしてましたね。ある規則に従ってノードに色を付けるとか、コメントをちゃんと付けるとか、ウィキペディアなどアルゴリズムの参考にした WEB サイトの URL をコメントに書いておくとか、接続線のスパゲッティにならないようノードの配置を工夫するとか、どのポートに何が接続されているのかひと目で分かるようにノードは畳まない方がいいとか、etc。  美しくノードを並べるために、時には2分や3分かけて執拗に配置を調整する俺は間違ってなかった!と安心しますた。ただしノードは畳んでしまう派です。今のところ。


あとはパフォーマンスタイマーって言うんですか、ICETree 上のストップウォッチみたいなやつですね、あれを使ってボトルネックになっているノードを探し出しましょう、という話もありました。たった1つのノードのためにやたら処理を重くなっているという場合が非常に多いそうで、犯人捜しに活用しましょうということでした。なるほど、これは便利そうだ。使ったことなかったよ。


repeat ノードは重いから使うな! って話でしたが、これは設計上何か不具合があって不当に重くなっているから避けましょう、という話だったですかね? っていうかそもそも repeat ノードって使ったことないです。何をするためのもの?


あと、フィリップさんの場合、1つの ICETree でちゃんと自分の支配下に置いておけるノードの数は約30個だそうで、それ以上の数になる時はコンパウンド化してツリーをシンプルに保つそうです。まったく正しいとは思いますが、俺なんかは全部表に出ていないと不安になるタイプでして、また、コンパウンドにした瞬間にそこがブラックボックス化してしまって、コンパウンドの中に不具合が潜んでいてもそれに気づきにくくなったり、いじるのを躊躇したり、億劫になったりします。なのでまあ、この辺は好みだと思いますがねー。


コンパウンドはバージョンを重ねることができて、内容を修正したら新しいバージョンとして保存しておき、ICETree のノードから右クリックで使用するバージョンを選ぶことができる、という話もありましたね。なるほど。そんなことすら知らなかったよ俺は。 バージョンを変更するべきコンパウンドがシーンの中に多数散らばっている場合は、コンパウンドバージョンマネージャというものがあるそうで、イッキに変更できるのかなこれは。 調べてみよう。




以上、軽いメモと記憶をたどって無理やりまとめてみた感じですね。
正直、フルの値段を払った人や遠方から来た人にとっては、対価に釣り合わない内容だったんじゃないですかね? もうちょっと濃くして欲しいと思いました。

講演者と主催者側との間で、もっと綿密な打ち合わせをすればいいんでないの? ターゲットを絞った方がいいんでないの? などと思いました。 どのレベルのユーザを対象にしているのかがとても曖昧に感じたので。

まずこれは ICE-K の有償セミナーなんだから、ICE そのものの説明は要らん。ICE-K のことに集中してくれ、と思います。 Execute ノードの説明など要らん。黄色の線は Vector なんだよ、とか要らん。Show Value の説明など要らん。 ひたすら ICE-K の話だけをして欲しかったと思います。 まあ、そういう周辺の説明でそれほど時間を食ったというほどでもないと思いますが、要らんものは無い方が話がわかりやすくなって良い。

そして、高額な参加費を払う以上、「ICE-K のさわりを紹介して欲しい」 というレベルではなく、もう一段階上の具体的な成果を求めて参加している人が多いはずだと思うから、思い切ってハードルを上げることも必要だと思うんですよね。 主催者側が参加者にあらかじめ宿題を出して、「ここいら辺までは理解した上で参加してくれよ。それを前提に進めるからな」 という感じでもいいと思うんですよね。 

あるいはあらかじめどういう内容をやって欲しいかユーザにアンケートを採るなど、事前にマーケティングがあってもいいのではないかと思う。 なんだか、内容もレベルも講演者に丸投げしちゃってる印象があるんだよなあ。 そんなことないですか。そうですか。

まあ、先に大まかな内容を知らされてるわけだし、金払うか払わないかを決めるのは俺たちなので、物足りないとかアヤシイと思ったら参加しなければいいだけのことではありますがね。  それにしても後半は物足りなかったなー。 前半で基礎をやって期待させといて、後半ですんげえ具体的な活用法をビシバシ見せてくれてクライマックスか! と思いきや、なんだか尻すぼみに思えました。 ま、私見です。いち参加者の私見ということで許してください。






以下完全にどうでもいい話。



フィリップさん、すんげーたくさんの XSI を同時起動していた。でも 32bit だった。メモリ足りんのか

XSI は 2011 だったが SP1 ではなかった

プレゼンには MacBook を使っていたので、XSI はブートキャンプでやってんのか?と思いきや足元に PC らしきマシンが置いてあった。たぶん XSI は普通に PC 上で動いてます

やはり MacBook なのね。 SIGGRAPH とかこういうイベントの時、プレゼンターはほぼ Mac を使っている印象がある

Windows は英語版で、現在時刻は PM 2:00 頃のはずなのに Windows の右下の時計は AM 1:00 とかになっていた。どうやらフィリップさん、自前のマシンを自国から持ち込んだと思われる。 PC ごと持って来るなんて大変ね

Softimage というコトバも使っていたが、XSI というコトバも何回も使っていた。印象では XSI と言った回数の方が多いような気が

Euler をオイラーではなく、ユーラーと発音していた。 どっちが正しいんですかね? 日本語ではオイラーと言う場合が多いと思うんですが、つづりを見るとユーラーと発音したくなります。 ユーラーで統一しませんかみなさん

会場の椅子は、ヒジ置きが非常に邪魔でした。俺は椅子の上であぐらをかきたいんだゴルァ

会場寒すぎ。 でも先日の六本木での嘔吐デスクのイベントよりはマシだったかな

フィリップさん、シャイなのか、とても優し~い喋り方で、眠くなっちまうのですよ

デコのソリ込みが素敵でした。 不惑のソリ込みと見受けましたが、ほんとは若いんだよねきっと


おみやげは、ぴちょん君扇子ですた。
Img_0101
これが最大の収穫。







.

| | コメント (14) | トラックバック (0)

2010年7月15日 (木)

友愛その8。

まだ続くの?


今回は新しい話はあんまり出てきません。
Logic の部分の応用例とでも言うか。
応用ってほどでもないですね。
まあいいや。


var oProp = XSIFactory.CreateObject ( "CustomProperty" );
oProp.name = "hoge";

oProp.AddParameter2 ( "XRes",  siInt4,  1280 );
oProp.AddParameter2 ( "YRes",  siInt4,  720 );
oProp.AddParameter2 ( "Aspect",  siDouble,  1280/720);

var oLayout, oItem;
oLayout = oProp.PPGLayout;

oItem = oLayout.AddItem ( "XRes", "X" );
oItem = oLayout.AddItem ( "YRes", "Y" );
oItem = oLayout.AddItem ( "Aspect", "Aspect Ratio" );

//    Logic
oLayout.Language = "JScript";
oLayout.Logic =    XRes_OnChanged.toString( )+
                     YRes_OnChanged.toString( )+
                     Aspect_OnChanged.toString( );

function XRes_OnChanged( )
{
    PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
}

function YRes_OnChanged( )
{
    PPG.XRes.value = PPG.YRes.value * PPG.Aspect.value;
}

function Aspect_OnChanged( )
{
    XRes_OnChanged( );
}

InspectObj ( oProp );


よくある、解像度の X と Y と Aspect の自動アップデートですね。

これを実行すると
Ui_81
こうなります。 
X や Y の値をテキトーに変えてみて下さいまし。 X の値が変更されると Y が自動で更新され、その逆も同じく、という挙動になっています。あまり説明するまでもない話ではありますが一応説明すると、


function XRes_OnChanged( ) //   X の値が変更されたら Y を変更
{
    PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
}

function YRes_OnChanged( ) //   Y の値が変更されたら X を変更
{
    PPG.XRes.value = PPG.YRes.value * PPG.Aspect.value;
}

function Aspect_OnChanged( )  //  Aspect の値が変更されたら、Xが変更された時と同じ挙動
{
    XRes_OnChanged( );
}


こういうことなんですがね。コメント付けただけですが。
さらに冗長な説明になりますが、画像のアスペクト比というのはヨコ÷タテで出てくる比率ですので、基本的に、

 Aspect =  X / Y

ですわね。 なので、

 Y = X / Apect
 X = Y * Aspect

という式が成り立ちますね。その計算を Logic の function の中でやっているだけです。 Aspect が変更された時は X を基準にして Y を変えるという仕様にしていますが、まあこの辺の仕様はツールの目的によりけりでしょう。 で、その仕様に従えば、Aspect が変更された時の挙動は X が変更された時の挙動と全く同じわけです。なので、Aspect_OnChanged( ) の中から XRes_OnChanged( ) を呼び出しています。このように、ある function から他所の function を呼び出すことももちろん可能で、何度も同じ処理を記述して冗長なコードになるのを避けるためにこうしたりもします。

※でもちょっとしたことなら、敢えて他所の function を呼び出さずに冗長でもいいから同じ処理をそこに書いちゃった方が、後からコードを見直した時に処理が把握しやすかったりするんですよね。






次。 現在の仕様だと X や Y を変えた場合は強制的に他方の値が更新されてしまいます。 X と Y の値はもう決まっているがアスペクトは判っていない、という場合には不便です(ま、X と Y が決まっていればアスペクトは自動的に X / Y なんですが)。 Y を自由に入力したいのに X を入れた瞬間に Y の値は更新されてしまい、あわてて Y の値を入れ直すと X が変わってしまう、という無限ループになるわけですからね。 アスペクトを計算してあらかじめ入力しておかなければいけないことになり、不便ですね。

なので、アスペクトのロックをオンオフする機能を付けます。 

 アスペクトが現在の比率でロックされている場合 → これまでと同じ挙動
 アスペクトのロックが解除されている場合 →  X や Y の値の変更によって Aspect が変わる

という仕様にしてみます。


var oProp = XSIFactory.CreateObject ( "CustomProperty" );
oProp.name = "hoge";

oProp.AddParameter2 ( "XRes",  siInt4,  1280 );
oProp.AddParameter2 ( "YRes",  siInt4,  720 );
oProp.AddParameter2 ( "Aspect",  siDouble,  1280/720);
oProp.AddParameter2 ( "LockAspect",  siBool,  true);

var oLayout, oItem;
oLayout = oProp.PPGLayout;

oLayout.AddRow( );
    oItem = oLayout.AddItem ( "XRes", "X" );
    oItem = oLayout.AddItem ( "YRes", "Y" );
oLayout.EndRow( );

oLayout.AddRow( );
    oItem = oLayout.AddItem ( "Aspect", "Aspect Ratio" );
    oItem = oLayout.AddItem ( "LockAspect", "Lock Aspect Ratio" );
oLayout.EndRow( );


//    Logic
oLayout.Language = "JScript";
oLayout.Logic =    XRes_OnChanged.toString( )+
                     YRes_OnChanged.toString( )+
                     Aspect_OnChanged.toString( );

function XRes_OnChanged( )
{
    if  ( PPG.LockAspect.value )
    {
        PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
    }
    else
    {
        PPG.Aspect.value = PPG.XRes.value / PPG.YRes.value;
    }

}

function YRes_OnChanged( )
{
    if  ( PPG.LockAspect.value )
    {
        PPG.XRes.value = PPG.YRes.value * PPG.Aspect.value;
    }
    else
    {
        PPG.Aspect.value = PPG.XRes.value / PPG.YRes.value;
    }

}

function Aspect_OnChanged( )
{
    PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
}

InspectObj ( oProp );



赤字が変更もしくは追加部分です。 ついでに AddRow も追加してますが、これはまあ UI の見た目の話なのでどうでもいいです。

実行すると、
Ui_82
こうなります。
Lock Aspect Ratioオンの時は X が変われば Y も変わりその逆も同じく、というこれまでと全く同じ挙動になると思います。

一方、Lock Aspect Ratio がオフの時は、X や Y を変更すると、他方が変わるのではなく、Aspect が変更されるという挙動になっています。 前のバージョンでは Aspect ありきだったのが、このバージョンではロックを解除したときに限り、X と Y の値ありきで Aspect がそれに従うという状態になりました。

さらにさらに冗長な説明になりますが、 何が起こっているのかを X の値が変わった時を例に説明すると、

function XRes_OnChanged( )
{
    if  ( PPG.LockAspect.value )
    {
        PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
    }
    else
    {
        PPG.Aspect.value = PPG.XRes.value / PPG.YRes.value;
    }

}



これを日本語で言うと、


X の値が変わったときは以下を実行しろ
{
  もしも  LockAspectパラメータがオンなら、
  {
     Y の値は、 X / Aspect にしろ
  }
  さもなくば(つまりオフなら)
  {
    Aspect の値は、X / Y にしろ
  }

}

こういうことになりますね。


全くの蛇足ですが、if の行の

  if  ( PPG.LockAspect.value )

は、

  if  ( PPG.LockAspect.value == true )

と同じ意味になります。 true かどうかを調べるだけであれば、こういう省略した書き方ができるみたいです。 と言うよりも、 == true などの条件を省略したときは、常に結果が true かどうかを判別するという挙動になる、と言った方がいいかもしれません。 よくわからんまま、いつもこうしています。


これまた蛇足的になりますが、上のコードをよく見てみると、X が変更された時、Y が変更された時の両方で、else 以降は全く同じコードが書かれています。 同じコードなのであれば、1つの function にまとめてもいいわけで、

//    Logic
oLayout.Language = "JScript";
oLayout.Logic =    XRes_OnChanged.toString( )+
                     YRes_OnChanged.toString( )+
                     Aspect_OnChanged.toString( )+
                     AspectUpdate.toString( );

function XRes_OnChanged( )
{
    if ( PPG.LockAspect.value )
    {
        PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
    }
    else
    {
       AspectUpdate( );
    }
}

function YRes_OnChanged( )
{
    if ( PPG.LockAspect.value )
    {
        PPG.XRes.value = PPG.YRes.value * PPG.Aspect.value;
    }
    else
    {
        AspectUpdate( );
    }
}

function Aspect_OnChanged( )
{
    PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
}

function AspectUpdate( )
{
    PPG.Aspect.value = PPG.XRes.value / PPG.YRes.value;
}

InspectObj ( oProp );


このようにするのもアリかと思います。 AspectUpdate という function を新設して、else だった場合は両方ともこの AspectUpdate に飛ばしています。
ただ、前述のように、たとえ内容がダブっていても他の function に敢えて飛ばさない方が分かり易い場合も多く、微妙なところです。





次。
XSI の Scene Render Options を真似して、Lock Aspect Ratio がオンの時は Aspect の値はいじれないようにする、という仕様に変更してみます。

Logic 以降の部分に、以下のように追加します。

//    Logic
oLayout.Language = "JScript";
oLayout.Logic =    XRes_OnChanged.toString( )+
                     YRes_OnChanged.toString( )+
                     Aspect_OnChanged.toString( )+
                     AspectUpdate.toString( )+
                     LockAspect_OnChanged.toString( );

function XRes_OnChanged( )
{
    if ( PPG.LockAspect.value )
    {
        PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
    }
    else
    {
        AspectUpdate( );
    }
}
function YRes_OnChanged( )
{
    if ( PPG.LockAspect.value  )
    {
        PPG.XRes.value = PPG.YRes.value * PPG.Aspect.value;
    }
    else
    {
        AspectUpdate( );
    }
}

function Aspect_OnChanged( )
{
    PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
}

function AspectUpdate( )
{
    PPG.Aspect.value = PPG.XRes.value / PPG.YRes.value;
}

function LockAspect_OnChanged( )
{
    if ( PPG.LockAspect.value )
    {
        PPG.Aspect.ReadOnly = true;
    }
    else
    {
        PPG.Aspect.ReadOnly = false;
    }
}


InspectObj ( oProp );



LockAspect_OnChanged( ) が追加されました。チェックボックスのオンオフ変更を検知して、オンの時は Aspect パラメータの ReadOnly 属性をオンにする = つまりリードオンリーにする = つまり値を変更できなくする、 オフだった場合は ReadOnly 属性をオフにする = つまり値を変更できるようにする という挙動になります。

こいつを実行して、チェックボックスを数回オンオフ繰り返してみて下さい。
Ui_83
このように、 Lock Aspect Ratio のチェックがオンの時は Aspect がグレー表示になり、値が変更できなくなったのがわかると思います。


しかし。
現在のコードでは、オンオフ変更をした後は正しくグレー表示になるものの、PPG が立ち上がった時点ではチェックがオンであるにも関わらず Aspect はグレーになっていないという、本来の仕様に反した状態になってしまいます。

↓この状態ね。 PPG起動直後です。
Ui_82

これは、ReadOnly 属性をいじるための function LockAspect_OnChanged( ) という function が、LockAspect のチェックオンオフが変更された時のみ呼び出される、という書き方になっているからです。 PPG を起動した時点では値はまだ変更されていないため、この function は呼び出されず、結果、 Aspect の ReadOnly 属性はいじられないまま、という状態になっているのが原因だということです。


これを解消するにはいくつか方法があると思うのですが、ここでは、PPG が起動されたらまずは真っ先に Lock Aspect Ratio の状態を調べて Aspect の ReadOnly 属性を更新する、という方法を採ります。


//    Logic
oLayout.Language = "JScript";
oLayout.Logic =    XRes_OnChanged.toString( )+
                     YRes_OnChanged.toString( )+
                     Aspect_OnChanged.toString( )+
                     AspectUpdate.toString( )+
                     LockAspect_OnChanged.toString( )+
                     OnInit.toString( );

function XRes_OnChanged( )
{
    if ( PPG.LockAspect.value )
    {
        PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
    }
    else
    {
        AspectUpdate( );
    }
}

function YRes_OnChanged( )
{
    if ( PPG.LockAspect.value )
    {
        PPG.XRes.value = PPG.YRes.value * PPG.Aspect.value;
    }
    else
    {
        AspectUpdate( );
    }
}

function Aspect_OnChanged( )
{
    PPG.YRes.value = PPG.XRes.value / PPG.Aspect.value;
}

function AspectUpdate( )
{
    PPG.Aspect.value = PPG.XRes.value / PPG.YRes.value;
}

function LockAspect_OnChanged( )
{
    if ( PPG.LockAspect.value )
    {
        PPG.Aspect.ReadOnly = true;
    }
    else
    {
        PPG.Aspect.ReadOnly = false;
    }
}

function OnInit( )
{
    LockAspect_OnChanged( );
}


InspectObj ( oProp );



OnInit ( ) という function が追加されました。
この OnInit というのは特別な function で、PPG が起動されたとき(=イニシャライズされた時)に呼び出されるものです。これはもう、こういうものだと覚えてしまうしかない。
で、その中身は LockAspect_OnChanged( ) と書いてあるので、実際にはユーザが LockAspect の値を変更していないにも関わらず LockAspect_OnChanged( ) が呼び出され、ReadOnly 状態を更新しています。

つまり、
PPGの起動を検出 → OnInit に飛ばされ、→ OnInit から LockAspect_OnChanged( ) に飛ばされる

という順番で処理されています。これでめでたく、起動直後から Aspect のグレー表示が正しい状態になったはずです。





ごきげんよう。




.

| | コメント (0) | トラックバック (0)

2010年7月12日 (月)

友愛その7。

まだ続き。



数値パラメータでスライダを出さない方法やります。


var oProp = XSIFactory.CreateObject ( "CustomProperty" );
oProp.name = "hoge";

oProp.AddParameter2 ( "HageHage",  siInt4,  5 );
oProp.AddParameter2 ( "HegeHege",  siDouble,  4);

var oLayout, oItem;
oLayout = oProp.PPGLayout;

oItem = oLayout.AddItem ( "HageHage", "はげはげ" );
oItem.SetAttribute ( siUINoSlider, true );

oItem = oLayout.AddItem ( "HegeHege", "へげへげ" );

InspectObj ( oProp );



HageHage パラメータを AddItem の呪文で UI に登録する時に一度 oItem に代入しておき、その oItem に対して SetAttribute の呪文で、siUINoSlider は true だぜ、と指定しています。 これはその名の通り、 ノースライダ つまりスライダ無しにするかどうかのフラグを true にしているつまりオンにしています。

結果、スライダは消えます。
Ui_71

こうして はげはげ は手打ちフィールドのみの UI になりました。パッと見は文字列パラメータと見分けがつきませんが、AddParameter2 の呪文のところでこの はげはげ は siInt4(整数)だと宣言しているので、整数以外は入力できません。 文字列を打ってもはじかれます。

狭いエリアに AddRow を使ってパラメータをいっぱい詰め込んで場所の余裕がなくなると、どのみち XSI は勝手にスライダを消してしまいます。 また、スライダを消すか消さないかくらいの瀬戸際の時は、スライダは表示されていますが肝心のスライダ部分の面積は狭く、スライダの左側に必ず付く手打ちフィールドの方が大きくなってしまい、スライダとしての使い勝手は非常に悪くなってしまいます。 このようにどのみちスライダが意味をなさず、結局はスライダに付属する手打ちフィールドしか使わないのであれば、いっそのこと最初からスライダを無しにして手打ちのみにしてしまった方がスッキリした UI になることが多いと思います。 なのでこの siUINoSlider はよく使います。


ちなみにこの siUINoSlider ですが、もとからスライダを持ちようがないパラメータには効きません。例えば siString (文字列)のパラメータはスライダを付けようがないので、siUINoSlider が意味を成しません。ただ、意味を成さない場合でも、実行したときにエラーは出ないと思います。








ついでに UI 面積の節約をもうひとつやります。


var oProp = XSIFactory.CreateObject ( "CustomProperty" );
oProp.name = "hoge";

oProp.AddParameter2 ( "Ho",  siInt4,  1280 );
oProp.AddParameter2 ( "Ha",  siInt4,  720);
oProp.AddParameter2 ( "Fu",  siDouble,  1.7778);

var oLayout, oItem;
oLayout = oProp.PPGLayout;

oLayout.AddGroup( "" );
    oLayout.AddRow( );

        oItem = oLayout.AddItem ( "Ho", "ほ~" );
        oItem.SetAttribute ( siUINoSlider, true );

        oItem = oLayout.AddItem ( "Ha", "は~" );
        oItem.SetAttribute ( siUINoSlider, true );

        oItem = oLayout.AddItem ( "Fu", "ふ~" );
        oItem.SetAttribute ( siUINoSlider, true );

    oLayout.EndRow( );
oLayout.EndGroup( );



これを実行すると、
Ui_71b
こうなります。 数値入力フィールドがでかいために、パラメータ名の後で改行されちゃって、2行になってしまっています。 これ、1行でおさめたいじゃないですか。

なので、単純に考えると、入力フィールドの大きさを小さくすればいいわけですね。
なので前にも出てきた siUICX を指定してやればいいわけです。

以下のように赤字の部分を追加します。

oLayout.AddGroup( "" );
    oLayout.AddRow( );   

        oItem = oLayout.AddItem ( "Ho", "ほ~" );
        oItem.SetAttribute ( siUINoSlider, true );
       oItem.SetAttribute ( siUICX, 40 );

        oItem = oLayout.AddItem ( "Ha", "は~" );
        oItem.SetAttribute ( siUINoSlider, true );
       oItem.SetAttribute ( siUICX, 40 );

        oItem = oLayout.AddItem ( "Fu", "ふ~" );
        oItem.SetAttribute ( siUINoSlider, true );
        oItem.SetAttribute ( siUICX, 40 );

    oLayout.EndRow( );
oLayout.EndGroup( );



すると結果は、
Ui_72
こうなります。 

1行におさめることができました。でも、この赤線のスペースが無駄ですよね。 パラメータ名の後に続く破線を少なくして、その分数値入力フィールドを広げてあげたいわけです。特にパラメータ値のケタ数が大きくて入力フィールドにおさまらない時は、すぐに値を読み取れなくて不便ですからね。だからもうちょっと入力フィールドを広くしましょう。

単純に考えると、現在 siUICX = 40 としているので、この数値を少し大きくしてやればいいと思うじゃないですか。やってみましょう。


oLayout.AddGroup( "" );
    oLayout.AddRow( );   
   
        oItem = oLayout.AddItem ( "Ho", "ほ~" );
        oItem.SetAttribute ( siUINoSlider, true );
       oItem.SetAttribute ( siUICX, 50 );
   
        oItem = oLayout.AddItem ( "Ha", "は~" );
        oItem.SetAttribute ( siUINoSlider, true );
       oItem.SetAttribute ( siUICX, 50 );
   
        oItem = oLayout.AddItem ( "Fu", "ふ~" );
        oItem.SetAttribute ( siUINoSlider, true );
        oItem.SetAttribute ( siUICX, 50 );
   
    oLayout.EndRow( );
oLayout.EndGroup( );



すると、
Ui_73
こうなりますた。

だーかーらー 破線を少なくすればいいだけじゃんかよ まだスペース十分あるだろが なんで改行しちまうんだよゴルァ  モニタを破壊します。


XSI 様にとってはラベル(=パラメータの表示上の名前。この場合 「ほ~」 とか) を表示するための面積もある程度必要みたいです。 siUICX = 50 とした場合、この 50 という値は数値入力フィールド(スライダがある場合はスライダも含む)の大きさであり、ラベルは含みません。 50 という指定によって、ラベルの表示で消費されるべきスペースに入力フィールドが侵食を始めているようで、そうなると勝手に改行されてしまうように見えます。


こういう場合は、LabelMinPixels を試してみるとよいです。
以下のように、赤字の部分を追加します。


oLayout.AddGroup( "" );
    oLayout.AddRow( );   

        oItem = oLayout.AddItem ( "Ho", "ほ~" );
        oItem.SetAttribute ( siUINoSlider, true );
        oItem.SetAttribute ( siUICX, 50 );
       oItem.SetAttribute( siUILabelMinPixels, 10 );

        oItem = oLayout.AddItem ( "Ha", "は~" );
        oItem.SetAttribute ( siUINoSlider, true );
        oItem.SetAttribute ( siUICX, 50 );
       oItem.SetAttribute( siUILabelMinPixels, 10 );

        oItem = oLayout.AddItem ( "Fu", "ふ~" );
        oItem.SetAttribute ( siUINoSlider, true );
        oItem.SetAttribute ( siUICX, 50 );
        oItem.SetAttribute( siUILabelMinPixels, 10 );

    oLayout.EndRow( );
oLayout.EndGroup(
);



すると、
Ui_74
ようやく、siUICX = 50 を満たしつつ1行におさめることができますた。

siLabelMinPixels は、ラベルの表示に使うピクセル数の最小値を決めていることになると思います。 この場合 10 ピクセルと指定しているので、10 ピクセルはラベル用に確保し、その 10 ピクセルに侵食が始まったら改行されるがそれ以下なら改行されない、という状態を作り出したのだと解釈しています。合っているかな。どうかな。


ちなみにですね、マニュアルを見ると、siUILabelMinPixels を使うときは siUILabelPercentage とセットで使うべきだ、と書いてあるんですね。これの意味がよくわかりません。 siUILabelPercentage は、「そのパラメータのコントロール全体のうち、ラベル部分が何%占めるか」 を規定しているように読めます。つまりスライダや入力フィールドやラベルまで全て含めて、その1つのパラメータが占める UI 上の面積のうち、ラベル部分は何%食っているのか、という意味だと思うんですが、セットにしなければいけない理由がわかりません。現に、上の例ではセットにせず、siUILabelMinPixels 単体で使っていますが、目的は達成できています。 マニュアルに従い siUILabelPercentage まで書くとなると、

oLayout.AddGroup( "" );
    oLayout.AddRow( );   
   
        oItem = oLayout.AddItem ( "Ho", "ほ~" );
        oItem.SetAttribute ( siUINoSlider, true );
        oItem.SetAttribute ( siUICX, 50 );
       oItem.SetAttribute( siUILabelMinPixels, 10 );
      
oItem.SetAttribute( siUILabelPercentage, 5 );
   
        oItem = oLayout.AddItem ( "Ha", "は~" );
        oItem.SetAttribute ( siUINoSlider, true );
        oItem.SetAttribute ( siUICX, 50 );
       oItem.SetAttribute( siUILabelMinPixels, 10 );

        oItem.SetAttribute( siUILabelPercentage, 5 );
   
        oItem = oLayout.AddItem ( "Fu", "ふ~" );
        oItem.SetAttribute ( siUINoSlider, true );
        oItem.SetAttribute ( siUICX, 50 );
        oItem.SetAttribute( siUILabelMinPixels, 10 );

        oItem.SetAttribute( siUILabelPercentage, 5 );
   
    oLayout.EndRow( );
oLayout.EndGroup(
);



こんな風に更に青字の部分が追加になってしまいます。そして、実行した結果は、siUILabelPercentage を記述していない場合と全く変わらないように見えます。 おそらく特定の条件下で意味が出てくるんだと思いますが、俺の経験ではまだ出くわしてない・・・・かな? どうかな、忘れた。





うーむ、めんどくせえ。 UI のチューニングはめんどくさいですね。

このように、そのパラメータが UI 上にどのように描画されるのかチューニングすればするほど書かなければいけないコードが増えて、開発に時間がかかり、ソースは読みにくくなり、後で見た目を変えたくなった時に書き直す箇所が劇的に増加し、ストレスは溜まり、納期はせまり、雨はやまず、洗濯物は乾かず、クラッチプレートは焼け、車検に金がかかり、破壊されるモニタの数が増えます。 でも見た目が気に入った UI じゃないと使う気になれないので、ここはスクリプト書きの頑張りどころです。 そのスクリプトが実用上便利かどうかなんてもはや二の次ですし、ましてやそのスクリプトを利用しながら作ったCG映像のデキが良いかどうかなんて全くどうでもいいです。 大事なのは UI なんです。そうなんです。





ごきげんよう。




.

| | コメント (0) | トラックバック (0)

2010年7月10日 (土)

友愛その6。

続いてます。


AddGroup と AddRow の呪文を組み合わせれば、それなりに複雑な配置の UI にすることもできますよ、というだけのサンプルです。



var oProp = XSIFactory.CreateObject ( "CustomProperty" );
oProp.name = "hoge";
oProp.AddParameter2 ( "Icon",  siInt4,  1,  0,  10 );
oProp.AddParameter2 ( "Size",  siDouble,  1,  0.01,  100 );
oProp.AddParameter2 ( "Hage",  siDouble,  1,  0.01,  100 );

var oLayout = oProp.PPGLayout;

oLayout.AddRow( );

    oLayout.AddGroup( "", false, 20 );
        var oItem = oLayout.AddButton ( "Set",  "切吐" );
        oItem.SetAttribute( siUICX, 75 );
        oItem.SetAttribute( siUICY, 200 );
    oLayout.EndGroup( );

    oLayout.AddGroup( "", false, 80 );

        oLayout.AddGroup( );
            oLayout.AddGroup( );
                oLayout.AddGroup( );
                    var aItems = Array( "None", 0, "Null", 1, "Rings", 2,
                                        "Arrow Rings", 3, "Box", 4, "Circle", 5,
                                        "Square", 6, "Diamond", 7, "Pyramid", 8,
                                        "Pointed Box", 9, "Arrow", 10);
                    var oItem = oLayout.AddEnumControl ( "Icon", aItems, "愛根", siControlCombo );
                oLayout.EndGroup( );
             oLayout.EndGroup( );
             oLayout.AddItem ( "Size",  "砕図" );
        oLayout.EndGroup( );

        oLayout.AddRow( );
            oLayout.AddGroup( "", false, 25 );
                var oItem = oLayout.AddButton ( "Button2",  "牡丹 2" );
                oItem.SetAttribute( siUICX, 60 );
                oItem.SetAttribute( siUICY, 50 );
            oLayout.EndGroup( );
            oLayout.AddGroup( "", false, 40 );
                var oItem = oLayout.AddButton ( "Button3",  "牡丹 3" );
                oItem.SetAttribute( siUICX, 80 );
                oItem.SetAttribute( siUICY, 15 );
                var oItem = oLayout.AddButton ( "Button4",  "牡丹 4" );
                oItem.SetAttribute( siUICX, 80 );
                oItem.SetAttribute( siUICY, 15 );
                var oItem = oLayout.AddButton ( "Button5",  "牡丹 5" );
                oItem.SetAttribute( siUICX, 80 );
                oItem.SetAttribute( siUICY, 15 );
            oLayout.EndGroup( );
            oLayout.AddGroup( "", false, 35 );
                var oItem = oLayout.AddButton ( "Button6",  "牡丹 6" );
                oItem.SetAttribute( siUICX, 70 );
                oItem.SetAttribute( siUICY, 50 );
            oLayout.EndGroup( );
        oLayout.EndRow( );

        oLayout.AddRow( );
            var oItem = oLayout.AddButton ( "Button7",  "牡丹 7" );
            oItem.SetAttribute( siUICX, 40 );
            var oItem = oLayout.AddButton ( "Button8",  "牡丹 8" );
            oItem.SetAttribute( siUICX, 40 );
            oLayout.AddItem ( "Hage",  "はげはげ" );
        oLayout.EndRow( );

    oLayout.EndGroup( );

oLayout.EndRow( );

InspectObj ( oProp );


結果はこれ。
Ui_61


左側にウインドウのタテいっぱいの長さのボタンがあり、右側はタテ並びとヨコ並びが混ざってます。 このように、左右非対称とでも言うか、左はタテいっぱいだけど右は色々詰め込みたい、みたいな場合は、AddRow と AddGroup を駆使すればできますよ、というだけの話です。

具体的には、なんのことはない、不可視の Group を AddRow でヨコに並べているだけです。


上のソースで、赤字の部分は左側のボタン部分の不可視 Group です。

oLayout.AddGroup( "", false, 20 );

このように、ラベル無し、ワクの描画無し、占有面積は 20% と指定しています。
そして EndGroup で閉じられるまでには、ボタン 「切吐」 しか出てこない。

そして緑字の部分が、上記のボタン以外の全部です。

oLayout.AddGroup( "", false, 80 );
このように始まり、以下に残りの部分全てが記述されています。占有面積は 80%。

図で表すと、
Ui_62
このようになっているということですね。
左のタテ長ボタンで1つの不可視 Group を使い切っています(赤ワク)。
右はいっぱいパラメータやボタンが並んでいますが、すべて1つの不可視 Group の中に入っています(緑ワク)。

なので右の緑ワクの中でも全く同じことを入れ子構造にしているだけで、
Ui_63
こういう Group 構造になっているわけです。

緑の Group の中には3つの黄色 Group がある。
黄色 Group の真ん中のやつは、Group 内にさらに3つの青 Group がある。
そしてこの青Group 達は AddRow によってヨコ並びにさせられている。
3つの青 Group のうち中央のやつ(牡丹 3, 4, 5 )をタテ並びにしたかったので、こうしたわけです。

それに対して、3つめの黄色 Group (一番下)では、全てのボタンやパラメータが1列しかないので、さらに下位の Group を作る必要はない。なので AddRow でパラメータやボタンを直接並べただけ。Group を並べているのではないです。


うーむ、わかりますかね。わかりにくいですよね。でもまあ、そういうことなんです。




すき間を埋めて見やすくするために使うこともあります。
例えば、

var oProp = XSIFactory.CreateObject ( "CustomProperty" );
oProp.name = "hoge";
oProp.AddParameter2 ( "HogeHoge",  siBool,  1 );
oProp.AddParameter2 ( "HageHage",  siInt4,  5 );
oProp.AddParameter2 ( "HegeHege",  siString,  "へげへげ~");

var oLayout = oProp.PPGLayout;

oLayout.AddItem ( "HogeHoge", "ほげほげ" );

oLayout.AddRow( );
    oLayout.AddItem ( "HageHage", "はげはげ" );
    oLayout.AddItem ( "HegeHege", "へげへげ" );
oLayout.EndRow( );

InspectObj ( oProp );

これの結果は
Ui_64
こうなるんですが、最初の ほげほげ のチェックボックスが、パラメータ名からすごく離れていて見づらいじゃないですか。

なので、

var oProp = XSIFactory.CreateObject ( "CustomProperty" );
oProp.name = "hoge";
oProp.AddParameter2 ( "HogeHoge",  siBool,  1 );
oProp.AddParameter2 ( "HageHage",  siInt4,  5 );
oProp.AddParameter2 ( "HegeHege",  siString,  "へげへげ~");

var oLayout = oProp.PPGLayout;

oLayout.AddRow( );
    oLayout.AddGroup( "", false, 30 );
        oLayout.AddItem ( "HogeHoge", "ほげほげ" );
    oLayout.EndGroup( );
    oLayout.AddGroup( "", false, 70 );
    oLayout.EndGroup( );
oLayout.EndRow( );

oLayout.AddRow( );
    oLayout.AddItem ( "HageHage", "はげはげ" );
    oLayout.AddItem ( "HegeHege", "へげへげ" );
oLayout.EndRow( );

InspectObj ( oProp );

このようにすると、
Ui_65
チェックボックスが近くなって、ちょっと見やすくなりました。

赤字の Group と 緑字の Group を、青字の AddRow で並べてやっただけですね。
そして緑字の Group は、中身が何にもありません。AddRow の呪文の後、次の行ですぐに EndRow の呪文で閉じられてしまいます。空の Group です。

こういうことですね。
Ui_66
赤のワクは 30% 占有。 緑のワクは何も中身がなくて、かつ 70% 占有。
結果、ほげほげのパラメータ名とチェックボックスの位置が近くなったということです。


とかなんとか。
ごきげんよう。



.

| | コメント (0) | トラックバック (0)

2010年7月 9日 (金)

友愛その5。

まだ続きます。




ソース全文。

var oProp = XSIFactory.CreateObject ( "CustomProperty" );
oProp.name = "hoge";
oProp.AddParameter2 ( "Icon",  siInt4,  1,  0,  10 );
oProp.AddParameter2 ( "Size",  siDouble,  1,  0.01,  100 );

var oLayout = oProp.PPGLayout;
oLayout.AddGroup( );
    oLayout.AddRow( );

    
// 先に Array に 「表示名」, 「その表示名が選ばれた時にセットされる値」 のセットをブチ込んでおく
        var aItems = Array(    "None", 0, "Null", 1, "Rings", 2,
                                 "Arrow Rings", 3, "Box", 4, "Circle", 5,
                                 "Square", 6, "Diamond", 7, "Pyramid", 8,
                                 "Pointed Box", 9, "Arrow", 10);


      // AddEnumControl の呪文で、siControlCombo(ドロップダウンメニュー)を指定
        var oItem = oLayout.AddEnumControl ( "Icon", aItems, "愛根", siControlCombo );

        oLayout.AddItem ( "Size",  "砕図" );
    oLayout.EndRow( );
oLayout.EndGroup( );

oLayout.AddButton ( "Set",  "切吐" );

oLayout.Language = "JScript";
oLayout.Logic = Set_OnClicked.toString( );

function Set_OnClicked( )
{
    Logmessage ( "愛根 = " + PPG.Icon.value );
    Logmessage ( "砕図 = " + PPG.Size.value );
}

InspectObj ( oProp );


赤字の部分以外は前回までにやったことです。コメントはもはや冗長なので必要箇所以外は省略します。


これを実行すると、
Ui_51
「愛根」パラメータがドロップダウンメニューになりました。
以下に解説します。




ドロップダウンとかラジオボタンとかを出す場合でも、そのドロップダウンで制御されるパラメータ自体は普通に Int4(整数)だったり String(文字列)だったりと、通常となんら変わりません。 ただしそれを UI に追加する際、これまでのように AddItem の呪文を使うと値のタイプ(整数か文字列かなど)に沿った形で UI が構築されるのですが、今回のように AddEnumControl の呪文を使うと、値のタイプに関係なくドロップダウンメニューのような UI にすることができます。 例えば整数のパラメータは AddItem でそのまんまやるとスライダが出てくるわけですが、今回の愛根パラメータの例のように、整数パラメータであるにも関わらずスライダではなくドロップダウンメニューにするということが可能になります(※後に注釈あり)。


// 先に Array に 「ラベル」, 「そのラベルが選ばれた時にセットされる値」 のセットをブチ込んでおく
var aItems = Array(    "None", 0, "Null", 1, "Rings", 2,
                         "Arrow Rings", 3, "Box", 4, "Circle", 5,
                         "Square", 6, "Diamond", 7, "Pyramid", 8,
                         "Pointed Box", 9, "Arrow", 10);


まずは、ドロップダウンメニューに出現する項目を、「ラベル」 「そのラベルが選ばれた時にセットされる値」 の順で Array にブチ込んでおきます。 ラベルというのはただの表示名です。 上の例で言うと、"None" という表示名が選ばれたら 0 をセットしなさい、"Square" が選ばれたら 6 をセットしなさい、と指定していることになります。

改行を入れているのは、ソースコードを見易くするためだけです。この例のように10個とか項目があると、1行で書こうとするとエディタ上での作業時に横スクロールが必須になり、めんどくさいです。


そしてこの Array を、実際の UI に反映させます。

// AddEnumControl の呪文で、siControlCombo(ドロップダウンメニュー)を指定

var oItem = oLayout.AddEnumControl ( "Icon", aItems, "愛根", siControlCombo );



カッコの中のオプションですが、最初の "Icon" はパラメータ名です。 すでに AddParameter2 の呪文で作成してあるパラメータです。このパラメータに対して、お前はドロップダウンメニューの形で UI に出現しろよ、スライダじゃねえぞゴルァ と言っていることになります。

2つめの aItems は、さっき定義した Array です。

3つめは、ラベルです。ただの UI 上の表示名です。

4つ目に、UI のタイプを指定します。今回は siControlCombo を指定しているので、ドロップダウンメニュー(プルダウンメニュー、コンボメニュー)になります。


毎回繰り返しますが、AddEnumControl のヘルプを読めば全部書かれてますので。


※注釈
上で 「 AddItem の呪文を使うと値のタイプ(整数か文字列かなど)に沿った形で UI が構築される」 と書きましたが、実は AddItem の呪文の3つめのオプションで指定すれば、どれでも好きな UI のタイプにすることはできます。しかし、ドロップダウンメニューなどをやるには結局ラベルと値がセットで入った Array がないと意味がないので、結局は AddEnumControl を使うことになります。



siControlCombo の部分を書き換えると、他のタイプのコントロールにすることができます。種類はいっぱい用意されています。XSI 標準の何かのプロパティを見ても、例えば普通のスライダもあれば RGBAの色スライダもありますよね。Fcurve でコントロールするパラメータもあります。 でも今回のような AddEnumControl を使うようなパラメータの場合、実質的に意味があるのは、AddEnumControl のヘルプのページに出てくるドロップダウン、ラジオボタン、リストボックス、アイコンリスト、これだけじゃないでしょうかね。

じゃあ書き換えてみましょう。

siControlRadio
Ui_52
ラジオボタンです。 ドロップダウンとは違い、選択できる項目がクリックせずともひと目でわかるというのが長所だと思うんですが、場所を食うので俺はあまり使いません。
せめてボタンが横並びにできるといいのですが、タテ並びしかできません。ずっと昔から変わってないので、嘔吐デスクさんは今さらこんな部分改良するつもりはないと思います。ノルァ


siControlListBox
Ui_53
これはどういうときに便利なんだろう。 まだ使ったことありません。


siControlIcon
Ui_54
画像を使った項目です。プリセットをいっぱい用意してそこから選ぶような場合とかに分かり易くなると思います。 でもスクリプトをローカル環境だけでなく、他人にも渡す(配布する)とかの場合に画像の Path がうまく解決できるのか、わかりません。

ちなみにこの siControlIcon を使う場合はこういう書き方になります。

var aItems = Array( "C:\\XSIManIcons\\Front.bmp", 0,
                             "C:\\XSIManIcons\\Side.bmp", 1 );
var oItem = oLayout.AddEnumControl ( "Icon", aItems, "愛根", siControlIconList  );


Array のラベル(表示名)に相当する部分に画像の Path を記述しているだけですね。そしてその画像が選ばれた時にセットされる値を次に書いているわけで、これまでと仕組み自体はなんら変わりません。 ちなみに画像のフォーマットは .bmp 以外はダメだそうです。 なんで? 嘔吐デスクさん? Jpeg くらいは使えるようにしなさいよドルァ





次。 ボタンの大きさ変えます。
現在はボタンの大きさを指定してないので、自動的にラベル(ボタンの名前)の文字に合わせたサイズのボタンになっています。これだと小さくて見づらいので、ちょっとだけ大きくしてみましょう。AddButton の呪文を以下のように書き換えます。

oItem = oLayout.AddButton ( "Set",  "切吐" );
oItem.SetAttribute ( siUICX, 320 );
oItem.SetAttribute ( siUICY, 800 );


SetAttribute というのは、そのまんまですが、属性をセットする呪文です。 siUICX というのは、そのアイテムの横幅をピクセル数で指定します。 siUICY はタテの大きさです。 そして、この SetAttribute を使うために、最初にボタンを作成する AddButton の行で、oItem = というのを頭に付けて、結果を代入しています。結果というのは AddButton の呪文が実行された結果、つまりボタンそのものです。 ボタンそのものが oItem に代入され、次の行で oItem に対して SetAttribute の呪文を唱えているので、結果的にボタンの属性が変わるというしくみです。

実行してみると、
Ui_55
切吐ボタンが少しだけ大きくなりました。


ちなみにこの SetAttribute はボタンにだけ効くわけではなく、通常のパラメータにも効きます。 例えば砕図のスライダの幅を変えるのにも使えます。 この辺は、いずれ実際にそれが使えそうな例が出てきた場面でまた解説すると思います。解説しないかもしれません。知りません。

それと前から思っていたんですが、ボタンのラベルの文字をタテ書きにする方法、誰か知りませんかね。 AddButton の名前のところに "\r\n" のような改行コードを入れてみてもダメなんですよ。 それとボタンに画像を使うこともできないように見えるんですね。 上の IconList のように、ボタンの中に画像を入れる、あるいは画像そのものがボタンになっているという状態、できないんですかね。 知っている方がいらっしゃいましたら、どうか教えて下さい。



ごきげんよう。




.

| | コメント (6) | トラックバック (0)

2010年7月 7日 (水)

友愛その4。

続きます。

 1.シーンに実体を残さない PPG にします
 2.パラメータの値に変化があったらアクションを起こすようにします
 3.パラメータ をワクで囲みます
 4.パラメータを横並びにします
 5.パラメータの表示形式を、ドロップダウンメニューにします
 6.ボタン の大きさを変えます

前回で2まで終わりました。3以降行きましょう。



パラメータをワクで囲みます。ジャンルごとにワクで囲ったりすると、見た目がすっきりして良いです。 このワクのことを PPGLayout 的には Group と呼ぶようです。

PPGLayout.AddItem の呪文を唱える部分を、以下のように AddGroup と EndGroup で囲みます。

//    さっき作ったPPGLayout に、すでに作成してあるパラメータを登場させる
//    AddItem メソッドで、作成済みのパラメータ名、UI に登場させる時の名前を指定。

//    AddGroup と EndGroup で囲うことによって、ワクが描かれる
oLayout.AddGroup ( "ぬるん" );
  oLayout.AddItem ( "Icon", "愛根" );
  oLayout.AddItem ( "Size",  "砕図" );
oLayout.EndGroup ( );



すると、こんな風になります。
Ui_41

愛根と砕図のパラメータのまわりに、「ぬるん」 というラベルのついたワクが描かれました。 このワクが Group です。AddGroup の呪文によって召還します。その後は EndGroup の呪文を唱えてワクの終了を明示してあげないとダメです。

ちなみに2行目と3行目、つまり囲われた部分は行頭に空白を入れてありますが、別に必須ではありません。 AddGroup と EndGroup の影響範囲が視覚的にわかりやすいかな、と思ってやっているだけです。



次にこのように書くと、

oLayout.AddGroup ( "ぬるん" );
  oLayout.AddItem ( "Icon", "愛根" );

oLayout.EndGroup ( );

oLayout.AddGroup ( "ぬるる~ん" );
  oLayout.AddItem ( "Size",  "砕図" );
oLayout.EndGroup ( );



当然このようになります。
Ui_42


ボタンまで含めてももちろん桶です。

oLayout.AddGroup ( "ぬ ら り ひ ょ ー ん (゚∀゚)" );
  oLayout.AddItem ( "Icon", "愛根" );
  oLayout.AddItem ( "Size",  "砕図" );

  oLayout.AddButton ( "Set",  "切吐" );
oLayout.EndGroup ( );

Ui_43





次。横並び行きます。 現在は愛根と砕図それぞれが1行を占有しています。 この2つを1行の中に詰め込みます。

oLayout.AddGroup ( "ぬらぬらりん" );
  oLayout.AddRow ( );
    oLayout.AddItem ( "Icon", "愛根" );
    oLayout.AddItem ( "Size",  "砕図" );
  oLayout.EndRow ( );
oLayout.EndGroup ( );

また新しい呪文が出てきました。 AddRowEndRow です。 Row ってのは 「行」 のことですね。 AddRow と EndRow に囲まれた部分は、「1行」になるわけです。


こうなります。
Ui_44

AddGroup とは違い、AddRow も EndRow もカッコの中で指定できるオプションはありません。 この呪文は囲んだ部分を1行に押し込めろと言っているだけなので、オプションもクソもありません。



もちろん3つでも4つでも押し込められます。

oLayout.AddGroup ( "ぬらぬらりん" );
  oLayout.AddRow ( );
     oLayout.AddItem ( "Icon", "愛根" );
    oLayout.AddItem ( "Size",  "砕図" );

    oLayout.AddButton ( "Set",  "切吐" );
  oLayout.EndRow ( );
oLayout.EndGroup ( );

このように AddButton の呪文も Row の包囲網に入れてしまえば、
Ui_45
このように1行に3つ並びます。



しかし、よく見てみると、愛根と砕図のスライダが消えて、手打ちフィールドしかない状態になっています。 これは、ウインドウのサイズに対していっぱいいっぱいで、スライダを入れる余裕がないからです。こういうとき、XSI 様は勝手にスライダを省略します。

ウインドウをヨコに広げてやれば、スライダは復活します。
Ui_46

でも、ヨコに広げないとスライダが出てこないのも嫌ですよね。デフォルトのウインドウの広さのままスライダも入れたい場合は、ひとつのパラメータが占有する面積を制限してやったり、ラベル(パラメータの表示上の名前)を短くしたり、ラベルを表示オフにしたり、ラベルが表示されるために最低限必要な面積をデフォルトよりも小さくしてあげたり、色んな工夫が必要です。この工夫こそが UI 作りの肝であり、見易いかどうか、使い易いかどうかを決定付けることになります。たぶん。逆にスライダは要らねえ俺は手打ちだけがいいんだゴルァという場合も多いですね。

この辺の制御はいずれまとめて説明することになるでしょう。いずれまとめて説明しないことになるかも知れません。 わかりません。


もういっちょ例を挙げます。

AddGroup と AddRow の順番を入れ替えて、1行の中に複数の Group を入れることも、もちろんできます。

oLayout.AddRow ( );
  oLayout.AddGroup ( "スキスキダーリン" );
    oLayout.AddItem ( "Icon", "愛根" );
    oLayout.AddItem ( "Size",  "砕図" );

  oLayout.EndGroup ( );

  oLayout.AddGroup ( "頭はピッコピコよ" );

    oLayout.AddButton ( "Set",  "切吐" );

  oLayout.EndGroup ( );
oLayout.EndRow ( );
 

一番外側に、AddRow と EndRow の呪文があります。つまりその範囲内は全て1行に押し込められます。 そしてその範囲内には、2つの AddGroup - EndGroup のセットがあります。

こうなります。
Ui_47
Group が2つ、横並びになりました。



今度は、AddGroup で指定するラベル(Gorup の名前)を "" という風にクォーテーション2連発=つまり中身の文字列が無い という状態にしてみます。

oLayout.AddRow ( );
  oLayout.AddGroup ( "" );
    oLayout.AddItem ( "Icon", "愛根" );
    oLayout.AddItem ( "Size",  "砕図" );

  oLayout.EndGroup ( );
      
  oLayout.AddGroup ( "" );

    oLayout.AddButton ( "Set",  "切吐" );

  oLayout.EndGroup ( );
oLayout.EndRow ( );


こうなります。
Ui_48b
ワクのみが出現し、名前の無い Group になりました。



次は、AddGroup のカッコの中で、false というオプションを入れてみます。

oLayout.AddRow ( );

  oLayout.AddGroup ( "",false );
    oLayout.AddItem ( "Icon", "愛根" );
    oLayout.AddItem ( "Size",  "砕図" );

  oLayout.EndGroup ( );
      
  oLayout.AddGroup ( "",false );

    oLayout.AddButton ( "Set",  "切吐" );

  oLayout.EndGroup ( );
oLayout.EndRow ( );



すると、
Ui_48c
このように、ワクも無くなりました。

AddGroup のオプションの1つめは Group の名前ですが、2つめのオプションは 「ワクを描くか描かないか」 です。 何も指定しなければデフォルトの true が適用されて、ワクが描かれます。 false を指定してやると、ワクを描きません。

ただし、AddGroup ( "ぬぬぬぬるん" , false ) のように、「 Group のラベルは指定しているけど、ワクは描かないでね」 という指定はできません。 できませんと言うより、false が無視されます。 ラベルを指定した時点で必ずワクは描かれてしまいます。 ワク無しにしたければ、ラベルも無しにしなければいけないということです。 仕様ですねこれは。



今度は、AddGroup の3つめのオプションを指定してやります。

oLayout.AddRow ( );
  oLayout.AddGroup ( "どうしてダーリン あなたを" , true, 70);
    oLayout.AddItem ( "Icon", "愛根" );
    oLayout.AddItem ( "Size",  "砕図" );

  oLayout.EndGroup ( );
      
  oLayout.AddGroup ( "信じてたのに", true, 30 );

    oLayout.AddButton ( "Set",  "切吐" );

  oLayout.EndGroup ( );
oLayout.EndRow ( );

それぞれの Group で 7030 を指定しています。

こうなります。
Ui_49

3つめのオプションは、その Group がウインドウの横幅のうち何%を占めるかという指定です。 この場合、最初の Group は 70% 使え、2つ目の Group は 30% 使えと指定しているので、左側の Group の方がより広く、右側の Gorup は狭くなっているのがわかると思います。

この辺は、AddGroup のヘルプを見れば全部載ってます。



このように、色んなオプションを駆使して望みの UI を作っていくのであります。これこそがスクリプト書きの楽しみです。 メインルーチンなんてどうでもいいです。




それではごきげんよう。

あれっ また最後まで行けなかったよ?

ごきげんよう。





.

| | コメント (0) | トラックバック (0)

2010年7月 6日 (火)

友愛その3。

さらに続き。

 1.シーンに実体を残さない PPG にします
 2.パラメータの値に変化があったらアクションを起こすようにします
 3.パラメータをワクで囲みます
 4.パラメータを横並びにします
 5.パラメータの表示形式を、ドロップダウンメニューにします
 6.ボタンの大きさを変えます


まずは1と2からやりましょうかね。
前回のソースをちょこっと改造しています。

//    CustomProperty を 作るが Scene には実体を残さない
var oProp = XSIFactory.CreateObject ( "CustomProperty" );
oProp.name = "hoge";


//    oProp(さっき作った CustomProperty)に、パラメータを追加する
oProp.AddParameter2 ( "Icon",  siInt4,  1,  0,  10 );
oProp.AddParameter2 ( "Size",  siDouble,  1,  0.01,  100 );


//    PPGLayoutを作る

var oLayout = oProp.PPGLayout;

//    さっき作ったPPGLayout に、すでに作成してあるパラメータを登場させる
//    AddItem メソッドで、作成済みのパラメータ名、UI に登場させる時の名前を指定。

oLayout.AddItem ( "Icon", "愛根" );
oLayout.AddItem ( "Size",  "砕図" );


//    ボタンを作るときは AddButton メソッドを使う
oLayout.AddButton ( "Set",  "切吐" );

//  言語を指定しないといけない
oLayout.Language = "JScript";

//  ロジック設定

oLayout.Logic = Set_OnClicked.toString( ) +
                      Icon_OnChanged.toString( )+
                 Size_OnChanged.toString( );


//  クリックされた時に実行される Function

function Set_OnClicked( )
{
    Logmessage ( "愛根 = " + PPG.Icon.value );
    Logmessage ( "砕図 = " + PPG.Size.value );
}


//    Icon /Size パラメータが変更された時に実行される Function
function Icon_OnChanged( )
{
    Logmessage ( "愛根が変わりますた = " + PPG.Icon.value ); 
}
function Size_OnChanged( )
{
    Logmessage ( "砕図が変わりますた = " + PPG.Size.value ); 
}


//  表示しておしまい。
InspectObj ( oProp );



例によって赤い文字が変更部分です。
まずは実行してみましょう。

同じ PPG が表示されましたよね。
でも Explorer を見ても、シーンルートにプロパティは存在しないですよね。
愛根と砕図のスライダをいじると、結果がスクリプトエディタ内にログされますよね。
Ui_31

順に行きましょう。



まず最初のやつ。

//    CustomProperty を 作るが Scene には実体を残さない
var oProp = XSIFactory.CreateObject ( "CustomProperty" );
oProp.name = "hoge";


前回までは、ActiveSceneRoot.AddProperty の呪文を使ってシーンルートにプロパティを作っていました。実行するたびに新しいプロパティが作られるというものでした。
でも今回は、シーンに何も残さない形でプロパティを作っています

XSIFactory オブジェクトCreateObject の呪文を唱えてカスタムプロパティを作っているんですが、まあ、もう細かい説明は抜きにして暗記してしまえばいいんです。そうなんです。なぜならば、説明しようにも、俺自身が丸暗記しているだけなんで説明しようがないんです。そうなんです。

CreateObject の呪文は TypeName を与えてやることによって Object を生成するものですが、それゆえに汎用です。カスタムプロパティを作るときだけに使う呪文ではありません。最も一般的には XSI Collection を作る時などによく使います。 ともかく、汎用なので細かいオプションは存在せず、1行の中で名前を決めたりとかはできません。 なのでわざわざ次の行で oProp の名前は "hoge" だぞと指定しています。たぶんこれ以外に名前を決める方法はありません。たぶん。



次にロジックの設定の部分です。

//  ロジック設定

oLayout.Logic = Set_OnClicked.toString( ) +
                      Icon_OnChanged.toString( )+
                 Size_OnChanged.toString( );


Icon_OnChanged というものと、Size_OnChanged というものが追加されています。
この Icon や Size の部分は、AddParameter2 の呪文であらかじめパラメータを作ってあるので、その名前と完全に一致していないといけません。
そして OnChanged に続きます。 ボタンのクリックを検知する場合は OnClicked だったけど、パラメータの変化を検知するには OnChanged だと覚えてしまえばよろしいです。

ちなみに、今回の書き方のように、+ の後に改行を入れて、複数行に分けて記述することも可能です。改行コードを入れているわけではなく、スクリプトエディタ上で読みやすいように改行してインデントしているだけです。 ロジックをすんげぇ大量に書かなければいけない場合もあるので、1行で書いていたらひじょーーーーーに横長なドキュメントになってしまい、横スクロールで進めども進めども右の端が見えて来ず、またマウスの操作をちょっと間違ったりするとカーソル位置があっという間に先頭の行に戻されたりしてモニタを破壊します。 なのでガンガン改行して、タブスペースなどでインデントして読みやすくするべきだと思います。


で、このロジックに呼応するファンクションを書いてやればいいということです。

//    Icon /Size パラメータが変更された時に実行される Function
function Icon_OnChanged( )
{
    Logmessage ( "愛根が変わりますた = " + PPG.Icon.value ); 
}
function Size_OnChanged( )
{
    Logmessage ( "砕図が変わりますた = " + PPG.Size.value ); 
}

これで、スライダで値をいじった瞬間に何かが実行されるというスクリプトになりました。インタラクティブにシーンのブツに変更を加えるようなツールにもなり得るし、PPG 内の値さえもこれをトリガにして変更することができます。例えば X 解像度のパラメータを変えた瞬間にY解像度も一定の比率を保って自動的に変化する、なんてのがよくあるパターンです。

UI 構築とは関係ないですが、さすがに値を Logmessage するだけじゃつまらんので、ちょっとマトモっぽくしてみましょう。ロジックのファンクションの部分を以下のように書き換えてみます。


//    Icon /Size パラメータが変更された時に実行される Function
function Icon_OnChanged( )
{
    for ( var i = 0; i < Selection.count; i++ )
    {
        if ( Selection( i ).type == siNullPrimType )
        {
            Selection( i ).primary_icon.value = PPG.Icon.value;
        }
    }
}

function Size_OnChanged( )
{
    for ( var i = 0; i < Selection.count; i++ )
    {
        if ( Selection( i ).type == siNullPrimType )
        {
            Selection( i ).size.value = PPG.Size.value;
        }
    }
}



UI の話ではないので超簡単にだけ説明しておくと、

 for ループで Selection(現在選択中のオブジェクト)の数(count)だけ繰り返す
 選択中のものが null オブジェクトかどうかを調べる
 null だった場合は Icon と Size を PPG で指定した値に書き換える
 null じゃなかった場合の記述は無いので、null 以外のものが含まれていてもスルーされる
 何も選択されてなかったら count は 0 なので 0 回のループが発生する=つまりループが発生しない=何も起こらない

以上です。
適当に null を数個出して、適当に選んで、スクリプトを実行してみてください。

Ui_32
おおっ 選択してる null の Icon と Size がインタラクティブに変わる!
なんだかそれっぽい! (;゚∀゚)


と思ったのもつかの間、こんなもの、null を複数選んで Enter 押して、出てきたマルチモードの PPG で普通に Icon と Size いじってやりゃそれで済みます。わざわざスクリプトでやるようなことではありません。相変わらず役に立ちません。





と、ここまで書いてめんどくさくなりました。
それではごきげんよう。
1と2しか終わってねえじゃねえか。
気にしない。
ごきげんよう。





.

| | コメント (0) | トラックバック (0)

2010年7月 5日 (月)

アエ~。

ちょっと前に、毛唐どもからビールを巻き上げようと思って某所に置いてみたツールなんですが、ビール1滴ももらえてません Orz

ガイジンさんビールよこせゴルァ


まあいいや。自分で発泡酒買って呑みますよ(´・ω・`)ショボンダマ




アエとXSI

http://homepage3.nifty.com/jjj/XSIFiles/Plugin/JJJ_XSI_Plugins.html

AfterEffects のレイヤのトランスフォーム情報を XSI に持って来るツールです。AE からのエキスポータスクリプトと、XSI でのインポータスクリプトのセットです。AE で、例えばレイヤや null をアニメーションさせて、それをテキストファイルに書き出し、XSI にインポートすると、こんな風に null で再現されます。


想定している使い方は、

 AE で 2D トラッキングしたデータを XSI に持っていく
 AE のモーションスケッチやウィグラーや wiggle の結果を XSI に持っていく
 フェイシャルキャプチャしてAEでトラッキング後に XSI でデフォーマとして使う

とかなんとか。 うん、これは想定じゃないな。妄想ですただの妄想。そんなに使えるツールじゃありません。もろもろ制限などありますので、まあ詳しくはダウンロードページで。


ひとつの可能性として、ワークフローのサンプルビデオをアップしました。

AEtoXSI Demo from junki the junkie on Vimeo.

手持ちカメラで撮影した動画を AE でトラッキングして、そのデータをこのツールを使って XSI にブチ込み、XSI 上でのカメラの手ブレに使えないかな、という実験です。

まずテキトーに何かをトラッキングします。この場合、ポスターを壁に貼っつけているマグネット2個が常に入るように撮りました。
次にこの2個のマグネットを AE でトラッキングし、AE のヌルなどのポジションに突っ込んでおきます。
次にこれを アエと何か を使ってエキスポートし、XSI では アエとXSI を使ってインポートします。
生成された2つの null に2ポイントコンストレインで着いていく3つ目の null を作成します。
そしてこの3つ目のヌルにカメラを親子付けしたり、2ポイントコンストレインから得られる傾きをカメラのロールにエクスプレッションで結んだり、揺れの大きさを調整するために null の階層まるごとスケールしたりして、なんとなく完成。

このツールがマッチムーブに使えるぞと言っているわけでは決してありません。 単に、実写で撮った手ブレ感などを XSI で生かせないかなーという実験です。




このツールに一番欠けているのは、AE の3Dカメラを XSI 上で再現する機能ですね。AEカメラのトランスフォーム情報は持っていけますが、カメラとして必要な情報(FOVその他)は持っていけません。今後そこをどうにかしようと思ってます。

それと逆向きのツール、つまり XSI とアエ を作らねばいかん。 XSI -> AE のツールはすでにいくつか世の中に存在してますが、もっと自分に都合のいいものが欲しいですからね。っていうか、実は原型はもう作ってあります。XSI でカメラを決め、オブジェクトの位置を決め、AE に カメラ情報一式とオブジェクトのポジション情報だけ持っていって AE でそのオブジェクトの上に画像をマッピングするような使い方で、仕事にも使えました。カメラアニメーション対応、カメラの Optical Center Shift にも対応しているあたりがミソになっています。しかしもう少し整理したり機能追加したりしたくて、現在半端な状態です。


ちなみに AE からのエキスポータを書いたのは俺ではなく、curryegg という助平男です。カレーと焼きそば以外食えないという助平さです。この助平男を女で釣ってエキスポータ書かせました。この助平男のブログも見てあげて下さい。AE関係の助平なツールが色々アップされています。彼にもビールや焼きそばやカレーやキャバクラをおごってあげて欲しいです。 このツールに関しては、彼はまだ自分のブログ上では正式に公開してないようですが、そんな彼の意向は無視してこちらでは発射してしまいます。世間の荒波に出てからの方が発展するものです。



エキスポータの方は、汎用的に使えると思うんですよね。もともとは「俺様がこういう風にインポータを書くから、キミはそれに合わせてくれたまえ」という感じで、俺様に都合がいいようなテキストファイルを書き出すように作ってもらったんだけど、結果的にまずまず汎用的で簡単なフォーマットになっていると思うので、フォーマットの仕様さえわかれば XSI 以外のソフトウェアへのインポータを作るのはそんなに難しくないはずです。 Max や Maya へのインポータを書きたいなんていうお方がいらっしゃいましたら、俺か curryegg に問い合わせてもらえれば、ビール1杯でフォーマットの仕様をお教えいたします。 とか言って、実は見りゃわかるくらいの簡単なフォーマットですよ。



AE カメラ → XSI と XSI → AE なツールは、今後仕事が少し忙しくなりそうなので、しばらく進められないかもしれません。うーむ、まだ熱いうちにイッキにやってしまいたかったんだが・・・・。



アエ~。






.

| | コメント (0) | トラックバック (1)

2010年7月 3日 (土)

友愛その2。

その1からの続き。
ボタンを押したときに何かの処理が実行されるようにします。


前回のコードにちょっと書き足し。

//    CustomProperty を SceneRoot に作る。Branch はオフ。 
//    名前は "hoge"。 作られた CustomPropety は oProp に代入される

var oProp = ActiveSceneRoot.AddProperty ( "CustomProperty",  false,  "hoge" );

//    oProp(さっき作った CustomProperty)に、パラメータを追加する
oProp.AddParameter2 ( "Icon",  siInt4,  1,  0,  10 );
oProp.AddParameter2 ( "Size",  siDouble,  1,  0.01,  100 );

//    PPGLayoutを作る
var oLayout = oProp.PPGLayout;

//    さっき作ったPPGLayout に、すでに作成してあるパラメータを登場させる
//    AddItem メソッドで、作成済みのパラメータ名、UI に登場させる時の名前を指定

oLayout.AddItem ( "Icon", "愛根" );
oLayout.AddItem ( "Size",  "砕図" );

//    ボタンを作るときは AddButton メソッドを使う
oLayout.AddButton ( "Set",  "切吐" );

//  言語を指定しないといけない
oLayout.Language = "JScript";

//  ロジック設定
oLayout.Logic = Set_OnClicked.toString( );

//  クリックされた時に実行される Function
function Set_OnClicked( )
{
    Logmessage ( "愛根 = " + PPG.Icon.value );
    Logmessage ( "砕図 = " + PPG.Size.value );
}

//  表示しておしまい。
InspectObj ( oProp );




赤字の部分が書き足された部分です。

「ボタンが押された」 「パラメータの値が変わった」 とかいうのを検出するためには Logic とかいうものを設定しないといけないようです。 実は俺もよくわかっておらず、丸暗記でやってるだけです。


まずは言語を明示しないといけないそうです。
oLayout.Language = "JScript";

こういうもんだと覚えてしまえばよろしい。


次にボタンの設定。
oLayout.Logic = Set_OnClicked.toString( );

この Set というのは、先に AddButton メソッドで
oLayout.AddButton( "Set",  "切吐" );

と指定しているので、それと一致した名前でなくてはいけません。

そして アンダーバー でつなぎ、 OnClicked と書きます。そして続けて .toString( ) と書きます。理屈はわかりません。こういうもんなんです。

そして、そのボタンが押された時に実際に呼び出される function を書きます。
function Set_OnClicked( )
{
    Logmessage ("愛根 = " + PPG.Icon.value);
    Logmessage ("砕図 = " + PPG.Size.value);
}

function の名前は、Logic の所で設定した名前と完全に一致していなくてはいけません。

function の { } の間に、やりたい処理を何でも書けば良いです。
今回の場合は、PPG 内の Icon パラメータの値と Size パラメータの値を単にログしているだけです。

ここで、
PPG.パラメータ名.value

という言い方が出来ます。
PPG はいわば自分自身を表します。 自分自身、つまりこのカスタムプロパティ内にある Icon という名前のパラメータの値 (Value)にアクセスしてるわけですね。

Ui_20
ボタンを押すと、ログされますた。
基本的には以上。




もうちょっとやりますか。では、Set_OnClicked( )ファンクションの部分を、以下のように書き換えてみましょう。

function Set_OnClicked( )
{
    var oNull = ActiveSceneRoot.AddNull( 'NullNull' );
    oNull.primary_icon.value = PPG.Icon.value;
    oNull.size.value = PPG.Size.value;
    InspectObj ( oNull );
    SelectObj ( oNull );
}


まず、シーンルートに AddNull の呪文を使って null を1つ取り出しています。カッコの中のオプションは null の名前です。そして結果として出てきた null は oNull に代入されます。

次に、null の primary_icon の値を、PPG の Icon で設定した値で上書きしています。
次に、null の size の値を、PPG の Size の値で上書きしています。

そして InspectObj コマンドでその null の PPG を表示し、
最後に SelectObj コマンドで null を選択状態にして終わりです。

つまり、アイコンの種類とサイズをあらかじめ決めてから null を取り出すというツールになりました。
Ui_21
なんの役にも立ちません。


ところで、function の中から別の function を呼び出すことはできるのでしょうか。
以下のように書き換えてみます。

oLayout.Language = "JScript";
oLayout.Logic = Set_OnClicked.toString( );

function Set_OnClicked( )
{
    var oNull = ActiveSceneRoot.AddNull ( 'NullNull' );
    oNull.primary_icon.value = PPG.Icon.value;
    oNull.size.value = PPG.Size.value;
    InspectObj ( oNull );
    SelectObj ( oNull );
    HogeHoge( );
}

function HogeHoge( )
{
    Logmessage ( "ほげほげ~" );
}


赤字が追加部分です。
最初の function の中で、最後に HogeHoge ファンクションを呼び出しています。
続く HogeHoge ファンクションでは、 ほげほげ~ とログするコマンドが書かれています。
つまり、PPG のボタンを押すと、ひと通り null の処理をやった後、最後に ほげほげ~ とログされるスクリプトになったはずです。

しかし実行してみると、
Ui_22
このようにエラーで止まってしまいます。 ほげほげ~ はログされません。

どうやら、HogeHoge ファンクションも、あからじめ Logic の所で設定しておかねばならないようです。 以下のように書き換えます。

oLayout.Language = "JScript";
oLayout.Logic = Set_OnClicked.toString( )
+ HogeHoge.toString( );

function Set_OnClicked( )
{
    var oNull = ActiveSceneRoot.AddNull ( 'NullNull' );
    oNull.primary_icon.value = PPG.Icon.value;
    oNull.size.value = PPG.Size.value;
    InspectObj ( oNull );
    SelectObj ( oNull );
    HogeHoge( );
}

function HogeHoge( )
{
    Logmessage ( "ほげほげ~" );
}


赤字が追加部分です。 Logic の行のところに、HogeHoge ファンクションも書いておきます。 + (プラス符号)でつなぎます。 toString メソッドを使っているので結果は文字列に変換され、そして文字列同士をつなぐのに + を使っている、という考え方なのでしょう。たぶん。

すると、
Ui_23
今度はエラーではなく、ちゃんと ほげほげ~ がログされました。

これ、こういうもんなんですかね? function は全部 Logic の所に書いておかなければいけないの? めんどくさいよね? 俺もよくわかってません。わかる人、教えて下さい。


ちなみに Self-Installing 形式のプラグインにすると、Logic の設定は要らないし、また、スコープって言うんですか、function の有効範囲というか? 違う? ともかく、どこに function を書いてもOKになるので、実にラクです。 この辺はまた別の機会に書くかもしれません。書かないかもしれません。知りません。

それと、この 「ボタン押されたら」 とか 「値が変わったら」 の部分って、スクリプト言語によってだいぶ書き方が違ったような気がします。 なので、JScript 以外では全く別の表記になると思うので、この記事は参考にならないと思います。

思えばその昔、VBScript でこれを書いていて、function を全部クォーテーションで囲まなければいけないとかなんとか、すんごく不便だったのでブチ切れて、それをきっかけに JScript に移行していったという経緯を思い出しました。これに関しては JScript の方が VBScript より 1024倍ラクです。 Python はどうなのでしょう?




次回は何しましょうか。
「ボタンが押されたら」 だけじゃなくて 「値が変わったら」 もやりましょうかね。
あとは、Icon パラメータが現在はスライダが付いた整数ですが、null の PPG みたいにドロップダウンメニューにしたいですよね。 あとは、ボタンの大きさでも変えてみるか。


そんな程度のことしかしません。



次回以降があれば、の話です。
わかりません。


.

| | コメント (0) | トラックバック (0)

2010年7月 2日 (金)

友愛その1。

まったく気まぐれです。
いつかこんなの書いてみようと漠然と思っていただけです。
大してわかっているわけでもないのに。
何をエラソーに。


XSI のスクリプトで、独自の PPG を作る方法。
カスタムプロパティの UI の作り方ということです。
ユーアイです。ユーザーインターフェース。
XSI 的には PPGLayout と言うらしい。
簡単なスクリプトは書けるけど、UI にまとめる方法がわからない、という人には参考になる可能性があります。
簡単なことしか書きません。っていうかそれしか書けません。
スクリプトをガンガンやるお方は、どうかスルーして下さい。
とうか、間違い勘違いを指摘して下さいマジで。
JScript しか知りません。
やっぱ今時 Python やるべきだよねえ?
知りません。







まずはいきなりソースコード全文。

//    CustomProperty を SceneRoot に作る。Branch はオフ。 
//    名前は "hoge"。 作られた CustomPropety は oProp に代入される

var oProp = ActiveSceneRoot.AddProperty("CustomProperty",  false,  "hoge");

//    oProp(さっき作った CustomProperty)に、パラメータを追加する
oProp.AddParameter2("Icon",  siInt4,  1,  0,  10);
oProp.AddParameter2("Size",  siDouble,  1,  0.01,  100);


//    PPGLayoutを作る
var oLayout = oProp.PPGLayout;

//    さっき作ったPPGLayout に、すでに作成してあるパラメータを登場させる
//    AddItem メソッドで、作成済みのパラメータ名、UI に登場させる時の名前を指定

oLayout.AddItem("Icon",  "愛根");
oLayout.AddItem("Size",  "砕図");


//    ボタンを作るときは AddButton メソッドを使う
oLayout.AddButton("Set",  "切吐");

//  表示しておしまい。
InspectObj(oProp); 



これを実行すると、

Ui_11

こんな感じになりますよね。なるんです。

Explorer を見ると、Scene Root に hoge という Custom Property が出来ています。
hoge という Custom Property の PPG が表示されています。
愛根というパラメータとそのスライダがあります。
砕図というパラメータとそのスライダがあります。
切吐というボタンがあります。


これを最初から説明していきます。って言ってもチョー簡単です。コード内のコメントを読むだけでもまあわかるでしょう。 //(スラッシュ2発)で始まる行はただのコメントで、スクリプトエンジンに無視されます。 人に説明する時や後で自分がわけからなくならないように、注釈を付けているということです。





まず最初、

var oProp = ActiveSceneRoot.AddProperty("CustomProperty",  false,  "hoge");

これですが、まずはイコールの右側だけ見て下さい。「シーンルートにカスタムプロパティを作りやがれ。ブランチはオフだ。カスタムプロパティの名前は hoge にしねえと殺すぞゴルァ。」 と言っていることになります。ひとつひとつを以下に解説します。

XSI のシーンルートは、スクリプト内では ActiveSceneRoot という言い方で表せます。これからやる処理は、シーンルートに対してやるんだよと言っていることになります。

次にドットでつないで AddProperty というのが出てきますが、 これは指定したブツに Property を与えろという呪文です。 この場合シーンルートに Property を与えろと言っていることになります。続くカッコの中身でその Property の詳細を指定しています。 XSI のスクリプトはだいたいこういう構造です。

最初は "CustomProperty" となっています。 さっき「シーンルートに Property を与えろ」と言ったわけですが、いったいどんな Property なのかを指定してやらないことには、XSI 様が働いてくれません。 XSI の Property って言ったら、例えば Visibility も Property ですし Rendermap も Property です。Explorer で白黒のグラデーションアイコンが付いているものは何でも Property です。 今回の場合、独自の Property を作るわけなので、CustomProperty  と指定してやります。 

次に false と出てきますが、AddProperty の場合これはブランチフラグです。与える Property を、シーンルートだけに与えるのか、シーンルートにぶら下がる子供オブジェクトたちにもブランチで与えるのかを指定します。false の時はブランチではなく、シーンルートのみに与えられます。true にするとブランチとして与えられます。

次に "hoge" と出てきますが、これは Property の名前です。独自 Property なので好きに名前を決められます。

以上が AddPropety の呪文の解説です。

次にイコールの左側を見ると、var oProp = となっています。これは、AddProperty の呪文の結果として作られた Custom Property を、oProp という箱の中に入れておけという意味です。 この後何度もこの Custom Property を呼び出すので、oProp に代入しておいて、以後は oProp と言ったらこの Custom Property のことを指しているんだからな、間違えんじゃねえぞドルァ と言っていることになります。


以上がこの行の説明ですが、こんなものは、スクリプトエディタ内で AddProperty の部分を選択状態にし、F1 キーを押せばヘルプ様が降臨して全部教えてくれます。 とにかくヘルプを読むことが近道です。各ページにはサンプルのスクリプトがあるので(2011 など最近のバージョンではサンプルのページへのリンクが貼られています)、まずはサンプルをコピペするのがいいと思います。 そして、あんまり深く考えずに、これはもうこういうもんだとして覚えてしまうのが手っ取り早いです。 俺もあんまり理解してないまま結果オーライでやってきてしまっています。 弊害もありますがまあそれはまた別の話。


さて、試しにこの行だけをペーストして実行してみて下さい。
シーンルートに hoge ができますよね?
hoge をクリックして PPG を表示させてみましょう。
Ui_12
こんな風に、中身が空っぽの PPG が表示されましたよね。
そりゃそうです。まだパラメータを作ってないんですから。
hoge は単にパラメータをまとめて入れておく入れ物であって、パラメータ自体を作ってなければ空っぽに決まってます。ガレージを作ったけど車はまだ買ってないという状態です。あるいは炊飯ジャーを新調したけどそこで金を使い果たして米が買えないような状態です。違いますか。まあいいや。なので次にパラメータを作ります。



oProp.AddParameter2("Icon",  siInt4,  1,  0,  10);
oProp.AddParameter2("Size",  siDouble,  1,  0.01,  100);

繰り返しになりますが、さっき oProp を指定してあるので、ここからはもう、oProp という言い方で Custom Property を表せます。oProp に対して以下の処理をしろよ、と言っているということです。

そして AddParameter2 という呪文を使ってパラメータを作っています。

最初の "Icon" というのは、このパラメータの名前です。自分で決められます。

次の siInt4 というのは、この Icon というパラメータは整数だぜ、と指定しています。
パラメータと言っても色んな種類があるわけで、整数だったり小数だったり、文字だったり、オンオフしか値を持てないブールというものだったりするわけです。テキトーに何かのオブジェクトの PPG を出してみればわかるでしょう。オブジェクトの Name(名前)というパラメータは、文字列です。でも、例えば sphere の分割数は数値です。数値は数値でも、整数というタイプの数値になります。 しかし同じ sphere でも Radius (直径)は倍精度数とか呼ばれるタイプの数値であり、小数点以下の値を持つことができます。こんな風にパラメータには色んな種類があるので、AddParameter2 の呪文を唱える段階でどのタイプなのか宣言する必要があります。 一度宣言してしまったら他のタイプを受け付けないパラメータになります。例えば分割数は整数なので、0.1 などという数値は入りません。”hogehoge" などという文字も入りません。 この Icon というパラメータの場合は、整数だと宣言したので、整数以外は入らないパラメータになりました。
ちなみに XSI で扱えるパラメータの値のタイプ全種は、ヘルプの中で siVariantType というのを調べればわかります。

siInt4 の次の 1 という数値は、デフォルト値です。この PPG が呼び出されたときに最初に入っている数値が 1 だということです。

次は 0 です。ここは最小値です。最小値よりも小さい値は、たとえ PPG に数値を手打ちしても入らなくなります。この場合は、-1 などと手打ちしても受け付けてくれず、勝手に最小値である 0 に上書きされます。

次の 10最大値です。10 は受け付けるけど 11 は受け付けません。
はい、これで最初の AddParameter2 の呪文終わり。

二つ目の oProp.AddParameter2("Size",  siDouble,  1,  0.01,  100); も同じですね。

 パラメータの名前は "Size" だぜ。
 パラメータの数値のタイプは siDouble (倍精度数)だぜ。
 デフォルトの値は 1 にしておけよ。
 最小値は 0.01 だ。それ以下の数値は拒否しろよ。
 最大値は 100 だ。101 とかを受け付けやがったら電気アンマかますぞウルァ

と言っていることになります。
はい、2つめの AddParameter2 呪文終わり。これも、まんま暗記するのが良いと思います。っていうか俺、暗記すらしていない。毎回 AddParameter2 のヘルプを見て、何番目の数字が何を指定しているのか確認しています。とにかく毎回ヘルプを見ることです。ここでは指定を省略した機能もいっぱいあります。例えば最小/最大値とは別に、UI 上に表示するときだけのスライダの最小/最大値を指定できたりもします。ヘルプ読めばわかります。



次、
var oLayout = oProp.PPGLayout;

これですが、PPGLayout ってのは UI 上のパラメータの並び方とかを指定するための、いわば「見た目」を司る魔法の呪文の受け皿みたいなもんですね。 「いいかお前、oProp の PPG が表示された時の見た目はだな、これから唱える一連の呪文に従ってもらうからな、以降はこれを oLayout と呼ぶから忘れんじゃねえぞノルァ」 と言っていることになります。

こんなん、もう暗記するしかないですよね。呪文だと思えばいいです。バルス。



次。
oLayout.AddItem("Icon",  "愛根");
oLayout.AddItem("Size",  " 砕図");


さっき宣言した oLayout、つまり oProp の「見た目」の部分に、AddItem の呪文を唱えています。

"Icon" ってのは、さっき AddParameter2 の呪文で作ったパラメータです。すでに作成済みのパラメータを、この AddItem の呪文によって UI にぶち込んでいくわけです。なのでさっき指定した名前と完全に一致したものでなくてはいけません。

次に "愛根" とありますが、これは UI 上に表示される名前 です。 スクリプトを書いていると、スクリプト書きの都合上はこういう名前がいい、でもその名前のまま PPG に出てしまうとユーザにとってはわかりにくい、ということがよくあります。だから、PPG 上のハンドルネームをつけよう、ということです。 この場合、本当は Icon という名前のパラメータなんだけど、PPG 上でだけ "愛根" という名前を名乗ろう。という意味です。
次の行の Size = "砕図” も全く同じです。

しつこいようですが、ヘルプで AddItem のページを見れば、カッコの中の何番目の数値が何を指定しているのか、一目瞭然でわかります。ヘルプを見ましょう。




次。

oLayout.AddButton("Set",  "切吐");

この AddButton というのは、ボタンを作るときの呪文です。oLayout に "Set" という名前のボタンを与えよ~ ただし PPG では "切吐" という名前で表示せよ~ と呪文を唱えているわけです。

ちなみにこのボタン、押しても何も起こりません。ボタンを押したらこれを実行せよ~という呪文を書いてないからです。 これはまた次回以降に書きます。たぶん。



次ラスト。
InspectObj(oProp); 

これは、ここまでの呪文で作った oProp つまり CustomProperty つまりこの場合 hoge を、PPG として表示せよという呪文です。  UI 作成とは直接関係ないですが、作った hoge プロパティを確認するためにいちいち Explorer からクリックするのがめんどくさいので、自動でポンと表示させるようにしているだけです。


以上であります。
別に難しくないです。
ひたすらヘルプ見ながらやれば、あっちゅう間にわかるでしょう。


ちなみにこのスクリプト、何度も実行させればシーンルートにどんどん hoge が増えていきます(名前は hoge1, hoge2 などとなりますが)。 毎回 oProp に新しい CustomProperty として作成しているからです。 現在はとりあえずこうしていますが、いずれは「もしシーンルートに hoge という Property があったらそれを表示する。無ければ新たに作る」という呪文も説明しようと思います。たぶん。


次回以降、

・ボタンを押したときに何かを実行させる
・もっといろんな種類のパラメータを作る
・ドロップダウンメニューとかを作る
・パラメータを横並びにする
・整理された見た目にするために、複数パラメータをグループ(ワク)で囲う
・スライダの大きさを変える
・ボタンの大きさを変える
・数値のくせにスライダが表示されない(つまり手打ちのみの)パラメータにする
・ファイルを指定するようなパラメータを作る
・あるパラメータの値が変わったら、他のパラメータの値も連動して変わるようにする
・スイッチのオンオフによってパラメータが出現したり消えたりする
・シーンルートとかに実体を残さない、その場限りの Property を作る

とかなんとかを、解説するかもしれません。しないかも知れません。わかりません。
ここまで書いて、想像していたより 64倍くらい大変だったので、次回以降は説明を大幅に省略して「いいからこう書いときゃできるんだよ」方式になる可能性が高いです。どうやら説明ビデオを作る方が圧倒的にラクなようです。でもビデオを作るのは、やり始める最初のエネルギーがとてつもなく必要になります。なのでやはりこうしてテキスト中心になる可能性が高そうです。



次回以降があれば、の話です。
わかりません。



.

| | コメント (4) | トラックバック (0)

« 2010年6月 | トップページ | 2010年8月 »