- 締切済み
ランダムに画像ファイルを選出するには?
Flash初心者です。 下記の例を実現する方法を教えていただけないでしょうか。 例) Aというフォルダに10000枚の画像ファイルがあるとします。 Aからランダムに画像を選び、ムービークリップ(以下MC)に読み込ませて表示し、任意の秒数で画像が切り替わっていくビューワーを作成するとして、"ランダムに画像をMCに読み込ませる(ただし、一度読み込んだものは二度は読み込まない)"にはどのようなやり方があるのでしょうか。 簡易かつ動作が軽いActionScriptの記述法を教えていただけると助かります。 よろしくお願いします!
- みんなの回答 (2)
- 専門家の回答
みんなの回答
- horsewater
- ベストアンサー率50% (1/2)
ActionScriptをはじめ、最近の言語は動的にサイズを変更できる配列を使用できて楽なのですが、内部的にはメモリの使用効率が悪かったりします。 sassakun様の例で使用されている splice はその象徴的なものなのでしょう。 おそらく内部では 1.splice後のサイズの領域を確保。 2.splice前の値をすべてコピー。 といったことをやっているのでしょう。 サイズが大きければ大きいほど、この処理にも時間がかかり、またループする回数も多いので 1000と 10000ではだいぶ処理時間が違うのでしょう。 こんなときには昔ながらの方法が役に立ちます。 ↓が私がやったスクリプトです。 CPU2000MHz程度ですが、300ミリ秒程度でランダム配列を作成できました。 -------------------------------------------------- // 写真の枚数の登録 var pNum:Number = 10000; // //※処理時間を求めるためのスクリプト1 var time1:Number = getTimer(); // // 配列 aArr 要素の作成 var aArr:Array = new Array(pNum); // aArr[0]~aArr[9999] の配列に 0~9999 の数を代入 for (var i:Number = 0; i<pNum; i++) { aArr[i] = i; } // 配列をランダムにSWAPする for (var i:Number =0; i<pNum; i++) { // i番目と入れ替えるインデックスをランダムに選ぶ var j:Number = Math.floor(Math.random()*aArr.length); // SWAP var tmp:Number = aArr[i]; aArr[i] = aArr[j]; aArr[j] = tmp; } //※処理時間を求めるためのスクリプト2 var time2 = getTimer(); // 確認のため100件だけ表示してみる for(var i:Number =0; i<100; i++){ trace(aArr[i]); } trace("所要時間:" + (time2-time1) + "(ms)");
10000枚を重複無しで出すと言うのは結構難しいですね。 ActionScript で, 0~9999 の重複しないランダムな要素を持つ配列の作成に挑戦しましたが, その時点で断念しました↓(失敗部品)↓。 ----------------------------------------------- // 写真の枚数の登録 var pNum = 10000; // //※処理時間を求めるためのスクリプト1 var time1 = getTimer(); // // 配列 aArr 要素の作成 var aArr = new Array(); // aArr[0]~aArr[9999] の配列に 0~9999 の数を代入 for (var i = 0; i<pNum; i++) { aArr[i] = i; } // 配列 bArr 要素の作成 var bArr = new Array(); // for (i=0; i<pNum; i++) { // 変数rNumに aArr の要素数までの整数の乱数を取得 rNum = Math.floor(Math.random()*aArr.length); // 出力配列 に入力配列の乱数番目の要素を代入 bArr[i] = aArr[rNum]; // 入力配列の乱数番目の要素を抜き取る // (aArr.length はこの時点で1つ減る) aArr.splice(rNum, 1); } // //※処理時間を求めるためのスクリプト2 var time2 = getTimer(); trace(time2-time1); ----------------------------------------------- ※注意 上記スクリプトは実行させない方が良いです。 私の CPU 2000MHz くらいの PC ではフリーズしました。 フリーズというか,例の(私は)よく見る, ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ ムービー内のスクリプトが原因で Flash Player の 実行速度が遅くなっています。 このまま継続すると、反応しなくなることがあります。 スクリプトの実行を中止しますか? [ はい(Y) ] [ いいえ(N) ] ・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・ のダイアログ出るという意味です。 var pNum = 10000; を var pNum = 1000; に 変更する(1/10 にする)と大丈夫で, 処理時間 約 1100ミリ秒 (1秒少し)で, 0~999 の重複しないランダムな要素を持つ配列が 作成できました。 上の重複しないランダムな整数が取得できると後は簡単なのですけどね。 上のようにあらかじめ重複しない数を出すのではなく, 前に出した数字と重複しない数をその都度出すというのも, 後になればなるほど処理が増すのでいやですし, ActionScript だけで解決するのは困難ではないかと思います(おそらく)。 //////////////////////////////////////////////////////// 前置きの失敗談はこの辺で置いておいて, 別案で, Excle を使ってデータを用意する方法を提案します。 Excle の「A1」セル(一番左上のセル)に =RAND() と書きます。 そして, 「B1」セル(一番上の左から2列目のセル)に ="&p"& ROW() &"="&RANK(A1,$A$1:$A$10000,1)-1 & "&" と書きます。 そして, 「A1」 セルと「B1」セルの2つのセルを選択した状態で, 「B1」セルの右下のコーナー辺りにある□(ハンドル)を, 10000行目まで,ダーーーッとドラッグします。 つまり オートフィル機能で連続コピペをします。 すると, 「B1」~「B10000」に, ------------------ &p0=2593& &p1=5289& &p2=9651& &p3=60& &p4=6546& &p5=2831& &p6=3022& &p7=2448& &p8=9452& &p9=9921& &p10=187& …略… &p9997=3631& &p9998=8418& &p9999=1465& ------------------ のようなものが得られます。 ランダムなので,当然右辺は人や時によって違います。 2593 とか 5289 とか 9651 など右辺の値は, 0 ~9999 までの重複しない数になっているはずです(確率的におそらく)。 もし心配であれば, =IF(COUNTIF(A:A,A1)>1,"重複","") と 「C1」 セルに書いて, これまた 10000行 までオートフィルでコピーしてください。 重複があれば「重複」が表示されると思いますから, もし「重複」が表示されれば再計算させれば良いと思います。 Excel でも計算はかなりもたつきますよ。 でもそのもたつく処理は作成時の1度きりですし, Flash のようにアニメーションソフトではないので, もたついても問題はないでしょう。 また,Flash がもたつくと多くの見る人にとって迷惑ですが, 作者だけがもたつくわけですから,少々は我慢です。 それで,この, 「B1」~「B10000」を選択して「コピー」, メモ帳などテキストエディタを起動させて, 「ペースト」します。 そして, そのテキストデータを, 例えば「mydata.txt」というテキストファイルで保存します。 改行コードなども指定できるテキストエディタだと, 「改行=LF」など1つにしておくとファイル容量も削減できます。 改行コードを LF のみにして,「mydata.txt」を保存すると, 私の場合 124 KB (127,780 バイト) のテキストファイルになりました。 この「mydata.txt」のデータを Flash でロードして使えば良いのではないでしょうか。 Aというフォルダにある10000枚の画像ファイルは, 「0.jpg」,「1.jpg」,「2.jpg」,「3.jpg」,…,「9999.jpg」というファイル名にしておきます。 Flash も 「mydata.txt」 も「Aフォルダ」と同じフォルダに置くものとします。 任意のフォルダ ├ ○○.html ├ ○○.swf ├ mydata.txt └ A ├ 0.jpg ├ 1.jpg ├ 2.jpg … 略 … └ 9999.jpg Flash の作成ですが, まず,ステージ上に JPEG をロードするためのムービークリップを作成します。 これは,1辺10px くらいの小さなムービークリップで良いです。 そのムービークリップに「myMC」というインスタンス名を付けたとします。 そして, _root のタイムラインのフレーム1に次のように書けば一応完成です。 -------------------------------------------- // 写真の枚数の指定(可変) var pNum = 10000; // // カウンタの初期化 var cnt = 0; // // 画像を読み込むユーザ定義関数の定義 function loadGazou() { // myMC に Aフォルダ内のランダムjpgをロード _root.myMC.loadMovie("A/"+myLV["p"+cnt]+".jpg"); // カウントの加算 cnt++; // もしカウントが指定枚数に達すれば if (cnt>=pNum) { // setIntervalを解除 clearInterval(myID); } } // LoadVareクラスのインスタンス myLV を作成 var myLV = new LoadVars(); // // 外部テキストがロードされたときの処理を定義 myLV.onLoad = function() { // ユーザ定義関数 loadGazou の初回実行 loadGazou(); // そのあと loadGazou を2000ミリ秒ごとに実行 myID = setInterval(loadGazou, 2000); }; // // LoadVarsインスタンスにテキストのロード myLV.load("mydata.txt"); -------------------------------------------- 一応以上で,ご質問の回答にはなっていると思います。 上の例で行くと,2秒間隔で写真が変わるので, 約5時間半の間,重複しない画像がランダムに表示されます。 その後(約5時間半後)のことは知りませんよ。 上の例の場合,約5時間半後,10000枚目が表示されたままストップします。 普通の場合は,また始めの写真に戻ると思いますが, その場合,「重複」になるので,この回答にはならないためここで終わりとなります。 しかし, 上の方法は,大きな欠点が1つありますよね。 ご質問には書かれていませんから,どうでもいいことなのかもしれませんが, 1回目に見るときも,2回目に見るときも,何回目に見るときも, 例えば「p0=2593」は固定なのですから,毎回 「2593.jpg」 からの閲覧になります。 この欠点は, SharedObject(Cookieみたいなもの) を使って, 変数 cnt の値を保存しておくと, 次回閲覧からは続きの JPEG から見ることが可能になり,解消されます。 -------------------------------------------- // 写真の枚数の指定(可変) var pNum = 10000; // // SharedObject mySO の作成&データの読み込み var mySO = SharedObject.getLocal("mysol"); // もし mySO のデータに cnt という変数が存在しなければ if (mySO.data.cnt == undefined) { // mySO のデータに cnt の初期化 mySO.data.cnt = 0; } // // 画像を読み込むユーザ定義関数の定義 function loadGazou() { // myMC に Aフォルダ内のランダムjpgをロード _root.myMC.loadMovie("A/"+myLV["p"+mySO.data.cnt]+".jpg"); // mySO のカウントを加算 mySO.data.cnt++; // もしカウントが指定枚数に達すれば if (mySO.data.cnt>=pNum) { // setIntervalを解除 clearInterval(myID); } } // LoadVareクラスのインスタンス myLV を作成 var myLV = new LoadVars(); // // 外部テキストがロードされたときの処理を定義 myLV.onLoad = function() { // ユーザ定義関数 loadGazou の初回実行 loadGazou(); // そのあと loadGazou を2000ミリ秒ごとに実行 myID = setInterval(loadGazou, 2000); }; // // LoadVarsインスタンスにテキストのロード myLV.load("mydata.txt"); -------------------------------------------- 10000枚目到達までであれば, 何回見ても重複することはありません。 毎日1時間ずつ見ても5日目くらいまでは重複無しです。 しかし, これも,10000枚目が表示された後のことは知りませんよ。 同じく,最後は10000枚目が表示されたままストップします。 SharedObject に関しては度々回答しております。 つい先日回答したものがありますが参考になるかもしれません。 「一日一度の抽選の方法」 http://oshiete1.goo.ne.jp/qa2809647.html ↑教えて!goo ↓OKWave (同じです) http://okwave.jp/qa2809647.html ====================== あと 先に書くべきだったことかもしれませんが, この方法で,実際に作業し始めるとわかると思うのですが, A というフォルダにある10000枚の画像ファイルは, 「0.jpg」,「1.jpg」,「2.jpg」,「3.jpg」,…,「9999.jpg」というファイル名にしなくても良いのですよ。 Excel で,先に単に重複しないランダムな数を出したので, 後から上のように 「0.jpg」,「1.jpg」,「2.jpg」,「3.jpg」,…,「9999.jpg」というファイル名にしなければならないだけです。 実際は何でも良いのです。 何でしたら今現在の実際の名前のままでも良いです。 Excel の VBA フォルダ内のファイル名の一覧表を作って, それをランダムにシャッフルして, とにかく,外部テキストを, ------------------ &p0=sep0003& &p1=apr0009& &p2=may0121& &p3=oct0010& &p4=jun6546& &p5=dec1831& &p6=may0002& &p7=jan00048& &p8=mar0052& &p9=sep09921& &p10=feb0170& …略… &p9997=aug0031& &p9998=dec0018& &p9999=jul0014& ------------------ このような形にしさえすれば, 上記スクリプトをそのままの使えます。 こういう点においても,データ作成はExcle を使うと便利ですね。 ファイルネームの自動取得も簡単です↓。 「Office TANAKA - VBA講座:ファイルの操作(ファイルの一覧を取得する)」 http://officetanaka.net/excel/vba/file/file07.htm ActionScript では,こんなことは普通はできません。 VBA だと簡単です。 ファイルネームの自動取得後のランダムシャッフルは, 上の =RAND() 関数を使えばできますよね。 「A列」 にファイル名を得た場合, 「B列」にダーーーーッと =RAND() を書いて, 「A列」全体と「B列」全体を選択した状態で, 「データ」→「並べ替え」を選択して, 「優先されるキー」を「列 B」にして,並べ替えをすれば良いだけです。 VBA でもできますが, VBA を考える時間があったら,上の方法で何百回でもシャッフルできると思います。 私自身,Flash 作成のために, 実際に シート関数 や VBA を使うことは多々あります。 こういう大きなデータを扱う場合は,ActionScript だけでなく,色んなものを利用する方が良いと思います。
お礼
素晴らしい回答・・・助かります! なるほど、、、やはりぼんやり考えていた通り外部テキストファイルを利用するのですね。 なにぶん初心者なもので当回答を解釈するのも難しそうですが、土日にでもご回答を参考にいろいろ試してみようと思います。 これがOKWeb初の質問なのですが、これほどまでに丁寧で詳細な回答がいただけるとは思いませんでした。 本当にありがとうございました!!