投稿
第3回目の解説付きサンプル講座です! 今回は待望(?)のBGスクロールになります。興味のある方は是非読んでください。
25そうだね
プレイ済み
返信[1]
親投稿
そして今回のソースです。
0そうだね
プレイ済み
返信[2]
親投稿
今回はどちらかというと1画面に収める方に苦労しました…。なので、ちょっとコードがわかりにくく感じるかもです。本当はこういうコードは好ましくありません。読みやすいコードは良いコードに繋がるので、本当ならわざわざこんな書き方をする必要はないので。 とりあえず一応説明でその辺のコードは補足はしますが、ご了承ください…。 と打ち込んでいる途中に急用が入ってしまったので、一旦席を外します。興味ある人は、その間にソースを打って動きを見てみたりしてみるのも一興かと…。すいません。戻ってきたら再開します!
0そうだね
プレイ済み
返信[3]
親投稿
それでは再開です! いつものように始めていきます。 今回はちょっと変数がいつもより多いので、先に変数の説明を書きます。 W: BGの横幅(横2画面分) H: BGの縦幅(縦2画面分) BX: BGのスクロールX座標 BY: BGのスクロールY座標 X: キャラのX座標 Y: キャラのY座標 B: ボタン入力値 D: キャラの定義番号(アニメーション用) U: キャラのX移動量 V: キャラのY移動量 XS: BGのXスクロール量 YS: BGのYスクロール量 と大体こんな感じです。
0そうだね
プレイ済み
返信[4]
親投稿
そういえばまた10行目でD=0と書いてますが13行目で0にしているのでここは不要です。コピペの際の不具合です。すいません。 ちなみにWとHは定数のように固定された値なので途中で書き換えたりはしません。本当はそういう値にはちゃんとした名前をつけた方がいいのですが、今回は1画面に収める都合上短くなっています。他にも変数がほとんど短いのはそういう都合があったりします。 なので長さを気にしなくていい場合はもうちょっと適切でわかりやすい名前をつけるべきだと思います。
0そうだね
プレイ済み
返信[5]
親投稿
では本題のコードの説明をしていきます! 1: 定番の画面初期化のACLSをしてます。その上で使う変数に値をいれています。WとHは先ほど書いたように固定の値です。 またここで、BGSCREEN命令でBGレイヤー0番のBGサイズを広げています。デフォルトでは1画面分しかないので今回はスクロールのサンプルなので、2×2画面のサイズに変更してます。 2: まず変更したサイズのBGをBGFILL命令を使って全て草原のキャラで埋めています。始めの画面を作るための作業ですね。今回は画面を作るのに2〜8行まで使ってるので結構長いです。しょうがないのですが1画面に収めるにはちょっと困るところです。
0そうだね
プレイ済み
返信[6]
親投稿
3: 画面内に点在する岩を配置しています。1つの配置はBGPUT命令で行いますが、この時にRND関数を使ってランダム(バラバラ)な位置に配置するようにしています。 RND関数は0〜引数の値-1までの値がランダムで生成される便利な命令です。なので画面サイズを入れてフルの範囲に配置してます。 ここで注意点は、ループは0〜99の100回繰り返していますが、必ず100個配置されるとは限りません。それはたまたまランダムが前に置いた岩と同じ位置になった場合や次以降の処理で画面の周囲に木を配置するので、その位置に出したものは上書きされてしまうので消えてしまうのです。なのでもし厳密に100個出したい場合などは、岩を配置する前に前に置いた岩がないか判定したり、RND関数の引数をRND(W-2)+1,RND(H-2)+1に変える必要があります。
0そうだね
プレイ済み
返信[7]
親投稿
なぜ2を引いて1を足しているかは、もともとのWの乱数が0〜49だとして、これを周囲を加味して1〜48にしたいわけなので、式の変更により、0〜47の乱数に対して+1することで、1〜48までの乱数になるというわけです。このように求めたい乱数にあわせて式を調整する事はよくあるので覚えておくと良いですよ。 ちなみに今回そうしてないのは単純にコードを短くするためです。なので始めから上書きされるのがわかっている場所に配置する必要はないので長さを気にしないのなら始めからこの式にしておいた方がいいですね。(それでも前に置いた岩と同じ場所に配置されて100個じゃなくなる可能性は残ってますが)
0そうだね
プレイ済み
返信[8]
親投稿
4: 今度は周囲の木を配置する為のFORループです。ただちょっとここ、ややっこしい感じになってるんですよね…。 本来なら縦と横で2つのFORを使いたいところです。ただそうするとどうしても行が増えてしまうので1画面に収まらなくなってしまいます。実際には空行があるので無理すれば収まらないこともないかもですが、僕は適度な空行も出来れば入れたいと思っていて、どうにもならない場合は削りますがこうなってしまいました。 とりあえず説明を続けていきます。 このFORループ自体は0〜Wまで繰り返すので横方向に対する配置の為に必要なループです。ただ前提条件として、縦方向は横方向よりも短いというのを利用しているので、それが大前提の処理になっています。ここは注意です。
0そうだね
プレイ済み
返信[9]
親投稿
5: 横方向の上と下の木をBGPUTで配置しています。あ、すいません、せっかく定数のHがあるのに直接数字で書いてました…。実はWとHはコードを短くするためにあとで足した変数なので始めは数字だったんです。なのでここは、BGPUT 0, I, H-1,100がベストですね。 実は僕のサンプルは長い行でも2行にならないように心がけています。WとHが数字だと3行目で1行に収まらなかったので変数(定数)になったという裏事情(?)があったりします(^^;
0そうだね
プレイ済み
返信[10]
親投稿
6: ここがトリッキーというかちょっと特殊なところです。先ほども書いたように本当なら2つのFORにするべきなのですが1つなので、このような条件文を足しています。 これはループ変数IがH(縦サイズ)以上になった時に、CONTINUEという構文を使ってループを強制的に次に進めています。強制的に進めると言うことはループ内の以降のプログラムが実行されないので、Iが縦サイズを超えたときは7行目は実行されないことになります。 先ほど書いたように横サイズは縦サイズより大きいので、必ずIは縦サイズより大きくなる時があります。その時にスキップすることで必要以上に縦の配置を行わないようにしているわけです。 ちょっとややっこしいけどわかりましたか? よくわからなかったら質問してください!
0そうだね
プレイ済み
返信[11]
親投稿
7: 今度は左右の木をBGPUTで配置しています。ここのBGPUTも変数(定数)を使って、BGPUT 0,W-1,I,100とした方が良かったですね。 8: 木を配置するためのループ終了の為のNEXTです。 この8行目まででやっとBGを配置する事が出来ました。ただここで一つだけ実は問題が残っています。それは岩の配置はランダムなのでもしプレイヤーキャラの周囲が全て岩で囲まれていたり岩の場所に出てきたりしたら、初っぱなから動けなくなる可能性があります…。 本来ならこのような事は起こらないように処理をするのですが、今回はコードの長さ重視で処理がないので、レアケースですがもしそういう状況になってしまったら一度強制終了して再度実行し直してくださいー…。
0そうだね
プレイ済み
返信[12]
親投稿
10: プレイヤーキャラ用の変数を初期化してデフォルトのスプライトを表示しています。ここは定番で今までも何度もやっているので、その通りです。 12: メインループ開始のラベルです。これも定番ですね。 13: ボタン入力を変数Bに入れ、各種変数を初期値の0にしています。変数の役割は前述した通りです。とくに難しい事はしていません。
0そうだね
プレイ済み
返信[13]
親投稿
14: 上方向のボタンが押されていた場合の条件で処理しています。 押されていた場合、いつもならY座標などを直接変更しているのですが、今回はちょっと違います。まず変数Dに関してはいつも通りな感じです。わからない人は第2回や第1回の講座を参考にしてください。 そして次ですが、変数Vに-2を入れています。このVはY座標の移動量として使っているので、これに上方向である-2を入れています。この時点ではまだ実際のY座標は変化させません。 そして次です。上方向に移動予定なのはわかっているので、YSに縦方向のBGスクロール量を求めています。この際にMIN関数を使っていますが、これは実際のスクロール開始位置(この設定では100)に達していないときは値を0にするための処理になっています。よくわからない人は実際の値を入れて計算してみるとわかると思います。この結果、値はかならず0〜マイナス値になります。
0そうだね
プレイ済み
返信[14]
親投稿
15〜17: 14行目と同様の処理を各方向に対して行っています。 その結果としてキー入力があったときは、 D: 定義番号 U: X座標移動量 V: Y座標移動量 XS: BGのX座標移動量(スクロールしない場合は0) YS: BGのY座標移動量(スクロールしない場合は0) が入ることになります。 先ほども書いたように、この時点では移動のための情報を変数に求めただけで、実際の移動などは行っていません。これは次の行に影響するためです。
0そうだね
プレイ済み
返信[15]
親投稿
19: 上記で求めた情報を元に移動先のBGをBGGET命令で調べています。この時はX座標にさらに+8、Y座標に+13して大体キャラクタの足元ぐらいになるような位置を指定しています。そして、足元がBG番号99(草原)だった時は、20〜22行目の処理を実行するようにしています。 もし草原以外だったときは移動出来ないことになるので、20〜22行目の移動のための変数処理をスキップさせることで移動をスキップさせているわけです。 実はこの判定は結構手抜きです。なぜならキャラ中央足元1箇所でしか当たり判定してませんし、判定もぶつかりそうなら動かない、という処理です。 なので結構BGに食い込んだりしますし、今回は移動が2ドットぐらいなので気になりませんが10ドットとか移動量が多くなったときには障害物にギリギリまでは近づけない、みたいな事が起こる可能性もあります。
0そうだね
プレイ済み
返信[16]
親投稿
今回は長さ重視で手を抜いてますが、実際はこの辺もしっかりチェックや処理した方がもっと良い感じの処理になります。 20: X(X座標)に実際の移動量(U)を足しています。この時、もしスクロールが発生していたら、その分は逆に戻してBGと同期させないといけないので、BGの移動量(XS)も計算に加えてその対応をしています。 そしてBGのX座標(BX)もスクロールが発生したとしたら当然変えないといけないのでBXにもXSを加算しています。 21: 20行目と同様の処理をY方向に対してやっているだけなので詳細な説明は省きます。そのままです。 22: 最終的に決定したキャラの座標は27行目で設定するので、ここでは決まったBGのスクロール値だけどBGOFS命令で設定しています。BGレイヤー0に対してBXとBYを設定するだけです。
0そうだね
プレイ済み
返信[17]
親投稿
23: 障害物BGに当たらなかったときに実行されるIF文の終了ポイントです。そのままですね。 ここまで来ればあとは今までと同じです。駆け足でいくと、 25: キャラの定義番号切り替えアニメーションのための処理 27: キャラのスプライト座標を変更 28: 定番VSYNC 29: メインループ終わり。@LOOPへ繰り返す。 という感じでフィニッシュです!
0そうだね
プレイ済み
返信[18]
親投稿
やっと終わりましたー…。疲れました…。 ご清聴ありがとうございました! ということで第3回も終わりましたが、第4回は無いかも…。 というかだんだん1画面に入れるのが大変になってきてて、1画面を超えれば出来ることは増えますが、それだとコンセプトが違うし、まあこれぐらいでいいかなぁ、と。希望があって、それが1画面以内に収まりそうなら考えるんですがー…。 ということで、気まぐれでスタートした解説付きサンプルですが、一旦閉幕とさせてもらいます。ありがとうございました!!
2そうだね
プレイ済み
返信[19]
親投稿
pinfu ANNAININ
でんぺんさんのシリーズ、ありがたく参考にさせてもらっています!!
0そうだね
プレイ済み
返信[20]
親投稿
コメントありがとうございます! たいした事はしてませんがプログラム理解のとっかかりになればと思ってゲーム作りの際に出てきそうな問題をサンプルにしてました。お役に立てたら嬉しいです!
0そうだね
プレイ済み
返信[21]
親投稿
Smoothie ryukey-h
質問です!どうやってBGの岩を判定してるのですか?
0そうだね
プレイ済み
返信[22]
親投稿
Smoothie ryukey-h
あとこのプログラムを使っていいですか?
0そうだね
プレイ済み
返信[23]
親投稿
>スムージーさん 質問どうもです〜。 岩の判定は19行目の解説でもあるように、移動先のBGをBGGET命令で調べています。というか今回は正確には岩の判定はしていません。(プログラムの長さの都合上) 歩ける場所を草原(99)に限定しているので、それ以外(木や岩)は歩けないという逆説的な感じので判定になっています。なので岩の判定をしていると思って見るとちょっと違和感があるかもしれないですね。 ちなみにプログラムは利用してOKですよ〜。その為にも出してますし、どんどん活用して色々なゲームを作ってください!
0そうだね
プレイ済み
返信[24]
親投稿
Smoothie ryukey-h
草原以外は進めないようにするとそうなるんですね。勉強になりました。BGのマップの広さは3画面×3画面でマップのBG配置はDATA&READで作りました。町マークのBGに触れたら町マップに行けるみたいに出来るよう試行錯誤して見ます!(結論RPG的なのを作るということ)
0そうだね
プレイ済み