【プチコン4講座】スプライトでステージを構成してみよう
こんにちは。なおキーヌです。
ブログ毎日更新は259日目になります。
衝突判定が実装できたので、ゲームらしくするためにステージを作りましょう。
前々回に足場を作った時にも言ったのですがマップは本来テキストスクリーンで作った方がいいのですが
覚えることが多くなってしまうことと、衝突判定が面倒臭いということから全部スプライトでやります。
そして画面全体にやると配置が大変なので、小さめのマップを用意してステージ1を作ってみましょう。
それではプチコンミニゲーム講座第8回目を始めようと思います。
ステージ用のデータを作ろう
現状はキャラクターの下に丸太が横1本にあるだけなので、
ちゃんとしたステージに書き換えてやりたいと思います。
1つ1つスプライトを座標においてもいいのですが、メンテナンス性も考慮すると
現実的ではないのでマップ用のデータを作ってしまいましょう。
今回は配列という概念を覚えてもらいますが、
配列という概念は次のゲームでやろうとしたのですがマップを作るうえで
配列を使わないとものすごくめんどうなので使ってしまいます。
今回は詳しく解説しませんが、こんな風に使うんだなとか
変数をまとめておけるんだなとかで覚えておいてください。
それでは簡単なマップデータを作ってみます。
DIM MAPDATA[] = [\
1,1,1,1,1,1,\
1,0,0,1,0,1,\
1,0,0,1,0,1,\
1,0,0,1,0,1,\
1,0,0,0,0,1,\
1,1,1,1,1,1\
]
単純な6x6のマップデータになります。
ちょっと狭苦しいかもしれませんが、最初はこんなもんでいいでしょう。
パッと見でマップぽくみえませんか?
1が壁で0が通れる場所です。
今回はマス目移動ではないのですがスプライトは16×16のサイズなので、
データはマス目タイプで問題ありません。
今回「配列」という概念を使ったので軽く説明すると、
1つの箱にたくさんの変数が入る箱と最初は覚えておいた方がいいかもしれません。
今回のマップ用配列でみると
' 配列はVARではなくDIMで宣言してブラケット(イコールの後のかっこのこと)をつけて代入する
DIM MAPDATA = [1,2,3]
' MAPDATA配列の0個目を表示 => 表示される値は:1
PRINT MAPDATA[0]
' MAPDATA配列の1個目を表示 => 表示される値は:2
PRINT MAPDATA[1]
' MAPDATA配列の2個目を表示 => 表示される値は:3
PRINT MAPDATA[2]
中身を使いたい時は名前の後ろにブラケットをつけてその中に何個目かを記述する。
配列には1と2と3という値が別々に入っているので
コメントにも書いてる通り、0個めをPRINTしたら1が表示されるという感じですね。
部隊で例えると、配列は変数を1つにまとめてくれる名ばかりの隊長です。
その中にある0個目〜2個目は隊員というわけですね。
隊員の名前は数字で管理されます。無機質で機械的ですね(笑)
ちょっとわかりづらいたとえだったでしょうか…?
配列に関しては次のミニゲームを作る時に中心に覚えて欲しいので、
今回はざっくりとした感じで使い方を覚えておいてください。
ステージ用のデータを元にスプライトを表示しよう
マップデータは作るだけでは意味がありません。
武具を買ったら装備していないと意味がないのと同じですね。
早速マップデータを元にスプライトを表示してみましょう。
配列は連番で管理されているのでFORによるループが効果的です!
' ループ用変数
VAR G_I = 0, G_J = 0
' マップ用スプライトの準備
FOR G_J=0 to 5
FOR G_I=0 to 5
' 数字の連番を作る
VAR NUMBER = G_I+(G_J*6) + 200
' マップ配列の中身をみてスプライトの見た目を決める
IF MAPDATA[NUMBER-200] == 1 THEN
' マップデータが1なら丸太を表示
SPSET NUMBER, 1244
' 壁になるものは衝突判定を準備しておく
SPCOL NUMBER
ELSE
' マップデータが0なら見た目を透明にする
SPSET NUMBER, 0,0
ENDIF
' ループに合わせてスプライトの位置を決める
SPOFS NUMBER, G_I*16, G_J*16
NEXT
NEXT
ちょっと複雑なコードになってしまいました。
が、平面のマップを表示するにはこのFORの入れ子を使いますので
こういうものなんだと思っておいてください。
FORの入れ子で使っている変数のG_IとG_Jがキモになっています。
最初のFORのG_Jは、G_Iが6回(0~5)繰り返さないと1回めのループは終わりません。
一方G_IははG_Jが増えるたびに最初のループに戻ります。
詳しく言うと
G_J=0の時、G_Iは0-5回繰り返すとG_Jの0回目のループが終わりG_Jが1になる
G_J=1の時、G_Iは0-5回繰り返すとG_Jの1回目のループが終わりG_Jが2になる
ちょっとゲシュタルト崩壊しそうな感じになりましたが、
ループの中にループを入れるとこんな感じに処理できます。
2次元(横縦)タイプの処理をしたい時はこのやり方が一番シンプルです。
今回のマップも見た目は二次元なので入れ子ループを使っていると言うわけですね。
このコードを実行すると以下のようになります。
ステージ内を動き回ってみよう
先ほどのループの時に壁になる丸太のときだけSPCOLで衝突判定をつけましたね。
壁はすり抜けられないようにしたいので衝突判定を準備しました。
しかし今回は配列と入れ子ループのせいで頭がパンクしそうなのではないでしょうか?
無理にたくさん処理を覚えても忘れてしまったら意味がないので、
今回はもう難しいことはしないでおきましょう。
現状プレイヤーと敵がステージの外に居ている状態になっているので、
指定位置に移動させてやります。
' チップサイズ
CONST #CS = 16
' 座標用変数
VAR X=#CS*1, Y=#CS*1, EX=#CS*4, EY=#CS*4
何をしているのか説明すると、
まず定数という概念を使って#CSにスプライトのサイズの元である16という数値を入れます。
そしてそれに数値をかければマップの指定座標にキャラクターをおくことができます。
別に直接16とか32とか書いてもいいのですが、計算する時間が無駄ですし
もし3つずらしたい!ってなったとき数値を直接変えないといけませんよね。
定数を使えばずらしたい数値だけ増やしてあとは16を掛けてくれるので
直感的にキャラクターの座標を決めることができます。
コンピューターは高性能な計算機なので計算させてあげましょう。
本来のコンピューターの役目ですね(笑)
これでマップの指定座標にキャラクターをおくことができました。
自由に歩き回ってみましょう。
…これじゃあ前回と変わらないですよね。
壁にめりこめますからマップの意味をなしてません。
次回は壁にめりこめないように衝突判定をつかって条件を作ってみます。
今回は覚えることが多かったので次回に回しましょう。
グリッド移動とドット移動について
ゲームを作る上で移動という仕組みはほとんどのジャンルのゲームにも使われます。
クイズゲームは移動なんてなくない?
って思いますがよく考えてみるとカーソル移動があるとおもます。
マウスのように動かすタイプから、コントローラーで一定の位置に動かすタイプがありますね。
マウスのように動かせるのはドット移動と言います。
一方コントローラーのボタンを押して1つ動くのがグリッド移動になります。
もう少しわかりやすくいうとBW2までのドット絵時代のポケモンはグリッド移動で、
XY以降の最近の3Dのポケモンはドット移動になります。
今回作っていくゲームはドット移動ですね。
グリッド移動はRPGとかに適しています。
グリッド移動に関して知りたい場合、私が過去に作ったRPG作成講座で説明しているので
そちらを参考にしてみてください。
それでは。