はい、今回はアクションゲームを作りましょう。
アクションとは「動作」のことなので動作するゲーム、つまりほぼ全てのゲームが当てはまっちゃいますね。
ココでは皆さんが普通に言う横スクロールのアクションゲームを作りましょう。
有名どころでは、マ●オとかロッ●マンのようなものですね^^
まずはキャラクターや背景となる画像がなくてはなりません。
キャラ画像は一から作っていると時間が掛かってしまうので、character.bmpを使いましょうか。
持ってない方(大半かな?)は上記リンク先を保存してください。
背景画像の利用方法は大きく分けて二つ作り方がありますよね?
一枚絵と言われる大きな画像の一部切り取って行くやり方と、
細かいチップに分けたものにIDを振り分けこの位置にはこのチップを使う、と言ったやり方です。
どちらにも一長一短があります。
一枚絵だとユニークな(他にはない)画像にでき、チップ特有のカクカクではない滑らかな画像ができますが、
すべて用意するのに多大な時間・サイズが必要となります。また、通行の可・不可を示す情報も必要となります。
チップだとある程度のチップを作っておけば何回でも使い回すことができ、たくさんの場合でも比較的楽です。
また、チップ単位に通行の可否を示す情報を埋めておけて画像毎に設定する必要がなくなります。
しかし上記で書いたようにカクカクした画像となりますし、
かなりのチップを用意しなければ似たようなチップばかりでマンネリ化してしまいます。
細かいところを書くともっとありますが、全体的に見てチップの方が作りやすいはずです。
ですのでチップタイプのもので進めていきます。背景用のチップ(1チップ=32×32)を用意してください。
また、配置は
|
といった感じにしてください。サンプルでは上記の方法を使用しています。
私はHSPで色ごとに塗りつぶしたもの使用しています^^;
#define TIP 32 ; チップサイズ screen 0,TIP*5,TIP*2 color : boxf
; 通行可能 color 1,1,1 : boxf TIPX,TIPY,TIPX+31,TIPY+31 : TIPX+=TIP ; 夜空をイメージ color ,255,255 : boxf TIPX,TIPY,TIPX+31,TIPY+31 : TIPX+=TIP ; 昼空をイメージ color 100,150,200 : boxf TIPX,TIPY,TIPX+31,TIPY+31 : TIPX+=TIP ; 朝空をイメージ color 230,100,100 : boxf TIPX,TIPY,TIPX+31,TIPY+31 : TIPX+=TIP ; 夕空をイメージ color 230,230,230 : boxf TIPX,TIPY,TIPX+31,TIPY+31 : TIPX+=TIP ; 霧をイメージ
; 通行不可 TIPX=0 : TIPY+=TIP color ,200 : boxf TIPX,TIPY,TIPX+31,TIPY+31 : TIPX+=TIP ; 草原をイメージ color 150,230,230 : boxf TIPX,TIPY,TIPX+31,TIPY+31 : TIPX+=TIP ; 氷をイメージ color 100,100,100 : boxf TIPX,TIPY,TIPX+31,TIPY+31 : TIPX+=TIP ; コンクリートをイメージ color 100,50 : boxf TIPX,TIPY,TIPX+31,TIPY+31 : TIPX+=TIP ; 土をイメージ color 200,150,100 : boxf TIPX,TIPY,TIPX+31,TIPY+31 ; ボックスをイメージ color 50 : ;font "",32,1 : pos TIPX,TIPY : mes "?" ; (^^; bmpsave "background.bmp" ; 作成
それでは早速ゲームの作成に入りましょうか。
まずは背景描画部分から入りたいと思います。
描画の流れ的にいつものようにするわけですが、今までの情報の取得は、
0段目 →→→→→ 終了して左端に折り返し
1段目 →→→→→ 終了して左端に折り返し
2段目 →→→→→ 終了して左端に折り返し
n段目 →→→→→ 終了してまとめて描画
こうでしたが、今回は横スクロールと言うことでチップ情報の配置を横スクロールに適した形
| 0列目 | 1列目 | 2列目 | n列目 |
|---|---|---|---|
| ↓ ↓ | ↓ ↓ | ↓ ↓ | ↓ ↓ |
| 1列目へ | 2列目へ | 3列目へ | 終了して描画 |
こういう風にした方が横移動した時の描画し直しのために取ってくるチップ情報の取得が楽になります。
マップを下記のようにしたい場合は
|
|
チップの配置は下記の様になります。(0…通行可,1…通行不可)
map = "00000" map = "00001" map += "00000 map += "00001" map += "00000" ─→ map += "00011" map += "00111" map += "00011" map += "11111" map += "00011"
それでは一度配置してみましょう。
#define WX 640 #define WY 480 #define TIP 32 #define TIPX 20 #define TIPY 15 sdim map,3200 map ="111111111111155" map+="111111111111155" map+="111111111111155" map+="111111111111155" map+="111111111111155" map+="111111111111555" map+="111111111111555" map+="111111111111111" map+="111111111111555" map+="111111111111155" map+="111111111111115" map+="111111111111115" map+="111111111111111" map+="111111111111155" map+="111111111111155" map+="111111111111155" map+="111111111111115" map+="111111111111115" map+="111111111111115" map+="111111111111155" map+="111111111111155" map+="111111111111155" buffer 2 picload "character.bmp" buffer 3 picload "background.bmp" buffer 4,WX,WY ; 背景描画用に用意 repeat TIPX*TIPY peek TIPid,map,cnt ; チップ情報を取得 if TIPid=0 : break ; もしデータ終端が来たら終了 int TIPid : TIPid-=48 ; IDを数値化する pos cnt/TIPY*TIP,cnt\TIPY*TIP gcopy 3,TIPid\5*TIP,TIPid/5*TIP,TIP,TIP ; 5 = X方向のチップ数 loop gsel 0 pos 0,0 : gcopy 4,0,0,WX,WY ; まとめて描画 stop
大きな流れ的にはこんな感じ。
とりあえずはマップ情報を今までのように外部ファイルにして読み込みを行うようにしましょう。
上記と同じサンプルマップはコチラ。
キャラクターの操作も入れてみるとこんな感じになります。
#define WX 640
#define WY 480
#define TIP 32
#define TIPX 20
#define TIPY 15
#define MOVEMENT 4 ; 一歩の移動距離
#define HEGIHT 8 ; ジャンプの移動距離
#define JUMPMAX 30 ; ジャンプ最大
sdim map,3200
bload "action_map1.txt",map
strlen mapx,map
mapx=mapx/TIPY ; マップX方向のチップ数(マップの広さ)
font "",30,1
pos WX-230,WY-30 : mes "Now Loading..." ; サンプルマップではなくても構わない
buffer 2
picload "character.bmp"
buffer 3
picload "background.bmp"
buffer 4,mapx*TIP,TIPY*TIP
repeat TIPX+1*TIPY
peek TIPid,map,cnt
if TIPid=0 : break
int TIPid : TIPid-=48
pos cnt/TIPY*TIP,cnt\TIPY*TIP : gcopy 3,TIPid\5*TIP,TIPid/5*TIP,TIP,TIP
loop
gsel 0
gmode 2
ichi=0 ; マップ初期位置
x=2*TIP : y=12*TIP ; キャラクター初期位置(2,12)
direction=1 ; 方向(0=左向き,1=右向き)
leg=1 ; 前にある足(0=右足,1=一緒,2=左足)
*main
redraw 0
gosub *draw_back
gosub *draw_char
gosub *controll
redraw
wait 1
goto *main
*draw_back ; 背景描画
pos 0,0 : gcopy 4,ichi,0,WX,WY
return
*draw_char ; キャラクターの描画
if k&5 : leg+ : if leg>2 : leg=0 ; 移動時は足を動かす
pos x,y : gcopy 2,leg*32,direction*64,32,32
return
*controll ; キャラクターの操作
stick k,21,1
if k&1 : x-=MOVEMENT : direction=0
if k&4 : x+=MOVEMENT : direction=1
if k&16 : gosub *jumpaction
if jump { ; ジャンプ中の処理
if JUMPMAX/2>jump.1 : y-=HEGIHT : else : y+=HEGIHT
jump.1+
if jump.1=JUMPMAX : jump=0
}
return
*jumpaction ; ジャンプ処理
if jump : return
jump=1 ; ジャンプフラグON
jump.1=0 ; ジャンプした高さ
return
当たり判定やウィンドウの端チェックもまだしていません。
ジャンプは最高点まで上がって行き最高点に達すると落ちてくるやり方ですが、
「空中で方向転換」という行為は非常にやりにくいです。マ●オのようにしたい場合、単純な方法よければ
if JUMPMAX/2>jump.1 : y-=HEGIHT : else : y+=HEGIHT ↓ if JUMPMAX*2/5>jump.1 : y-=HEGIHT : else : if JUMPMAX*3/5-1<jump.1 : y+=HEGIHT
としてみるとか…。
難しい方法では1ループでのジャンプ距離が徐々に減って、距離が0になった後落ちていくとか。
上記のサンプルスクリプトではマップデータを全て初めに読み込んでいますが、
実際に使う場合はサンプルのような横幅の狭いマップではないと思います。
そうなってくるとバッファにものすごいサイズのマップが入ることとなります。
サンプルでも「3(24bit)×(32(チップXサイズ)×44(X方向の数))×(480(32×15))」約1.93MB消費します。
そうならないようにするにはマップの一部分(表示部分)だけをバッファに読み込む方法で回避できます。
ただ移動する度にマップ情報を読み込むので移動処理とは別の処理が加わり重くなるかもしれません。
また移動に関してですが、通常は自分が画面の中央にいて移動は足踏みだけで背景がスクロールします。
そしてマップ端に来たら背景はストップし、自キャラだけが動きます。
RPGの画面スクロールでも普通に使えるものですね。
これらの処理を書いてみると、
:
#define HEGIHT 8
#define JUMPMAX 35 ; 空中停止時間が入ったためちょっと増やす
sdim map,3200
bload "action_map1.txt",map
strlen mapx,map
mapx=mapx/TIPY
buffer 2
picload "character.bmp"
buffer 3
picload "background.bmp"
buffer 4,TIPX+1*TIP,TIPY*TIP ; 横幅は1チップ分大きめにする
gsel 0
gmode 2
ichi=0
ichi.1=1 ; マップ描画フラグ(0以外はON)
x=2*TIP : y=12*TIP
direction=1
leg=1
*main
redraw 0
gosub *draw_back
gosub *draw_char
gosub *controll
redraw
wait 1
goto *main
*draw_back
if ichi/TIP!=ichi.1 { ; 必要以上に再描画させないトラップ
ichi.1=ichi/TIP
gsel 4
count=0 ;ループのカウント初期化
repeat TIPX+1*TIPY,ichi/TIP*TIPY
peek TIPid,map,cnt
if TIPid=0 : break
int TIPid : TIPid-=48
pos count/TIPY*TIP,count\TIPY*TIP : gcopy 3,TIPid\5*TIP,TIPid/5*TIP,TIP,TIP
count+
loop
gsel 0
}
pos 0,0 : gcopy 4,ichi\TIP,0,WX,WY
return
*draw_char
if k&5 : leg+ : if leg>2 : leg=0
pos x,y : gcopy 2,leg*32,direction*64,32,32
return
*controll
stick k,21,1
if k&1 {
direction=0
if x>0 {
if x/TIP=9&(ichi-MOVEMENT>0) {
ichi-=MOVEMENT ; 背景を移動させる
} else {
x-=MOVEMENT ; マップ端でない時,中央にいない時は自分が移動
}
}
}
if k&4 {
direction=1
if x/TIP+1<TIPX {
if x/TIP=9&(ichi/TIP+TIPX<mapx) : ichi+=MOVEMENT : else : x+=MOVEMENT ; 左移動と同じ
}
}
if k&16 : gosub *jumpaction
:
バッファの使用サイズはウィンドウ+横に1チップ分だけになりました。
なぜ「+1」しているのかというと、
画面サイズ丁度だと一歩移動する度に次の列を表示するためにバッファにマップ情報をリロードします。
そうすると処理が重くなってしまい非力なマシンではどうなることやら…。
少しでも軽くするために1チップ大きめに確保して、1チップ分以上動くとリロードするようにしたわけです。
バッファの確保はお好きな方をお選びください。
1章では長くなりそうですので2章に分けて、前半はこの辺で終わりにしたいと思います。
スクリプトはコチラ。