ようこそ ゲスト さん、新規登録(無料)して気になる疑問を解決しませんか?

質問

質問者:Elased アヒルの親子
困り度:
  • すぐに回答を!
宜しくお願いします。

アヒルではないのですが、一番先頭のMCをバウンドしながら横に移動するアニメーションを付けて、その後を少し時間差をつけて同じ動きで別MCが追っていくと言う感じの物を作りたいのですが、ASで制御するにはどうしたらいいでしょうか?

何方かご教授頂ければと思います。
どうぞよろしく願い致します。
質問投稿日時:09/08/27 15:24
質問番号:5241042
最新から表示回答順に表示

回答

 

回答者:DPE 「グラディウス」というシューティングゲームに出てくる、”オプション(マルチプル)”のような動作でしょうか?

 ※”オプション”とはこういうやつです↓
   ・YouTube:オプション50個のグラディウス
    ​http://www.youtube.com/watch?v=lVXPe6Qeaac

   戦闘機が通った軌跡を追従する火の玉のようなものが”オプション”です。
   これは創作映像で、実際のゲームでは最大4つしか付きません ^^;


オプションの動きは、先頭のキャラクター(自機)が移動するたびに座標を配列変数に記録していき、後続のオプションはその記録を見ながら、自機が数回前に通った位置に移動する、と考えると作れます。
複数のオプションにそれぞれ時間差をつけて追従させるには、オプションごとに配列変数を見る位置を変えます。
例えばオプションが2つある場合、最初に装備したオプションは現在の1つ前の記録、2つ目のオプションは2つ前の記録を見て、その位置に移動するようにします。
つまり、後で装備したオプションほど古い記録を見て移動するわけです。

自機を移動できるようにし、自機が移動した軌跡をたどる、オプションのサンプルを作ってみました。
よろしければご参考になさってください。

------------------------------------------------------

自機とオプションのムービークリップを作り、自機をステージに配置してインスタンス名を付けてください。ここでは仮に”myship”とします。
オプションのムービークリップシンボルにリンケージを設定してください。リンケージ名を”OPTION”とします。

以上で準備は完了です。
次はスクリプトを書きます。


ステージにムービークリップのインスタンス”myship”があり、リンケージ名”OPTION”と付けたムービークリップシンボルがライブラリにあるとします。
このスクリプトは、メインのタイムラインのフレームに記述してください。
Flash Player 6 以上、ActionScript 1.0 または 2.0 で動作します。

(↓各行頭に全角のスペースが入っています。コピーする際は、全て半角のスペースかタブに置き換えてください)


/**********************************************************/

 /////////////////////////////////////////////////
 //初期設定
 /////////////////////////////////////////////////

 //オプションの最大個数
 option_max = 4;

 //前回の自機の位置を記録する変数
 //現在の座標がこれと変わっていなければ、軌跡の記録に残さない
 before_x = myship._x;
 before_y = myship._y;


 //自機の軌跡を記録する配列変数
 pos_tbl = new Array();

 //現在の自機の座標が記録されているインデックス番号を保持する変数
 //最初は最後(=自機分+オプションの総数)に設定しておく
 ply_index = option_max;

 //軌跡を記録する配列変数の初期化:さしあたって全て自機の座標を入れておく
 //実際は、自機→オプション1→オプション2…最後のオプションの座標を表す
 for( i = 0 ; i <= option_max ; i++ )
 {
  pos_tbl[ i ] = { px : myship._x , py : myship._y };
 }

 //記録順序を反転し、最後のオプション→…自機の位置の順に並べ替える
 pos_tbl.reverse();


 //オプションの初期設定
 for( i = 1 ; i <= option_max ; i++ )
 {
  //配列変数を見ながら、初期位置にオプションを配置する
  clip = this.attachMovie( "OPTION" , "option" + i , i );
  clip._x = pos_tbl[ i ].px;
  clip._y = pos_tbl[ i ].py;
 }


 /////////////////////////////////////////////////
 //自機とオプションの移動処理
 /////////////////////////////////////////////////

 myship.onEnterFrame = function()
 {
  var next_index , tbl_ref;
  var i;


  //自機の移動処理
  //↑↓←→キーで上下左右に移動
  if( Key.isDown( Key.UP ) ) this._y -= 50;
  if( Key.isDown( Key.DOWN ) ) this._y += 50;
  if( Key.isDown( Key.LEFT ) ) this._x -= 50;
  if( Key.isDown( Key.RIGHT ) ) this._x += 50;


  //前回の位置と比較し、変わっていれば移動の記録を残す
  //さらに、自機が移動した場合に限りオプションも移動する
  if( ( before_x != this._x ) || ( before_y != this._y ) )
  {
   //現在の記録位置に前回の座標を、その次の記録位置に今回の座標を記録
   next_index = ( ply_index + 1 ) % pos_tbl.length;
   pos_tbl[ ply_index ] = { px : before_x , py : before_y };
   pos_tbl[ next_index ] = { px : this._x , py : this._y };

   //現在の記録位置を更新
   ply_index = next_index;

   //今の自機の位置を記憶しておく(自機停止時のオプション重複防止のため)
   before_x = this._x;
   before_y = this._y;


   //全てのオプションを動かす
   for( i = 1 ; i <= option_max ; i++ )
   {
    //自機の移動記録を見るインデックスを求める
    //後ろのオプションほど古い記録を見る
    tbl_ref = ( ply_index - i + pos_tbl.length ) % pos_tbl.length;

    //自機の軌跡をたどる位置に移動
    this._parent[ "option" + i ]._x = pos_tbl[ tbl_ref ].px;
    this._parent[ "option" + i ]._y = pos_tbl[ tbl_ref ].py;
   }
  }
 };

/**********************************************************/


「ムービープレビュー」で動作を確認してみてください。
最初は自機とオプションが全て重なった状態で表示されます。
方向キー ↑ ↓ ← → で自機を動かしてみてください。自機が移動した通りに、後続のオプションが追従します。
オプションの数は、冒頭にある変数 option_max の値を書き換えると変更できます。オプション 50 個も理論的には可能ですので、いろいろ遊んでみてください。

------------------------------------------------------

主な構成です。


まず、自機が”移動するたびに”座標を配列変数に記録します。
自機の移動・移動の記録やオプションの移動処理は、myship の onEnterFrame イベントハンドラを借りて一定時間ごとに行います。
ただし、enterFrame イベントはフレームレート分の1秒ごとに常に発生しているため、無条件に座標を記録すると、自機が移動しなかった時の座標も記録されてオプションが次第に自機のもとに集まってくることになります。
(ちなみに「ツインビー」というゲームでのオプションは、止まると自機に重なる方式を採用しています)
そこで、前回の位置を変数に保存しておき、移動処理後の座標と照合して変化していた場合にのみ、移動した記録として残すようにします。上記のスクリプトでは、before_x と before_y という変数に前回の座標を記録し、移動処理の後で自機の座標と照合して移動記録を残すかどうかを決めています。

オプションは配列変数に残っている自機の移動記録を見ながら、その座標に移動するだけです。
先述のとおり、後ろのオプションほど古い記録を参照して移動します。
オプションが動くのも自機が動いた場合だけですから、移動記録を残す(=自機が移動したと判断される場合)時にのみ、オプションの移動処理を行います。

***********************************

細かい移動の仕組み等はさておき、今回のポイントは、移動記録を残す方法と、オプションごとに履歴を見る方法です。


先のサンプルでは、配列変数 pos_tbl に自機が通った座標を記録します。
この配列変数には初期値として仮に全要素に自機の座標を詰めていますが、実際は

 [ P , 1 , 2 , 3 , 4 ]

↑このような並び方で位置情報が入っているものと思ってください。Pは自機の座標、1〜4は最初のオプションから最後のオプションまでの座標ですが、例えば2の部分は”2回移動する前に自機がいた座標”とも言えます。
これですと新しい記録ほど前のインデックスにくることになり、少々イメージしにくくなるので、reverse メソッドで逆転しています。
つまり、ムービー再生開始時の pos_tbl は

 [ 4 , 3 , 2 , 1 , P ]

この順番で座標が記録されている状態です。
今回は初期値が一律自機の座標のため、反転する意味がありません。
しかし、例えば、自機の下に若い番号のオプションから順に等間隔に配置した状態でスタートする時などは、for ループで自機→オプション1→…最後のオプションの順に格納する方が初期化しやすいと思います。そのような場合に利用してみてください。

以降は、自機が動くたびにPの情報を前の要素に移し、その次のインデックスに新しい座標を書き込んでいきます。
前の要素だの次だのというと難しく聞こえますけれど、Pが入っているインデックスを変数で管理することで簡単に作れます。

上記のスクリプトでは、Pが入っているインデックスを変数 ply_index で管理しています。
初期状態ではPは pos_tbl の末尾(インデックス4番)にあります。配列変数は0番から始まり、配列変数には自機の情報を書き込みますので、末尾の番号はオプションの総数を表す変数 option_max の値と同じになります。
座標を記録する時は、まず、ply_index が指す要素に前回の座標を記録します。前回の座標はこの作品では before_x と before_y に保存されていますが、ply_index が指すインデックスの1つ前の要素を見ても構いません。
そして、ply_index の次にあたるインデックスの要素に現在の新しい座標を書き込み、ply_index の値も1つ先に更新しておけば、履歴の出来上がりです。


その、インデックスの件ですが。
初期状態での自機の情報であるPは、配列変数の末尾・4番目に記録されています。
自機が移動した時に、新しい座標を次のインデックスに記録するのですが、配列変数にはもう残りがありません。
Flash の配列変数はいつでも要素を切り落としたり追加したりできて便利ではあるものの、頻繁に削除・追加を繰り返すと CPU やメモリに負担をかけます。オプションの総数が 50 個など多くの履歴を残したい場合は配列変数が長くなり、この処理の重さが侮れなくなります。

ですから、今ある要素だけでやりくりする方法を考えてみましょう。
ply_index の値は新しい座標を記録するごとに1ずつ増えます。
古くなった情報はいつまでも取っておかなくてもいいのですし、かといっていらないものをわざわざ削除するのも面倒です。末尾の 4 まで書き込んだら再び 0 に戻して、上書きしてしまいましょう。
つまり変数 ply_index の値は、座標を記録するごとに 0 → 1 → 2 → 3 → 4 → 0 …と変化することになります。
これは、整数同士の除算の剰余が必ず 0 〜 除数 - 1 に収まる点を利用すると簡単です。

今回の例ですと、配列変数には5つの要素があり、pos_tbl.length にはこの 5 という数値が入っています。
この点と剰余を利用して、

 ply_index の次の位置 = ( ply_index + 1 ) % pos_tbl.length

とすると、4 の次は 0 と求めることができます。
こうして算出した値を、次回のために ply_index の新しい値として書き換えておきます。
上記のスクリプトでは、移動記録を残す時、ply_index の次のインデックスを求める

 //現在の記録位置に前回の座標を、その次の記録位置に今回の座標を記録
 next_index = ( ply_index + 1 ) % pos_tbl.length;

↑この部分で使用しています。

***********************************

次に、オプションについて考えてみましょう。
n番目のオプションは、現在のPの位置である ply_index からn個前の移動記録を見て、その位置に移動します。
例えば ply_index が 4 の時は、オプション1はその1つ前の pos_tbl[ 3 ] を、オプション2は2つ前の pos_tbl[ 2 ] を・・・というように、若い番号のオプションほど”現在の記録に近い履歴を見る”特徴があります。

ここで問題になるのは、例えば ply_index が一巡して 0 に戻った時などです。
この場合の最も古い情報とは ply_index の1つ先であるインデックス1番、逆に現在の記録に最も近い情報は末尾のインデックス4番の要素になりますから、

 [ P , 4 , 3 , 2 , 1 ]

このように参照しなければなりません。
Pが真ん中の pos_tbl[ 2 ] にある時などは、

 [2 , 1 , P , 4 , 3 ]

・・・とまあ、簡単に引き算等では求められないように見えます。

実はこれも、剰余を使って解決できます。
基本的には、現在のPのインデックスを表す ply_index からオプションの番号( 1 〜 option_max )を減算し、これを pos_tbl.length で除算した時の剰余を求めます。
ただし、ply_index - オプションの番号 の計算結果がマイナスになると計算結果が狂ってしまいますから、予め pos_tbl.length を加算して水増ししておきます。
式にまとめますと、

 n番目のオプションの参照インデックス = ( ply_index - n + pos_tbl.length ) % pos_tbl.length

となります。

------------------------------------------------------

この例では先頭に立つ自機は自動的には動きませんが、タイムラインを使って動かしている場合も同様に、移動記録を残しながらオプションを追従させることができます。
例えばオプションが5フレーム分遅れて追従してくるとは、要するに5フレーム前の自機の座標が配列変数に残っていればいいわけです。
従って、”自機が動いた時”ではなく”5フレームに1度”座標を記録するように変更していただくと対応できます。

「5フレームに1度」という処理は例えば、カウントをとる変数を1つ用意して onEnterFrame = function の中で随時カウントをアップし、5の倍数になった時に何かをする、といった方法があります。
nの倍数かどうかも、剰余を使って判断できます。
また、カウントが 5 になったら 0 に戻し、「カウントが5であるかどうか」で判断する方法もあります。

ただ、数フレーム前の場所に移動すると、オプションの動きがぎこちなくなったり、オプションの移動が遅れてずるずる引きずってついていくような感じに見える場合があります。
この場合は、フレームを空ける発想をやめて、タイムラインでの自機そのものの移動量を見直すなどの工夫が必要になるかもしれません。
種類:アドバイス
どんな人:経験者
自信:参考意見
回答日時:09/09/01 14:45
回答番号:No.3
この回答への補足この回答に補足をつける(質問者のみ)
この回答へのお礼この回答にお礼をつける(質問者のみ)

回答

 

回答者:BlurFiltan #1です。

> 出来ましたら、「mcB」(小トトロ?)が1つではなく、複数の場合も教えて頂けますか?

それは作者の方向性によって変わるのですよ。
だからあえて書かなかったんです。

#1で書いたスクリプト

> この場合,
> 最初のフレーム(フレーム1)には次のようなスクリプトを書きます。
>
> ----------------------------------------------
> // 遅延させるフレーム数を設定(※変える)
> var chienFrm = 5;

〜略〜

> // 「mcB」 を座標記録配列の要素chienFrmの座標にする
> mcB._x = zahyouArr[chienFrm][0];
> mcB._y = zahyouArr[chienFrm][1];
> };
> ----------------------------------------------

これを,「mcB」 (追従する小トトロ)内のフレーム1に書き直して,
スクリプト中の 「mcB」 となっている部分を全て「this」 に書き直して,
スクリプト中の 「mcA」 となっている部分を全て「this._parent.mcA」 に書き直しするなどして
ターゲットパスなどの辻褄を合わせるとどうなるでしょうか。

さらにこの路線を進めると,
ムービークリップクラスを継承するカスタムクラスの方向になります。


===========

また,クラスとは全く別で,
配列が2次元配列なので,1つのデータ[x,y]しか管理しにくいのです。
3次元にすると,複数のインスタンスに関してのデータ管理ができるかもしれませんね。


===========

また,
配列は2次元1つのままでも時間差を付ければ複数のMCは管理できますよね。


1. 2次元配列のままやってみる。
2. ムービークリップ内にスクリプトを書いてみる。
3. 3次元配列にしてみる。
4. クラスに挑戦してみる。

1.2.3.4 の順に簡単だと思います(1と2は同程度かも)。
しかし,発展の順ではなく方向性が全く違うのです。

考えてみることをお薦めします。
種類:アドバイス
どんな人:経験者
自信:参考意見
回答日時:09/08/31 20:19
回答番号:No.2
この回答への補足この回答に補足をつける(質問者のみ)
この回答へのお礼この回答にお礼をつける(質問者のみ)

回答

 

回答者:BlurFiltan > ASで制御するにはどうしたらいいでしょうか?

「ASの質問」ですよね?
「ASの質問」であれば,少なくとも「ASのバージョン」がわからないとまともな回答できません。
また,
仮に ActionScript1.0 に限定したとしても,
Flash のバージョンによってムービーの構造やスクリプトを変える必要がある場合がほとんどです。



◎=== 考え方例 ========================

バージョンは違っても(ActionScriptなどが違っても),
大まかな考え方は同じでできると思います。

たとえば
「mcA」 と 「mcB」 というインスタンス名を付けた2つのムービークリップがあったとします。
そして 「mcA」 を 「何かの方法」 で動かしたとします。
※ この 「何かの方法」 とは
  モーショントゥイーン で動かす場合もあるでしょう。
  ActionScript で動かす場合もあるでしょう。
  閲覧者が 「mcA」 をドラッグして動かすかもしれません。
  とにかく「何かの方法」です。
その動く 「mcA」 の座標データを随時 配列などに保存して,
「mcB」 の座標を過去の保存データから随時動かしてやれば良いと思います。

過去の座標を配列に保存する必要は特に無いのですが,
配列だとデータをかたまりとして扱えますし,
配列には便利なメソッドがたくさんあるため楽に操作ができます。



◎=== ActionScript 1.0 での例 ============

勝手に ActionScript 1.0 ,Flash MX 以上で作成可能な例を書きます。

【下の図】↓のように,
ステージ上に 「mcA」(中トトロ?) と 「mcB」(小トトロ?) という "インスタンス名" を付けたムービークリップを
別々のレイヤーに配置し,
「mcA」 のみをモーショントゥイーンで動かしたとします。
「mcB」 はフレームを伸ばすだけで何もしません。

この場合,
最初のフレーム(フレーム1)には次のようなスクリプトを書きます。

----------------------------------------------
// 遅延させるフレーム数を設定(※変える)
var chienFrm = 5;

// 座標記録用の配列を作成
var zahyouArr = new Array();

// 座標記録用配列 要素0 に
// 「mcA」の [x, y] 座標配列を代入
zahyouArr[0] = [mcA._x, mcA._y];

// 座標記録配列 要素1〜chienFrm に
// 「mcB」の [x, y] 座標配列を代入
for (var i = 1; i<=chienFrm; i++) {
zahyouArr[i] = [mcB._x, mcB._y];
}

// 1フレーム進む時間毎に毎回実行する関数を定義
this.onEnterFrame = function() {
// 座標記録配列の先頭に「mcA」の [x, y] 座標配列を挿入
zahyouArr.unshift([mcA._x, mcA._y]);
// 座標記録配列の最後の要素を削除
zahyouArr.pop();
// 「mcB」 を座標記録配列の要素chienFrmの座標にする
mcB._x = zahyouArr[chienFrm][0];
mcB._y = zahyouArr[chienFrm][1];
};
----------------------------------------------


そして,最終フレームにキーフレームを作成し,
その最終フレームのキーフレームに次のようなスクリプトを書けばできあがりです。

----------------------------------------
// 再生停止
stop();

// 1フレーム進む時間毎に毎回実行する関数を削除
delete this.onEnterFrame;
----------------------------------------


図示の勝手な都合上,
「mcA」 と 「mcB」 はステージ内からスタートさせていますが,
本当は両方ともステージ外の場所から動かした方が自然に見えるとは思います。



◎=== スクリプトの説明 ========================

基本的に,

 // 遅延させるフレーム数を設定(※変える)
 var chienFrm = 5;

この部分(5 という数) 以外は,変える必要はないと思います。


配列についてですが,

 // 座標記録用の配列を作成
 var zahyouArr = new Array();

これで1本の配列しか作成していないように見えるかもしれませんが,
この配列の各要素(各エレメント)には [x座標, y座標] という配列をさらに代入しているため,
zahyouArr は,配列が入れ子になった 2次元配列 になります。

配列の中身は1フレーム進む時間毎に順繰りで入れ替えています。
「あ」を mcB の最初の座標 [x, y] だとします。
「い」「う」「え」〜「こ」を mcA の各フレームでの座標 [x, y] だとします。

 [い, あ, あ, あ, あ, あ] ←フレーム1
 [う, い, あ, あ, あ, あ] ←フレーム2
 [え, う, い, あ, あ, あ] ←フレーム3
 [お, え, う, い, あ, あ] ←フレーム4
 [か, お, え, う, い, あ] ←フレーム5
 [き, か, お, え, う, い] ←フレーム6
 [く, き, か, お, え, う] ←フレーム7
 [け, く, き, か, お, え] ←フレーム8
 [こ, け, く, き, か, お] ←フレーム9

最初の要素が「mcA」のそのときの座標です。
つまり「mcA」の座標は
い→う→え→お→か→き→く→け→こ
と動きます。

最後の要素の座標に「mcB」を移動させます。
「mcB」の座標は
あ→あ→あ→あ→あ→い→う→え→お
と動きます。

これで「mcA」の座標に5フレーム遅れて「mcB」が表示れます。

データの動きは,
芯を後から順に押しこんで入れ替えるという 昔あった 「ロケット鉛筆」 とか 「ロケットペン」 のようなイメージです。
Array(配列)クラスの unshift() メソッドや pop() メソッドを使うと,
上記のような順繰りの入れ替えプログラムを自作しなくても済むわけです。



◎=== 補足 ===========================

ActionScript 2.0 でもほぼ同様でできます。

ActionScript 3.0 が必要な場合は,
そんなに難しいスクリプトではありませんし,短いので,
どうすれば良いか考えてみてください。

Flash 5 では上のスクリプトをそのままは使えませんし,
書く場所も変える必要があると思います。

Flash Lite 1.x 用 SWF でもできますが,
上のスクリプトはまるっきり全部使えません。
ムービーの構造からして変える必要があります。

とにかく,
勝手な推定しかできない色々な場合について,いちいちの回答はできません。
種類:回答
どんな人:経験者
自信:参考意見
回答日時:09/08/28 18:53
回答番号:No.1
画像
この回答への補足この回答に補足をつける(質問者のみ)
この回答へのお礼ありがとうございます。
とても参考になりました。
ActionScriptは1.0か2.0で考えていました。

出来ましたら、「mcB」(小トトロ?)が1つではなく、複数の場合も教えて頂けますか?
最新から表示回答順に表示