(BACK)

8時限目.スプライトの複製


これを作る
使う画像(右クリックで保存) →  ボタン
先の時間では、スプライトにクリップアクションをつける事によって、「スプライトを表示すると、クリップアクションに書かれた通りに変化する」という方法を学んだ。

しかし、スプライトを表示したあと、親イベントから、状態変数を使って操作することももちろん可能であるし、この方法のほうがしっくり来る者もいるだろう。

そこをあえて、比較的面倒な方法を選んだ理由は、
・スプライトに全て詰めこむ事で、親イベントが簡潔になる
・動くスプライトの複製が容易になる
の2点である。

そういう訳で、当講義の最後を飾るのは、スプライトの複製である。
実際の作業は、「スプライトにクリップアクションを付ける」ことで7割は終わっているので、概念さえ理解出来ればそれほど難しくはない。

8−1.スプライト複製の考え方
「クリップアクションにより変化が記録されたスプライト」がすでにある場合、
それを普通に表示するだけで、記録されたように変化する。

命令により、すでに存在するスプライトを、別のインスタンス名で複製し、表示することが出来る。

スプライトを完全にコピーするので、スプライトのイベント、スプライトのスクリプトもコピーされ、複製した時点で表示される。
表示されるという事は、当然、スクリプトに記録されたように変化し始めることになる。

元のスプライトを「親スプライト」、
複製されたスプライトを「子スプライト」と呼ぶ。


上に貼ってあるflashを見てほしい。
「左の端から出てきて、右に進み、右の端までたどりついたら消える」というスプライトを大量に複製している。
スプライトの中が回転しているのは「スプライトのイベント」。スクリプトではない、普通の使い方である。

全て完全に同じスプライトを複製しているが、出現位置と移動速度がまちまちである。
これは、「最初の状態」をランダムに決めているからである。
onClipEvent (load) {
	_x = 0;
	_y = int(Math.random()*200);
	ido = int(Math.random()*20)+1;
	// 1フレームあたり横移動量
}
-
onClipEvent (enterFrame) {
	_x += ido;
	if (_x >= 200) { 
		this.removeMovieClip();
	}// 画面右端までいったら自殺する
}
このように、最初の位置の決定と、移動量の決定について、乱数を使っている。

flashの大きさは200x200であり、最初に「左端のどこか」から出現するので、
最初の位置は、(X=0,Y=0〜199の乱数) になる。
1フレームあたりの移動量idoもこの時ランダムで決める。移動量が0だと困るので、1〜21の乱数である。(21という数字にとくに意味はない)

画面端まで行ったら、用が済んだので自殺する。これについては後述。

この内容のスプライトを単純に複製しまくれば、あんなflashが作れるという事になる。

8−2.複製と自殺の命令
では、複製のための命令と、スプライトを消すための命令について詳しく説明する。

duplicateMovieClip(デュプリケート ムービークリップ)
すでに表示されているスプライトを複製し、表示する命令
使い方:
複製元のインスタンス名.duplicateMovieClip(新しいインスタンス名の文字列,深度);

説明:
ピリオドとカンマに注意する事。

存在するスプライトを、指定されたインスタンス名で、指定された深度に複製し、表示する。(表示場所は(0,0))
新しいスプライトのターゲットは、親スプライトと同じになる。

新しいインスタンス名は文字列で指定する。
いくつの子スプライトを複製する場合でも、それぞれ別のインスタンス名にする事が望ましい。
同じインスタンス名のスプライトが複数あると、制御が効かなくなる場合がある。

深度は、paraflaで言う「深度」と同じであるが、数の範囲は違う。
数字が大きいほど手前に表示され、同じ深度にはひとつの物しか表示出来ない。
深度は-16384〜+1048575の範囲で指定する。

例
_root.sakura.duplicateMovieClip("sakura2",2000);
// _rootにあるスプライトsakuraを「sakura2」のインスタンス名でコピーし、深度2000に表示する

removeMovieClip(リムーブ ムービークリップ)
スプライトを消去する命令
使い方:
インスタンス名.removeMovieClip();

または

this.removeMovieClip();

説明:
表示されているスプライトを消去する。
thisを指定すると、自分自身を消去する。自殺。

ただし、スクリプトで表示したスプライトだけである。
paraflaで表示したスプライトは消せず、またそれは自殺も出来ない。

8−3.位置と深度とスプライトが消せないという話
さて先の項で、気になる記述が2か所あった。
「深度」という言葉と、スプライトを消去する時の制限の話である。

深度とは、スプライトが重なる優先順位を示した数字で、-16384〜+1048575の範囲があり、数が大きいほど手前であり、同じ深度には1つの物しか表示出来ない。
これはparaflaの「深度」と全く同じ理屈であるが、取りうる深度の範囲が違う。
それぞれの比較は以下にようになっている。

ASでの深度ParaFlaでの深度






手前
深度 -16384

深度 -16383
 :
深度 -16127
 :
深度 -15871

深度 -15870
 :
深度 +1048575
(なし)

深度 -256
 :
深度 0
 :
深度 +256


(なし)

(ver 0.9a9現在)

ASでの深度-16127と、paraflaでの深度0は同じ画面であり、合わせて1つの画像しか置けないという事である。
paraflaでの深度と重なる部分は使わず、通常正数の深度を使う。
全ての画面よりも奥の深度を使いたい場合は上を参考に設定する。

そして、
paraflaで表示したものは、paraflaでしか消せない
スクリプトで表示したものは、スクリプトでしか消せない

という大事なルールがある。(消去の大原則)

先の例では、親スプライトは普通に表示したので「paraflaで表示」。
子スプライトはスクリプトで複製したので「スクリプトで表示」という事になる。
removeMovieClipは、この場合は子スプライトしか消す事が出来ない。
親スプライトは、普通に「アクション:画像消去」で消せばよい。

この事は、大量にスプライトを複製した後、ちゃんと全部消そうとする時に重要である。



さてこの事をふまえて、先ほどのクリップアクションの自殺する部分をもう一度見てみる。
onClipEvent (enterFrame) {
	_x += ido;
	if (_x >= 200) { 
		this.removeMovieClip();
	}// 画面右端までいったら自殺する
}
この内容は当然、親スプライトにも書かれているので、親スプライトが画面右端まで行ったら自殺するように書いてある。
親スプライトが無くなると、当然子スプライトへのコピーが出来なくなるので、それは避けなければならない。

が、親スプライトは「位置」に表示しているのでremoveMovieClipで消せない。よって、親スプライトのみ、この自殺の部分は自動的に失敗する。
従って、この、親スプライトを「位置」に表示する方法の場合は、親を間違って消さないようにする工夫は必要ないという事になる。

親スプライトは画面から見えなくなった後も永遠に右に向かって進み続けるが、見えないので気にしない事にする。アーメン。
用が済んだらそのうち消すこと。

8−4.実際に複製してみる
さて、実際に複製するにあたって、問題点が2つある。

・全て別々にしなければならない、子スプライトのインスタンス名をどうするか
・全て別々にしなければならない、子スプライトの深度をどうするか

複製した後に、それを消す必要がない投げっぱなしflashであれば、新しいインスタンス名を決める必要すらないのだが(適当な固定の名前でOK)、やはりそのへんはちゃんとする事にする。
普通は連番を使って順番にインスタンス名をつけていく。
instance_name = "sakura" + counter;
といった感じで、1つずつ増えていくcounterを使ってインスタンス名を決めていく。
実際に作られるインスタンス名は以下のようになる。
「sakura1」「sakura2」…「sakura9」「sakura10」…「sakura99」「sakura100」

これを、「5フレームごとに複製する」という事を混ぜて、イベントとして書いてみる。
・アクション:スクリプト「カウンターの初期化」
counter = 0;
・ラベル
・アクション:スクリプト「スプライトの複製」
// 複製元のインスタンス名は「sakura」
counter++; //カウンターに1を足す
instance_name = "sakura" + counter; //インスタンス名
sindo = int(Math.random()*1000)+1000; //深度は約1000~2000の範囲の乱数
_root.sakura.duplicateMovieClip(instance_name,sindo);
・アクション:何もしない(5フレーム)
・アクション:ラベルへジャンプ「_PrevLabel」

とりあえずこれで正しく動く。
しかし、これには少々の問題がある。作成されるインスタンス名が多すぎるのだ。
実際には、作るそばから次々自殺していくので、生きているスプライトは20もあれば多いほうだろう。しかし、何千というインスタンス名がどんどん生成されていく。
これでは、後で全部消そうとする時に大変困ったことになる。

そこで、インスタンス名を使いまわすという工夫がある。
例えば、使うインスタンス名を100個と決める。
sakura1〜sakura100 の100個のインスタンス名をつけたら、その次はsakura1に戻る。
その時には、古いsakura1はとっくに自殺して居なくなっているはず、という事を読み切っての処置である。画面に表示される量に応じて、この「100個」は変える。

処理としては、カウンター用の変数counterを、1回複製するたびに1ずつ増やしていき、100を越えていたら1に戻す。

ついでに、同じ方法で深度もきっちり設定してみる事にする。
counterの数字をそのまま使い、単純に1000を足して、1001~1100の範囲の深度を使う。
// 複製元のインスタンス名は「sakura」
counter++; //カウンターに1を足す
if (counter > 100) {
	counter = 1;
} // カウンターが100を越えたら1に戻す
instance_name = "sakura" + counter; //インスタンス名
sindo = 1000 + counter; //深度はカウンターの数字を利用
_root.sakura.duplicateMovieClip(instance_name,sindo);
これでだいぶスマートになる。



また、深度のほうは、かなり無責任だが簡単な方法がある。
ランダムに決めるのだ。
深度は百万以上の数を使えるので、例えばこの複製のために「深度1000〜2000」の部分を使うとする。深度を決めるたびに、1000〜2000の範囲の乱数で決めていけばOK。
この場合、約1000分の1以上の確率ですでに使われている深度と重複し、その場合、すでに存在しているほうのスプライトは消える事になる。
複製したスプライトが勝手に消えられては困る場合、この方法は採らないこと。



さて、この複製する部分のイベントは親イベントに書いていると思うが、やはりこの「スプライトを複製する」仕事の部分も、親イベントからどけてみよう。
適当なスプライトを作り、その中にイベントとして書けばそれだけで完了。
複製された子スプライトは、常に親スプライトと同じ場所に作られるので、「どこから複製しても同じ」である。duplicateMovieClipで指定する、親スプライトのターゲットが正しければ問題は起きない。

実際に複製をスタートさせる時には、
「動きの入ったスプライト」と「複製するイベントが入ったスプライト」を、同時に固定画面に表示すればOKである。用が済んだらスプライトごと消す。

8−5.あとしまつ
さて、次のシーンに行きたいので複製のシーンは消したいという事になった。
消去の大原則により、親スプライトと子スプライトの消し方は異なる。

親スプライトは(とっくに地平の彼方に行ってるだろうが)、「アクション:画像消去」で消す。
複製イベント用スプライトを使った場合は、それも「アクション:画像消去」で消す。

子スプライトは、removeMovieClipでひとつずつ消していくのだが、100個あるスプライトに対してそんなだるい事やってられないので、はるか昔に紹介したforを使う事にする。
消すインスタンス名は、「sakura1」〜「sakura100」の100個とする。
for (i=1;i<=100;i++){
	eval("_root.sakura"+i).removeMovieClip();
}
forによる繰り返しの中では、繰り返し用の変数iが増えていく事を利用している。
iは1から100まで変化するのを利用し、それぞれ「sakura1」〜「sakura100」の名前にしてevalして、removeMovieClipを使っている。
evalを忘れるとかなり残念なので注意。

ここで「evalで囲む範囲がおかしいんじゃないか」と思ったら鋭い。
この場合、ターゲットは「_root」でインスタンス名が「sakura1」の筈である。
しかし、この場合はこれで合っている。こんな感じにターゲットの後にくっつく命令の場合は、最後のピリオドより左がターゲットの原則が適用される。

これで全て、綺麗に消す事が出来た。

8−6.最後の宿題
さて、必要な事は全て説明した。
お手本flashのとおりに、「ボタンを押すと花が全部消える」機能をつけて、flashを完成させる事。

この宿題をもって、当講義の全ての終了とする。

当講義の内容を全て理解出来れば、ASの基礎は充分に身についたと思う。
この先は、ASに関する資料やサイトを読み、決して全てを理解しようとはせずに、興味を持った命令や表現や内容を1つずつ吸収していく事でさらなる技術の向上へと繋がるだろう。

当講義の最後まで辿り付く事の出来た諸君らの健闘を期待する。


(BACK)