【プチコン4講座】弾を発射できるようにしよう

プチコン4

プチコン4 シューティングゲーム 弾の発射

こんにちは。継続の錬金術士なおキーヌです。

ブログ毎日更新は213日目になります。

第7回「1~4回目のコードまとめ&関数を覚えよう」で関数の作り方を覚えてソースコードを最適化しました。

今回からは本格的にシューティングゲーム作りに入っていくのでSTGらしく弾を撃てるようにしましょう。

仕組みをネタバレしておくとボタンを押した瞬間にスプライトを出現させて前方に動かすだけです。

それではプチコン4でSTG作りその第8回目始めましょう。

  1. 弾を撃つにはどうすればいいか考える
  2. 撃った弾は再利用しなければいけない
  3. 弾の撃てる制限を設ける
  4. シューティングゲーム作り講座第6回まとめ

学んだことを使ってゲームを考えてみよう

トモカズー、そろそろシューティングゲーム作りに進もうぜ

そうだね、ここまではゲーム作りの準備みたいなもんだから今回からはちゃんとシューティングゲームらしいことするよ。

リキは弾を撃ちまくりたいって言ってたわよね。

レトロなゲーセンではずっと弾を撃ち続けてるゲームが多かったなー

どういうゲームにするかにもよるね。戦闘機だと常に打ちまくってるバルカンやレーザーのようなものだと自然だけど、車の場合は弾を連続で打ち続けると違和感しかないからボタンを1回押したときに1つ弾が飛んでいくピストルみたいなものにしようか。

なんかあんまり爽快感なさそうなシューティングになりそうだな……

考えて撃たないと難易度が上がるって考えると面白いかもね。

そうだね。弾幕系のゲームをディスるわけじゃないけどずっと打ち続けてるシューティングって撃つのがメインじゃなくて避けるのがメインになってしまってるから初心者にあまり向かないんだよ。

確かに……思えばギリギリの範囲をすり抜けていくのがメインになってた気がするぜ。

それじゃあ弾を撃つ仕組みを作っていくよ。次のコードを打ちこんでね。

' スプライトの初期化
'───────────────────────────────
def D_SpriteInitialize
  ' スプライトを定義
  SPSET 0, 352 ' プレイヤー
  SPSET 10, 1322 ' 弾
end

とりあえずわかりやすいようにスプライト初期化で球を作成するよ。

定義番号10?次は1じゃないのか?もしかしてこれも二進数なのか……?

いや、ただの10進数だよ。ゲームを作るときは基本的に同じ系統のものを作ることが多いからその分の予備を開けておくんだ。
プレイヤーの見た目が変わるかもしれないって考えるととりあえず残り8個分開けておいて損はないでしょ。

後からあれが必要だった!ってなったときに新しい所に入れてたら確かに関連性がぐっちゃぐちゃになるわね。

昔のゲームならやってたら容量が足りなくなっちゃうからできなかったけど今は十分な容量があるから問題ないね。次はとりあえず画面に弾を表示してみよう。

' スプライト(プレイヤー)の移動
'───────────────────────────────
def D_PlayerMove
  ' プレイヤーの移動
  SPOPS 0, X, Y
  SPOFS 10, 100, 100
end

プチコン4 シューティングゲーム 表示結果1

なんかイクラみたいなのがでてきたな。

魂斗羅ほどイクラではないけど確かに見えなくもないね。

ねぇ、なんか砂漠が明かるすぎて弾が見辛いんだけどどうにかなんないの?

そうだね。ちょっと背景の明度を下げてみようか。

' マップの描写
'───────────────────────────────
' マップの塗りつぶし描写
def D_MapDraw
  ' 背景の色味を変更
  TCOLOR 0, &Hdddddd00
  ' 背景の塗りつぶし
  TFILL 0,0,0,24,14,CHR$(&HE8D8)
end

プチコン4 シューティングゲーム 表示結果2

うん!だいぶ見やすくなった!

ボタンを押したら弾が出現するようにする

最初から弾が出てたら不自然だからボタンを押したら弾が現れるように変更するよ。

' スプライトの初期化
'───────────────────────────────
def D_SpriteInitialize
  ' スプライトを定義
  SPSET 0, 352 ' プレイヤー
  SPSET 10, 1322,0 ' 弾
end

スプライトには非表示フラグというものが用意されていて、キャラクターグラフィックナンバーの後ろにカンマをつけて0にすれば非表示になるんだ。

' スプライト(プレイヤー)の移動
'───────────────────────────────
def D_PlayerMove
  ' プレイヤーの移動
  SPOPS 0, X, Y
  SPOFS 10, X+16, Y+4
end

ついでに弾はプレイヤーと同じ座標に常に移動しておくか、画面外に置いておくといいよ。今回はプレイヤーの変数を利用して前方に出現するようにするよ。

' コントローラー
'───────────────────────────────
def D_Controller
  ' 0番目のコントローラー(つまり1コン)の押されているボタンを取得
  B = BUTTON(0)
  ' 押されているボタンの情報を表示
  if (B AND 1 << #B_LUP) != 0 then
    ' 上を押されたらY座標を減算
    DEC Y
  elseif (B AND 1 << #B_LDOWN) != 0 then
    ' 下を押されたらY座標を加算
    INC Y
  endif
  if (B AND 1 << #B_LLEFT) != 0 then
    ' 左を押されたらX座標を減算
    DEC X
  elseif (B AND 1 << #B_LRIGHT) != 0 then
    ' 右を押されたらX座標を加算
    INC X
  endif

  if (B AND 1 << #B_LREFT) != 0 then
    ' Yボタンが押されたら弾を出現させる
    SPSHOW 10
  endif

end

変更したところは一番下の#B_RLEFTの追加だけだよ。SPSHOWの後に定義番号を与えると非表示フラグが消えるんだ。一度実行してみよう。

プチコン4 シューティングゲーム 表示結果3

お!ボタンを押したらイクラ前に現れたぞ!しかもついてくる!

弾だよ弾(笑) プレイヤーの座標を+して前方に移動させてるからずっとついてくるよ。

弾を発射させてみよう

でもこれじゃ近距離の敵しか倒せないからシューティングになってないね。

押したら弾を前方に発射させるには、発射中のフラグを設ける必要があるんだ。フラグがないと常に画面に居続けて次の発射ができなくなっちゃうからね。

フラグってどうやればいいんだ?

基本的には0か1か(TRUE or FALSE)でフラグを作ることが多いんだけど、何パターンか切り替えがあるときは数値をフラグに見立てるんだ。今回弾は敵に当たったら消滅する処理をしたいし、倒せない敵は弾をはじき返すようにしたいから数値で管理しよう。それと弾の座標も管理したいから変数を作るよ。

var shot_x = 0, shot_y = 0
var shotFlag = 0

shotFlagが0の時は弾が発射されてない且つ発射準備が完了している状態にしよう。0の時以外はYボタンを押しても何もしないという処理にすれば1発だけ弾を撃てるっていう状況を作れるよ。

ってことはYボタンを押したらフラグを1にすればいいんだな?

そう、1は発射されて前方に進んでいるという状態でよさそうだね。とりあえずそこまで作ってみようか。コントローラーの部分、十字ボタンのところはちょっと省略するよ。

' コントローラー
'───────────────────────────────
def D_Controller
  ~~~ 十字操作省略 ~~~

  if (B AND 1 << #B_LREFT) != 0 then
    if shotFlag == 0 then
      ' Yボタンが押されたら弾を出現させる
      SPSHOW 10
      shotFlag = 1
      shot_x = X+8
      shot_y = Y+4
    endif
  endif

end

これでshotFlagが0の時だけSPSHOWが動くよ。そしてボタンを押したときにプレイヤーの前方から発射したいから弾の座標をプレイヤーの座標を元に代入してあげるんだ。
でもこのままだとさっきと大して変わらないから弾が発射される関数を作ろうか。

' ループ開始
loop
  D_Controller
  D_PlayerMove
  D_PlayerShot
  ' 垂直同期
  VSYNC
endloop


' プレイヤーショット処理
'───────────────────────────────
def D_PlayerShot
  ' ショットフラグが1なら
  if shotFlag == 1 then
    ' 弾のX座標を加算
    INC shot_x, 5
    ' 弾を移動させる
    SPOFS 10, shot_x, shot_y

    ' 弾の座標が400を越えたら
    if shot_x > 400 then
      ' 画面外に行くのでショットフラグを0にして非表示にする
      shofFlag = 0
      SPHIDE 10
    endif
  endif
end

この関数は常にループで動き続けてるんだけど、shotFlagが1になるまでは何もしない関数ということになるのさ。

' スプライトの初期化
'───────────────────────────────
def D_SpriteInitialize
  ' スプライトを定義
  SPSET 0, 352 ' プレイヤー
  SPSET 10, 1322,0 ' 弾
  SPOFS 10, -16, -16
end

' スプライト(プレイヤー)の移動
'───────────────────────────────
def D_PlayerMove
  ' プレイヤーの移動
  SPOPS 0, X, Y
end

弾の発射用関数を作ったからスプライトの移動処理を入れていたところを消して初期化の時点で初期座標をマイナスにしておこうか。
もしなんらかのバグで見えちゃっても画面外だから安心だね。よし、それじゃあ実行してYボタンを押して弾を発射できるか試してみてよ。

プチコン4 シューティングゲーム 表示結果4

おぉ!弾が発射されたぞ!すげぇ!

画面外に行ったらちゃんともっかい発射できるようになっているわね!なんか一気にゲームっぽくなったわ!

こんなに喜んでもらえるとなんかこっちまで嬉しくなるよ。それじゃあオマケで発射SEもつけてみようか。

' コントローラー
'───────────────────────────────
def D_Controller
  ~~~ 十字操作省略 ~~~

  if (B AND 1 << #B_LREFT) != 0 then
    if shotFlag == 0 then
      ' Yボタンが押されたら弾を出現させる
      SPSHOW 10
      shotFlag = 1
      shot_x = X+8
      shot_y = Y+4
      BEEP 10
    endif
  endif

end

更に発射されてる感がでた!音を足しただけなのにこんなにも違うのか!

効果音って大切よね。テレビなんかもよく来たら後から付けてる効果音のお陰で面白くなってたりするし。

そうだね。適した音を当ててあげると一気に臨場感が増すんだ。逆にそぐわない音を当てるとこの音は合わないなってなるんだ。人間の脳って不思議だよね。

シューティングゲーム作り講座第6回まとめ

TCOLOR スクリーンID, 16進数RGBA値

指定スクリーンの色味を変更できます。

16進数RGBA値は&Hから始まりRRGGBBAAにそれぞれ00~FF値を当てはめます。

SPSHOW, スプライト管理番号

スプライト管理番号の表示フラグをONにして画面に現れるようにします。

SPHIDE, スプライト管理番号

SPSHOWの逆の処理でフラグをOFFにして画面から非表示にします。

やっとシューティングゲームぽい講座になりました。

リキくんやマユミちゃんがはしゃいでたように、初めてゲームを作るときに弾を撃てる処理を作っただけでも感動するモノです。

私はRPGをはじめに作ったのでグリッド移動出来た時にすごく感動しました。ただRPGのように移動しただけなのに自分で作ったのがすごくうれしかったんだと思います。

次回はスプライトが持つ変数、スプライト変数を学んでいきます。

それでは。

' ゲーム初期化
ACLS
' ボタンの状態を格納する変数
var B
' プレイヤー移動用変数
var X = 0, Y = 0
' 弾用変数
var shot_x = 0, shot_y = 0
var shotFlag = 0


D_Initialize

' ループ開始
loop
  D_Controller
  D_PlayerMove
  D_PlayerShot
  ' 垂直同期
  VSYNC
endloop


' ゲーム初期化
'───────────────────────────────
def D_Initialize

  ' マップ描写
  D_MapDraw

  ' スプライト初期化
  D_SpriteInitialize
end

' コントローラー
'───────────────────────────────
def D_Controller
  ' 0番目のコントローラー(つまり1コン)の押されているボタンを取得
  B = BUTTON(0)
  ' 押されているボタンの情報を表示
  if (B AND 1 << #B_LUP) != 0 then
    ' 上を押されたらY座標を減算
    DEC Y
  elseif (B AND 1 << #B_LDOWN) != 0 then
    ' 下を押されたらY座標を加算
    INC Y
  endif
  if (B AND 1 << #B_LLEFT) != 0 then
    ' 左を押されたらX座標を減算
    DEC X
  elseif (B AND 1 << #B_LRIGHT) != 0 then
    ' 右を押されたらX座標を加算
    INC X
  endif

  ' Yボタン処理
  if (B AND 1 << #B_LREFT) != 0 then
    if shotFlag == 0 then
      ' Yボタンが押されたら弾を出現させる
      SPSHOW 10
      shotFlag = 1
      shot_x = X+8
      shot_y = Y+4
      BEEP 10
    endif
  endif
end

' スプライトの初期化
'───────────────────────────────
def D_SpriteInitialize
  ' スプライトを定義
  SPSET 0, 352 ' プレイヤー
  SPSET 10, 1322,0 ' 弾
  SPOFS 10, -16, -16
end

' スプライト(プレイヤー)の移動
'───────────────────────────────
def D_PlayerMove
  ' プレイヤーの移動
  SPOPS 0, X, Y
end

' マップの描写
'───────────────────────────────
' マップの塗りつぶし描写
def D_MapDraw
  ' 背景の色味を変更
  TCOLOR 0, &Hdddddd00
  ' 背景の塗りつぶし
  TFILL 0,0,0,24,14,CHR$(&HE8D8)
end

' プレイヤーショット処理
'───────────────────────────────
def D_PlayerShot
  ' ショットフラグが1なら
  if shotFlag == 1 then
    ' 弾のX座標を加算
    INC shot_x, 5
    ' 弾を移動させる
    SPOFS 10, shot_x, shot_y

    ' 弾の座標が400を越えたら
    if shot_x > 400 then
      ' 画面外に行くのでショットフラグを0にして非表示にする
      shofFlag = 0
      SPHIDE 10
    endif
  endif
end