よくある問題

ここでは、エクスプレッションのフロー制御文でよくある誤りの解決方法について説明します。

テスト条件で評価される変数の値を変更する

エクスプレッションで while ループや do ループ、for ループを使用する場合は、ループのテスト条件で評価される変数またはアトリビュートの値を必ず変更するようにしてください。この変更を忘れると、Maya が応答しなくなる場合があります。

例 1

Balloon というオブジェクトを作成し、while ループを使用してアニメーション再生の 3 秒後以降に Balloon の Y スケーリングを大きくするケースについて考えてみましょう。

while (time > 3)
	Balloon.scaleY = time;

このエクスプレッションでは、アニメーション時間が 3 秒を超えた後も Balloon のスケール Y (scaleY)アトリビュートに time の値が大きくなりながら代入されると思われるかもしれませんが、実際には time が 3 を超えるすぐに Maya が応答しなくなります。このとき、while 条件は true なので、while ループ文 Balloon.scaleY = time は無限に繰り返されます。

アトリビュートはエクスプレッション内部で文によって設定されますが、エクスプレッションの実行が終了するまでは、アトリビュートの実際の更新は行われません。この例ではエクスプレッションが決して終了しないため、Maya が応答しなくなってしまいます。

while ループ内の time を 3 以下の値に変更しないかぎり、文は無限に実行されます。

Maya を停止せずに目的の結果を得るには、次のエクスプレッションを使用します。

if (time > 3)
	Balloon.scaleY = time;

time キーワードの詳細については、「time および frame キーワード」を参照してください。

例 2

Cone と Ball というオブジェクトを作成し、while ループを使用して Ball の移動 Y (Translate Y)アトリビュートを Cone の移動 Y アトリビュートにリンクするケースについて考えます。

while (Cone.translateY > 0)
	Ball.translateY = Cone.translateY;

一見すると、このエクスプレッションでは Cone の移動 Y アトリビュートが 0 より大きい場合は、Ball の移動 Y アトリビュートに Cone の移動 Y アトリビュートが代入されるように思われます。

実際には、Cone の移動 Y アトリビュートが 0 より大きくなるとすぐに Maya が応答しなくなります。このとき、while 条件は true なので、while ループ文 Ball.translateY = Cone.translateY は無限に実行されます。

ユーザ インタフェースでどんな操作をしても、Cone の移動 Y (translateY)アトリビュートは変更されず、同じ位置(0)に留まります。

while ループ内部の Cone.translateY を 0 以下の値に変更しないかぎり、文は無限に実行されます。

Maya を停止せずに目的の結果を得るには、次のエクスプレッションを使用します。

if (Cone.translateY > 0)
	Ball.translateY = Cone.translateY;

== 演算子を使用して浮動小数点数と 0 を比較する

== 演算子を使用して float (浮動小数点)型の変数またはアトリビュートを 0 と比較すると、エクスプレッションが正しく動作しない場合があります。cosd などの組み込み関数が返す値がちょうど 0 であると決めてかかったような場合に、こうした状況がよく発生します。

float $x = cosd(90);
if ($x == 0)
	print("This equals 0.\n");
else
	print("This doesn't equal 0.\n");

このエクスプレッションを実行すると、次の結果が表示されます。

This doesn't equal 0.

90 度のコサインは数学的には 0 ですが、cosd (90)関数が返す値は 6.123e-17 です。この値は極めて 0 に近いものの、正確には 0 ではありません。通常は 0 と同じとして扱われますが、コンピュータによる浮動小数点数の内部処理では、0 より大きい小数としてコンピュータ内部に格納されます。

この問題を解決するには、次のようなエクスプレッションを使用して値を比較します。

float $x = cosd(90);
if (($x > -0.0001) && ($x < 0.0001))
	print("This equals 0.\n");
else
	print("This doesn't equal 0.\n");

このエクスプレッションを実行すると、次の結果が表示されます。

This equals 0.

$x の値が -0.0001 ~ 0.0001 の範囲にあるかどうかをテストすることによって、適切な print 文が実行されるようになります。cosd (90)が返す値は、上記の if 文の数値比較で指定した 0 付近の狭い範囲内にあります。