“RUN” それは魔法の言葉

私とコンピュータの思い出を、だらだらと綴ります。最近はHSP3でのゲーム作り日記です

125.HSPでゲームを作ろう:シューティングゲームっぽい何か その2

通常のショット処理ができたので、続けて、いろんな種類のショットを作ることにした。
グラディウスシリーズのショット系を実装する。

大体、こんな感じ。

  • NORMAL 前方
  • DOUBLE 前方+前方斜め上
  • TAIL GUN 前方+後方
  • VERTICAL 前方+上方
  • DOWN DOUBLE 前方+前方斜め下
  • BACK DOUBLE 前方+後方斜め上
  • FREEWAY 前方+レバー入力方向

ショットは、画面上には2発までという、グラディウスの仕様に合わせてみた。
ノーマルショット以外は、どちらか1発だけ当たる、もしくは画面外に出ても、もう1発が残っていると、発射できない。

まずダブルの実装からスタート。
画面上には、2つしか自弾用スプライトが要らないということで、es_exnew命令で空きスプライト番号を探すのを、1番号ずつ、2回実行することにしてみた。

es_exnew snum1,100,100,1
es_exnew snum2,101,101,1

こうすると、片方の弾が画面外に消えて空きになったとしても、もう一方が画面外に残っていれば、空き無しということで、-1が返ってくる。
なので、両方ともが0を超える値になる=弾が画面外に1発もない、ことになる。

前方のショットは、右方向に進む。なので、es_adir命令で、方向と進む速度を指定する。
方向の指定にちょっとクセがある。下方向を0として、右回りに数字が大きくなっていく。その分割数は64*1
右方向に移動するには、90度回転した数字を指定する必要がある。なので、90÷(360÷64)=16を指定する。
これだと、後でソースを見たときに、なんのこっちゃわからん!と思い、無駄とは思いつつ、8方向に番号を振って、その数字×8(64を8分割)を指定するようにした。

  方向
 543
 6✳2  右方向の指定は、2*8=16 
 701  

ダブルの場合は、右上45度に進めたいので、方向は 3*8=24 を指定する。

これで万事OK!と思っていたが、斜め上に発射する弾のグラフィックをどうするかで迷った。
初めは、GIMPで45度ずつ回転した画像を用意すればいいや。と思っていたが、そのままだと当たり判定に問題が出る。
斜め方向の画像は対角線になるから、それを含む矩形は、当然大きくなる。
そうすると、ショットの画像以外のブランク部分にも当たり判定が付く。いわゆる詐欺判定wってやつになる。

スプライトの当たり判定は、パーセンテージ指定で小さくできるけど、おそらく中央から同心円状に広がるはず。
(おそらくというのは、何パーセントならこんな感じの当たり判定になるという情報がどこにもない!)
それでは、弾のグラフィック部分のみに当たり判定を指定するのは難しくなる。

ということで、右方向の自弾グラフィックを、es_setrotで回転することにした。これならes_checkで回転角度も考慮したチェックになる。
ただ、ここでも回転角度は、360度を64分割した値で指定する必要がある。
ここでさらに一癖あり。今度は水平方向が0で、右回りになる。es_adirでは、下が0基準なので、さっき割り振った数字とまた違う値になる。
これも計算式は不要なんだけど、割り振った方向数字と一致するように記述した。
右斜め上は3番。90度分マイナスする必要があるので、(3-2)*8と指定するようにした。

一応方針は決まったので、同じ要領で残りのショットも作った。
とりあえずswitch~caseで、処理を分岐したたけど、1つ作ってコピペしたので、処理や条件式などに無駄があるのはご愛敬でw

#include "hsp3dish.as"
#include "m_joystick.hsp"

 screen 0,800,600,0
 title "シューティングっぽい何か(仮称)"

 es_ini ;スプライト初期化

 buffer 2
 picload "bg.png" ;仮背景画像

 buffer 3 ;バッファを作成
 picload "player.png" ;自機画像を読み込み
 es_size 64,64,1 ;パターンサイズ、ヒット領域1%で
 es_pat 0,0,0 ;パターンを読み込み

 buffer 4 ;バッファを作成
 picload "shot.png" ;ショット画像を読み込み
 es_size 64,64,20
 es_pat 1,0,0

 gsel 0 ;操作バッファを0に戻す

 px=300:py=200 ;自機のXY座標
 es_set 0,px,py,0,0,1 ;自機のスプライトをセット
 gamecnt=0

 dim houkou,3,3 ;FREEWAY用
  houkou(0,0)=5:houkou(1,0)=4:houkou(2,0)=3
  houkou(0,1)=6:houkou(1,1)=2:houkou(2,1)=2
  houkou(0,2)=7:houkou(1,2)=0:houkou(2,2)=1
  
 sdim weapon_name,7
 weapon_name="NORMAL","DOUBLE","TAIL GUN","VERTICAL","DOWN DOUBLE","BACK DOUBLE","FREEWAY"

 es_area 0,0,800,600 ;スプライトの領域設定

 move_angle=2 ;自機の弾の方向
 weapon_type=0 ;ショット種類

*main
 redraw 0
 ix=1:iy=1:sht=0 ;移動の相対方向フラグとショットボタンフラグ
 ;Stick key,1+2+4+8
 JStick key,1+2+4+8
 if key&128 : end ;ESCでゲーム終了(暫定)
 if key&64 : gosub *shotchange ;Ctrlでショット種別変更
 if key&1 : px=px-5 : ix=0 ;カーソル左
 if key&4 : px=px+5 : ix=2 ;カーソル右
 if key&2 : py=py-5 : iy=0 ;カーソル上
 if key&8 : py=py+5 : iy=2;カーソル下
 if (key&2048) or (key&8192) : sht=1 ;ボタンZ、Cとボタン1、ボタン3(6BパッドのA)
 

 px=limit(px,0,800-64)
 py=limit(py,0,600-64)
 if (ix != 1 or iy != 1) : move_angle=houkou(ix,iy) ;FREEWAY用
 
 pos 0,0
 gcopy 2,0,0,800,600
 color 255,0,0:gmode 2
 pos 0,0:mes "アングル="+str(move_angle) : mes "ix="+str(ix)+" iy="+str(iy) :
 mes str(weapon_type)+":"+weapon_name(weapon_type)
 gamecnt++
 es_pos 0,px,py

 gosub *shoot ;自弾の発射
 es_draw 

 redraw 1
 await 1000/60
 goto *main

*shoot
 if sht=1 {
  es_exnew snum1,100,100,1
  es_exnew snum2,101,101,1

  switch weapon_type
  case 0 ;ノーマルショット 
   if snum1>0 or snum2>0 { ;ショットの空きがあれば
    ;どっちのスプライト番号が空いてるかを判定
    if snum1>0 : snum=100
    if snum2>0 : snum=101
     
    es_set snum,px,py,1
    es_type snum,2 ;弾はタイプ2
    es_adir snum,2*8,1000 
   }
   swbreak
  case 1 ;ダブル
   if snum1>0 and snum2>0 { ;ショットの空きがあれば
    ;前方
    es_set snum1,px+32,py,1
    es_type snum1,2 ;弾はタイプ2
    es_adir snum1,2*8,1000

    ;斜め上
    es_set snum2,px+40,py,1
    es_setrot snum2,(3-2)*8 ;ショットグラフィックを回転
    es_type snum2,2 ;弾はタイプ2
    es_adir snum2,3*8,1000
   }
   swbreak
  case 2 ;テイルガン
   if snum1>0 and snum2>0 { ;ショットの空きがあれば
    ;前方
    es_set snum1,px+32,py,1
    es_type snum1,2 ;弾はタイプ2
    es_adir snum1,2*8,1000

    ;後方
    es_set snum2,px,py,1
    es_setrot snum2,(6-2)*8 ;ショットグラフィックを回転
    es_type snum2,2 ;弾はタイプ2
    es_adir snum2,6*8,1000
   }
   swbreak
  case 3 ;バーティカル
   if snum1>0 and snum2>0 { ;ショットの空きがあれば
    ;前方
    es_set snum1,px+32,py,1
    es_type snum1,2 ;弾はタイプ2
    es_adir snum1,2*8,1000

    ;上
    es_set snum2,px,py-20,1
    es_setrot snum2,(4-2)*8 ;ショットグラフィックを回転
    es_type snum2,2 ;弾はタイプ2
    es_adir snum2,4*8,1000
   }
   swbreak
  case 4 ;ダウンダブル
   if snum1>0 and snum2>0 { ;ショットの空きがあれば
    ;前方
    es_set snum1,px+32,py,1
    es_type snum1,2 ;弾はタイプ2
    es_adir snum1,2*8,1000

    ;斜め下
    es_set snum2,px+40,py,1
    es_setrot snum2,(1-2)*8 ;ショットグラフィックを回転
    es_type snum2,2 ;弾はタイプ2
    es_adir snum2,1*8,1000
   }
   swbreak
  case 5 ;バックダブル
   if snum1>0 and snum2>0 { ;ショットの空きがあれば
    ;前方
    es_set snum1,px+32,py,1
    es_type snum1,2 ;弾はタイプ2
    es_adir snum1,2*8,1000

    ;斜め上
    es_set snum2,px,py,1
    es_setrot snum2,(5-2)*8 ;ショットグラフィックを回転
    es_type snum2,2 ;弾はタイプ2
    es_adir snum2,5*8,1000
   }
   swbreak
  case 6 ;フリーウェイ
   if snum1>0 and snum2>0 { ;ショットの空きがあれば
    ;前方
    es_set snum1,px,py,1
    es_type snum1,2 ;弾はタイプ2
    es_adir snum1,2*8,1000

    ;レバー方向
    es_set snum2,px+(move_angle=2)*16,py,1 ;前方の場合だけショットの間隔を少し開ける
    es_setrot snum2,(move_angle-2)*8 ;ショットグラフィックを回転
    es_type snum2,2 ;弾はタイプ2
    es_adir snum2,move_angle*8,1000
   }
   swbreak
  swend
 }

return

*shotchange
 weapontype++
 if weapontype>6 : weapontype=0
 return

そして、これを動かしてみたのがこれ。ショット種類は暫定でCtrlキーで切り替られるようにしてみた。
youtu.be


さて、次は敵の動きを作ってみるか。

*1:es_iniで変更はできる

思い出のゲーム達:22.ZANAC(ポニーキャニオン/コンパイル)

しばらく書いてなかった、思い出のゲーム達のコーナー。
思い入れがあるというより、インパクトがあったとか、クセがあったなーなど、引っかかるものがあったゲーム達を書いて行こうと思う。

復帰第1弾は、MSXZANACザナック)。
www.youtube.com

有名なのはファミコンディスクシステム版だと思うけど、私にとってのザナックMSX版。
MSXシューティングゲームは、正直コナミ以外ではあまりいいものはなかった。はじめに買ったスターフォースも、ファミコンに比べるとあのガクガク8ドットスクロールだし、もっさりしてた。
そんな中、このザナックは(MSXにしては)超高速スクロールでテンポよく遊べる良作だった。
開発は、「ぷよぷよ」でおなじみのコンパイル。発売がポニーキャニオンだった。

ザナックスターフォースと同じように、空中物、地上物の区別があるけど、通常ショットでどちらも破壊できる。
メインショットは、途中に出てくるボックスの内、青くなったものを破壊すると出てくるチップでパワーアップする。
それとは別のスペシャルウェポンが0~7番まであり、番号の書いた地上物を破壊してできてたアイテムを取ると変更できる。
それぞれ特徴があるが、破壊力&貫通力のある7番ウェポンがお気に入りだったな。

そして、このゲームの売りは、AI搭載。広告にもしっかり書いてあった。
で、その実態は難易度調整システムだった。連射が結構必要なゲームなんだけど、無駄打ちするとそれだけで難易度が上がっていく。
装着したスペシャルウェポンの種類に寄ったり、敵の破壊なんかでも変わっていた様子。
って、ほぼほぼゼビウスや、スターフォースと変わらない感じかな。

全8面あり、何とか1周クリアはしたはず。
ファミコン版をそのあとプレイして、まったく違うものでナニコレ?と思ったものだった。

のちにこれが「アレスタ」に派生して、人気シューティングの礎になったんだよね。

124.HSPでゲームを作ろう:シューティングゲームっぽい何か その1

標準スプライトの学習を兼ねて作ったのが、あれっぽいあれwだったのは、「シューティングゲームっぽい何か」を作るアイデアが浮かんだからだった。
イデアの大筋から、細かな仕様を固めて作る前に、シューティングゲームの基礎である、自機の移動と自弾の発射処理を作ることにした。

移動処理自体はすでに作っているので、自弾の発射処理を作っていく。
オーソドックスな作りなら、スペースキーをショットキーにするのだが、3つ以上のキー同時押しに対応できないことが多いので、Zキーをショットキーにした。

で、JStickであれば、ボタン1が同時に検出できて好都合。

だけど、動作確認に使っているメガドラミニの6BPADは、ちょっと変態さんな並びのようで、Yボタンがボタン1になる。

「PAD設定さん」では、そのあたりをコンフィグできるのだが、まだあとで対応する。
とりあえず、Aボタンがボタン3なので、ボタン1と3をショットキーになるようにした。この副産物として、Cキーもショットキーになった。

弾は画面外に出たら勝手に消えてくれるように、便利な命令es_areaで、画面サイズの座標値を指定した。

ショットキーを押したら、自弾を処理するサブルーチンに飛ばす。es_exnewでスプライトの空きを調べて、空いてたらスプライトのセット、タイプ番号の設定、そして、es_adirで、自動的に移動するようにした。

#include "hsp3dish.as"
#include "m_joystick.hsp"

  screen 0,800,600,0
  title "シューティングっぽい何か(仮称)"

  es_ini ;スプライト初期化

  buffer 2
  picload "bg.png" ;仮背景画像

  buffer 3 ;バッファを作成
  picload "player.png" ;自機画像を読み込み
  es_size 64,64,1 ;パターンサイズ、ヒット領域1%で
  es_pat 0,0,0 ;パターンを読み込み

  buffer 4 ;バッファを作成
  picload "shot.png" ;ショット画像を読み込み
  es_size 64,64,20
  es_pat 1,0,0

  gsel 0 ;操作バッファを0に戻す

  px=300:py=200 ;自機のXY座標
  es_set 0,px,py,0,0,1 ;自機のスプライトをセット
  gamecnt=0

  es_area 0,0,800,600 ;スプライトの領域設定
  moveangle=2 ;自機の弾の方向

*main
  redraw 0
  sht=0 ;ショットボタンフラグ
  JStick key,1+2+4+8
  if key&1 : px=px-5 : ix=0 ;カーソル左
  if key&4 : px=px+5 : ix=2 ;カーソル右
  if key&2 : py=py-5 : iy=0 ;カーソル上
  if key&8 : py=py+5 : iy=2;カーソル下
  if (key&2048) or (key&8192) : sht=1 ;ボタンZ、Cとボタン1、ボタン3(6BパッドのA)

  px=limit(px,0,800-64)
  py=limit(py,0,600-64)
  
  if key&128 : end ;ESCでゲーム終了(暫定)

  pos 0,0
  gcopy 2,0,0,800,600 ;背景画像のコピー
  color 255,0,0:gmode 2
  gamecnt++
  es_pos 0,px,py ;自機スプライトの位置変更

  gosub *shoot ;自弾の発射
  es_draw 

  redraw 1
  await 1000/60
  goto *main

*shoot
  if sht=1 {
    es_exnew snum,100,101,1 ;ショットは画面内に最大2発
    if snum>0 { ;ショットの空きがあれば
      es_set snum,px,py,1
      es_type snum,2 ;弾はタイプ2
      es_adir snum,2*8,1000 ;方向はとりあえず前方。
    }
  }

return

そして、これを動かしてみたのがこれ。うん。雰囲気は出てくるね。
youtu.be

自機のグラフィックがダサいのは、ご愛敬ということでw
これからもう少しマシに見えるように頑張って描いて行くつもり。

123.HSPでゲームを作ろう:技術研究 MUCOM88でMMLを演奏しよう

BASICなら、たいてい音楽を演奏するPLAY命令があり、その音楽を記述する言語がMMLMusic Macro Language)だった。
ドレミファソラシを、C D E F G A B で表し、オクターブや音量、長さなどは、記号+数字で記述する。

HSPには、MMLを演奏する命令はなく、別に用意したmp3などのメディアファイルを再生することで、BGMを鳴らすようになっていた。
でもHSP3.6から、知る人ぞ知るゲームミュージック界の神、古代祐三氏が作ったPC-8801FM音源用ドライバー「MUCOM88」のWindows版が、拡張プラグインで使用できるようになった!
MUCOM88を使って、単純にMMLで音楽を鳴らすだけでなく、ゲームのBGMに使えないかなと思ってサンプルを見始めた。

イメージは単純にMMLをそのまま書いて、PLAY命令のように鳴らせると思っていたのだが、どうやら事前に専用のバイナリファイル,*.mubを作っておいて、読み込みんで鳴らすのがメインの使い方のようだ。
16ファイルまでバンク指定で読み込めるようなので、BGMにできるかな?

ファイルを使わずに、直接HSP3上でMMLを鳴らそうと思ったが、そうすると固定でバンク0になってしまうので、複数の音楽を切り替えて再生することは出来なさそう。
とりあえず、MUCOM88 for Windowsをダウンロードして、MMLからmubファイルを作るか、他の方法でBGM用のMP3ファイルを作ることにしよう。