Flash Tutorial for Beginners Procreo Flash Design
ProcreoFlashDesign
初心者のためのFlashレベルアップ講座

ボールを投げるアクション(重力、空気抵抗、はねかえり係数)
 
ステージの用意、ボールの作成
まず、第一に初期準備として、新規ファイルを開き、画面下のプロパティにある、サイズの右のボタンを押すか、画面中央上にあるフレームレートの表示場所(初期では12.0fpsと表示されているところ)をダブルクリックし、ドキュメントプロパティの画面を表示します。
そして、画面サイズを(幅)600px(高さ)400pxにし、フレームレートを30fpsにしておきましょう。そのほかの値は初期のままです。
次にツールを楕円ツールに持ち替え、ステージにボールとなる円を描きます。このときShiftキーを押しながらドラッグすると真円を簡単に描くことができます。大きさは適当でよいです。このままでも良いのですが、ボールの立体感を出すためにバケツツールに持ち替え、塗りのカラーを放射状のグラデーションにし、円中央より左上あたりをクリックします。
すると左上から光があたったような球体っぽくなります。


ボールが出来たついでにムービークリップにしてしまいましょう。
ツールを矢印ツールに持ち替え、ボールをダブルクリックするか、ボール全体を囲むようにドラッグするかして、ボール全体が選択された状態にします。そして、メニューバーから「挿入」→「シンボルに変換」を選択するか、もしくはF8キーを押します。名前は別に何でもよいのですが、とりあえず「ball」とでも入力します。タイプがムービークリップになっていることを確認してOKを押します。さらにインスタンス名にもballと付けておきましょう。

次に、ボールが跳ね返る壁となる枠をステージの幅、高さで描画しましょう。矩形ツールを選択し、塗りのカラーをなしにし、線のみにしてステージに合うようなおおよその枠を描画します。その後、情報ウインドウでW:600 H:400 X:300 Y:200に合わせ、ぴったりステージと枠が一致するようにしておきます。

以上で、ステージとボールの用意の出来上がりです。


アクションスクリプトの記述

続いて、ボールの動きとなるアクションスクリプトを記述していきます。
まず、ボールを選択し、画面下プロパティの一番右の矢印ボタンを押すか、メニューバーから「ウインドウ」→「アクション」を選択、もしくはF9キーを押してアクションウインドウを表示します。ここで、ノーマルモードになっている方はアクションウインドウの右上(×ボタンの下)のボタンを押して、エキスパートモードに変更してください。
そして、とりあえず、以下の簡単なアクションスクリプトを記述してみましょう。

onClipEvent (load) {
	speedx = 5;
}
onClipEvent (enterFrame) {
	this._x = this._x+speedx;
}

このスクリプトの意味は、ムービークリップのボールが画面に読み込まれたとき(onClipEvent (load))にとりあえず、適当に用意したspeedxという変数に値5を代入しています。この値がx方向への初速となっています。
そして、ボールがある間中(onClipEvent (enterFrame))、this._x=this._x+speedxを繰り返します。(ここで、thisとは、このアクションスクリプトはムービークリップであるボールに記述していますので、このボール自身のことを指しています。)つまりこのボールのx位置(this._x)にどんどん5を足していきます。さらに言えば、フレームレートを30fpsにしていますので、1秒間に30回この処理を繰り返します。
計算の速い人はボールが枠内の左端から右端まで4秒で到達するとわかるでしょう。
では、「制御」→「ムービープレビュー」で見てみましょう。
ボールが右方向へどんどん進んでいきます。壁を越えても画面をでてもずっと・・・
当然もう戻ってきません。
では、壁にあたるとボールが跳ね返ってくるようにしましょう。ここで、ボールが跳ね返るためにはいままで右方向に進んでいたものを左方向にする必要があります。簡単に言えば、いままで5で進んでいたものを-5で進むようにすれば良いことになります。つまり、壁にあたったときspeedxの符号を逆転させればよいのです。そこで、壁にあたったとき、いいかえれば、ボールがステージの右端の600を超えたとき、speedxに-1を掛けてやります。
これをスクリプトにすると

	if (this._x>600) {
		speedx = speedx*-1;
	}

こうなります。これを、onClipEvent (enterFrame)内に追加します。
すると、全体では

onClipEvent (load) {
	speedx = 5;
}
onClipEvent (enterFrame) {
	if (this._x>600) {
		speedx = speedx*-1;
	}
	this._x = this._x+speedx;
}

こうなります。しかし、このままだと、プレビューすればわかりますが、ボールのx位置とはボールの中心を指す為、ボールが壁に半分突き抜けてから反対方向に跳ね返ることになります。
そこで、600を超えたときというのを、ボールの半径分減らす必要があります。ボールの半径はボールの幅(this._width)の半分ということで、this._width/2です。これを600から差し引いてやります。
つまり、

		if (this._x>600) {
この一行を
		if (this._x>600-this._width/2) {
こう変えれば良いことになります。
プレビューして見るとちゃんと跳ね返るようになっています。ただ、今度は左端から消えていってしまいます。そこで、おなじ要領で、左の壁に当たった時、つまり、xが0より小さくなったときの処理も加えましょう。ボールの半径も忘れずに・・・

onClipEvent (load) {
	speedx = 5;
}
onClipEvent (enterFrame) {
	if (this._x>600-this._width/2) {
		speedx = speedx*-1;
	}
	if (this._x<0+this._width/2) {
		speedx = speedx*-1;
	}
	this._x = this._x+speedx;
}

左の壁も考慮するとこうなります。これで、画面外にボールが飛び出すことはなくなりました。次に縦方向の動きも加えましょう。考え方はいままでのx方向とまったく同じです。
縦方向の速度としてspeedyという変数を加えて、同じようにスクリプトを記述してみましょう。yの初速を4とするとこうなります。

onClipEvent (load) {
	speedx = 5;
	speedy = 4;
}
onClipEvent (enterFrame) {
	if (this._x>600-this._width/2) {
		speedx = speedx*-1;
	}
	if (this._x<0+this._width/2) {
		speedx = speedx*-1;
	}
	if (this._y>400-this._width/2) {
		speedy = speedy*-1;
	}
	if (this._y<0+this._width/2) {
		speedy = speedy*-1;
	}
	this._x = this._x+speedx;
	this._y = this._y+speedy;
}

この状態でプレビューしてみるとボールが枠内を跳ね返りながら動き回るのがわかります。


ボールをつかむ、投げる

それでは、次にボールをつかんで投げれるようにしましょう。
それにはまず、ボールをつかめるように、ボールのムービークリップの上にボタンを作ります。ムービークリップのボールをダブルクリックすると、シーン1 ball となり、ballというムービークリップを編集している状態になります。そこで、さらにボールをダブルクリックするか、すべて囲むようにドラッグし、ボールを全選択状態にします。そのまま、「挿入」→「シンボルに変換」を選択して、名前は適当に入力し、タイプをボタンに設定します。これで、ボタンの準備ができました。
それでは、次にボタンにつかんで離すアクションスクリプトを記述していきます。
アクションウインドウのタイトルが▼アクション-ボタン になっていることを確認して、ボタンに次のスクリプトを入力してください。

on (press) {
	startDrag(this, true);
}
on (release) {
	stopDrag();
}

この意味は、ボタンを押した時(on (press))に、これ(this:ボタンのアクションに記述されたthisはそのボタンが配置されているムービークリップを指します。今回の場合はボールのムービークリップ)のドラッグを始める(startDrag)という意味です。trueというのは、ドラッグを始めたとき、その物体の中心をマウスの位置に自動的に合わす、ということになります。逆にfalseと記述すると、マウスのボタンをを押した位置のまま物体をドラッグできるようになります。
その次のスクリプトは、ボタンを離したとき(on (release))に、ドラッグをストップさせる(stopDrag())というスクリプトです。

基本的なドラッグアンドドロップのスクリプトは上記のものですが、今回の場合、今の状態でプレビューするとわかるようにうまくいきません。とてもぎこちなく変な動きになってしまいます。それは、ドラッグ中もボールのムービークリップが動きつづけている為です。そこで、ドラッグ中はボールを動かさないようにしましょう。言い換えれば、ドラッグをしていない時だけ、動きつづけるようにすれば良いのです。
わかりやすくするため、適当な変数を用意します。ここではpickupということにします。
たとえば、このpickupが1の時はドラッグ中、0の時はドラッグしていない時とします。
つまり、pickupが1でないときだけ、ボールが動くようにします。

実際にスクリプトを追加してみましょう。
まず、先ほどのボタンのアクションにpickupの式を追加します。

on (press) {
	pickup = 1;
	startDrag(this, true);
}
on (release) {
	pickup = 0;
	stopDrag();
}

ボタンを押したときpickupを1(ドラッグ中)とします。そしてボタンを離したときpickupを0(ドラッグ中でない)とします。
次に、ボールが絶えず動くスクリプトを変更しますから、ムービークリップのアクションを変更します。シーン1を押してムービークリップのボールを選択すると、アクションウインドウには先に記述したスクリプトが表示されるはずです。

そして、ボールの動きを延々計算しているところ、つまり、onClipEvent (enterFrame)内をpickupが1(ドラッグ中)の場合と0(ドラッグ中でない)の場合に分けます。
すると、

	if (pickup == 1) {
		// ドラッグ中の処理
	} else {
		// ドラッグしていない時の処理・・・動きつづける
	}

このようになります。ドラッグ中の処理は結構重要なので後述しますが、とりあえず、今の状況は次のようになっています。

onClipEvent (load) {
	speedx = 5;
	speedy = 4;
	pickup = 0;
}
onClipEvent (enterFrame) {
	if (pickup == 1) {
} else {
if (this._x>600-this._width/2) { speedx = speedx*-1; } if (this._x<0+this._width/2) { speedx = speedx*-1; } if (this._y>400-this._width/2) { speedy = speedy*-1; } if (this._y<0+this._width/2) { speedy = speedy*-1; } this._x = this._x+speedx; this._y = this._y+speedy; } }

ついでに、ボールが読み込まれたとき(onClipEvent (load))にドラッグ中ではなく、動き回るようにpickup=0を追加しておきました。
この状態でプレビューすると一応ちゃんとボールをつかんでドラッグできるようになっています。ドラッグしている間はボールが動かず正常に動作しているようです。しかし、ボールを離すとまた動き出しますが、投げた感じがしません。では、ここで、投げる動作のスクリプトを記述しましょう。投げるスピードや方向をどうやって計算しているのか、ちょっとわかりにくいかもしれませんが、これを覚えると結構応用が効きますのでしっかり理解してください。
では、投げたときのスピードや方向は何を求めれば計算できるかといえば、ボールを離した時の位置と、その直前の位置がわかりさえすれば求めることができます。
簡単にするために横方向、つまりx方向だけを考えることにします。ボールを離した時の位置(仮にx2とします。)、その直前の位置(仮にx1とします)がわかれば、x方向のスピードはx2-x1で求めることができます。
ボールを離した時のx2の位置はそのままボールの現在位置ですので、すぐわかりますが、
その直前の位置はどうやればわかるでしょうか?
結論から言うと直前の位置x1に前回計算したx2の位置を代入してやります。そうすると、常にx1はx2の直前の値が入ることになります。スクリプトで書くとこうなります。

		x1 = x2;
		x2 = this._x;
		speedx = (x2-x1);

この3行の式をこの順番で延々計算を繰り返していると考えると、x2には現在の位置、x1には前回計算したときの現在位置つまり直前の位置が入っていることになります。そして、x方向のスピードspeedxは常にx2とx1との差として求められています。
同じようにy方向も考慮すると

		x1 = x2;
		y1 = y2;
		x2 = this._x;
		y2 = this._y;
		speedx = (x2-x1);
		speedy = (y2-y1);

こうなります。そして、これをどこに記述するのかと言えば、この計算はドラッグ中のボールの動きですから、onClipEvent (enterFrame)内のpickupが1(ドラッグ中)の場合に記述してやります。

onClipEvent (load) {
	speedx = 5;
	speedy = 4;
	pickup = 0;
}
onClipEvent (enterFrame) {
	if (pickup == 1) {
		x1 = x2;
		y1 = y2;
		x2 = this._x;
		y2 = this._y;
		speedx = (x2-x1);
		speedy = (y2-y1);
	} else {
		if (this._x>600-this._width/2) {
			speedx = speedx*-1;
		}
		if (this._x<0+this._width/2) {
			speedx = speedx*-1;
		}
		if (this._y>400-this._width/2) {
			speedy = speedy*-1;
		}
		if (this._y<0+this._width/2) {
			speedy = speedy*-1;
		}
		this._x = this._x+speedx;
		this._y = this._y+speedy;
	}
}

これで、だいぶそれらしく見えるようになってきました。ただ、あまり強く投げると、枠外に出ておかしな動きをすることがあります。それは、枠外に出たとき、枠外で延々スピードの符号を反転しつづけ、枠内に戻ってこない場合です。そこで、その回避策のひとつとして枠外に出たときは強制的に枠内の位置に戻ってこさせることにします。

一案:(xが右枠を越えた場合の処理に追加) x位置を枠内の位置まで戻す

			this._x = 600-this._width/2;

つまり、上下左右の枠を越えた時の処理にこの1行を追加しておくと強制的にボールを枠内に戻し、枠外で振動するような状況を避けることができます。これを各枠外処理に追加しておきます。

onClipEvent (load) {
	speedx = 5;
	speedy = 4;
	pickup = 0;
}
onClipEvent (enterFrame) {
	if (pickup == 1) {
		x1 = x2;
		y1 = y2;
		x2 = this._x;
		y2 = this._y;
		speedx = (x2-x1);
		speedy = (y2-y1);
	} else {
		if (this._x>600-this._width/2) {
			this._x = 600-this._width/2;
			speedx = speedx*-1;
		}
		if (this._x<0+this._width/2) {
			this._x = 0+this._width/2;
			speedx = speedx*-1;
		}
		if (this._y>400-this._width/2) {
			this._y = 400-this._width/2;
			speedy = speedy*-1;
		}
		if (this._y<0+this._width/2) {
			this._y = 0+this._width/2;
			speedy = speedy*-1;
		}
		this._x = this._x+speedx;
		this._y = this._y+speedy;
	}
}


重力、空気抵抗(摩擦)、はねかえり係数

もう、ここまで来ると完成間近です。重力や摩擦、はねかえり係数は簡単に設定できます。まず、はねかえり係数とは、ボールが壁に当たった時、どのくらいの力ではねかえるかということです。言い換えると100の高さからボールを落としたとき、どのくらいまではねかえるかということです。スーパーボールにするなら値を大きくし、スポンジのようなものだとすれば、値を小さくしてあまりはねかえらないようにします。現実には100%を超えるものはありませんが、スクリプト上では可能です。しかし、やってみればわかりますが、だんだんはねかえる度にスピードが上昇し、やがてはどっかに飛んでいきます・・・。
このはねかえり係数は壁にあたったときのspeedに変化をつけます。つまり、今まで

			speedx = speedx*-1;
符号の逆転しかさせていませんでしたが、この状態だと100%の力ではねかえるということです。そこで、仮に80%まではねかえるとしましょう。

			speedx = speedx*-1*0.8;
このように0.8を掛ければはねかえる度にスピードが80%に縮小されることがわかると思います。これを上下左右の壁に設定すれば完了です。

次に、空気抵抗、摩擦を考えていきます。上記のはねかえり係数と同様にspeedに手を加えていきましょう。空気抵抗や摩擦はボールが動いている間中、微小ながらスピードを減少させつづけていると考えます。そこで、スクリプト中の

		this._x = this._x+speedx;
		this._y = this._y+speedy;

この2行の上にでも

		speedx = speedx*0.99;
		speedy = speedy*0.99;

この2行を追加して、計算する度に1%ずつスピードを減少させてやります。これだけで、だんだんとスピードが落ちてきて摩擦が生じているように見えます。

最後に、重力の設定です。重力は下方向に常に力が働いていると考えます。つまり、yのプラス方向に向かって力が常にかかっています。そこで、上記のspeedyにさらに重力分としてスピードを加算し続けます。式としては

		speedy = speedy*0.99+0.98;

このように、この場合は0.98の重力を加算し続けるだけです。以上のスクリプトの入力が出来たらプレビューして確認してみてください。上手く動いていれば基本的なボールアクションの完成です。いろいろ値を変更して楽しんでください。


ボールアクションスクリプト(ball_action.fla:16KB)
○ボールのムービークリップのアクションスクリプト

onClipEvent (load) {
	speedx = 5;
	speedy = 4;
	pickup = 0;
}
onClipEvent (enterFrame) {
	if (pickup == 1) {
		x1 = x2;
		y1 = y2;
		x2 = this._x;
		y2 = this._y;
		speedx = (x2-x1);
		speedy = (y2-y1);
	} else {
		if (this._x>600-this._width/2) {
			this._x = 600-this._width/2;
			speedx = speedx*-1*0.8;
		}
		if (this._x<0+this._width/2) {
			this._x = 0+this._width/2;
			speedx = speedx*-1*0.8;
		}
		if (this._y>400-this._width/2) {
			this._y = 400-this._width/2;
			speedy = speedy*-1*0.8;
		}
		if (this._y<0+this._width/2) {
			this._y = 0+this._width/2;
			speedy = speedy*-1*0.8;
		}
		speedx = speedx*0.99;
		speedy = speedy*0.99+0.98;
		this._x = this._x+speedx;
		this._y = this._y+speedy;
	}
}

○ボールのムービークリップ上のボタンのアクションスクリプト

on (press) {
	pickup = 1;
	startDrag(this, true);
}
on (release) {
	pickup = 0;
	stopDrag();
}
補足

上記解説ではこのページトップのサンプルムービーにあるような影は付いていません。サンプルのような影でも良いから付けたいという方は、こちらのファイル(sample_ball_shadow.fla:16KB)のソースを参考にしてみてください。また、このサンプルムービーのスクリプトには、スピードが微小になると、スピードを0にするスクリプトを記述してあります。ボタンアクションについても多少異なっています。合わせて確認してみてください。

注記

このページの解説では分かり易くするため、 できるだけ直感的なスクリプトにしています。その為、最適化されたスクリプトとはいえませんので、悪しからずご了承ください。また、間違った言葉の使い方等しているかもしれませんので、参考程度にお考えください。間違いや不備のご指摘等ありましたら是非ご一報ください。
(June,2003 FlashMX)
E-mail
| HOME | About | Flash Laboratory | Flash Tutorial | Flash Free Material | Books | Links |