サイトトップ

Director Flash 書籍 業務内容 プロフィール

Adobe Flash CS3 Professional ActionScript 3.0

□05 マウスでスクロールさせるMovieClip

05-01 ループする水平スクロールのアニメーション ー ifステートメント
マウスポインタの位置に合わせて、水平スクロールするMovieClipインスタンスのアニメーションを作成します。これは、サムネールを水平に並べて、スクロールするメニューのインターフェイスなどに使われる動きです。マウスポインタの位置がステージ中央から離れるほど、スクロールスピードは増します。

図05-000■サムネールがマウスポインタの位置に応じてスクロール

スクロールするメニューのインターフェイスに使われる。

単純な動きから始める
スクリプトは例によって、単純な動きから、徐々に機能を加えていきます。これは単に本書の説明の便宜ではありません。実際にコンテンツを制作する際も、小分けにして考え、細かくテストしていくことが大切です。

そのようにすると、第1にスクリプトの誤り、つまりバグを早く修正することができます。初めての目的地に行く道順を確かめる際、どこの角を曲がるとか、途中のどのあたりにどんな目印があるとか、ポイントを定めるはずです。そのポイントが見当たらなかったとき、道間違いをしている可能性に気づきます。

しかし、そのひとつ前のポイントまでは正しくたどってきた訳ですから、そこまで一旦戻って改めて確認しながら進めば、最小限のロスで正しい道に戻れます。スクリプトの場合も同様に、小分けでテストしつつ進めていれば、正しい動作が確認できたポイント以降を見直すだけで、ミスを素早く見つけることが可能になります。

小分けで考えることは、第2にスクリプトのパーツ化にもつながります。それは別の表現をするなら、記事や論文を項目立てて構成するのと同じです。結論だけ決めていきなり本文を書き連ねれば、途中で論旨がずれてきたり、矛盾した論理を展開してしまうこともあります。

論理の骨組みとなる項目をまず考え、ひとつひとつの位置づけやそれらの間の関わりを予め決めてから、個々の内容を詰めていけば、論旨がぶれることはありません。スクリプティングでは、この項目がパーツ化を考えるひとつの単位となり、必要に応じて関数(function)として定義することになります。

05-001
小分けにして考えると、ミスが見つけやすく、パーツ化もしやすい。

単純に水平移動するアニメーション
水平スクロールするアニメーションのもっとも単純な動作は、マウスポインタとは関係なく、一方向に等速度で移動するものです。このスクリプトは、これまでの知識の復習として作成できるでしょう。

リスナー関数の名前をxScroll()とし、フレーム当たりの移動ピクセル数は変数nSpeedに格納するものとします。アニメーションを処理するイベントは、もちろんDisplayObject.enterFrame(Event.ENTER_FRAME)です(スクリプト05-001)。

スクリプト05-001■MovieClipインスタンスをマウスポインタの水平方向の動きに追随させるフレームアクション

// MovieClip: 水平スクロールするインスタンス
var nSpeed:Number = 5;
addEventListener(Event.ENTER_FRAME, xScroll);
function xScroll(eventObject:Event):void {
  x += nSpeed;
}


Tips 05-001■DisplayObject.enterFrameイベントとEvent.ENTER_FRAME定数
enterFrameイベントは、DisplayObjectクラスに定義されています。したがって、DisplayObjectのサブクラスであるMovieClipクラスのインスタンスに、このイベントに対するリスナー関数を登録して処理することが可能です。

Event.ENTER_FRAME定数は、EventDispatcher.addEventListener()メソッドの第1引数としてDisplayObject.enterFrameを指定するイベントのタイプ(Event.typeプロパティ)で、値は文字列"enterFrame"です。

つまり、毎フレーム処理するためのインスタンスのイベントはDisplayObject.enterFrameで、そのイベントをEventDispatcher.addEventListener()メソッドに第1引数として指定するのがEvent.ENTER_FRAME定数だということです。

[ムービープレビュー]を確認すると、毎フレーム5ピクセル(デフォルトの12fpsなら60ピクセル/秒)の速度で、MovieClipインスタンスが右に水平移動します。

ifステートメント
スクリプト05-001は、MovieClipインスタンスが単に右方向に移動するだけです。ステージ右端を超えて見えなくなっても、そのまま移動を続けます。そこで、ステージ右端を超えたら、左端にループしてスクロールを続けるように修正してみます。

ステージ右端を超えたという条件を設定して、その条件が満たされるかどうかによって処理を分けるのはifステートメントです。ifステートメントは、つぎのようなシンタックスで用います。

if (条件) {
  // 条件が満たされた場合の処理
}

条件には、多くの場合不等式や等式を指定します。不等式は、数学で習ったのと同じく、不等号<>により両辺を比較します。等しい場合を含めるには、不等号の右に等号を加え、<=あるいは>=とします。これらは「関係演算子」と呼ばれます。

両辺が等しいとする等式には、数学と異なり、ふたつの等号==(「等価演算子」)を用います。ActionScript(ECMAScript)では、等号=は代入を行うもので、両辺を比較する等価演算子==とは区別されます。しかし、if条件に等号=を使った代入式を指定しても、シンタックスエラーにはならないので、注意が必要です。

両辺が等しくないことを示すには、不等価演算子!=を用います。

Tips 05-002■等価と厳密な等価
等価演算子==は、両辺のデータ型が異なる場合でも、自動的にデータ型を変換して比較評価することがあります。たとえば、数値の1と文字列の"1"を等価演算子==で比較すると、等しいと評価されます。式が成立つ場合、式の値としてtrue(真)が返されます。

trace(1 == "1");   // 出力: true

ただし、[パブリッシュ設定]の[ActionScript 3.0設定]でデフォルトの[Strictモード](がオンの状態)では、データ型の異なる値を比較するとコンパイルエラーになります(図05-001)。

図05-001■データ型の異なる値を比較するとコンパイルエラーが発生

数値と文字列とを比較することができない。

したがって、上記スクリプトを試すには、[Strictモード]をオフにして、Standardモードに設定する必要があります。[Strictモード]は、[パブリッシュ設定]の[Flash]タブで[ActionScript 3.0設定]のダイアログボックスを開いて選択します。

図05-002■[ActionScript 3.0設定]で[Strictモード]を設定

[Strictモード]では、コンパイル(SWF書出し)時も型チェックを行う。

データ型も含めて両辺が等しいかどうかを評価するには、厳密な等価演算子===を用います。

trace(1 === "1");   // 出力: false

Maniac! 05-001■厳密な等価演算子の自動データ変換
厳密な等価演算子===は、データ型を含めて比較しますので、原則として自動データ変換は行いません。ただし、数値すなわちNumberとint、uintの間だけは自動的にデータを変換し、異なった数値型であっても値が等しければtrue(等価)と評価します。

if条件は、それが成立つかどうかが判定されます。もう少し厳密にいうと、if条件はブール(論理)値として評価されます。その結果は、true(真)かfalse(偽)のどちらかです。

前述の関係演算子や等価演算子を用いた式(「論理式」)は、truefalseかいずれかの値を返します。trueは人間の目から見て条件が成立つ場合、falseが成立たない場合に当たります。

trueと評価されると、if条件の後の中括弧{}に括られたステートメントを実行します。falseのときは、中括弧{}内のステートメントはスキップし、その後の処理を続けます。

Word 05-001■ブール(論理)値
Boolean型の値で、true(真)とfalse(偽)の2値のみで構成されます。

if条件は、それが成立つ(真の)ときtrue、成立たない(偽の)ときはfalseとして評価されます。また、各種のプロパティのうち、設定にオン/オフ、あるいはYes/Noの2つの状態があるものは、オンまたはYesはtrue、オフまたはNoがflaseという値で示されることが多いです。

trueとfalse、すなわち真と偽は、論理学から来た用語です。命題が成立つときは真、成立たないときが偽と表現されます。したがって、これらの値のことを「論理値」と呼ぶこともあります。

なお、ブールとは、デジタル回路の設計に必須とされる「ブール代数」の考案者、ジョージ・ブールの名に由来します。

たとえば、以下のフレームアクションを[ムービープレビュー]で試すと、変数nに代入された整数は2より小さいのでif条件はtrueと評価され、中括弧{}内に記述されたtrace()関数により、変数nの数値1が[出力]パネルに表示されます。2以上の整数を変数nに代入すると、if条件はfalseと評価されるので、何も[出力]されません。

var n:int = 1;
if (n<2) {
  trace(n);
}

もうひとつ、03-05「Stringクラスで文字列を操作する」で1桁の整数を2桁の文字列に変換した操作も、ifステートメントを使って以下のように処理することができます。整数1を文字列に変換した文字数String.lengthプロパティの値は1となり2より小さいので、if条件はtrueと評価されて、文字の前に"0"が連結されます。変数nに代入した整数が2桁以上であれば、"0"を連結する操作は行われません。

var n:int = 1;
var n_str:String = String(n);
if (n_str.length<2) {
  n_str = "0"+n_str;
}
trace(n_str);   // 出力: 01

Tips 05-003■if条件の評価
if条件には、多くの場合関係演算子や等価演算子を用いた論理式が指定されます。論理式は、truefalseかのブール(論理)値を返します。

しかし、ブール(論理)値を返さない式であっても、if条件として指定することはできます。「式」には、ひとつの変数や単純な数値、文字列なども含まれます。そして、if条件は、必ずブール(論理)値として評価されます。つまり、シンタックスエラーを生じないかぎり、truefalseのどちらかとして扱われるのです。

if条件に指定した式がブール(論理)値以外の値を返した場合、それをブール(論理)値として評価した結果は、下表05-001のとおりです。

表05-001■ブール(論理)値以外の式の値の評価
式の値 評価
0 false
NaN false
0とNaN以外の数値 true
空文字列("") false
空でない文字列 true
null false
undefined false
クラスのインスタンス true

そこでたとえば、変数nの値が1に等しいかどうかを判定するつもりで、if条件に等価演算子==でなく、誤って以下のように代入演算子=を用いた場合について考えてみましょう。

var n:int = 0;
if (n = 1) {
  trace(n);
}

if条件に指定したのは代入式ですので、まず変数nに1が代入されます。そして、代入式は代入された値を返します。それは1で、「0とNaN以外の数値」ですのでtrueと評価され、中括弧{}内のステートメントが実行されて、変数nの新たな値である1が[出力]パネルに表示されます。

このように、if条件に代入式を指定しても文法的に問題はないので、シンタックスエラーは起こりません。また、変数に値を代入したうえで、その値を条件としてブール(論理)値評価するという処理も、実際上考えられない訳ではありません(後述スクリプト08-010参照)。

水平スクロールをループする
それではスクリプト05-001に修正を加えて、MovieClipインスタンスがステージ右端を超えたら、左端にループしてスクロールし続ける処理を考えてみましょう。ステージ右端はデフォルトなら550ピクセル、ステージ左端は0ピクセルですから、関数xScroll()につぎのようなifステートメントを加えればよさそうに思えます。

if (x>550) {
  x = 0;
}

これも間違いではありません。しかし、今回の処理では、少し問題があります。今作成しようとしているアニメーションは、スクロールするメニューを想定しています。ということは、いくつものインスタンスが、等間隔に並んで移動することになるでしょう。その場合上記の処理では、アニメーションが進むにしたがい、間隔はバラバラになってしまいます。

上記のifステートメントの処理は、インスタンスの座標がステージ右端(550ピクセル)を超えたら、左端(0ピクセル)に移動するというものです。しかし、右端の超え方には、幅があります(図05-003)。毎フレーム5ピクセル移動するとしたら、ステージ右端を最小1ピクセルから最大5ピクセルまで、はみ出す場合があります。

図05-003■ステージからはみ出すピクセル数はインスタンスによって異なる

いわば折返し地点の遠いインスタンスと近いインスタンスができる。

はみ出した量を考えず一律にステージ左端(0ピクセル)に戻すとすれば、マラソンで折返し地点が人によって遠かったり近かったりするようなことになります。ステージ右端を1ピクセル超えただけで左端に移動したインスタンスは、得をして他に先んじます。逆に、5ピクセル超えてようやく折返したインスタンスは遅れをとります。したがって、最初に等間隔で並べてあっても、ループを繰返すうちにインスタンス間の距離がばらつく結果になるのです。

インスタンス同士の間隔を変えないようにするには、特定の座標(0ピクセル)に移動するのではなく、一定の距離左に戻すことです。移動させる距離は、ステージ幅(550ピクセル)でよいでしょう。なお、-=は、左辺の現在値から右辺値を差引く代入演算子です(Tips 01-008「代入の演算子」参照)。

if (x>550) {
  x -= 550;
}

Tips 05-004■括弧はすぐに閉じる
関数(function)内にifステートメントが入ってくるようになると、括弧の(始まりと閉じの)対応を間違えやすくなります。閉じ忘れたり、閉じ過ぎたりすると、エラーの原因を突き止めるのに時間が費やされてしまいます。

括弧の対応の誤りを防ぐには、括弧はすぐに閉じることです。可能であれば空のまま閉じてしまい、[シンタックスチェック]または[自動フォーマット]を一旦通します。そのうえでステートメントを加えていけば、エラーが起こったときも括弧を閉じた以降の記述に原因を絞れます。

図05-004■括弧はすぐに閉じてしまう

if条件の括弧()内には、数字をひとつ入れてチェックをかける。

ただし、if条件の丸括弧()は、空のままではシンタックスエラーになります。そこで、丸括弧()内に何か数字をひとつ入れて、[シンタックスチェック]または[自動フォーマット]すれば、括弧の対応が正しいことを確認できます(図05-004)。その後で、条件を正しく書替えればよいでしょう。

関数で処理を構成する
スクリプト05-001に前述の処理を加えて、スクロールをループさせます。ただし、関数xScroll()にステートメントを直接書き加えるのではなく、処理内容を小分けにして、関数として定義していくことにします。本節の初めに述べたとおり、関数に小分けすることでバグ修正などのメンテナンスがしやすくなり、機能を追加したり拡張する際にも管理しやすいからです。

そうすると、関数xScroll()は、大きくふたつの処理で構成される予定です。

  1. マウスポインタの位置から、移動スピードを計算する。
  2. そのスピードで移動すべき場所に、インスタンスを配置する(ステージ外に出たらループする)。

第1は移動スピード、つまり移動する方向とピクセル数の計算です。この値は、マウスポインタの位置によって変化させることになります。もっとも現在は、まだ固定値(5ピクセル)になっています。

第2は、第1で求めたスピードに応じて、移動すべき位置にインスタンスを配置します。ここに、ステージ外に出たとき、反対の端にループさせるという処理が加わります。したがって、まずこの処理を関数として定義しましょう。

関数名はxSetPosition()とし、関数xScroll()内を上記ふたつの処理に合わせて、2行のステートメントで構成することにします。

function xScroll(eventObject:Event):void {
  // [1]移動スピードの計算
  var nNewSpeed:Number = nSpeed;
  // [2]インスタンスの配置
  xSetPosition(nNewSpeed);
}

関数xScroll()内の第1ステートメントは、前記1の移動スピードの計算の処理に対応します。ただし、まだ今の段階では、単に変数nSpeedの値を、ローカル変数nNewSpeedに代入しているだけです。しかし、この代入式の右辺は後で、マウスポインタの位置に応じて移動スピードを計算する関数の呼出しに代わる予定です。それまでの暫定的な記述だと考えてください。

第2ステートメントが、これから定義する関数xSetPosition()の呼出しです。移動すべきピクセル数を引数として渡し、インスタンスを移動すべき位置に配置します。座標がステージ外にはみ出した場合には、ステージ反対端にループする処理も含めます。

インスタンスを配置する関数の定義
それでは、スクリプト05-001に修正を加えましょう。関数xSetPosition()を定義するほか、後の調整や修正がしやすいように、ステージ両端の座標とステージ幅を変数として宣言します(スクリプト05-002)。

スクリプト05-002■インスタンスを配置する関数の追加

// MovieClip: 水平スクロールするインスタンス
var nSpeed:Number = 5;
var nStageLeft:Number = 0;
var nStageRight:Number = stage.stageWidth;
var nStageWidth:Number = nStageRight-nStageLeft;
addEventListener(Event.ENTER_FRAME, xScroll);
function xScroll(eventObject:Event):void {
  // [1]移動スピードの計算
  var nNewSpeed:Number = nSpeed;
  // [2]インスタンスの配置
  xSetPosition(nNewSpeed);
}
// インスタンスを配置する関数
function xSetPosition(nNewSpeed:Number):void {
  x += nNewSpeed;
  if (x>nStageRight) {
    x -= nStageWidth;
  }
}

ステージの左右の端の座標値は、それぞれ変数nStageLeftとnStageRightに設定しました。ステージ右端の座標は、Stage.stageWidthプロパティの値を用いています。また、ステージ幅を格納する変数nStageWidthも宣言し、ステージ両端の座標の差を取って代入しました。

インスタンスを配置する関数xSetPosition()内の処理の流れは、すでに説明したとおりです。第1ステートメントは、インスタンスの水平座標に移動ピクセル数(スピード)を加えて、まず座標を変更します。

つぎに、ifステートメントを用いて、移動した座標がステージ右端を超えたかどうか判定します。超えていれば、ステージ幅を差引いて、ステージ左端に移動します。

今のところインスタンスは右方向にしか移動しないため、ステージ右端から左端へのループしか行っていません。インスタンスを左方向に動かす処理(前記1の移動スピードの計算)を加えてから、反対方向のステージ左端から右端へのループも対応します。

[ムービープレビュー]を確かめると、右にスクロールしたインスタンスが、ステージ右端を超えると左端にループしてアニメーションを続けます。

Tips 05-005■長い識別子はコピーする
スクリプトのタイプミスは、犯しやすく、しかも見つけにくいものです。とくに自分が設定した識別子については、シンタックスカラーも適用されないため、慣れた人でも探すのに大変骨が折れます。変数や関数の長い名前は、できるだけコピー&ペーストを使いましょう。

識別子やキーワードを選択するには、その単語をダブルクリックします。1行の選択は、スクリプトペイン左端の行番号の表示されている位置をクリックします(図05-005。行番号のさらに左の余白をクリックするとブレークポイントの設定になります)。

図05-005■行番号の位置をクリックすると1行の選択

[Ctrl](Windows)または[option](Macintosh)を押しながら選択をドラッグ&ドロップすると、コピーができる。

コピーは、通常のメニューやショートカットキー(Windowsは[Ctrl]、Macintoshは[command]キーを押しながら、Cでコピー、Vでペースト)、コンテキストメニュー(右クリック)で、コピー&ペーストを使ってももちろん構いません。さらに、スクリプトペイン内では、Windowsは[Ctrl]、Macintoshは[option]キーを押しながら、ドラッグ&ドロップでコピーすることもできます。


05-02 マウスポインタの位置に応じてスクロール方向を変える ー elseステートメント
マウスポインタの位置に応じて、スクロールする方向を変えてみたいと思います。ここでは方向、つまり移動ピクセル数のプラスマイナスの符号を変えるだけです。マウスポインタの位置から、移動ピクセル数を計算してスピードに反映するのは、もう少し後の課題とします。

elseステートメント
マウスポインタがステージ中央より左寄りならインスタンスを右にスクロールし、右寄りなら左に移動させることにします。マウスポインタの座標がステージ中央より左かどうかを判定するにはifステートメントを用います。

ただし、前節では条件に当てはまる(trueと評価される)場合にのみ一定のステートメントを実行したのに対して、今回は当てはまる(trueの)場合は右スクロール、当てはまらない(falseの)場合には左スクロールと、ふたつの処理のいずれか一方を行うことになります。

ifステートメントには、条件がtrueと評価される場合の処理を記述しました。条件がfalseと評価される場合の処理は、elseステートメントで加えます。

if (条件) {
  // 条件がtrueの場合の処理
} else {
  // 条件がfalseの場合の処理
}

05-002
ifかelseのどちらかの処理を必ず実行する。

移動スピードを計算する関数の定義
マウスポインタの水平座標は、DisplayObject.mouseXプロパティで調べます。座標の基準は今回ステージになりますので、ターゲットとしてDisplayObject.stageプロパティを参照する必要があります。

このマウスポインタの位置に応じてスクロール方向を変える処理は、関数xGetSpeed()として定義し、リスナー関数xScroll()から呼出します。関数を上手に定義するコツは、呼出し方から考えることでした。リスナー関数xScroll()内のステートメントを、移動スピードの計算についてつぎのように修正することにします。

function xScroll(eventObject:Event):void {
   // [1]移動スピードの計算
   var nNewSpeed:Number = xGetSpeed();
   // [2]インスタンスの配置
   xSetPosition(nNewSpeed);
}

関数xGetSpeed()の呼出しに、引数は渡しません。戻り値は、マウスポインタの水平位置に応じて、プラスマイナスの符号がついた移動ピクセル数です。この関数xGetSpeed()の定義を加えたのが、以下のスクリプト05-003です(インスタンスを配置する関数xSetPosition()は、とくに変更がありませんので省略してあります)。ステージ中央の水平座標を保持する変数として、nStageCenterを設定しました。

まず、変数nStageCenterに代入するステージ中央の水平座標値は、ステージ両端の座標を「平均」して求めます。ですから、ステージ幅nStageWidthとは異なり、ステージ両端の座標を引き算するのでなく、足し算することにご注意ください。

スクリプト05-003■移動スピードを計算する関数の追加

// MovieClip: 水平スクロールするインスタンス
var nSpeed:Number = 5;
var nStageLeft:Number = 0;
var nStageRight:Number = stage.stageWidth;
var nStageWidth:Number = nStageRight-nStageLeft;
var nStageCenter:Number = (nStageRight+nStageLeft)/2;
addEventListener(Event.ENTER_FRAME, xScroll);
function xScroll(eventObject:Event):void {
  // [1]移動スピードの計算
  var nNewSpeed:Number = xGetSpeed();
  // [2]インスタンスの配置
  xSetPosition(nNewSpeed);
}
// 移動スピードを計算する関数
function xGetSpeed():Number {
  var nNewSpeed:Number;
  if (stage.mouseX<nStageCenter) {
    nNewSpeed = nSpeed;
  } else {
    nNewSpeed = -nSpeed;
  }
  return nNewSpeed;
}
// インスタンスを配置する関数
// xSetPosition()は変更がないので省略

つぎに、移動スピードを計算する関数xGetSpeed()の定義内容です。プラスマイナスの符号がついた移動ピクセル数を格納するNumber型のローカル変数として、nNewSpeedを宣言しました。

そして、ステージから見たマウスポインタの水平座標は、stage.mouseXで調べます。この値が変数nStageCenterに設定されているステージ中央の水平座標より小さい、つまりマウスポインタがステージ中央より左側にあるかどうかをif条件として指定してあります。

if条件がtrueと評価され、マウスポインタがステージ中央より左側にある場合には、インスタンスは右に移動させますので、ローカル変数nNewSpeedには変数nSpeedの値をプラスのまま代入します。elseステートメントは、マウスポインタがステージ中央もしくはそれより右側にある場合の処理になりますので、ローカル変数nNewSpeedにマイナスの符号をつけた値-nSpeedを代入します。

関数xGetSpeed()の処理は、プラスマイナスの符号が与えられた変数nNewSpeedの値を返して完了します。

[ムービープレビュー]を確かめると、マウスポインタの位置に応じて、インスタンスのスクロール方向が変わります(図05-006)。ただし、ステージ左端を超えた場合のループ処理は、まだ関数xSetPosition()に加えていません。したがって、今の段階ではステージ反対端にループするのは、右方向のスクロールのみです。

図05-006■マウスポインタの位置に応じて移動方向が変わる

マウスポインタがステージ中央より左寄りだと、インスタンスは右にスクロールする。

05-03 左方向スクロールをループする ー else ifステートメント
インスタンスがステージ左端を超えた場合についても、右端にループする処理を追加します。修正するのは、インスタンスを配置する関数xSetPosition()です。

現在if条件として設定されているステージ右端を超えたかどうかの判定に加えて、もうひとつ新たな条件を設定します。それは、ステージ左端を超えたかどうかという条件で、その場合には右端にループする処理を行います。

else ifステートメント
複数の条件を指定したいときには、ifステートメントの後にelse ifステートメントを加えます。else ifステートメントには、ifステートメントと同じように条件が指定できます。その条件がtrueの場合に、else ifステートメントの処理が実行されます。

if (条件1) {
  // 条件1がtrueの場合の処理
} else if (条件2) {
  // 条件1がfalseで条件2がtrueの場合の処理
}

このとき注意しなければならないのは、else if条件はif条件がfalseの場合に初めて判定されるということです。if条件がtrueであればifステートメントの処理が行われ、else ifステートメントはその条件を判定することなくスキップされます。

else ifステートメントは、いくつでも記述することができます。また、elseステートメントと組合わせることも可能です。ifelseステートメントは、最初と最後にひとつずつしか置けません。

if (条件1) {
  // 条件1がtrueの場合の処理
} else if (条件2) {
  // 条件1がfalseで条件2がtrueの場合の処理
……
} else if (条件n) {
  // 条件1〜条件n-1がfalseで条件nがtrueの場合の処理
} else {
  // すべての条件がfalseの場合の処理
}

この場合も、条件は上から順に判定され、trueと評価されたらそのステートメントを実行し、残りのelse ifelseステートメントは処理されません。丁度、勝ち抜けのクイズと同じです。1問正解したら、解答者の席から抜け、後の問題には解答しません。ifまたはelse if条件を初めから順に判定し、ひとつtrueと評価されたらそのステートメントを処理して抜け、後の条件は判定しないのです。

05-003
if/else if/else条件の判定は、勝ち抜けクイズと同じ。ひとつでもtrueと評価されたら、後の条件は判定しない。

ステージ左端を超えたら右端にループする
それでは、インスタンスを配置する関数xSetPosition()に、左方向にループする処理を加えます。右方向のループと反対の操作ですから、さほど難しくはないでしょう(スクリプト05-004)。

スクリプト05-004■移動スピードを計算する関数の追加

// インスタンスを配置する関数
function xSetPosition(nNewSpeed:Number):void {
  x += nNewSpeed;
  if (x>nStageRight) {
    x -= nStageWidth;
  } else if (x<nStageLeft) {
    x += nStageWidth;
  }
}

else if条件は、ステージ左端の座標値nStageLeftよりインスタンスの水平座標DisplayObject.xの方が小さいという比較(不等)式になります。そして、else if条件がtrueと評価されたときは、ステージ右端にループしますので、ステージ幅nStageWidthをインスタンスのDisplayObject.xプロパティに足し込みます。

[ムービープレビュー]で確かめると、左方向のスクロールについても、ステージ左端を超えると右端にループします。


05-04 マウスポインタの位置に応じてスクロールスピードを変える
マウスポインタの位置に応じてスクロールスピードが変われば、このムービーも完成です。移動スピードを計算する関数xGetSpeed()の処理内容が、修正の対象になります。

マウスポインタのステージ中央からの距離にスピードを比例させる
まず、関数xGetSpeed()の処理を大幅に書替えて、マウスポインタのステージ中央からの距離に、インスタンスのスクロールスピードを比例させてみましょう。ステージ中央の水平座標値nStageCenterからマウスポインタの水平座標値を差引いて、移動ピクセル数nNewSpeedの値とします。

function xGetSpeed():Number {
  var nNewSpeed:Number = nStageCenter-stage.mouseX;
  return nNewSpeed;
}

マウスポインタの座標がステージ中央より左か右かによって、代入式右辺のプラスマイナスの値が変わります。そのため、以前記述してあった、マウスポインタの水平座標がステージの中央の水平座標より小さいか大きいかといった判定は不要になります。

これで[ムービープレビュー]を確認してみると、マウスポインタがステージ中央付近にあればよいものの、ステージ端にポインタを動かすと移動ピクセル数が大きくなり過ぎて、左右どちらにスクロールしているのかもわからないアニメーションになります。

そこで、スクロールに制限速度を設けることにします。移動ピクセル数には、左右つまりプラスマイナスふたつの場合がありますので、速度制限の処理はif/else ifステートメントを用いることになります。

制限速度を±20ピクセルとしたのが、以下のスクリプト05-005です。関数xGetSpeed()の処理内容が変わり、移動ピクセル数を設定する変数nSpeedが不要になったため、この変数に制限速度となるピクセル数20を収めることにしました。

スクリプト05-005■移動スピードを計算する関数に速度制限の処理を追加

// MovieClip: 水平スクロールするインスタンス
var nSpeed:Number = 20;
var nStageLeft:Number = 0;
var nStageRight:Number = stage.stageWidth;
var nStageWidth:Number = nStageRight-nStageLeft;
var nStageCenter:Number = (nStageRight+nStageLeft)/2;
addEventListener(Event.ENTER_FRAME, xScroll);
function xScroll(eventObject:Event):void {
  // [1]移動スピードの計算
  var nNewSpeed:Number = xGetSpeed();
  // [2]インスタンスの配置
  xSetPosition(nNewSpeed);
}
// 移動スピードを計算する関数
function xGetSpeed():Number {
  var nNewSpeed:Number = nStageCenter-stage.mouseX;
  if (nNewSpeed>nSpeed) {
    nNewSpeed = nSpeed;
  } else if (nNewSpeed<-nSpeed) {
    nNewSpeed = -nSpeed;
  }
  return nNewSpeed;
}
// インスタンスを配置する関数
// xSetPosition()は変更がないので省略

関数xGetSpeed()内のif条件はプラスの制限速度nSpeedを超えた場合に移動ピクセル数nNewSpeedをnSpeedに設定し直し、else ifステートメントはマイナスの制限速度-nSpeedを超えたときnNewSpeedの値を-nSpeedに変更しています。

比例係数を調整する
[ムービープレビュー]を見ると、移動速度が速くなり過ぎる問題は解決しています。しかし、マウスポインタを少し中央から離しただけで速度が変化してしまい、インターフェイスとしてまだ扱いにくい状況です。実際、マウスポインタの位置でインスタンスを停止させるには、かなり微妙な操作が要求されます。

図05-007■マウスポインタを少し動かしただけで速度が変わる

マウスポインタの位置でインスタンスを停止させるのは難しい。

ここで間違えてはいけないのが、制限速度nSpeedの値を下げても問題は解決しないということです。操作しにくい原因は、ステージ中央の水平座標とマウスポインタの水平座標との差を、そのまま移動ピクセル数として比例(係数1)対応させていることにあります。ですから、インスタンスの移動を止めようとすれば、正確にステージ中央の水平座標にマウスポインタをぴったり合わせなければならないのです。

したがって、現在の比例係数1をもっと小さい値に変えれば、マウスポインタの動きに対する反応を緩めることができます。マウス操作にいわば遊びが生じる訳です。比例係数の変数として新たにnSensitivityを宣言し、値は0.2を設定することにしましょう(スクリプト05-006)。

スクリプト05-006■移動スピードの比例係数を追加

// MovieClip: 水平スクロールするインスタンス
var nSpeed:Number = 20;
var nStageLeft:Number = 0;
var nStageRight:Number = stage.stageWidth;
var nStageWidth:Number = nStageRight-nStageLeft;
var nStageCenter:Number = (nStageRight+nStageLeft)/2;
var nSensitivity:Number = 0.2;
addEventListener(Event.ENTER_FRAME, xScroll);
function xScroll(eventObject:Event):void {
  // [1]移動スピードの計算
  var nNewSpeed:Number = xGetSpeed();
  // [2]インスタンスの配置
  xSetPosition(nNewSpeed);
}
// 移動スピードを計算する関数
function xGetSpeed():Number {
  var nNewSpeed:Number = (nStageCenter-stage.mouseX)*nSensitivity;
  if (nNewSpeed>nSpeed) {
    nNewSpeed = nSpeed;
  } else if (nNewSpeed<-nSpeed) {
    nNewSpeed = -nSpeed;
  }
  return nNewSpeed;
}
// インスタンスを配置する関数
function xSetPosition(nNewSpeed:Number):void {
  x += nNewSpeed;
  if (x>nStageRight) {
    x -= nStageWidth;
  } else if (nX<nStageLeft)
    x += nStageWidth;
  }
}

改めて[ムービープレビュー]を確認すると、マウスポインタのわずかな動きでインスタンスの移動速度が増すことはなくなります。実際に操作をした感触に応じて、係数nSensitivityの値を調整してもよいでしょう。


05-05 プロパティ値の有効桁数
理屈の上では、スクリプトはこれでできあがりです。ところが、実際に使用してみると、少々不具合を感じる場合があります。

このフレームアクションの設定されたインスタンスを複数、水平にぴったりとすき間なく並べます。そして、10分以上しばらくの間、それらのインスタンスをスクロールさせ続けます。すると、インスタンス同士の間隔に、ばらつきが生じるのです(図05-008)。

図05-008■スクロールし続けるとインスタンス同士の間隔にばらつきが生じる

ぴったり並べてあったインスタンス間に重なりやすき間ができる。重なりが確認しやすいように、インスタンスのアルファは少し下げてある。

これはインスタンスの座標値のプロパティDisplayObject.xの有効桁数が原因です。プロパティ値をtrace()関数で[出力]してみればすぐに確認できるとおり、DisplayObject.xは小数点以下2桁しか値をもちません。小数点以下第2位より小さな端数のある値を設定しても、2桁に丸められてしまいます。そのため、その丸められた値の累積が、インスタンスの位置座標の誤差となり、間隔にばらつきが生まれるのです。

お金の貯め方に「小銭貯金」というのがあります。毎日、財布の中のたとえば500円玉以下の硬貨を、貯金箱に入れます。この小銭貯金を数人で同時に始めたとして、1年後に貯まる金額は人によって幅が出てしまうでしょう。小銭の出やすい人と出にくい人がいるからです。

今回の座標値のばらつきも、丸められた端数の累計が大きいインスタンスと小さいインスタンスとの差により生じるものです。この問題の影響を最小限にとどめるには、小銭貯金の対象を1円玉などできるだけ小額の硬貨にすればよいでしょう。

幸いなことに、ActionScriptのNumber型変数は、約15桁の有効桁数があります。ですから、変数を利用すれば、実際上1ピクセルの誤差も生じさせないことが可能です。

Tips 05-006■有効桁数
有効桁数」とは、数字の桁数のうちどこまで正確で、どこから誤差が含まれるかを示す値です。その誤差の含まれる最初の桁までの数で示されます。小数点以下の桁数ではありません。

たとえば、座標値が小数点以下第3位で丸められて、123.45と第2位までの値に設定されたとき、有効桁数は5桁になります。位取りをするための0は、有効桁数には含めません。日本の総人口が1億2千8百万人といったとき、数十万の単位では誤差が含まれると考えられますので、有効桁数は3桁になります。

そこで、インスタンスの水平座標値を、変数nXに保持することにしました(スクリプト05-007)。初期設定として、変数nXの宣言時にインスタンスのDisplayObject.xプロパティの値を代入しています。

その後は、関数xSetPosition()におけるインスタンスの水平座標値の処理は、すべて変数nXに対して行います。そして、関数xSetPosition()内の最後のステートメントで、変数nXの値をインスタンスのDisplayObject.xプロパティに設定しています。

スクリプト05-007■座標値の処理を変数で行うように修正

// MovieClip: 水平スクロールするインスタンス
var nSpeed:Number = 20;
var nStageLeft:Number = 0;
var nStageRight:Number = stage.stageWidth;
var nStageWidth:Number = nStageRight-nStageLeft;
var nStageCenter:Number = (nStageRight+nStageLeft)/2;
var nSensitivity:Number = 0.2;
var nX:Number = x;
addEventListener(Event.ENTER_FRAME, xScroll);
function xScroll(eventObject:Event):void {
  // [1]移動スピードの計算
  var nNewSpeed:Number = xGetSpeed();
  // [2]インスタンスの配置
  xSetPosition(nNewSpeed);
}
// 移動スピードを計算する関数
function xGetSpeed():Number {
  var nNewSpeed:Number = (nStageCenter-stage.mouseX)*nSensitivity;
  if (nNewSpeed>nSpeed) {
    nNewSpeed = nSpeed;
  } else if (nNewSpeed<-nSpeed) {
    nNewSpeed = -nSpeed;
  }
  return nNewSpeed;
}
// インスタンスを配置する関数
function xSetPosition(nNewSpeed:Number):void {
  nX += nNewSpeed;
  if (nX>nStageRight) {
    nX -= nStageWidth;
  } else if (nX<nStageLeft) {
    nX += nStageWidth;
  }
  x = nX;
}

ここで注目すべきは、インスタンスのDisplayObject.xプロパティの値を変数nXに代入したのが、変数を宣言した初期設定のときだけだということです。その後の関数xSetPosition()内の座標の処理はすべて変数nXに対して行い、そして関数の最後のステートメントでは変数nXの値をインスタンスのDisplayObject.xプロパティに設定しています。

初期設定で1度だけプロパティ値を変数に代入した以降は、変数値をプロパティには設定しても、プロパティ値を変数に代入することはありません。それをしてしまったら、小数第3位以下の誤差が累積された値で、変数値を上書きしてしまうことになるからです。ですから、値の設定は変数からプロパティへの一方通行にしてあるのです。

これは、JPEG画像を加工するとき、作業ファイルをPNGやPSDなどの劣化しないフォーマットで取っておくのと同じことです。加工したJPEG画像に修正が入ったとき、そのJPEGファイルを開いて修正したら、再度JPEGに保存するときまた劣化してしまいます。修正・加工はつねに作業ファイルで行い、そこからJPEGに書出せば劣化が累積されることはないでしょう。

05-004
プロパティの値は1度変数に代入したら、変数からプロパティへの一方通行。誤差のあるプロパティ値で変数を上書きしない。

スクリプト05-007は、有効桁数約15桁の変数で座標の処理を行っていますので、長い時間スクロールしても、その誤差が小数点以下の桁数を十数桁分も累積して1ピクセルの単位にまで至ることは実際上ありません。


Column 05 if/else if条件の順序
if/else if/elseステートメントは、条件を上から順に判定し、trueと評価されればそこに記述された処理を実行して、以降のelse ifelseステートメントはスキップします。したがって、複数の条件判定の処理を考える場合、条件の順序が重要になります。

みかんの選別
条件の順序を考えるとき、みかんの選別機が参考になるでしょう。選別機にかけられたみかんは、コンベヤーで穴空きドラムに送られます。ドラムは3つあり、手前からS玉、M玉、L玉の大きさの穴が空いています。

小さいみかんは、まず手前のS玉の穴に落ちます。つぎに、そこを通り過ぎたみかんのうち、中くらいのものがM玉の穴に落ちます。そして、残ったみかんがL玉の穴に落ちることになります。これでみかんの選別ができる訳で、間違っても一番手前にL玉の穴をセットしてはいけません。

05-005
穴の大きさは、手前からS、M、Lでないとダメ。

ActionScriptのif/else if/elseステートメントでは、このみかん選別機と同じように、前の条件を通らなかった(falseと評価された)場合につぎの条件判定に進むという点が大切です。条件の順序をうまく設定すると、複雑な判定もシンプルに処理することができるのです。

うるう年の判別
複数の条件を使った判定の練習問題として、うるう年を調べる関数について考えてみます。年を整数で渡すと、その年がうるう年かどうかをブール(論理)値で返す関数です。

うるう年の決め方は、かつて2000年問題として知られるようになりました。原則として、4で割切れる年がうるう年です。ただし例外として、100で割切れる場合には、うるう年ではなくなります。それにはさらに例外があり、400で割切れるとうるう年にもどります。

3つ目の例外の例外を考慮していないプログラムが、400で割切れる2000年に問題とされたのでした。うるう年の決め方をひとつの条件として記述しようとすると、複雑になりますし、判定を誤る原因にもつながります。

みかん選別機の考え方を使えば、もっとシンプルに条件を構成できます。例外から除いていく、というのがひとつのコツです。

[1] まず第1に、例外の例外、「400で割切れる」という条件の穴を、一番手前にもってきます。すると、400で割れるうるう年は穴から抜けて、それ以外の年がつぎの穴に向かいます。

[2] つぎに第2として、例外となる「100で割切れる」条件の穴をセットします。400で割切れる年は一番手前の穴を落ちていますので、400で割切れない年のうち100で割切れる年がこの穴から抜けます。残るのは、例外に当てはまらない年のみです。

[3] そこで第3に、「4で割切れる」という条件を設定します。もはや100や400で割切れる例外の年は残っていませんので、原則にしたがって通常のうるう年がこの穴から抜けます。

[4] 最後に残ったのは、うるう年でない普通の年ということになります。3つの条件により、うるう年と普通の年の選別ができました。あとは、それぞれに応じた値を返せばよいだけです。

それでは、以上の考え方に沿って、渡された整数の年がうるう年かどうかをブール(論理)値で返す関数xIsLeapYear()を定義してみましょう(スクリプト05-008)。ある整数が別の整数で割切れるかどうかを調べるには、割った余りが返される剰余演算子%を用います。もちろん、割切れるとは、余りが0だということです。

Word 05-002■剰余演算子%
つぎのシンタックスで、式1を式2で割った余りがNumber型の数値で返されます。

式1 % 式2

「式」には、前述(Word 03-004「」)のとおり、単一の変数や単純な数値も含まれます。ふたつの式の値は、仕様としてはNumber型であれば足ります。しかし実際上、少なくとも割る数の式2は、正の整数を指定した方がよいでしょう(後述Maniac! 05-002「小数値や負数の剰余演算」参照)。

たとえば、365日後の曜日を知るには、Dateクラスを使わなくても、365日を1週間の7日で割った余りを求めればわかります。

trace(365%7);   // 出力: 1

余りが1ですので、明日と同じ曜日です。

また、120度回転したインスタンスをさらに270度同じ方向に回した場合の角度は、360度の剰余を求めれば、0度から360度までの角度で表すことができます。

trace((120+270)%360);   // 出力: 30

スクリプト05-008■うるう年を判定する関数の定義

// うるう年を判定する関数
function xIsLeapYear(nYear:int):Boolean {
  var bResult:Boolean;   // 戻り値をローカル変数宣言
  // まず例外から判定
  if (nYear%400 == 0) {   // 400で割り切れる
    // うるう年: trueを変数に代入
    bResult = true;
  // 400では割り切れない場合
  } else if (nYear%100 == 0) {   // 100で割り切れる
    // うるう年でない: falseを変数に代入
    bResult = false;
  // 100でも割り切れない場合
  } else if (nYear%4 == 0) {   // 4で割り切れる
    // うるう年: trueを変数に代入
    bResult = true;
  } else {   // その他の場合
    // うるう年でない: falseを変数に代入
    bResult = false;
  }
  return bResult;   // 変数の値を返す
}

スクリプト05-008の処理はコメントとして細かく加えましたので、内容についてそれ以上の説明は省きます。関数xIsLeapYear()に引数として年を示す整数を渡すと、うるう年ならtrueそうでなければfalseが返されます。

trace(xIsLeapYear(2000));   // 出力: true

Tips 05-007■if/else if/elseステートメントの[自動フォーマット]で余分な閉じ括弧}が入る
if/else if/elseステートメントを使った場合、elseステートメントの最後にコメントを入れると、[自動フォーマット]で不適切な記述に変わることがあります(Flash CS3 Professional 9.0.0.494)。

筆者の環境では、コメントがelseステートメントの外に出されたうえ、その後に余分な閉じる中括弧}が挿入されます(図05-009)。もちろん、中括弧の始まりと閉じの対応が合わなくなりますから、シンタックスエラーを発生します。

図05-009■[自動フォーマット]でコメント位置が変わり余分な括弧}が加わる


加わった閉じ中括弧}は、対応が合わないため、シンタックスエラーとなる。

なお、[パブリッシュ設定]で[ActionScriptのバージョン]を[ActionScript 2.0]に設定すると、この現象は解消するようです。


Maniac! 05-002■小数値や負数の剰余演算
剰余演算子%のふたつのオペランド(Word 03-005「オペランド」参照)はNumber型で定義されており、仕様の上では小数値もマイナスの数値も用いることができます。ただし、これらの数値の扱いは、注意が必要です。

まず、小数値については、ヘルプの[% 剰余演算子]の項(ヘルプに「modulo演算子」とあるのは、訳し漏れだと思われます)にも記載されているように、戻り値にわずかな誤差を含む場合があります。

trace(4.3%2.1);   // 出力: 0.09999999999999964

けれども、このような誤差を含む戻り値では、実際上処理の目的を遂げられない場合が多いでしょう。

つぎに、オペランドにマイナスの整数を使った剰余演算の問題は、答えが明白でないことです。もちろん、ActionScriptの仕様としては、定められています。

trace(5%(-3));   // 出力: 2
trace((-5)%3);   // 出力: -2
trace((-5)%(-3));   // 出力: -2

この仕様はJavaと同じです。しかし、結果がすぐに納得できるでしょうか。実際、プログラミング言語によっては、仕様が異なるようです。

少なくとも割る数が正の整数のときは、剰余も正数とした方が扱いやすいように思います。そうしたい場合には、割られる数に割る数の倍数を加えても、余りは変わらないという性質を使います。

たとえば、角度を0度から360度までの値で表す例で考えますと、以下のスクリプトは変数nDegreeに代入する値が-270でも450でも、数値90を[出力]します。

var nDegree:Number = -270;
var nResult:Number = (nDegree%360+360)%360;
trace(nResult);   // 出力: 90

処理の最適化
前記スクリプト05-008のうるう年を判定する関数xIsLeapYear()は、処理の最適化という観点からは少し考察すべき側面があります。これまた条件の順序に関わります。

05-03「左方向スクロールをループする ー else ifステートメント」で、if/else if/else条件の判定は、勝ち抜けクイズと同じだと述べました。それは、最初の問題に正解した人は、後の問題に答えなくてよいということです。逆に、最後まで正解できないと、すべての問題に答えるはめになります。つまり、後に残る人ほど、回答の手間が増えるのです。

そこで改めてスクリプト05-008の関数xIsLeapYear()を、条件判定の順序に注目して見ると、最初に勝ち抜けるのは400年に1度のうるう年です。つぎが、100年に1度の例外的にうるう年でない年です。3/4近くを占めるであろう普通の年は、最後のelseステートメントまで残されてしまいます。

もし大量のランダムな年を処理する必要があった場合には、スクリプト05-008の関数xIsLeapYear()では条件判定の効率が悪いということです。では、効率が上がるように、条件判定の順序を工夫してみましょう。もっともこの場合も、みかん選別の考え方は活用します。ただ、条件の設定に少しひねりが必要です(スクリプト05-009)。

最初の条件は「4で割切れない」として、まず普通の年を判定から抜けさせます。残るのは4で割切れる年だけですので、例外について判定を加えれば済みます。そこでつぎもひとひねりして、第2の条件も「100では割切れない」とすることで、普通のうるう年を先に抜出します。同じ要領で、第3の条件は「400で割切れない」とし、例外的にうるう年でない年を取出します。最後に残るのが、例外中の例外のうるう年です。

スクリプト05-009■うるう年を判定する関数に最適化の工夫を加える

//効率を考えた閏年を判定する関数
function xIsLeapYear(nYear:int):Boolean {
  // まず普通の年から判定
  if (nYear%4) {   // 4で割り切れない
    // 普通の年: falseを返す
    return false;
  //4で割り切れる場合
  } else if (nYear%100) {   // 100で割り切れない
    // 普通のうるう年: trueを返す
    return true;
  // 100で割り切れる場合
  } else if (nYear%400) {   // 400で割り切れない
    // 例外的に閏年でない: falseを返す
    return false;
  } else {   // 400で割り切れる場合
    // 例外中の例外のうるう年: trueを返す
    return true;
  }
}

この関数xIsLeapYear()は、前のスクリプト05-008と比べて、あとふたつ変更が加えてあります。

第1は、条件に剰余演算の式をそのまま指定し、たとえばif (nYear%4 != 0)のように論理式にしていないことです。しかし、前述Tips 05-003「if条件の評価」のとおり、数値を返す式が条件に指定されると、0(とNaN)以外はtrueと評価されます。

もっとも、論理式のかたちで記述しなかったことによる、最適化の効果はさほどありません。このような書き方もできるという程度に理解すれば結構です。

第2は、戻り値をローカル変数に代入せず、if/else if/elseステートメントから直接returnで値を返したことです。ローカル変数の宣言と代入を省いたことで、処理速度は少し(筆者のテストでは3割程度)上がります。もっとも、ActionScript 3.0は高速ですので、その差は1000万回処理を繰返したとしてコンマ数秒程度です。

とはいえ、効率のよい構成や処理を考えることは、一般にプログラムの品質を向上させます。また、もちろんチリも積もれば山となるでしょう。

他方で、処理スピードにばかり注目して、トリッキーなロジックを組立てれば、バグを生む原因にもな ります。正しく動作しないプログラムは、その時点で品質としては失格です。

処理効率をどれほど重視するかは、プログラム内容にもよるでしょう。シューティング・ゲームのようなコンテンツでしたら、処理スピードが重要かもしれません。

今回の例のうるう年の判定ですと、日本人全員の生年月日を最短時間で集計するなどという要求はあまり考えにくいといえます。そうだとすると、スクリプト05-008も十分合格点の範囲内です。

Tips 05-008■条件演算子?:を使う
条件演算子?:は、条件に応じた値を返します。つぎのシンタックスで、条件とふたつの式を指定し、条件の評価がtrueであれば式1、falseなら式2の値を返します。3つのオペランド(項)を取りますので、三項演算子とも呼ばれます。

条件 ? 式1 : 式2

?:は演算子として値を返しますので、代入式の右辺に記述して、戻り値を代入することができます。たとえば、以下のスクリプトは、今日がウィークディの場合はtrue、週末であればfalseを変数bWeekdayに代入し、その値を[出力]パネルに表示します。

var _date:Date = new Date();
var bWeekday:Boolean = (_date.day+1)%7>1 ? true : false;
trace(bWeekday);

条件演算子?:は、入れ子にして複数の条件を組合わせることが可能です。すると、うるう年を判定する関数は、1行のステートメントで記述できます(スクリプト05-010)。条件や入れ子構造がわかりやすいように括弧()をつけましたので、処理内容については式を細かく確認してみてください。

スクリプト05-010■条件演算子?:によるうるう年を判定する関数

function xIsLeapYear3(nYear:int):Boolean {
  return (nYear%4) ? false : ((nYear%100) ? true : ((nYear%400) ? false : true));
}

ひとつだけ注意は、1行で記述することは必ずしも最適化することを意味しません。実際、条件演算子?:を使った関数(スクリプト05-010)の処理速度は、前述の最適化したスクリプト05-009の関数とほとんど変わりはありません。

条件演算子?:は、値を返すことが明らかで、代入式の右辺に使えるという機能に特長があります。短い行数で記述できるというのは、副次的に捉えるべきでしょう。

[Prev/Next]


作成者: 野中文雄
更新日: 2008年3月18日 図05-000を追加。
更新日: 2008年3月9日 Tips 05-003にクロスリファレンス追加。
更新日: 2008年3月8日 Word 05-002を一部修正。
作成日: 2007年11月28日


Copyright © 2001-2007 Fumio Nonaka.  All rights reserved.