“RUN” それは魔法の言葉

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

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

細胞を出現させて、ショットとの当たり判定を行って、拡大して爆発させる(消す)ところまでできたので、次は、アイデアの核となる、細胞の吸着処理を作る。

イデアとかたいそうなことを書いたけど、プロトタイプで作った某キューブラッシュと同じ。
細胞が1段階でも膨らんだら、画面の端に来た時に、吸着する。(止まる)
吸着した後は、ショットが当たっても膨張も破壊もできなくする。
そして、別の吸着済みの細胞と接触したら、そこに吸着する。

この処理を簡単に実装するために、細胞の状態によって、3つのタイプ番号を割り当てるようにした。

 未膨張の細胞:4
 膨張した細胞:8
 吸着した細胞:16

ショットとの当たり判定は、es_checkを、4+8+16のタイプ番号とで行い、衝突していたスプライトのタイプ番号に応じて、処理内容を分岐させた。
こんな感じ。

ショットのスプライトを消す
if タイプ番号=4
 細胞を膨張する処理
 タイプ番号を8に変更
if タイプ番号=8
 if 細胞の膨張が最大だったら
  爆発の処理
 else
  細胞膨張の処理
(タイプ16は、当たっても膨らませないので、ショットのスプライトを消すだけ)

吸着する処理は、膨張した細胞となる、タイプ番号8のスプライトを総当たりでes_findで探して、各スプライトの座標が画面の端の座標になっているかで判定した。
タイプ番号8のスプライトをes_findで探したときに、合わせてes_checkで、吸着した細胞のタイプ番号16との当たり判定もチェックした。
これで、吸着する処理を行うための、前処理ができた。

実際に吸着させるとはどうすればいいか。
まずは、タイプ番号を吸着した細胞を表す16に変更する。次は移動を止めなくてはならない。
判定した地点で止まって、表示させ続けることなのだが、細胞はes_adirで動かしているので、解除すればいいのだが、方法がわからず。
es_aposで、X,Yともに移動量を0にすれば、その場に止まったので良しにしたw*1

あとはキャラパターンの変更。
スプライトを自動的にアニメーションするようにes_patanimで、パターン定義したのだが、吸着した後もアニメしてしまうと目が痛いw
なので、アニメーションも止めたいが、これまた方法がわからない。
結局、アニメしない細胞のスプライトパターンも、es_patで定義しておき、es_chrで、パターン番号を差し替えた。
これで、細胞同士の吸着処理もできた。

    n=200
    repeat
      es_find n,8,n ;タイプ8(1段階でも膨張した細胞)を対象にする
      if n=-1 : break
      es_check cellco,n,16 ;吸着済みの細胞との当たり判定
      es_getpos n,cx,cy,0
  
      ;端に到達するか、他の細胞に接触したら吸着
      if cx<=0 or cy<=0 or cx>=800 or cy>=600 or cellco>0 {
        es_type n,16 ;吸着したらタイプ16にする
        es_apos n,0,0 ;細胞の動きを止める
        es_chr n,100 ;細胞のスプライトパターンをアニメしないものに変更
      }
      n+
    loop

*1:のちに、HSP掲示板で質問して、es_adirで移動スピードを0%にすることにした。

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

ここで余談を。

細胞を撃つと徐々に膨れて爆発するということを再現するために、弾が当たったら、今の拡大率を取得して、それに10%ずつ加算してやろうと考えていた。
実際に処理を作ってみたのだが、うまく行かない。

弾が当たった細胞スプライトの、現在の拡大率をes_getで取得して、その倍率に+10して es_setrotで新しい拡大率をセットするようにしてみた。

でも、一回目は拡大するものの、二回目以降は全く拡大されない。
110%はいいが、120%にならない。増分を100にして、200%ならできる。
マニュアルには、250%とか書いているから10%刻みも行けるはずなのに、なんだこれ?となった。
logmesしながら、何で値が変わらないの?と、デバッグウィンドウとにらめっこ。

HSP3使いの諸先輩たちは、ピンときたかもしれないが、HSP3の数値の扱いが、基本は整数なのを忘れていたのが原因だった。

es_getで取得する倍率、ESI_ZOOMXの値は、65536倍されているので、取得したあと、65536で割り、100を掛ける必要がある。ここが落とし穴だった。
ESI_ZOOMX/65536*100と計算していたが、割り算部分は値が整数になる。
だから、ESI_ZOOMXが110%だとすると、72089÷65536=1.1ではなく、1になってた…
いくら100倍して10足そうとも、110にしかならない。

int(double(ESI_ZOOMX)/65536*100)+10 のように、いったん実数で割り算しないと、120には永遠にならなかった。

ということで、無事10%刻みでもスプライトを拡大できるようになったんだけど、膨張したときの倍率を、あらかじめ配列に持つことにしたので、この苦労は報われないのであったww

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

次は敵の動きを作ってみる。
敵は、沙羅曼蛇などに出てくる、撃つと膨らみ、最後には爆発する細胞をモチーフにする。

絵がないと始まらないので、まずはEDGE(否ブラウザ)でドット絵をかいた。
膨らむ→縮むを表現するため、簡単ではあるが2パターンのアニメをさせることにした。

細胞というより、フォゾン原子核みたいなデザインになった…ま、いいやwww

これを画面に登場させて、自機ショットとの当たり判定を行って、膨らむようにしていく。
まずは、オーソドックスに画面右から、左方向に進むように登場させる。

出現のタイミングは、1フレームごとにカウントするカウンタを使う。特定フレーム数ごとに出現させることにした。
細胞は、最大100個表示すると仮決めした。

スプライトを表示するには、スプライト番号を個別につけなくてはならないが、これぐらいなら管理不要。
es_exnew命令で、指定した範囲内で空いてるスプライト番号を探してくれる。
その値は変数に渡されるので、es_set命令で設定する。
このときのX座標は固定(右端の座標)、Y座標はランダムにした。
細胞スプライトの移動には、es_adir命令を使った。
es_adirは、スプライト番号と、移動する方向、1フレーム当たりの移動速度を指定すれば、勝手に動き続けてくれる。
左方向に進めるのは、ショットのときと同じく、1周を64分割した値をセットするので、6*8を設定。移動速度は、ゆっくり目の値を仮でべた書きした。

続けて、倍率の設定をes_setrot命令で行った。最初は100%に設定する。これをショットが当たったら、徐々に拡大して膨らむように見せる。
細胞はタイプ番号4にしたので、es_type命令で設定する。
これで、細胞側の処理はおしまい。

ショットとの当たり判定は、es_find命令と、es_check命令を組み合わせて行う。

ショットはタイプ番号2。そして当たり判定を取りたい細胞は4。
es_check命令で、細胞と当たっているスプライト番号が返ってきたら、ショットのスプライトをes_kill命令で消して、細胞のスプライトの拡大率をアップする。
やっぱり判定処理を作るのが楽だわ。

細胞はずっと膨張するのではなく、ある倍率を超えたら、爆発させたい。
爆発パターンのグラフィックを作っていないので、単純にスプライトをes_kill命令で消すだけにした。
これで、ショットを当てると細胞が膨らんで、爆発する(消える)という基本部分ができた。
ずいぶんとシューティングゲームっぽくなってきた♪

youtu.be

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で変更はできる