トピック
ひろきち henahenachoco

FOR文で気付いたこと

つい最近、FOR~NEXTをいじっていて気付いたことがありました。 インラインヘルプにもシッカリ書いてあるのですが、FOR文はループの終わりに変数を増やしてループするんですね。 例えば FOR I=0 TO 3 NEXT だと、BREAKでループを抜けない限りは、 FORの処理が終わった後は Iの中には4が入る、と。 これに気付いたので、ちょっと応用した使い方ができました。 他にも、この命令って実はこうなんですよ、 みたいな豆知識なことがあれば、情報交換したいです! お願いします。
4そうだね
プレイ済み
返信[1]
親投稿
私が開いているトピックに、「プチコン七不思議」ってのがあるんですが、そこに色々知らなかった使い方なんかが書かれてますよ
4そうだね
プレイ済み
返信[2]
親投稿
だにえる haru2016nen
FOR L=0 TO 5 *** NEXT は内部的に↓のような処理になっているかと L=0 WHILE L<=5 *** L=L+1 WEND
3そうだね
プレイ済み
返信[3]
親投稿
おちゃめ ochame_nako
これに気づくことができたらBREAK等でFOR~NEXTを抜けたかどうかもカウンタ変数の値を調べるだけで分かりますね。 動作しているスロットを取得する自作関数「SLOT」でもFOR~NEXTのこの特長を活かしています。 https://miiverse.nintendo.net/posts/AYIHAAAEAACHVRTs1c4kFA
2そうだね
プレイ済み
返信[4]
親投稿
おちゃめ ochame_nako
このSLOT関数は「VARは関数である」だけではなく左辺値にもなることを利用して作っています。 他にも「文字列と数値を比較演算したら3を返す」ということを利用して型判別を大幅に簡略化した自作関数SUFFIXなどがあります。 https://miiverse.nintendo.net/posts/AYMHAAADAAB2V0fSfxRUkA 「VARを関数として使う」「簡単な型判別」の合わせ技を使えば数値でも文字列でも入る変数を宣言できる関数などを作れます。 https://miiverse.nintendo.net/posts/AYMHAAACAAADVHkoSL6eyA こういうのを覚えておくとプログラムリストを短縮させるのを役立つだけではなく「こんなのできたらいいな」というのが簡単にできるので非常に便利ですね。 私はプチコン3号でこういう変なことばかりやっています。
1そうだね
プレイ済み
返信[5]
親投稿
ひろきち henahenachoco
おちゃめさん ちょっと高度すぎて今の僕には使いこなせないかもですが、VARが関数として使えるなんて知りませんでした! 貴重な情報ありがとうございます!
1そうだね
プレイ済み
返信[6]
親投稿
だにえる haru2016nen
関数のVARってフルプに載ってないんだよね。 ちなみに、別SLOTの配列をコピーしたい場合 はVARよりもCOPYを使ったほうが 劇的に速い。
1そうだね
プレイ済み
返信[7]
親投稿
だにえる haru2016nen
誤字:フルプ→ヘルプ
1そうだね
プレイ済み
返信[8]
親投稿
抜けるときの変数の値ね。地味に使ってる。
2そうだね
プレイ済み
返信[9]
親投稿
InDoor TheInDoor12
豆知識というと、 PRGGET$()を使うとカレント行が1行ずれる、とかですかね... 私はこれに気付くまでPRGGETを使うたびにPRGEDITでカレント行を指定してました。
4そうだね
プレイ済み
返信[10]
親投稿
ひろきち henahenachoco
だにえるさん FOR~NEXTは遅い、なんてことも聞いたことあるんですが、 このコードとこのコードは、外から見たら同じ処理だけど、こっちの方が速い、みたいなことって、どういう感覚でわかるんですかね? 命令をする回数が少ない方が、みたいな単純なこととは違う感じですかね?
1そうだね
プレイ済み
返信[11]
親投稿
だにえる haru2016nen
処理速度を求める場合は MILLSECというシステム変数を使います。 (MAINCNTだとフレーム単位なので 精度が荒い) 簡単にいうとミリ秒単位の時間を出して くれます。 1回のループにかかる時間を求めたい場合は、 ループ終わり→ループ開始 までの時間を引く必要があるので、 そんなときは おちゃめさんの自作関数TIMER()(うろ覚え)
2そうだね
プレイ済み
返信[12]
親投稿
だにえる haru2016nen
あ、4行目、PRINT抜けてました。
1そうだね
プレイ済み
返信[13]
親投稿
ひろきち henahenachoco
そんな命令があったんですね! 知らなかった… ありがとうございます!
1そうだね
プレイ済み
返信[14]
親投稿
おちゃめ ochame_nako
速いか遅いかは一定回数を繰り返すのに必要な時間を比較すれば簡単に分かります。(FOR~NEXTはカウンタ変数のインクリメント処理がある分だけ遅いけどループ内で別途インクリメントを行うのであればFOR~NEXTの方が速い場合がある) それが体感できる(感覚で分かる)レベルの差か時間を計ってみて初めて分かるレベルの差かは人によるため一定の基準はないと思います。 DIMとVAR、WAITとVSYNCのように「似ているけど動作が異なる部分がある」という命令はプチコン3号には多くあるので違いを知っておくといろいろと便利なこともあります。
4そうだね
プレイ済み
返信[15]
親投稿
おちゃめ ochame_nako
個人的にはVARの関数的な要素より(DIMには無い点)よりもWAITとVSYNCの違いを知っている人の方が少ない気がします。
1そうだね
プレイ済み
返信[16]
親投稿
ひろきち henahenachoco
おちゃめさん VSYNCは確か、前回VSYNCをから指定フレーム分待つ、ということだったかと思うんですが、それが機能的にどういう効果をもたらしているのかは知らずに使っていますね…
1そうだね
プレイ済み
返信[17]
親投稿
おちゃめ ochame_nako
VSYNCはあくまで前回のVSYNCから指定フレームほど待つ命令だから、Aボタン入力待ちループとしてよく使われる REPEAT VSYNC UNTIL BUTTON(2)==#A というのは正常に機能しない場合があるんですよね。 これは誤動作防止のためにはWAITを使うべき場面です。
3そうだね
プレイ済み
返信[18]
親投稿
だにえる haru2016nen
ちなみに、僕のSTGのタイトル画面では、 VSYNCの代用として画像のようなもの を入れています。
2そうだね
プレイ済み
返信[19]
親投稿
おちゃめ ochame_nako
だにえるさんへ MAINCNTで代用というのは初代プチコンから良く使われている手法ですね。 実は初代プチコンのVSYNCはmkII以降のWAITに相当する命令でした。 そのため60fpsのプログラム以外を作るのが困難であり、MAINCNTLを使って自前で管理をしていました。 VSYNC 6などのように「1よりも大きな値」を引数に指定しまうとBUTTON(2)等が正常動作したないためmkII以降であってもこの方法は覚えておくと便利な方法といえます。 MILLISECは16m秒を指定した場合には60fps(約16.66fps)との誤差によって稀に誤動作が発生するという点が難点ですが。
2そうだね
プレイ済み
返信[20]
親投稿
MIKI ifconfig
おちゃめさん >REPEAT VSYNC UNTIL BUTTON(2)==#A というのは正常に機能しない場合がある え?? どんな場合ですか???
1そうだね
プレイ済み
返信[21]
親投稿
REPEAT VSYNC UNTIL BUTTON(2)==#A が誤作動する場合って RUN(Aボタン) で実行してすぐにこのプログラムがある場合とか?
2そうだね
プレイ済み
返信[22]
親投稿
マギー M191246
なんだか高度な話になってる。 豆知識 ここに長い人は知ってると思うけど SMILEBOOMと打ってヘルプを押すと 特別な文が見られる。 ほかにもあった気がする。 動作上便利がいいとか そんなことではないんだけど…。
3そうだね
プレイ済み
返信[23]
親投稿
おちゃめ ochame_nako
MIKIさんへ 具体的に誤動作する場面でいえば「RUN+Aボタンで実行時」「INPUTやLINPUTでAボタンで入力確定時」がありますが、前者はタイトル画面において、後者はコンソールゲームを作っている場合において初心者がはまりやすい落とし穴と言えます。 これ以外にもAボタンを押した後に「Aボタン入力待ち」がある場合において前回のVSYNCから1フレーム以上経過している場合に誤動作となります。(※「Aボタン入力待ち」が実行されるのと同一フレームでAボタンを押した場合) そのため初心者にREPEAT VSYNC UNTIL BUTTON(2)==#Aという記述を安易にオススメしても改善策にはならない場合があるのです。 (続く)
1そうだね
プレイ済み
返信[24]
親投稿
おちゃめ ochame_nako
(続き) REPEAT VSYNC UNTIL BUTTON(2)==#AのBUTTUN(2)をBUTTON(3)に変更すれば正常動作する(Aボタンを離すまで待つルーチン)ように思えますが、今度は「DIALOGはAボタンを離した時に確定」なのでその場合に誤動作となってしまいます。 VSYNCがキャンセルされないためにはREPEAT VSYNC UNTIL BUTTON(2)==#Aの直前にもう1つVSYNCを記述すれば解決できますが、それをするくらいならば確実に待ってくれるWAITを使用してREPEAT WAIT UNTIL BUTTON(2)==#Aとすれば良いというのが私の考えです。 初心者に丸覚えで「Aボタン入力待ちルーチン」として教えるのであればこちらのWAITを使用したものをオススメしたいです。 (続く)
1そうだね
プレイ済み
返信[25]
親投稿
おちゃめ ochame_nako
(続き) 上記については詳しくは私のプチコン3号入門講座で書いていますが、初心者に教える場合は「待ちたいならばWAIT」「タイミングを取りたいならばVSYNC」で良いので実は何も知らない初心者の方が逆に両者の使い分けを理解しやすいかもしれません。 もちろん、VSYNCの特性を理解している人や自分のプログラムでは問題ないことを分かってREPEAT VSYNC UNTIL BUTTON(2)==#Aと記述するのは何ら問題はないですよ。
2そうだね
プレイ済み
返信[26]
親投稿
ひろきち henahenachoco
マギーさん 知らなかったあああ!!! おもしろいですね!
2そうだね
プレイ済み
返信[27]
親投稿
ひろきち henahenachoco
RUN+Aを押した時に、タイトル画面でもAを押したことになってしまう現象は僕も何度も味わっています。 WAITだと問題ないんですね。知らなかったです! おちゃめさんの入門講座、読んでみます!
1そうだね
プレイ済み
返信[28]
親投稿
おちゃめ ochame_nako
入門講座の方は以前修正したものがきちんと反映されてなかったのでこちらで補足しておきます。(現在はすでに反映済み) VSYNCは前回のVSYNCを起点として指定フレームほど待つ命令です。 ここで「最初のVSYNCは起点が無いから無視される」という考えの人もいますが、それは正しくありません。 「前回VSYNC」というのはあくまで「前回VSYNC」なのです。
0そうだね
プレイ済み
返信[29]
親投稿
おちゃめ ochame_nako
例えばVSYNC 300がプログラムの先頭にある場合はプログラムに依存します。 前回実行したプログラムにVSYNCが含まれていてそれを中断してすぐに実行すれば約300フレームの待ちとなります。 M=MAINCNT VSYNC 300 IF MAINCNT-M THEN ?"あなたは、前回中断して5秒以内にこのプログラムを実行しましたね?" このプログラムを一旦実行して再実行してみてください。 VSYNCはプログラムそのものを前回実行したときであってもそれが前回のVSYNCとなり起点となるためこのように前回実行した中断してからの時間も判定が可能です。 つまり、VSYNCを正しく理解するには起点がどこにあるのかを理解する必要があるということです。
0そうだね
プレイ済み
返信[30]
親投稿
おちゃめ ochame_nako
ここまで理解できていればタイトル画面表示時にREPEAT VSYNC UNTIL BUTTON(2)==#AとしてAボタン入力待ちを行っている際にRUN+Aボタンでの誤動作回避も簡単にできます。 回避パターン1 VSYNC 0(0でなくても任意の数字でOK)をAボタン入力待ちより前に入れる 本来はスルーされるVSYNC 0であってもVSYNCの起点においては有用であるためそこが起点となり、Aボタン入力待ちルーチンの中にあるVSYNCが正しく動作します。
0そうだね
プレイ済み
返信[31]
親投稿
おちゃめ ochame_nako
回避パターン2 FOR I=1 TO 30000:NEXTなどの1フレーム以上かかる処理をAボタン入力待ちルーチンより前に記述する これによって実行開始時にすでにAボタンを押している場合にBUTTON(2)でAボタンを入力した瞬間が取得できないためAボタン入力待ちルーチンのREPEAT~UNTILを抜ける条件を満たすことができず、一旦離して再びAボタンを押す必要がありAボタン入力待ちが正しく動作するようになります。
0そうだね
プレイ済み
返信[32]
親投稿
おちゃめ ochame_nako
回避パターン3 規模の大きいプログラムを作る プチコン3号は実行時に仮想マシンコードへの変換という処理が行われ実行開始と実際に実行されるまでにタイムラグがあるためそれに1フレーム以上かかる場合も「回避パターン2」と同じく最初からAボタンを押していてもBUTTON(2)でのAボタン入力した瞬間が取得できないため誤動作が起きなくなります。 こんな回避パターンを考えるならば素直にVSYNCではなくWAITを使うのがベターということが分かると思います。
0そうだね
プレイ済み
返信[33]
親投稿
おちゃめ ochame_nako
VSYNCやWAITが特別複雑な命令というわけではなく実は単純な命令や関数でも注意すべき点(思ったように動作しないパターン)が多く存在します。 数値を文字列に変換するSTR$でも実数型変数を文字列化した場合には6桁で丸められるけど整数型変数の場合は2の31乗-1までの数値が扱える(数値が小数の場合は長くなるため省略)などあるため「この命令って実はこうなんですよ」というのは非常に膨大なものになってしまいます。 VSYNCとWAITは当たり前のように使っているけど「当たり前ではない」という一例として書いてみました。
0そうだね
プレイ済み