HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2010
0516
ミント「サブルーチンやループのネストが深すぎます」のエラーについて19解決


ミント

リンク

2010/5/16(Sun) 16:56:28|NO.32668

どーも、こんにちは。そうでない方は始めました。

STGを作っていますがボスのスペカ攻撃を入れて、あたり判定を作ろうとしたら
「サブルーチンやループのネストが深すぎます」というエラーが出てしまいましたorz


それで原因を調べてみました。
http://quasiquote.org/hspwiki/HSP3%E3%81%AEFAQ%3a%E3%82%A8%E3%83%A9%E3%83%BC%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%81%AE%E6%84%8F%E5%91%B3%E3%81%A8%E5%AF%BE%E5%87%A6%E6%B3%95%E3%81%8C%E5%88%86%E3%81%8B%E3%82%89%E3%81%AA%E3%81%84#H-18ahz0y
>9 "サブルーチンやループのネストが深すぎます"
>gosub命令、repeat命令を多重に実行しすぎた時に表示されます。


それで「repeat」を少し削ってみましたが改善せず・・・
メインプログラムとにらめっこして、9個ぐらい「repeat」を削れそうなものを見つけました。
その反動で「gosub」を削ることも可能。

まだそちらは直してないのですが、まだ1面の中ボスなんですよねこれ。
(直した場合正常作動するかわからない)



URL先にありましたプログラム
--------------------------------------------------------

*Label_A ;31ループ/30ネストでエラー ;メイン gosub 19/if抜き 12/ifあり 7/repeat 28 repeat repcnt++: title "Label_Aループ:"+repcnt+"個目のrepeat/"+(repcnt-1)+"回ネスト" wait 50: goto *Label_B loop stop *Label_B repeat repcnt++: title "Label_Bループ:"+repcnt+"個目のrepeat/"+(repcnt-1)+"回ネスト" wait 50: goto *Label_A loop stop
---------------------------------------------------------------------
で調べてみると「31ループ/30ネストでエラー」でエラーが起きますけど
HSPでは「31ループ/30ネスト」が限界なんでしょうか?


ちなみにメインプログラムの方は「gosub 19/if抜き 12/ifあり 7/repeat 28」です。
・メインループの中に「gosub」が19個ある(もっとある)
・その中で「if」で飛ぶやつが7個
・「repeat」はプログラム全体で28個


自分はまだ初心者なので無駄な宣言等が多いのが原因のひとつだと思います。
予断だけどメインプログラムは1537行ある(1面の中ボスで)


この先1ボス〜6ボスまでプログラミングしなければならないので「repeat」が自然と増えると思います。
そう考えるとこの先不安です・・・
メインプログラムを大改造するしかないんでしょうか・・・

若干相談みたいのが混じってますが、よろしくおねがいします。



C言語を若干かじっているので、自由が利くC言語に行った方がいいだろうか・・・
だけど、C言語でゲーム作るの難しすぎてHSPに来たんだけどね(´・ω・`



この記事に返信する


f3d

リンク

2010/5/16(Sun) 17:23:17|NO.32670

repeat〜loop内から脱出するときは
goto*ラベルではなく、breakをつかいましょう。

それか、
*ラベル〜goto*ラベル
をループに使用しては??


まだ一面のうちに、ちゃんとプログラムを組んでおけば、
次の面を作るときなども、組みやすくなります。
早いうちに直しておいたほうがいいです。

>>予断だけどメインプログラムは1537行ある(1面の中ボスで)
多分、無駄な処理とか共通部分などがあるとおもいます。
#deffuncやサブルーチンなどを使ってみるともっと短くなるかもしれません。
自分が昔作った3Dゲームはソースが2447行ありましたが、
最近組みなおしたら、756行くらいに短縮できました。



ミント

リンク

2010/5/16(Sun) 18:42:34|NO.32673

>>f3dさん
>>repeat〜loop内から脱出するときは
>>goto*ラベルではなく、breakをつかいましょう。
「repeat〜loop内」の脱出があるのが一箇所(仕様)
なのでこちらは問題ないと思います。


>>*ラベル〜goto*ラベル
>>をループに使用しては??
すでにそういう構想になってますね。

簡単に言うと

*メイン gosub *自機移動 goto *メイン *自機移動 〜〜〜〜〜〜 return ;サブルーチンから復帰
みたいな感じで、弾を出す、敵を出すといったすべてのことは
この構造で処理してます。



>>まだ一面のうちに、ちゃんとプログラムを組んでおけば、
>>次の面を作るときなども、組みやすくなります。
>>早いうちに直しておいたほうがいいです。
やはりそうですよね・・・
んー、難しい。見やすくって結構良かったんだけど・・・



>>多分、無駄な処理とか共通部分などがあるとおもいます。
>>#deffuncやサブルーチンなどを使ってみるともっと短くなるかもしれません。
無駄な処理ありまくりだと思います。
共通部分もかなりあるので、配列とかでまとめるつもりです。



>>自分が昔作った3Dゲームはソースが2447行ありましたが、
>>最近組みなおしたら、756行くらいに短縮できました。
3Dで約2500行か・・・

明らかに3Dの方が難しいのに自分は2Dなので、結構無駄な処理が多いですねやっぱ。
がんばって見直して改造するしかないかな・・・



ちなみに今ちょいとがんばって
「gosub = 全19/if抜き12/if = 7/repeat = 28」から
「gosub = 全14/if抜き8/if = 6/repeat = 16」にしてみた。
(少し問題あるけど、最低限の動作はできる)

でも、これ以上小さく出来る気がしない・・・
ちくしょう・・・悔しいぜ。


アドバイスありがとうございます!



GENKI

リンク

2010/5/16(Sun) 19:24:35|NO.32675

> 明らかに3Dの方が難しいのに自分は2Dなので、結構無駄な処理が多いですねやっぱ。

3Dも必要な命令はそろってるので、仕組みを理解さえしてればスクリプトそのものはそれほどでもないです。


> 「gosub = 全19/if抜き12/if = 7/repeat = 28」から
> 「gosub = 全14/if抜き8/if = 6/repeat = 16」にしてみた。
> (少し問題あるけど、最低限の動作はできる)

なんでgosub減ってるんだろう…。
なんとなくですが、「無駄」の解釈を間違えてる気がします。

同じような処理をサブルーチンやモジュール、マクロ化することでまとめると全体がコンパクトになるだけではなく、スクリプトが読みやすくなり、必然的にデバッグしやすくなります。
マクロやモジュールを使えないなら積極的にサブルーチンを利用すべきです。
と、いう意味。



コーヒー

リンク

2010/5/16(Sun) 19:53:27|NO.32676

あーうー、どっかおかしいかも。

サブルーチン

screen 0,320,240 ;----------------------------------------------------------- ; メインループ ;----------------------------------------------------------- *start gosub *set *main_lp repeat redraw 0 gosub *bg ;背景 gosub *jiki ;自機 gosub *teki ;敵機 wait 1 redraw 1 loop goto *start ;----------------------------------------------------------- ; サブルーチン ;----------------------------------------------------------- *bg ;背景 title ""+count return ;■■■■■■■■■■■■■ *jiki ;自機 ;キー判定:移動 ;〜〜 ;キー判定:弾 ;〜〜 ;表示:自機 ;〜〜 ;表示:自機弾 ;〜〜 return ;■■■■■■■■■■■■■ *teki ;敵機 ;移動 ;〜〜 ;表示 ;〜〜 return ;■■■■■■■■■■■■■ *set ;変数の設定とか ;基本 gmode 2 ;自機 ;初期位置 ;〜〜 ;スピード ;〜〜 ;弾 0なし,1あり ;〜〜 ;弾位置 ;〜〜 ;敵機 ;敵機 0なし,1あり ;〜〜 ;初期位置 ;〜〜 return
自分はこういう書き方してる。

ループ

*スタート gosub *タイトル画面のループ gosub *ゲームのメインループ gosub *エンディングのループ wait 1 goto *スタート *タイトル画面のループ repeat title "今タイトル画面です" ;-サブルーチンここから gosub *クリックで進む ;-サブルーチンここまで if クリックしたよ=1 : クリックしたよ=0 : break wait 1 loop return *ゲームのメインループ repeat title "今ゲーム中です" ;-サブルーチンここから gosub *クリックで進む ;-サブルーチンここまで if クリックしたよ=1 : クリックしたよ=0 : break wait 1 loop return *エンディングのループ repeat title "今エンディングです" ;-サブルーチンここから gosub *クリックで進む ;-サブルーチンここまで if クリックしたよ=1 : クリックしたよ=0 : break wait 1 loop return *クリックで進む stick key if key=256 : クリックしたよ=1 return
クリックすると進む。
タイトル画面→ゲーム→エンディング
でタイトル画面に戻ってもう一回ゲーム。



コーヒー

リンク

2010/5/16(Sun) 20:20:51|NO.32677

コピペだけで投稿してしまた。
既に理解してたら読み飛ばしてくれ。

>>HSPでは「31ループ/30ネスト」が限界なんでしょうか?
まあそうなんだろう。知らないけど。
でもrepeatを31個しか使えないってわけじゃないし、エラーが出ないように使っていけばいいだけのこと。

>>URL先にありましたプログラム
これはLabel_Aのループを抜けてないのにLabel_Bへ飛ぶ。
そのLabel_Bでもループを抜けずにLabel_Aへ・・・ってのを繰り返してるからエラーがでる。
一旦breakでループを抜けて、そこからgotoで飛べばエラーはでない。


*Label_A ;31ループ/30ネストでエラー repeat repcnt++: title "Label_Aループ:"+repcnt+"個目のrepeat/"+(repcnt-1)+"回ネスト" wait 5: break ;ここでgotoで飛ばない loop goto *Label_B ;ループを抜けたらここで飛ぶ stop *Label_B repeat repcnt++: title "Label_Bループ:"+repcnt+"個目のrepeat/"+(repcnt-1)+"回ネスト" wait 5: break ;上に同じ loop goto *Label_A ;ループを抜けたらここで飛ぶ stop



ミント

リンク

2010/5/16(Sun) 20:23:34|NO.32678

>>GENKIさん
>>なんでgosub減ってるんだろう…。
>>なんとなくですが、「無駄」の解釈を間違えてる気がします。
同じような処理があって小さくしてみた。
けどいったん頭の中をリセットしたほうがよさそう・・・


>>同じような処理をサブルーチンやモジュール、マクロ化することでまとめると全体がコンパクトになるだけではなく、スクリプトが読みやすくなり、必然的にデバッグしやすくなります。
>>マクロやモジュールを使えないなら積極的にサブルーチンを利用すべきです。
「マクロやモジュール」を使えない(理解できない)
のでサブルーチンでやるしかないですね。


んー、もう少しがんばってみます。
ありがとうこざいます。



ミント

リンク

2010/5/16(Sun) 20:33:08|NO.32679

>>コーヒーさん

>> screen 0,320,240

〜〜省略〜〜

>> return


>>自分はこういう書き方してる。
ふむふむ。



>>*スタート
〜〜省略〜〜
>> return
これは私と同じ構造なのでわかりやすいですね。
0と1でオンオフしているのかφ( ̄ー ̄ )メモメモ



サンプルプログラムありがとうございます!



ミント

リンク

2010/5/16(Sun) 20:45:24|NO.32680

>>コーヒーさん
>>既に理解してたら読み飛ばしてくれ。
まだ理解してなかったりする・・・



>>でもrepeatを31個しか使えないってわけじゃないし、エラーが出ないように使っていけばいいだけのこと。
そうなんですか。考え方次第ですね。


>>そのLabel_Bでもループを抜けずにLabel_Aへ・・・ってのを繰り返してるからエラーがでる。
>>一旦breakでループを抜けて、そこからgotoで飛べばエラーはでない。

>>*Label_A ;31ループ/30ネストでエラー
〜〜省略〜〜
>> stop

おおー、なるほど・・・・
こりゃ疑い深くプログラムを見て修正したほうがよさそうね。





ちなみに・・・・

gosub *akahu ;赤札移動
*hitcheck2
repeat AKMAX ;ループの始まりの場所を示す(敵の最大数,8)
〜〜省略〜〜
}loop ;repeatに戻る
}return

をメインプログラムに追加しただけでエラーがおきた。
今まで大丈夫だったのに・・・



GENKI

リンク

2010/5/16(Sun) 21:11:48|NO.32681

> 同じような処理があって小さくしてみた。

一つ一つを小さくするのもいいかもしれませんが、まとめすぎて後で読みにくくなりがちです。
同じような処理はサブルーチンにして使いまわすようにするといいです。(っと、これはさっきも言ったか少し言い方を変えよう。)
同じ箇所が2箇所あればその処理部分は50%削減。3箇所あれば66%削減。効率的にスクリプトを整理できます。
また修正するときもそのサブルーチン1箇所の修正で済むので簡単。探して書き換える必要がなくなります。
サブルーチン単位で考えれば頭の中も整理が付きやすくなりますよ。


> 0と1でオンオフしているのかφ( ̄ー ̄ )メモメモ

こういった変数のことを「フラグ」といいます。(フラグ=flag=旗)
変数の中身を0→1(off→on)に変えることを「フラグを立てる」と言います。
有名なものに「死亡フラグ」があります。

死亡フラグ=0 ... if 台詞="俺、この戦争が終わったら結婚するんだ" : 死亡フラグ=1 if 死亡フラグ=1 : mes "【戦死しました。】"


> をメインプログラムに追加しただけでエラーがおきた。

行頭の}も気になるけど、「〜〜省略〜〜」のところに問題があるのかも。
あるいは、他のrepeat〜loopでbreak意外で抜けてるとか問題があってたまたま影響が出なかったのが今回表に出たのかも。
後者だととてもやっかい。



ミント

リンク

2010/5/16(Sun) 23:01:32|NO.32682

>>GENKIさん
>>一つ一つを小さくするのもいいかもしれませんが、まとめすぎて後で読みにくくなりがちです。
まさに今起きた。あせらずじっくりやります。


>>有名なものに「死亡フラグ」があります。
>>死亡フラグ=0
〜〜省略〜〜
>>if 死亡フラグ=1 : mes "【戦死しました。】"
無茶しやがって・・・

っとノリがよすぎて吹いたw




>>行頭の}も気になるけど、「〜〜省略〜〜」のところに問題があるのかも。
>>あるいは、他のrepeat〜loopでbreak意外で抜けてるとか問題があってたまたま影響が出なかったのが今回表に出たのかも。
>>後者だととてもやっかい。
色々やった結果どうやら前者に問題ありと判明。



repeat AKMAX ;ループの始まりの場所を示す(敵の最大数,8) loop ;repeatに戻る return


だけで、中の処理をすべて抜いたら正常に動きました。
どうやら中身を見直したほうがよさそうですね。


しかし「サブルーチンやループのネストが深すぎます」というエラーなんで出たんだろう・・・
と思ったらC言語であった嘘エラーていうやつですかな。
まーどっちみち見直す必要がありますね。



アドバイス等ありがとうございました!
ご迷惑おかけしました。



ANTARES

リンク

2010/5/17(Mon) 05:24:36|NO.32687

>「repeat〜loop内」の脱出があるのが一箇所(仕様)
>なのでこちらは問題ないと思います。
 何カ所あるかではなく、何回通るかが問題です。
単純化すると、以下のような構造でこのエラーが起きます。

その1

*label1 repeat goto *label1

その2

*label2 gosub *label3 goto *label2 *label3 goto *label2

 ネストが31重までというのは、repeatを31個続けた後に
loopを31個書いてもよいという意味なので(32個はダメ)、
上記のような構造になってない限り、通常は問題になりません。



ミント

リンク

2010/5/17(Mon) 07:33:16|NO.32690

>>ANTARESさん
>>何カ所あるかではなく、何回通るかが問題です。
何回通るかが大事なのか。


>>*label1
〜〜省略〜〜
>>goto *label2
ふむふむ。
参考にしてもう一回プログラムをにらめっこしてみます。


>>ネストが31重までというのは、repeatを31個続けた後に
>>loopを31個書いてもよいという意味なので(32個はダメ)
なるほどー。私の考え方がだめだったようでした。


>>上記のような構造になってない限り、通常は問題になりません。
φ( ̄ー ̄ )メモメモ




*スペカ当たり repeat AKMAX ;ループの始まりの場所を示す(敵の最大数5) if enemy2.cnt=1 { mes "こんちは" ;敵の存在 ;赤札 xxo = emx2.cnt yyo = emy2.cnt ;自機 txxo = x tyyo = y ;自機の横29 ;赤札の横14 ;自機の縦42 ;赤札の縦16 ; if (xxo < txxo+4) and (xxo+18 > txxo) and (yyo < tyyo+4) and (yyo+18 > tyyo){ ;pos myx-14,myy-21 ; x = 180.0 ;自機のx座標 ; y = 380.0 ;自機のy座標 ;効果音 mmplay 0 ;ピチューン再生}} } loop ;repeatに戻る return
ためしにメッセージ出して確認したら、ちゃんと出たのでやっぱり中身の問題ですね。



・・・
・・・・・
やってしもうた!orz



原因判明しましたよ・・・ええ、私のぼんミスです。
-------------------------------------------------
 -誤り-

}loop ;repeatに戻る }return


 -正しい-

} }loop ;repeatに戻る return
---------------------------------------------
こんなしょぼいミスで泣きたくなってきた。
でも、小さい方のプログラムの方では

}loop ;repeatに戻る }return
で問題なかったんだけどねー。なんでだろう?



何度もお答えいただきありがとうございます。



SYAM

リンク

2010/5/17(Mon) 10:51:08|NO.32691

repeat のあと(たとえば if 構文で分岐したりとかのせいで) loop を通らずに return するようなパターンがないか確認。
もしあったら、それは loop を通らずに何度も repeat する原因になっている可能性が高いですよね。



ミント

リンク

2010/5/17(Mon) 13:48:29|NO.32692

>>SYAMさん
>>repeat のあと(たとえば if 構文で分岐したりとかのせいで) loop を通らずに return するようなパターンがないか確認。
なるほど!ifの意外な落とし穴ですね。ifに頼った結果これだよ!!!!
もし飛ぶ場合、ついでに「break」すればよさそうな希ガス。


>>もしあったら、それは loop を通らずに何度も repeat する原因になっている可能性が高いですよね。
ふむふむ。
確かに追加しまくった部分が多いためその可能性がありますね。

アドバイスありがとうございます!



ミント

リンク

2010/5/17(Mon) 14:15:17|NO.32693

あー、連レスで申し訳ない。
やっと原因がわかったので一応報告を。


SYAMさんの
>>repeat のあとloop を通らずに return するようなパターンがないか確認。
まさにこれ。
これが2箇所もありました・・・

実はこれSTG講座のサイトを参考にして作ったやつなんですがね。
http://www.h2.dion.ne.jp/~takusoft/HSP/frame.html

このサイトの一番したの「第七章  時機弾を増やそう」の一番したの赤い部分ですね。
私もそうなってました(当然)


;***************敵VS←自弾のあたり判定************************ *tekiti repeat MYTMMAX ;ループの始まりの場所を示す(自機弾の最大数,100) mytm_cnt=cnt ;自機弾についてのカウント数 txt=mytmx.mytm_cnt ;自機弾の座標 tyt=mytmy.mytm_cnt ;自機弾の座標 repeat ENEMYMAX ;ループの始まりの場所を示す(敵の最大数,5) ;自機弾 1:発射している if teki.cnt=1 and mytm.mytm_cnt{ ;敵機との当たり判定 ;敵の存在 1:いる qx=tekix.cnt ;敵の座標 qy=tekiy.cnt ;敵の座標 ;弾の→側 ;敵の→側 ;弾の↓側 ;敵の↓側 if (qx < txt+8) and (qx+32 > txt) and (qy < tyt+16) and (qy+32 > tyt){ ;玉の横 ;敵の横 ;玉の縦 ;敵の縦 ;リセット teki.cnt=0 ;敵の存在 0:いない hitto += 1 ;ヒット回数 mytm.mytm_cnt=0 ;自機弾,0:発射していない 1:発射している ;効果音 dsplay 3 } } loop ;自機弾のループ loop ;敵のループ return ;サブルーチンから復帰

実はエラーが出たのもここでした(repeat ENEMYMA)
追加も何もしていない部分がエラー番号として出たので、最初は意味がわかりませんでした。


ですが今やっとわかりました。
どうやらここと、もう一箇所を直せばこの先いけそうです。

本当にありがとうございます。いい勉強になりました!



SYAM

リンク

2010/5/17(Mon) 18:55:14|NO.32695

老婆心ながら。
インデントが不正確だと、バグを見つけることが難しくなったり、そもそもバグを仕込む原因にさえなったりします。
また、人に見せたときに誤読させる原因にもなります。

if 構文 や repeat〜loop構文の範囲をただしく掴めるよう、インデントはそれが正しく意味のあるものにしないと不幸になります。
ほんまマジで不幸になります。



ミント

リンク

2010/5/17(Mon) 19:24:14|NO.32698

>>SYAMさん
>>また、人に見せたときに誤読させる原因にもなります。
そうですね。気をつけときます。


>>if 構文 や repeat〜loop構文の範囲をただしく掴めるよう、インデントはそれが正しく意味のあるものにしないと不幸になります。
サイトのコードも疑うようにしようかな・・・
まー今回のことでまた1つ覚えたので無駄なエラーではなかったですね。



y.tack

リンク

2010/5/22(Sat) 22:03:15|NO.32741

そういう場合はfor使いましょう
error

n=0 *l repeat 10 n++ logmes str(n) await 1 goto *l loop
これなら大丈夫
	n=0
*l for i,0,10,1 n++ logmes str(n) await 1 goto *l next



ミント

リンク

2010/5/23(Sun) 08:08:25|NO.32742

いつの間にかレスが。

>y.tackさん
>>そういう場合はfor使いましょう
for文ですか。ふむふむ。


>>error
〜〜省略〜〜
>> next
ふむふむ。これならエラーすら発生しませんね。
サンプルプログラムありがとうございます!



ONION software Copyright 1997-2021(c) All rights reserved.