■第1回 はじめに
■第2回 神の計画
■第3回 誰のどこに「いつ、だれの、何を、どうする」を書く
■第4回 変数を使いこなす
■第5回 Mathランダム徹底研究
■第6回 回転のメカニズム(番外編)
■第7回 正六面体の回転(番外編)

 第5回 Mathランダム徹底研究 2004.02.03 .
 公共の演算サービスを提供する「Math計算センター」

501

急患のときは、だれでも「119番通報」すれば、救急車がきて病院に運んでくれます。事件や事故にあったときは、どこからでも「110番通報」すれば、特に警察署を指定しなくても警察官が来て対処してくれます。
人間社会の中では、このように個人や自分達のグループで個別に処理するより、公共サービスとして行ったほうが効率的で高度な処理ができることが沢山あります。
実はアクションスクリプトの世界でも、このような公共サービスが用意されています。
そのひとつに、だれでもどこからでも、通報があれば高度な数値演算処理を提供してくれる”公共の計算センター”ともいうべき「Mathオブジェクト(※注)」があります。
”だれでもどこからでも”とは、命令の主体であるムービークリップインスタンスがどの階層にあろうと、そのアクションスクリプトの中から、いきなり「110番通報」して依頼内容(命令)を伝えるだけで、その公共機関が代わりに内容を処理し、結果を指定した”ところ”に返してくれるというものです。
Math計算センターへの「通報」の仕方は、ただ「Math(マス)」と記載するだけでOKです。
そして、そのうしろに依頼内容(「何をどうしてほしい」)である「メソッド名(第3回”327”番参照)」を「.(ドット)」を介して記載します。
処理結果として返される「値」は、「変数」や特定のインスタンスの「プロパティ」に代入して受け取ることもできますし、そのまま「値」として計算式の中で使うこともできます。

 @変数 = Math.メソッド名(); 
   ※Math計算センターに対し、「何をどうしてほしい」を伝え、処理された”答”を変数の値として代入。
 Atarou._x = Math.メソッド名();
   ※同様にして、Math計算センターで処理された”答”を「tarouのX座標」プロパティの値として代入。
 B変数 = (変数 + Math.メソッド名();) × 5;
   ※同様にして、Math計算センターで処理された”答”を計算式の中でそのまま「値」として使う。

※※ご参考までに・・・
第3回”327”番の「インスタンスに対するメソッド(this.stop();など)」との使い方の違いを改めてみておきますと、

  パス.インスタンス名.メソッド名();
  ※指定したインスタンスに対し、「何をどうする」を命じる。(決して上記のように「代入式」にはならない)
  ※実行を命じる主体と対象のインスタンスとの階層を考えて適切なパスを記載して指定する。
    (Mathに対するパスはない)
(※注)
「オブジェクト」という難解な単語の正確な意味は別にして、参考図書などの中で、単独で「オブジェクトが」とあれば「何か”もの”が」と、「○○オブジェクト」とあれば「○○という”もの”
」と、とりあえず読み替えてください。
その上で解説を読み進んでいくと、文脈からそれが”大体どんなもの”かぼんやり分かってくるかと思います。
この講座では、憲ちゃんが”こんなもの”と考えるイメージに近い単語を( )内に記載したり、文章で解説しています。
第4回までの「オブジェクト」は、編集画面で扱う様々な「部品」という意味で、ここでは「特別な仕事を行うためにあらかじめ用意された”仕組み”や”装置”」といった意味でとらえていただければ理解しやすいでしょう。

 加減乗除は自分でする

502

Math計算センターに依頼する高度な数値演算処理「メソッド」をご説明する前に、計算センターに頼らなくても自らできる数値演算について、簡単にご説明しておきます。

@アクションスクリプトにおいても、算数で使う「+」「-」「*(×)」「/(÷)」は全く同じ機能で使います。
これを「算術演算子」といいます。
算術演算子の中で毛色の変わったものに「%」があります。使い方は・・・

 変数 = 10 % 3; と記載して、

これを算数でいいますと、10 ÷ 3 = 3 の「
余り」を求めること。
「10」を「3」で割った余りを計算せよ!の意味で、変数の値は、「1」になります。
※一般でいう「パーセント(100分比)」という意味では使われません。

A「代入演算子」
 「=」:これまでもご説明してきた、右辺の値を左辺に代入(左辺←右辺)。
 「+=」:左辺の値に右辺の値を加えて、結果を改めて左辺に代入。
      A += B; つまり A = A + B; の省略形のこと
    ※前回まで使っていた、X座標の移動で・・・
      this._x = this._x + 11; ←現在のX座標の値に「11」を加えたものを、自分のX座標値として再配置
     これは、
      this._x += 11; と置き換えることができます。
同様に・・・
 「-=」:左辺の値から右辺の値を差し引いて、結果を改めて左辺に代入。
      A -= B; つまり A = A - B;の省略形のこと
 「*=」:左辺の値に右辺の値を掛け算して、結果を改めて左辺に代入。
      A *= B; つまり A = A * B;の省略形のこと
 
 「/=」:左辺の値から右辺の値を割り算して、結果を改めて左辺に代入。
      A /= B; つまり A = A / B;の省略形のこと

 「%=」:左辺の値から右辺の値を割り算した余りを、結果を改めて左辺に代入。
      A %= B; つまり A = A % B;の省略形のこと


B「インクリメント」「デクリメント」
 「++」:インクリメントといって、A++; と記載し、Aを1だけ加算する。(A = A + 1;の省略形のこと)
      Aの値が「5」だとして、A ++; を実行した結果、Aの値は「6」に変化する。
 「--」:デクリメントといって、A --; と記載し、Aを1だけ減算する。 (A = A - 1;の省略形のこと)
      Aの値が「5」だとして、A --; を実行した結果、Aの値は「4」に変化する。
 ※実行の繰り返しの中で、自動的にカウントアップ、カウントダウンしたいときなどに使います。

 高度な演算はMath計算センターにおまかせ!

503

以上は数値の”加減乗除”と”左辺←(代入)右辺”が中心ですが、実生活では、これ以外に小数点以下の四捨五入や切り上げ、切捨てによる整数化が必要だったり、中学校の”数学”レベルともなると「sin、cos、tan」といった三角関数、絶対値や平方根といった思い出すだけでも頭の痛い数値演算も扱いました(笑)
こうした高度な演算について「Math計算センター」に依頼すれば即座に引き受けてくれるというわけです。
個々詳細は参考図書などをご覧いただくとして、ここではその内一般によく使われるメソッドだけをご紹介します。

@小数点以下の整数化(小数点なしの数値にまるめること)
各メソッドの( )内に記載した数値や変数の値を、それぞれ整数化してくれます。
 ○四捨五入  Math.round ( 数値 );
 ○切り捨て  Math.floor( 数値 );
 ○切り上げ  Math.ceil( 数値 );
※この中でも特に「切り捨て」を多く使います。

A乱数の発生
          Math.random();
これを実行するたびに、その都度「乱数(前後関連のない不規則な値)」を提供してくれるので、FLASHで「おみくじ」や、各種ゲームなど一定の確率で何かをさせたり、予測しない動作を作りたいときなどに使われます。
ここで得られる「乱数」は、「0.000000000000000」から「0.999999999999999(1.0未満)」までのいずれかの値です。
          例:0.342988126456708
したがって、このままでは使いにくいので、実際には用途に応じて加工して使うことになります。

 「乱数」を表示してみよう!

504

「乱数を加工する」といっても、はじめて乱数に接する方にとっては、小数点以下何桁もある数値がランダムに発生することのイメージがつかみにくいと思います。
■そこで、第4回でご説明した「テキストボックス変数」を利用し、実際に乱数を発生させて、それを目で確認できる「乱数ツール」をFLASHで制作してみることにします。
@FLASH編集画面のメニューバー「ファイル」→「新規」で新しいファイル(random.fla)
を作ります。
Aムービー(ドキュメント)の大きさサイズを「500×250」にして、適当に背景色を選択してください。
Bレイヤーは「アクション」「実行ボタン」「テキスト」の3段にし、フレームは2フレームとします。(下図)
C「アクション」レイヤーの2フレームに空白キーフレームを挿入し、そこにフレームアクションで「Stop」アクション又は「this.stop();」として、タイムラインを停止するアクションスクリプトを記載。
※1フレームのフレームアクションについては、ここの”メインテーマ”ですので最後にご説明します。
D「実行ボタン」レイヤーの1フレームに適当に図形と文字を描き、これを「ボタン」インスタンスとします。
そして、ボタンアクションで、

 on( release ){ ←クリックされたら
   play();(又は「this.play();」) ←タイムラインを進行
 }
※ムービーが再生されると、1フレームのフレームアクション(まだ記載されていませんが)を実行して、2フレームで停止、以後ボタンがクリックされるたびに、1フレームのフレームアクションを実行して2フレームで停止するという、ループの仕組みをつくります。
E「テキスト」レイヤーで、文字ツールを使い、下図のように説明用の「静止テキスト」文字の記載と「ダイナミックテキスト」及び「テキスト入力」の「テキストボックス」を配置します。


※黄枠で囲っているのが「ダイナミックテキスト」、赤枠で囲っているのが「テキスト入力」のテキストボックス。
※それ以外の白文字は、内容を説明するための「静止テキスト」で書かれた普通の文字です。

Fテキストボックスにそれぞれ図中、黄文字で書かれた@〜Gまでの「テキストボックス変数」を設定してください。
※冒頭の○数字は記載せず、変数名としては「アルファベット」のみ記載してください。
   theRandom RndA など
この内「テキスト入力」の「Cbaisu」、「Dtyosei」については、「ダイナミックテキスト」のテキストボックスにも同じ変数名を2つづつ設定します。
G「テキスト入力」の初期値として、倍数値テキストボックスには「1」、調整値テクストボックスに「0」を記載しておきます。
H最後に、「アクション」レイヤーの1フレームにフレームアクションで、次のように記載します。

 theRandom = Math.random(); ←発生した「乱数」の値を変数theRandom に代入
 nBaisu = Number( baisu;     ←(※1)倍数値を「文字→数値に変換」し、変数nBaisuに代入
 nTyosei = Number( tyosei ;   ←(※2)調整値を「文字→数値に変換」し、変数nTyoseiに代入
 RndA = theRandom * nBaisu + nTyosei; ←「
変数「theRandom×倍数値+調整値」の結果を変数RandA に代入 
 RndB = Math.floor( theRandom * nBaisu + nTyosei ;←上記結果の小数点以下を切捨て変数RndBに代入
 minRndB = nTyosei; ←調整値を変数minRndB(RndBの最小値)に代入
 maxRndB = ( nBaisu - 1 ) + nTyosei; ←「(倍数値−1)+調整値」を変数maxRndB(RndBの最大値)に代入
 kosuRndB = maxRndB - minRndB + 1;←「RndBの最大値−RndBの最小値+1」を変数kosuRndB(個数)に代入

(※1・※2)「テキスト入力」のテキストボックスから入力される変数baisu並びに変数tyosei の値は、第4回”408”番Bでご説明しているように加減乗除できない”数字文字”です。
したがって、これをそのまま各右辺の計算式の中で使うことができません(使うと”変な答”がでます)。
Number( ”数字文字”」というのは、”数字文字”を、加減乗除できる「数値」に変換する「関数(変換アダプター)」のことで、これを使って、変数の値を”数字文字”から「数値」に変換し、新しく変数nBaisu並びに変数nTyosei に代入、以後、この新しい変数を計算式の中で使用しています。
※ご参考までに、その逆「数値」を”数字文字”に変換する変換アダプターは、「String( 数値」です。

Iメニューバー「制御」→「ムービープレビュー」で動作を確認してください。
以下がそのムービーです(^^



random.fla←このムービーのサンプル制作ファイルです。
ご自分で制作するのが面倒な方は、右クリックして「対象をファイルに保存」でダウンロードできます。

505

「theRandom = Math.random();」が実行され、その下の変数theRandomの右に表示される値が実際の「乱数」です。
「実行」ボタンをクリックするたびに、新しい乱数が次々表示されますので、出方を確認してみてください。
「実行」ボタンの下にある「倍数値」及び「調整値」のテキストボックス内をクリックすれば、その中に任意の数字を入力することができます。
最初は「倍数値」が「1」、「調整値」が「0」なので、変数RndAの値は、上記の変数theRandomと同じです。
変数RndBの値は、変数RndAの小数点以下を切り捨てた整数なので、この状態では何度実行しても「0」から変化することはありません。
※任意の数字を入力すると、同じ変数名を使っている変数RndA及び変数RndBの右辺の式の該当箇所の数字もそれに応じて変化し式が完成されますので、右辺の式と実際の値をイメージさせながらいろいろな値で試してみてください。

506

「調整値」を「0」のまま、「倍数値」を「10」に変更して「実行」ボタンをクリック。
変数RndAの値は、「乱数」の10倍の値、つまり小数点がひとつ右にシフトした値に変化します。
それの小数点以下を切り捨てて整数化した変数RndBの値もそれに応じて変化しているはずです。
しばらくこの状態で、何度も「実行」ボタンをクリックして、各変数の値を比較してください。
変数RndBには、その右にある「最小値:0」と「最大値:9」の間のいずれかの整数がランダムに表示されます。
そして、「RndBの個数:10」とあるのは、この”式から発生する整数の最大個数(10種類)”のことで、「倍数値」と対応しています。
つまり、「RndB」の右辺の式は、「0〜9」までの10個の整数をランダムに発生する式というわけです

アクションスクリプトの中で、「乱数を加工」して使用するというのは、主にこの変数RndBを導く式・・・

 Rnd = Math.floor(Math.random()*倍数値(個数)+調整値

を活用することで、通常このように「Math.random」は「Math.floor」とのセットで用いられます。

 乱数式の活用方法

507

では、上の式を活用して具体的に何ができるのでしょうか?
「RndBの個数(=倍数値)」に着目して、この場合、「0〜9」までの「10種類」のランダムな整数値が得られますから、この内「0」がでる確率は「10分の1」ということです。
例えば、「くじ」ボタンをクリックして、変数Rndが「0」のとき「あたり」、そうでないとき「はずれ」という場合、
「くじ」ボタンのボタンアクションで・・・

 on (release) {    ←ボタンがクリックされたら
   Rnd = Math.floor(Math.random() *10);←0〜9の「10種類」の整数をランダムに発生し変数Rndに代入

   if ( Rnd == 0 ) {      (その結果)もし、変数Rndが「0」に等しいなら
     this.gotoAndStop( "atari" ); ←自分のタイムラインの「atari」フレームへジャンプして停止
   } else {             そうでないなら
     this.gotoAndStop( "hazure" ); ←自分のタイムラインの「hazure」フレームへジャンプして停止
   
 }

イベントの種類や「あたり」のとき、「はずれ」のときの実行内容は、これ以外にも工夫次第で様々考えられますが、
太字の部分が「確率」として使用する場合のパターンだと覚えておいてください。
そして赤字の数字が確率の倍数になり、仮に「10」を「100」に置き換えれば「100分の1の確率」で、ある処理を実行することができるということです。

508

「サイコロ」の場合を考えてみましょう。
サイコロは、転がすたびに「1〜6」までの6種類の”目”が6分の1の確率でランダムに出現するものです。
この場合は「確率」というより、発生する変数Rndの値に応じたサイコロの目の数が対象になります。
「倍数値」を「6」にしただけでは、変数RndBの値は、「0〜5」の6種類ですから、「調整値」として「1」を加えて「1〜6」にする必要があります。
実際に、乱数ツールで「倍数値:6」、「調整値:1」を入力して値の出方を試してみてください。
そして例えば、サイコロの目を描いた図形を、あらかじめ10フレームから15フレームに順番に入れておいて、ボタンをクリックし、変数Rndの値が「10」なら10フレーム、「13」なら13フレームにジャンプさせ、該当する画像を表示させるというようにするなら、ボタンのボタンアクションは・・・

 on (release) {
   Rnd = Math.floor(Math.random()*6+10);←「10〜15」の6種類の整数をランダムに発生しRndに代入
   this.gotoAndStop( Rnd ); ←変数Rndの値で指定されるフレームにジャンプして停止
 }

このようなスクリプトになります。
同じ方法で「おみくじ」や「トランプ」に応用できるほか、インスタンス名の末尾やswfファイル名の末尾に「数字」を充て、ランダムに発生した整数値と対応させて読み込むといったことなどにも、この方法が活用できます。
これもイベントの種類や、対応させる対象について工夫次第で様々考えられますが、ランダムに発生した個々の整数値に着目し個別に対象を指定して実行する場合のパターンは、このようなものです。
青字の「個数」は対象の総数、「調整値」はそのときどきの対象の状況によって適宜調整します。

509

上記”508”番の同じ式で、変数Rndの値を個別の対象でなく、特定のムービークリップインスタンスの「プロパティの値」と読み替えれば、乱数ツールの「個数」は値の幅(範囲の大きさ)ということになります。

 onClipEvent (enterFrame) {  ←フレームレートごとに以下のことを繰り返し実行
   Rnd = Math.floor( Math.random() *11 -5 ); ←「-5〜5」の範囲で乱数を発生し変数Rndに代入
   this._x = this._x + Rnd; ←現在のX座標値に変数Rndの値を加え、それを新しいX座標値として再配置
 }

プロパティの値の場合、整数でなければならないということはありませんので、乱数ツールにある整数化前の「RndA」の式を活用しても構わないことになります。
ただし、乱数を整数化しないで使用するときは少し注意が必要です。
仮に乱数ツールで「倍数値:11」、「調整値:−5」として「実行」ボタンをクリックすると、最小値「−5※」最大値「+5」、個数(値の幅)「11」と表示されます。
何度も「実行」ボタンをクリックして変数RndAの値(小数点付)の変化を確認してみてください。
最小値「-4.999・・」、最大値「5.999・・」となり、実際の値の幅が「約−5」〜「約+6」の約12と「約1」だけ広がります。
また、値が丁度「3.000000000000・・」のように整数になることは理論上は考えられますが、その確率はほとんど無いに等しいものです。
したがってもし、このような「値の範囲の誤差」が問題になる場合やif条件式等の中で「A==B(等式)」を使用する必要があるときには、乱数を整数化していないと正しく動作しないことになります。
その他、条件によっては予期しないトラブルも考えられますので、はじめのうちはできるだけ整数化して使用されることをお勧めします。
(※参考※)
「-4.9999・・」を切り捨てて整数化すると、一見「−4」と思われるでしょうが、Math.floor()の切り捨ては、元の小数点付数値を”超えない”最大の整数値を返します。マイナスの数値の場合「−4」は「-4.999・・」を”超える(大きい)”整数ですから、”超えない”最大の整数は「−5」になります。

 インベーダをランダムに移動させる

510

さっそく、”509”番のアクションスクリプトを前回までの「tarou」インスタンスに移植して、動きを確認してみましょう。
■前回の「invader.fla」を開いて、「tarou
」のクリップアクションをみると・・・

 onClipEvent (enterFrame) {
   this._x = this._x +11 * _root.move;
   _root.tarouX = this._x;
   if (this._x > 440) {
     this._x = 56;
   }
 }

このようになっています。
「移動量」が太字の「+11」ですから、フレームレートごとに毎回、右に「11」移動する等速運動です。
この部分に「乱数」をあてはめれば、毎回、「乱数の値」だけ移動する不規則運動になるはずです。
@まずは「11」に代えて、上記”509”番「Rnd」の右辺の式をそのまま記載してください。

  this._x = this._x + Math.floor(Math.random()*11-5) * _root.move;



Aメニューバー「制御」→「ムービープレビュー」で動作を確認してください。

「tarou」が左右にブルブル振るえるような感じに動くようになると思います。
これは、フレームレートごとに毎回、「−5〜+5」の間のいずれかの整数値で移動しますが、正負(右移動と左移動)の確率は同じなので、最初の位置から大きく位置を変えることなく、その場で右往左往するからです。
※「調整値」の「−5」を「0」にすると、計算後の乱数が負になることがなく、右一方通行のランダムな移動量で移動し、「−10」にすると、正になることがなく、左一方通行でランダムに移動します。
またこれを仮に「−3」としてやると、値の範囲が「−3〜+7」となって正負の割合が「7:3」となり、左右の移動を繰り返しながらも少しづつ右方向へ移動(右傾化)していくことになります。
※「倍数値」の「11」の値を大きくすれば左右の振れ幅が大きくなり、値を小さくすれば振幅も小さくなります。
■実際にいろいろに「値」を変えて、数値と動作の関連をご自分の目で確認しておいてください。

511

上記@の代入式を”502”番A代入演算子「+=」を使った省略形で記載すれば・・・

  this._x += Math.floor(Math.random()*11-5) * _root.move;

そして、変数「_root.move」はこの際、乱数移動と直接関係がありませんのでとりあえず削除し、また以前の条件文は、このままではあまり意味がありませんので、これも削除して、

 onClipEvent (enterFrame) { フレームレートごとに毎回以下のことを繰り返し実行
   this._x += Math.floor(Math.random() * 11 -5 ); ←自分の位置を”乱数による移動量”だけ移動
 }

最終的に、このようにシンプルに記載して実験してください。

512

”乱数による移動量”計算を上記のように毎回実行せずに、”ある確率”のときだけ実行するよう仕組めば、もっと面白い動きをさせることができます。
普段は一定の”移動量の値”で等速移動を毎回繰り返し、”ある確率”のとき”乱数による移動量”を計算し以後その”移動量の値”で等速移動を繰り返すという仕組みにすると考えれば・・・
@まず、その”移動量の値”を別途一時保管しておく変数が必要になります。
X座標の移動量を一時保管する変数を例えば「xSpeed」とすると、
A乱数を使わない、通常の等速移動のスクリプトは・・・

  onClipEvent (enterFrame) {
   this._x += xSpeed; ←変数xSpeedの値だけ自分の位置を移動
 }

仮に変数xSpeedの値が「20」だとすれば、フレームレートごとに毎回右に「20px」等速で永久に移動します。
Bこの変数xSpeedの値を、”ある確率”のときだけ、”乱数による移動量”を使って計算するには、
乱数を確率として使用する書式は、上記”507”番の・・・

 Rnd = Math.floor( Math.random() * 倍数 );
 if ( Rnd == 0 ) {
   実行するアクション
 

でしたから、ここの「実行するアクション」の中で”乱数による移動量”を計算させればよいわけです。
Cそして”乱数による移動量”の値を変数xSpeedに代入して・・・

 Rnd = Math.floor( Math.random() * 倍数 ); 「0〜(
倍数-1)」の整数をランダムに発生し変数Rndに代入
 if ( Rnd == 0 ) {            (その結果)変数Rndが「0」なら
   xSpeed
= Math.floor(Math.random()*11-5) ←右辺の式で得られる乱数を変数xSpeedに代入
 


このようにすることができます。
DこれをAのスクリプトの中に追加記載して・・・

 onClipEvent (enterFrame) {  フレームレートごとに以下のことを繰り返し実行
   this._x += xSpeed; ←変数xSpeedの値だけ自分の位置を移動
   Rnd = Math.floor( Math.random() * 倍数 ); 「0〜(倍数-1)」の整数をランダムに発生し変数Rndに代入
   if ( Rnd == 0 ) {            (その結果)変数Rndが「0」なら
     xSpeed
= Math.floor(Math.random()*11-5) ←右辺の式で得られる乱数を変数xSpeedに代入
   

 }

「this._x += xSpeed;」は毎回、実行されて等速移動を繰り返しますが、
「EnterFrameエンジン」の回転数に対する”ある確率”のときだけ、”乱数による移動量”を計算し、
変数xSpeedの値を変更します。
変更された次の実行から、その移動方向・移動量にしたがって、引き続き等速移動を繰り返します。
インベーダが不規則に向きや速度を変えるスクリプトの一応のできあがりです。

※ここの「倍数」の値を大きくすれば、変化のタイミングが遅くなりそれだけ永く現在の移動を維持し、値が小さいと変化のタイミングが速く小刻みな動きになります。
具体的な数値は、移動量計算の際の「倍数値」「調整値」とも関連しますので、それぞれいろいろな値を試しながら調整してください。

 クリップアクションで使用する変数の初期設定は「onClipEvent(load)」で…

513

不規則に向きや速度を変えるインベーダの仕組みは大体以上のようなことですが・・
これを実際にムービーを再生して確認すると、しばらく右にも左にも動かないはずです。
原因は、 このムービークリップインスタンスが読み込まれた当初は、移動量である”変数xSpeedの中に何の値も入っていない”からです。
変数に値が代入されるのは「変数=値」というように、変数が左辺にある場合であって、「this._x += xSpeed;」のように右辺にあるときは、”変数の値を調べて取得する”意味ですから、最初にこれを実行しても”調べに行った結果何もなかったので使えなかった”という状態なのです。
そして、変数Rndの値がある確率で「0」になり移動量計算の式に行き当って初めて、

 xSpeed = Math.floor(Math.random()*11-5); ←右辺の式で得られる乱数を左辺の変数xSpeedに代入

変数xSpeedに値が代入され、それ以後は「this._x += xSpeed;」が有効になります。
第4回”402” でご説明した「変数を定義する」の具体事例のひとつがこのことです。
※「変数を定義する」とは、
  変数に値が代入され、どこからでもその変数を調べに行けば値を取得(参照)できる状態にすること。

514

ではこの場合、変数Rndの値が最初に「0」になって、変数xSpeedが定義されるまで我慢して待たないといけないのでしょうか?
変数の定義は、そのムービークリップインスタンスの中ならどこでも、ただ「変数=値」と記載するだけでしたから、この場合は、「EnterFrameイベント」が発生する前に、「xSpeed = 値」と記載しておけばよいわけです。
そして、「EnterFrameイベント」が発生する前に発生するイベントが、「Loadイベント」です。(第3回”312”番参照)
したがって、以下のように・・・

 onClipEvent (load) { (これが記載されたムービークリップ)インスタンスが読み込まれた最初に
   xSpeed = 20;       ←変数xSpeedに「値20」を代入(して、即使えるように定義する)
 
 onClipEvent (enterFrame) {
   this._x += xSpeed;
   ・・・・・・・・・・
 }

「onClipEvent()」ハンドラの「Loadイベント」の中に、「xSpeed = 値」と記載して変数xSpeedを定義しておきます。
ここでは、ムービー再生時の最初の移動量の設定ですから、値を「0」にすれば、変数は定義されても最初は同じように動かないことになってしまいます。
この例のように「0」以外の実効ある数値にしておきます。

 「変数=−(変数)」は、方向転換

515

「Loadイベント」の中で変数の初期値を設定し、「EnterFrameエンジン」の中でその変数と乱数を使ってランダムに移動する仕組みもできました。
ところが、このままでムービーを再生すると勝手に動き回わるのはいいのですが、画面の外に出てしまうこともありますので、行動範囲を限定する必要があります。
この場合も、前回同様「if条件文」を使うことになりますが、今回は行動範囲が左右に及びますので、左右にバリアが必要です。その範囲をここでは「X=56」〜「X=440」とすることにします。
そして、バリアを超えたときの行動として、ある特定の座標に強制的に戻すのではなく、ボールが壁に当たって跳ね返るように方向だけを反転させるようにしてみたいと思います。
移動の方向というのは、「移動量」の値の正負で決まります。

 onClipEvent (enterFrame) {
   this._x += xSpeed
 }

のとき、変数xSpeedの値が「+」なら右方向へ、「−」なら左方向へ移動する理屈はお分かりのことと思います。
変数xSpeedの値が「+11」とすれば、右方向に「11px」づつ移動します。そして「this._x」が「X=440」を超えたとき、変数xSpeedの値を「-11」に変えることができれば、その場で向きを反転させて左方向に「11px」づつ移動を始めることになります。
したがって、変数xSpeedの値の大きさに関係なく、正負符号だけを逆にすればよいので・・・ その場合、

 xSpeed = -xSpeed;

このように、右辺の変数の前に「マイナス符号」をつけます。
※現在の変数xSpeedの値を取得し、それにマイナス符号を付した値を、左辺の変数xSpeedに代入。
そこで、if条件文で・・・

 if ( this._x > 440 ) {
   xSpeed = -xSpeed;
 }

として、自分の位置が「440」を超えたとき、変数xSpeedの正負符号を反転させ左方向に向きを変えさせます。
そして左方向に移動を続けていた「this._x」が、「56」を下回ったときは、変数xSpeedの値を「+」に変えます。
このときの右辺の変数xSpeedの値はマイナスですが、マイナス値にマイナス符号を付けると「+」になるので、変数xSpeedの正負符号がいずれの場合であっても、同じ代入式が使えることになります。
上記の if条件文に続けて・・・

 if ( this._x > 440 ) {   もし自分のX座標値が「440」を超えていたら
   xSpeed = -xSpeed;      ←変数xSpeedの正負符号を反転する
 } else if ( this._x < 56 ) { そうでないときで、もし自分のX座標値が「56」を下回っていたら
   xSpeed = -xSpeed;      ←変数xSpeedの正負符号を反転する
 }

とすれば、「this._x」は、「X=56」〜「X=440」の間に封じ込めることができます。

516

勿論、このまま使用しても正常に動作しますが、これを「論理演算子」というものを使って、すっきりしたスクリプトに書き換えることもできます。

論理演算子というのは、以下のようなものです。
 「&&」:ANDのことで、左辺であり”かつ”右辺であるときという意味。
  A && B のとき、Aの内容(条件)もBの内容(条件)も両方とも満たしている必要があるので「必要条件」のこと。
 「||」:ORのことで、左辺であるか”又は”右辺であるときという意味。
  A || B のとき、Aの内容(条件)かBの内容(条件)のどちらか一方でも満たしておればそれで充分であるので
             「充分条件」のこと。
 「!」:NOTのことで、否定の意味。具体には必要なとき事例とともにご説明します。

ここでは、「this._xの値」が「56を下回る」か”又は”「440を超える」場合に、正負符号を入れ替えればよいので、
”OR”を使って・・・

 if (this._x < 56 || this._x > 440 ) {
   xSpeed = -xSpeed;
 }

と記載することができます。
※論理演算子の両辺に、それぞれ条件式を省略せずに記載してください。
  if (this._x < 56 || > 440 )という記載の仕方はありません。

517

■この方向転換の動作だけを確認するため、「乱数による移動」はひとまずおいて・・・
以下のスクリプトを「hanako」のクリップアクションに記載してください。

 onClipEvent (load) {  (これが記載されたムービークリップ)インスタンスが読み込まれた最初に
   xSpeed = -11;    ←変数xSpeedに「-11」を代入(して定義)
 }
 onClipEvent (enterFrame) {  フレームレートごとに以下のことを繰り返し実行
   this._x += xSpeed; ←自分のX座標を変数xSpeedの値だけ移動
   if (this._x <56 || this._x > 440) { (その結果)自分のX座標値が「56」を下回るか「440」を超えたとき
     xSpeed = -xSpeed;       ←変数xSpeedの正負符号を反転する
   }
   _root.hanakoX = this._x; ←シーンのテキストボックス変数hanakoXに自分のX座標値を代入
 }


■「ムービープレビュー」で確認すると・・・
「hanako」が、X=56、X=440の間で、左右に往復運動し、その時々の値が左下の「テキストボックス」に表示され
ます。

 問題が起こったときは具体的な数値を想定して考える

518

さて、いよいよこれに乱数を組み合わせて「インベーダ」を移動させます。
この「hanako」のスクリプトに、上記”512”番Dの乱数移動のスクリプトを組み合わせて、
■以下のとおり「tarou」のクリップアクションに記載してください。

 onClipEvent (load) {  (これが記載されたムービークリップ)インスタンスが読み込まれた最初に
   xSpeed = 20 ←変数xSpeedに初期移動量として「20」を代入(定義)
 }
 onClipEvent (enterFrame) {  フレームレートごとに以下のことを繰り返し実行
   this._x += xSpeed; ←自分のX座標を変数xSpeedの値だけ移動
   if (this._x <56 || this._x > 440) { (その結果)自分のX座標値が「56」を下回るか「440」を超えたとき
     xSpeed = -xSpeed;       ←変数xSpeedの正負符号を反転する
   }
   Rnd = Math.floor(Math.random()*10); 「0〜9
」の整数をランダムに発生し変数Rndに代入
   if ( Rnd == 0 ) {            (その結果)変数Rndが「0」なら
     xSpeed = Math.floor(Math.random()*41-20);
←右辺の式で得られる乱数を変数xSpeedに代入
   }

   _root.tarouX = this._x; ←シーンのテキストボックス変数tarouXに自分のX座標値を代入
 }

※乱数の倍数や調整値もひとまず太字のとおりの値で記載してください。

■一見うまく行きそうに見えますが、実際に「ムービープレビュー」で確認してください。
 

519

ムービーの再生が始まると左右にランダムに振れながら順調に移動しますが、あるとき突然、どちらかの端でピクピクと痙攣をおこし、しばらくその状態から抜け出ることができなくなります。
もし、そのような状態にならないときは、なるまで我慢してそのまま見ていてください(笑)
スクリプトをいくら論理的に筋道立てて検証しても、間違ってそうにないし・・・バグ(出荷前に潜んでいた予期しないプログラムの不具合)ではないかとマクロメディア社を疑う方もおられるかもしれません(笑)
ですが決してバグなどではありません。
このような予期しない事態になったときは、スクリプトを眺めるだけの表面的な筋道でなく、そこに極端なケースを想定し具体的な数値をあてはめて検証する必要があります。
このケースの場合・・・
@条件式で指定している境界線付近でいつも痙攣が起こるので、境界線付近の細かい動作に注目します。
A乱数を使わなかったときは、このような現象は起こらなかったのだから、境界線付近での乱数の出方とも関係するのではと考えてみます。
B変数xSpeedが一定の値のとき、自分自身が境界線を超えると条件式を満たして正負符号が反転し、次の瞬間は同じだけ戻ることになります。 戻ったときは条件式を満たさないので、反転したまま境界線から離れて行くので問題はありませんでした。
※例えば「this._x」が「X=438」の状態から「+11」移動して「449」になったとき、「this._x >440」を満たし、変数xSpeedは正負反転して「-11」
となり、次の移動では、直前の位置である「X=438」に戻って、条件式を満たさないからOK。
C移動量が乱数の場合、「this._x」が「X=438」
にあるとき、移動量が「+15」だったとすれば、次の移動で「X=453」となり「this._x >440」 を満たします。
そして正負反転し「-15」となりますが、たまたま次の瞬間、変数Rndが「0」となって乱数の移動量計算が行われると、せっかく反転した移動量「-15」は、再計算された移動量に入れ替わります。
再計算された移動量が、「X=440」以内に戻れるだけのマイナスの値(〜-13)であれば、「this._x >440」の条件を満たさずそのまま左へ移動を続けてくれますが、プラスを含むそれ以上のマイナスの値(-12〜-1)であれば、次の移動の結果も条件式を満たした状態です。
条件式を満たしたまま移動すると「xSpeed = -xSpeed」が適用され、次回、変数Rndが「0」になるまで同じ値の移動量のまま毎回正負符号を入れ替えるので、その場で左右に振るえる結果になったというわけです。

520

原因は、条件式を満たした状態のときに、移動量を再計算して、反転した移動量の値を変更してしまったことでした。
原因が分かったのでその解決策は、 条件式を満たしたときには、再計算させないようにすることです。
整理して詳細にご説明しますと・・・

今までは、
@条件式を満たしたときは、移動量の正負符号を反転させた。
A変数Rnd以下の「確率計算」には何の条件もつけていなかったので、毎回実行されていた。
                             ※※つまり、条件式を満たしても実行されていた。
 onClipEvent (enterFrame) {
   if ( 条件式 ) {
       @移動量の正負符号を反転(※条件式を満たすときは実行)
   
   A確率計算と”乱数による移動量”の再計算(※毎回実行)
 }

それを・・・
@条件式を満たしたとき、移動量の正負符号の反転させ、
A条件式を満たさないとき、変数Rnd以下の「確率計算」
をさせる。

 onClipEvent (enterFrame) {
   if ( 条件式 ) {
       @移動量の正負符号を反転(※条件式を満たすときだけ実行)
   } else {
       A確率計算と”乱数による移動量”の再計算(※条件式を満たさないときだけ実行)
                             ※※つまり条件式を満たすときは実行されない。
 }

に、改めるということです。
そこで修正後のスクリプトは・・・

 onClipEvent (enterFrame) {
   this._x += xSpeed;
   if (this._x <56 || this._x > 440) { ※条件式を満たしたとき
     xSpeed = -xSpeed;               ←変数xSpeedの正負符号を反転する
   } else {                ※そうでないとき
     Rnd = Math.floor(Math.random()*10);
 ←確率計算と”乱数による移動量”の再計算
     if ( Rnd == 0 ) {
       xSpeed = Math.floor(Math.random()*41-20);

     }

   }
   _root.tarouX = this._x;
 }


修正前と修正後のスクリプトを比べると、見た目には条件式に「else」がないかあるかだけの些細なことです。
しかし、その意味は微妙に異なり、その微妙な意味の違いが大きな問題になることがあります。
一度分かってしまうと、当然のことのように思ってしまうのですが、分かるまでは、違いが些細で微妙なだけに、原因の発見もそれだけむつかしいのです。
この問題の解決方法は、これひとつではありませんし、この段階で完璧という自信もまだありません。
問題事例もこれはほんの一例ですが、アクションスクリプトを組んでいるとこうした問題に突き当たることがよくあります。
こうした類のミスは、いくら本で勉強しても問題の解決に繋がらないし、いくらスクリプトに精通した方でも防げるものではありません。
初心者の方と精通した方の違いは、どれだけ多く失敗をし、問題に遭遇したときここでたどったような道のりで、どれだけ多く解決した経験をもっているかという量の違いだけです。”頭脳”ではなく、この経験が”勘”を働かせるのです。
やはり、失敗は初心者のときから努めて沢山するほうがよいみたいですよ(^^g/~~

※もしあなたが、このページをただ、目で追って読んでおられるだけで”分かった!”と満足しておられるなら、それはアクションスクリプトを理解するうえで遠回りです(笑)
※実際にここのとおり順を追ってやってみて、是非、失敗を実感されることをお勧めします(*^.^*)

 上下左右ランダムに動くインベーダ

521

さて、ここまでくれば、インベーダを上下にも移動させたいと思うのが人情です!
ムービークリップインスタンスのY座標のプロパティは「_y(アンダーバーと小文字のy)」です。
これまでの「this._x」に「this._y」の動きも同時に記載すれば、簡単に上下左右ランダムに移動するインベーダができあがります。
「乱数計算」の中で扱う、「確率倍数」や「値の幅」「調整値」の値によって動き方が変わりますので、これをあとからでも簡単に変更できるよう専用の変数を作成し、最初に「onClipEvent(load)」の中で定義しておくことにします。
「確率倍数」の変数として、X座標用を「xBaisu」、Y座標用を「yBaisu」。
「値の幅」の変数として、それぞれ「xSpan」、「ySpan」。
「調整値」の変数として、それぞれ「xTyosei」、「yTyosei」としますが、調整数は、「値の幅」の「−2分の1」にすることで、ほぼ(注)正負均等に乱数値が計算されるように決めておくことにします。(上記”510”番下段参照)
例えば「値の幅」が「30」なら、「調整値」はその半分の「-15」というように・・・
そこで、xTyosei = -( xSpan/2 );、yTyosei = -( ySpan/2 ); としておけば、「値の幅」が決まれば自動的に「調整値」が決まります。
X座標の移動量は「xSpeed」でしたから、Y座標の移動量の変数として「ySpeed」とします。
※「確率計算」で判定用に使われる変数Rndは、そのとき限りの変数ですから「Rnd」を共用で使用します。

(注)「ほぼ正負均等に・・」というのは、乱数ツールでご確認いただくと分かりますが、「倍数値」を「30」として、「調整値」をその”−2分の1”の「−15」としても、値が取りうる範囲は「−15〜+14」までとなり、ややマイナスに傾くということです。 正確に正負均等にするには、「倍数値」にプラス1して「31」にすれば「−15〜+15」の値になります。
しかし、この場合は許容範囲ですので、単純に「値の幅」の「−2分の1」としています。

522

以上の変数を使って「tarou」インスタンスを動かしますが、「tarou」が移動できるX座標の範囲は、「hanako」と同じ「X=56」〜「X=440」とし、Y座標の範囲を「Y=56」〜「Y=210」とします。
■そして時々刻々移動する「tarou」のY座標値も目で確認できるよう、シーン上の「tarou=」のテキストボックスの横に、もうひとつ「ダイナミックテキスト」の「テキストボックス」を作成し、そのテキストボックス変数を「tarouY」としておきます。
■説明用の「静止テキスト」文字は、それぞれ「tarouX =」「tarouY =」としておきます。

523

■最後に、以下のスクリプトを「tarou」にクリップアクションで記載してください。

 onClipEvent (load) {
   xBaisu = 10
   yBaisu = 10
   xSpan = 40

   ySpan = 40
   xTyosei = -( xSpan/2 );
   yTyosei = -( ySpan/2 );
   xSpeed = 20
   ySpeed = 20
 }
 onClipEvent (enterFrame) {
   this._x += xSpeed * _root.move
   if (this._x < 56 || this._x > 440) {
     xSpeed = -xSpeed;
   } else {
     Rnd = Math.floor(Math.random() * xBaisu);
       if (Rnd == 0) {
         xSpeed = Math.floor(Math.random() * xSpan + xTyosei);
       }
   }
   this._y += ySpeed * _root.move
   if (this._y < 56 || this._y > 210) {
     ySpeed = -ySpeed;
   } else {
     Rnd = Math.floor(Math.random() * yBaisu);
     if (Rnd == 0) {
       ySpeed = Math.floor(Math.random() * ySpan + yTyosei);
     }
   }
   _root.tarouX = this._x;←自分のX座標値を「tarouX」に代入
   _root.tarouY = this._y;←自分のY座標値を「tarouY」に代入
 }

もう解説なしで、スクリプトの意味はご理解いただけると思います。
「* _root.move」を復活させていますので、「STOP」ボタン、「PLAY」ボタンも有効です。

■「ムービープレビュー」で動作を確認してください。

そして・・・
「onClipEvent(load)」内で定義している赤字の数値を様々に変更してみて、動き方の違いを比べてください。
※アクションスクリプトを使ったFLASHの編集作業で、このような動く”もの”を作成するときには、はじめから思い通りに上手くいくことはまずありません。
完成までには、 自分の気に入った動きになるまで何度も何度も「値」を入れ替えて試験することになります。
したがって、実際の動きをつかさどる「onClipEvent(enterFrame)」内の関連数値を、このように変数に置き換えて、「onClipEvent(load)」の中にまとめて定義しておくと、スクリプトとしては多少分かりにくくなりますが、いちいち本文の値を修正しなくて済むという利点もあるということです。




invader.fla←ここまでのサンプル制作ファイルです。右クリックして「対象をファイルに保存」でダウンロードできます。

524

「hanako」はおとなしく水平移動し、「tarou」はそれこそ自由奔放にステージの中を我がもの顔で動き回っています。
まるで自分に意思があるとでも思っているように・・・。
確かに、彼らの次の瞬間の動作は分かりませんが、あなたには自分の計画したとおりの行動範囲(お釈迦様の手のひら)しか移動できないことが分かっています。
しかし彼らは、あなたによって造られたことも、あなたの気まぐれで「STOP」ボタンが押されたら瞬時にその動きを止められることも知りません!
どうです!少しは神様の気分になってきましたか(笑)

次回は、マウスで砲台を移動し、クリック砲で彼らを破壊してやりましょう!!


第5回 Mathランダム徹底研究 戻る このページの先頭へ 次へ

質問掲示板 過去記事 サンプル作品集 FLASH5講座 ACTION SCRIPT講座 リンク 憲ちゃんのHP FLASH制作代行 トップページ

トップページ