さあ、弾の連射だね!今回は盛り沢山だから、がんばってね。
フン、ロンリーエンジンのチカラもゲットしたオレサマの手にかかればちょろいもんだぜ!
論理演算ね。
今回は改造結果の仕様とプログラムを解説する事で進めるよ!
改造は、2段階でやろう。まずは今のプログラムをもっとシンプルにするよ。ちょっと長くなってきたし、もともとまわりくどい書き方してるからね。
わざわざまわりくどい書きかたしてたっていうのか。なんでそんなめんどくせえことするんだよ。
SmileBASICの便利な命令を使うともっとカンタンに書けたんだけど、それだと中でなにをやっているか良くわからないだろう?最初に中でなにをやっているかを知ってもらいたかったのさ!
'---てき----------------------------
'てきのかず
ECOUNT=10
'てきのざひょう
EX=200
EY=60
'てきのそくど
DIM EVX[ECOUNT]
DIM EVY[ECOUNT]
'てきのいどうじかん
DIM ETIME[ECOUNT]
'てきスプライトIDのせんとう
ETOP=100
'てきのえのばんごう
EPIC=1203
'てきのスプライトをつくる
FOR I=0 TO ECOUNT-1
EID=ETOP+I
SPSET EID,EPIC
SPOFS EID,EX,EY
SPCOL EID:'てきにあたりはんていをつける
NEXT
'---プレイヤー-------------------------
'プレイヤーのざひょう
PX=200
PY=200
'プレイヤーのスプライトID
PID=10
'プレイヤーのえのばんごう
PPIC=3323
'プレイヤーのスプライトをつくる
SPSET PID,PPIC
SPOFS PID,PX,PY
'---たま--ー-------------------------
'たまのスプライトID
TID=20
'たまのえのばんごう
TPIC=3386
'---メインループ-----------------------
LOOP
'---プレイヤーしょり----------------------
'さゆうにうごかす
IF BUTTON(1, #B_LLEFT) THEN PX=PX-1
IF BUTTON(1, #B_LRIGHT) THEN PX=PX+1
IF PX<0 THEN PX=0
IF PX>400-16 THEN PX=400-16
SPOFS PID,PX,PY
'たまがないときにボタンがおされたらたまはっしゃ!
IF !SPUSED(TID) && BUTTON(1,#B_RRIGHT) THEN
'たまのざひょうはプレイヤーとおなじ!
TX=PX:TY=PY
SPSET TID,TPIC
SPOFS TID,TX,TY
SPCOL TID:'たまもあたるように!
BEEP 111:'はっしゃおんもだすぜ!
ENDIF
'たまをうごかす
IF SPUSED(TID) THEN
'たまのざひょうをとりだす
SPOFS TID OUT TX,TY
'うえにうごかす
TY=TY-5
SPOFS TID,TX,TY
'たまががめんからとびでたらたまをけす
IF TY<0 THEN SPCLR TID
ENDIF
'---てきしょり-------------------------
FOR I=0 TO ECOUNT-1
EID=ETOP+I
IF !SPUSED(EID) THEN CONTINUE:'もうきえてたらスキップ
IF ETIME[I]==0 THEN
'うごくほうこうをきめる
EVX[I]=RND(3)-1
EVY[I]=RND(3)-1
ETIME[I]=20
ENDIF
'てきをうごかす
ETIME[I]=ETIME[I]-1
SPOFS EID OUT EX,EY
EX=EX+EVX[I]
EY=EY+EVY[I]
SPOFS EID,EX,EY
'たまにあたった?
IF SPHITSP(TID,EID) THEN
'あたった!
BEEP 11:'ばくはつ!
SPCLR EID:'てきをけす
SPCLR TID:'たまもけす
ENDIF
NEXT
VSYNC
ENDLOOP
そんなに変わってねえ気がするぞ?
ぱっと見はそうだけど、1行ずつ見ていってごらん。
変数がいくつか消えてるぞ。EX,EY,EIDとか、TAMAとか。
見たことのない書き方もあるね。48行でBUTTON関数の後ろの==1が消えてたり、65行のSPOFSでOUTがついてたり。
54行のSPUSEDってのも初耳だな。なんか!がついてるしよ。ふつう!がつくのは後ろなのに前についてるぞ!
76行のCONTINUEもはじめてだね
違いはだいたいそんなもんかな、シンプル化はもう2段階できるけど、これが第1弾になるよ。
今回のメインは、SPOFSの新しい使い方だよ。OUTってのがついてるところだね。
今まではSPOFSはスプライトの座標を設定する時に使っていたよね?OUTをつけると、逆に今設定されているスプライトの座標を取得できるんだよ。
ほほう、実はスプライトは自分の座標をおぼえていて、取り出すこともできるってワケだな、スプライトがおぼえているモノをわざわざ変数でおぼえとく必要はないってことだ!
そういうこと!それで敵と弾の座標を覚えておく変数はいらなくなったから消えたんだ。プレイヤーの座標を覚えているPXとPYは、消すとかえってややこしくなっちゃうから残しているよ。
SPOFS スプライトID OUT X,Yで、XとYにスプライトの座標が入るんだ。OUTはこれから良く使うけど、この変数に値を返してっていう意味で使うよ。
値を返してっていうと、関数?
関数に似ているけど、OUTを使うと2個以上の値も一度に返せるんだ。座標はふたつの値がセットで必要だろ?
次にEIDが配列じゃなくなった。これは、スプライトのIDは決まっているから、わざわざ変数で覚えておく必要はないからね。その代わりに、敵スプライトIDの先頭だけ覚えておいて、75行目、敵処理のFORループの最初で敵スプライトIDを作っているよ。EID=EIDTOP+Iってやつだね。
次にBUTTON関数の後ろの==1が消えた理由。前回の論理演算の説明を覚えてるかな?
正解ならランプがつくってやつか?
ランプがつくってのは数字だと何になるんだったっけ?
1だよな?
そう!そして、BUTTON関数は、今まで==1をつけて判定してたとおり、押されたら1、押されてなかったら0を返す関数なんだ。
ははあ、1を返すんだから、==1をつけてチェックするまでもないってことか!
そういうこと!これが2とか3も返す関数なら、==2とか==3とかで確認しなきゃいけないけど、1か0しか返さないからね。
押されてたらBUTTON関数自体がランプをつけるってことだな。
54行目のSPUSED、これは見ての通り関数で、指定番号のスプライトが存在しているかどうかを返す関数だよ。スプライトがあれば1を返すんだ。
その前のついてる!はなんだよ
前回の論理演算でNOTってのを覚えてるかな
正解不正解をひっくり返すってヤツだな。そうか、NOTは!って書くといってたな、これがそれか!
そう!普通の文章につけて、驚く意味で使う時は文章の最後に!をつけるけど、プログラムで正解不正解をひっくり返す場合、!はひっくり返したい式の最初につけて、後ろの式の正解不正解をひっくり返せというふうに使うんだ。!SPUSEDだとどういう意味になると思う?
スプライトがある、の反対だから、スプライトがない、っていう意味か!
そう!今まではTAMA==0って書いて、弾があるかどうかをTAMA変数で見てたよね?でも弾があるかどうかは弾のスプライトがあるかどうかで判断できる。TAMA==0っていうのは、!SPUSED(TID)で代用できるのさ!
次に76行目のCONTINUE。これはループの中だけで使えるんだ。BREAKって覚えてるかな?
ループを抜ける命令だったよね?
CONTINUEはBREAKの親戚みたいな命令で、ループを抜けるんじゃなくて、CONTINUE以降の処理をスキップしてループのいちばん最後にジャンプする命令だよ。
このCONTINUEは敵処理のループの先頭に書いてあるね、敵のスプライトがなかったら次の敵までスキップという事だと言えばわかるかな?
ここからうしろは敵の処理がいろいろ書いてるけど、スプライトがないならそいつらは必要ないから次に行くぜってことか。
そういうこと!ここでスキップしておかないと、存在しないスプライトを動かそうとしてエラーになっちゃうからね。
さて、連射改造の準備は整ったけど、結構な改造になるから、プログラムを書く前に先に連射の仕様を書いておこう。
弾は最大10連射できる。つまり同時に10発まで画面に出せる
弾には20番から29番までの10個のスプライトを使う
ボタンが押された時に弾用スプライトを作成する事ができれば、弾を撃つ
撃った弾は、敵と同じようにスプライトが存在していたら上に動かす
敵との当たりは、弾スプライト全部と当たったかどうかのチェックを行う
こんな感じかな!
言ってることはわかるけどよう、どういう命令を使えばいいのかさっぱりわかんねえぞ!
新しい命令は1個も使わないんだけど、今まで使ってきた命令を違った方法で使うんだ。
さっきのSPOFSにOUTをつけたみたいに、SmileBASICの命令は、例えばスプライトを作る命令はSPSETって決まっているんだけど、引数の数や内容を変えることで、いろんな作り方ができるようになっているんだよ。SPSETでヘルプを出すと何ページも出てくるだろう?1ページが1つの使い方の説明になってて、4ページあったら4通りの使い方があると思えばいいよ。
じゃあ、プログラムを発表!
'---てき----------------------------
'てきのかず
ECOUNT=10
'てきのざひょう
EX=200
EY=60
'てきのそくど
DIM EVX[ECOUNT]
DIM EVY[ECOUNT]
'てきのいどうじかん
DIM ETIME[ECOUNT]
'てきスプライトIDのせんとう
ETOP=100
'てきのえのばんごう
EPIC=1203
'てきのスプライトをつくる
FOR I=0 TO ECOUNT-1
EID=ETOP+I
SPSET EID,EPIC
SPOFS EID,EX,EY
SPCOL EID:'てきにあたりはんていをつける
NEXT
'---プレイヤー-------------------------
'プレイヤーのざひょう
PX=200
PY=200
'プレイヤーのスプライトID
PID=10
'プレイヤーのえのばんごう
PPIC=3323
'プレイヤーのスプライトをつくる
SPSET PID,PPIC
SPOFS PID,PX,PY
'---たま--ー-------------------------
'たまのかず
TCOUNT=10
'たまのスプライトIDせんとう
TTOP=20
'たまのえのばんごう
TPIC=3386
'---メインループ-----------------------
LOOP
'---プレイヤーしょり----------------------
'さゆうにうごかす
IF BUTTON(1, #B_LLEFT) THEN PX=PX-1
IF BUTTON(1, #B_LRIGHT) THEN PX=PX+1
IF PX<0 THEN PX=0
IF PX>400-16 THEN PX=400-16
SPOFS PID,PX,PY
'ボタンがおされたらたまはっしゃ!
IF BUTTON(1,#B_RRIGHT,2) THEN
'たまスプライトをつくれたらはっしゃ
TID=SPSET(TTOP,TTOP+TCOUNT-1,TPIC)
IF TID>=0 THEN
'たまスプライトつくれた
'たまのざひょうはプレイヤーとおなじ!
TX=PX:TY=PY
SPOFS TID,TX,TY
SPCOL TID:'たまもあたるように!
BEEP 111:'はっしゃおんもだすぜ!
ENDIF
ENDIF
'すべてのたまをうごかす
FOR I=0 TO TCOUNT-1
TID=TTOP+I
'たまがなかったらスキップ
IF !SPUSED(TID) THEN CONTINUE
'たまのざひょうをとりだす
SPOFS TID OUT TX,TY
'うえにうごかす
TY=TY-5
SPOFS TID,TX,TY
'たまががめんからとびでたらたまをけす
IF TY<0 THEN SPCLR TID
NEXT
'---てきしょり-------------------------
FOR I=0 TO ECOUNT-1
EID=ETOP+I
IF !SPUSED(EID) THEN CONTINUE:'もうきえてたらスキップ
IF ETIME[I]==0 THEN
'うごくほうこうをきめる
EVX[I]=RND(3)-1
EVY[I]=RND(3)-1
ETIME[I]=20
ENDIF
'てきをうごかす
ETIME[I]=ETIME[I]-1
SPOFS EID OUT EX,EY
EX=EX+EVX[I]
EY=EY+EVY[I]
SPOFS EID,EX,EY
'たまにあたった?
TID=SPHITSP(EID,TTOP,TTOP+TCOUNT-1)
IF TID>=0 THEN
'あたった!
BEEP 11:'ばくはつ!
SPCLR EID:'てきをけす
SPCLR TID:'たまもけす
ENDIF
NEXT
VSYNC
ENDLOOP
いちだんと複雑になったねえ・・・これはちょっと解読が難しいぞ。ねえワンパク君?
プスプスプスプス
わ、ワンパク君の頭から湯気が出ている!
ぜんぜんわからねえ!
あせらずに変わった場所を1行ずつ見ていこう!
まず39行目からは問題ないよね?敵の数と同じように、弾の数もTCOUNTという変数におぼえておくよ。弾のスプライトは20番からのままで問題ない。弾が10個なら、20番から29番まで弾で使うことになるよ。
つぎは56行目から。IF文の条件がまたシンプルに戻ったね。ここで弾が出せるかどうかをカンタンに判定できないから、まず発射ボタンが押された事だけ判定しちゃうようにしたんだ。
おい、BUTTONの引数が1個ふえてねえか?2ってなんだよ?
ごめんごめん、今までにない引数だよね。
実はBUTTON関数は、ボタンがどういう状態になったら1を返すかを3番目の引数で指定できるんだ。2はボタンが押された瞬間を意味するよ。今までの使い方ではボタンが押されてたらいつでも1っていう使い方だったんだけど、それだとボタン押しっぱなしで一瞬で弾を10発打ち尽くしちゃうから、押しっぱなしで弾が出ないようにしてるんだ。他にもボタンが離された瞬間とかも取れるよ。
ボタンが押されていたらいつでも1とボタンが押されたシュンカンだけ1のビミョーな違いなんてのが関係してくるのか!こまかすぎてメマイがするぜ・・・
普段気にもしないような細かい事まで気にしないと、プログラムはちゃんと動いてくれないのさ!じゃあ続けよう。
発射ボタンが押された後でSPSETを使ってるけど、ここでは関数として使っているよ。
引数3個の関数としてSPSETを使うと、第1引数から第3引数の間のスプライト番号で空いてる番号があったら、そこに第3引数の絵でスプライトを作って、作ったスプライト番号を返すという関数になるんだ。空いてる番号がなければ-1が返ってくるよ。
そして59行目のIF文で、SPSET関数の結果をチェックしているんだ。>=0ってことは、結果が0以上だったらってこと。-1だとこのIF文はまるまるスキップされる。
ということはスプライト作れた時だけTHENを実行、作れなかったらスキップってことか。それで60行目にたまスプライトつくれたって書いてるんだな!
そういうこと!60行目のコメントがなければ解読は大変なんだ。コメントが大事だって事が良くわかるよね。
弾スプライトが作れたら、やる事はいままでと同じだね。
もう一度58行目のSPSET関数でやってる事をおさらいしよう。
20番から29番までの間で弾スプライトが作れるって事は、まだ弾が出せる事を意味しているよ。スプライトを作れないって事は弾をもう出せないっていう事さ。弾を出せる出せない、出せるなら作った弾の番号という2つ意味がTID変数に入っているんだ。この使ってたらその番号、使ってなかったら-1という変数の使い方はとても便利で良く使うから覚えておくといいよ!
さあ、68行目から、作った弾を動かすところ。これは敵とほとんど同じだね。FORで0からTCOUNT-1まで回して、調べるスプライトIDを作って、そのスプライトが存在している時だけ処理する。83行目からの敵の処理と比べてみるといいよ。
ここまでは大丈夫かな?
だ、だだだ、ダイジョウブだ!
・・・
よし。それじゃあラストスパートだよ!最後に101行目からの弾に当たったかどうかの判定方法を見てみよう。
ここではSPHITSPを新しい使い方で使っているよ。実はSPHITSPは一度に複数のスプライトと当たり判定を調べる事ができるんだ。
ここでは引数を3つ指定しているね。この書き方だと、第1引数が敵のスプライトID、第2引数が弾スプライトIDの先頭、第3引数が弾スプライトIDの最後さ。となると、何をやっていると思う?
今までは引数2つで、敵のIDとタマのIDだったよな?それが3つに増えて、敵、タマ最初、タマ最後になったと。しかもさっきのSPSETの新しい使い方となんか似ているぜ。もしかして、タマ全部まとめて、敵と当たってるかどうか調べてるのか?
正解!ここでは第1引数のスプライトと、第2から第3までのスプライトとの当たり判定をまとめて調べて、1個でも当たったスプライトがあればそのIDを返すんだ。
つまり、敵と弾全部を一度に調べて、敵に当たった弾が1個でもあればそれを返すという事をやってくれるよ。ちなみに1個も当たってなければ-1を返すんだ。また-1が出てきたね。
102行目のIFも見おぼえあるぜ。ここでは-1なら弾に当たってない。それ以外なら、当たった弾のスプライトIDが入っているってことだな!
そういうこと!ちなみにこのSPHITSPでは、スプライトがなかった場合は勝手にスキップしてくれるんだ。だから弾がなかったらとか調べる必要もないのさ。便利だろう?
すげえなSPHITSP!これ1個でほとんどの仕事が済んじまうじゃねえか!
SmileBASICには、こういうゲームを作るのに便利な命令や関数がいろいろ入ってるんだよ。まだまだこんなものじゃないけど、それはじょじょに説明していくね。
さあ最後。当たった弾があるなら、後は前と同じ。敵を消して、弾を消して、音を出す!じゃあ動かしてみよう!
おおお!オレの求めていた物はこれだぜ!アットウテキな連射で敵をなぎ倒す!
すごいね!弾だけは売ってるゲームみたいだ!
どうだい?今回はプログラムを書いてもらわなかったけど、キミたちだけでもできそうかな?
お前の話を聞いてるとなんてことないように聞こえるが、なんだかごまかされてる気もするぜ・・・
ボクもインテリ君の解説聞きながらプログラムを見ている分にはわかった気になってるけど、自力でこのプログラム書けるかって言われるとむずかしい気がするよ。
そりゃいきなりこのプログラムを書けたら天才じゃ!
ハカセ!
今おまえさんたちにやってもらいたい事は、いきなりこのプログラムを書けるようになる事ではないんじゃよ。1行ずつ何をしているか理解して、あとで自分でプログラムを書く時に「あ、これのやり方は見た事があるぞ!」と思い出してくれればええんじゃ。そうやって何かをするプログラムのかけらを覚えて行く事を、引き出しを増やすとも言うのう。引き出しの多さイコール、プログラマーのレベルだと思ってええ!
キミたちはもうプログラムの基本は学び終えてるから、ハカセの言うとおり、ここから先は頭の中にたくさん引き出しを作っていく事で初心者から中級者にレベルアップするんだよ。そのためにも、いろんなプログラムを読むことが大切さ!
一流のミュージシャンになるには音楽をいっぱい聞かなきゃないのと同じか・・・
そういうことじゃ!
それにしても、最初のプログラムと2番目のプログラムって、改めて見るとあまり変わっていないんだね。すごく変わったように思ったのに。
それはインテリ君のプログラムが良くできてるからじゃよ。良いプログラムは変更に強いんじゃ!
お前さんたち、良いプログラムの条件はなんじゃと思う?
そりゃあ、すごいことができるとか、メチャクチャ速いとか、すげーでかいとか、そんなんだろう!
それはすごいプログラムじゃのう。じゃが、すごいプログラムが良いプログラムとは言えんのじゃよ。
良いプログラムの条件、それは、シンプルにわかりやすく書いていること、変更がカンタンにできるようになっていること。それだけじゃ!
まずシンプルでわかりやすいこと。ものすごい超絶テクニックを使ってものすごい事ができたとしても、誰にも理解できずデバッグすらままならないようでは何の意味もない!それにこういうプログラミングことわざもあるぞい。3ヶ月後の自分は他人。今日自分で書いたプログラムも、3ヶ月後に見たらまるで他人が書いたように何を書いているのかさっぱりわからん、という意味じゃ。わかりやすく書いておく事は、他人にとっても将来の自分にとっても役に立つという事じゃな。
そして変更がカンタン。今回も弾を単発から10連射にしただけであまり変わっていないじゃろ?これはインテリ君が最初からいじりやすいようなプログラムにしといたからじゃな。ヘタクソなプログラムだと、変更する事なんぞこれっぽっちも考えておらんから、わかりづらくなるわ、バグはいっぱい出るわ、大騒ぎになるんじゃよ。例えば「弾なんてTIDとかいう変数じゃなくて20って書いとけばいいや!」なんて書いてて、そこから連射させようと思ったらどうなるか考えてみるとええぞい。
とはいえ、変更がカンタンにできるようなプログラムを書くというのは、実は最高に難易度が高い!いろんな引き出しを増やして、いろんな経験を積んだ末にたどりつく達人プログラマーの境地が必要じゃ。
まあ、どっちもいきなりはムリじゃから、プログラムを書いている時にこれはわかりやすい書き方だろうか?、ここの仕様が変わったらどうなるだろう?と時々見直してみるクセをつけてみるとええぞい。そして人のプログラムを見て、わかりやすいなあとか、改造しやすいなあとかいうのがあれば、そのやり方をマネしたりして引き出しに追加しておくんじゃ。良いな?
なんてジミでめんどくせえんだ!プログラムはもっとバババッと派手に作るもんだと思ってたぜ!
ミュージシャンだって派手なパフォーマンスの陰ではジミな練習をコツコツしとるんじゃないかのう。
チッ、反論できねえ・・・
ダイタイミュージシャン志望なのに、ちゃんと楽器や歌の練習してるんですかネートサカ頭!
なんだと!オレサマの神のリフを聞いたことねえだろう!失神するぜ!
ヒー、ひどすぎて失神しそうデスヨ。
ムキー、やいダミー、ちょっと待ってろ!ギター取ってくるぜ!
(ボクもワンパク君がギター弾いてるところ見たことないんだけど)