敵が弾を撃つ(2/4)

 敵が自機に向けて弾を撃ちます。

 1.敵の弾の配列変数を宣言する

 敵の弾は画面上に最大100個まで存在できることにします。敵弾の存在フラグを配列変数eshotfで管理します。(eはenemyの略です。)敵弾のの座標値は、整数を入れる(eshotx,eshoty)と固定小数を入れる(smeshotx,smeshoty)の2通りを用意します。(smはsmall numberの略です。)例えば、敵弾のx座標が6.5の時、eshotxには6、smeshotxには1664(固定小数表現で6.5)を入れます。
 それと、1回にx方向、y方向に何ドット動かすかを(smeshotdx,smeshotdy)で管理します。これは固定小数です。
    dim eshotf,100
    dim eshotx,100:dim eshoty,100
    dim smeshotx,100:dim smeshoty,100
    dim smeshotdx,100:dim smeshotdy,100
 この処理は、「プログラム起動時の初期設定」部分に入れることにします。

 2.敵の弾の変数に初期値を代入する

 各ステージが始まった時に、敵の弾がまだ1発も存在していない状態にします。
    repeat 100
        eshotf.cnt=0
    loop
 この処理は、「ステージスタート時の初期設定」部分に追加します。

 3.敵の弾を射出する

 emsin,emcosを使います。
    emcos x,r
    emsin y,r
 それぞれ、角度rに対してコサイン値を求めてxに、サイン値を求めてyに代入します。角度は、0度以上360度未満の値を指定できれば十分ですね? 450度は90度と同じですし、−90度は270度と同じです。360度は0度と同じです。HSPEXT.DLLでは0度は0.0で、360度が1.0です。すると、0.0以上1.0未満の値、整数表現では0以上255以下の値を指定できれば十分ということになります。(emcos、emsinの角度にはこの範囲外の数値も指定できます。が、256は0と同じですし、−64は192と同じです。)ということは、角度が256個の数値しかないのですから、サイン、コサイン値もそれぞれ256通りしかないということです。ならば、プログラムの最初の方で前もって計算し、配列変数に入れておき、それを利用するようにすれば、敵弾の射出を行うたびにサイン、コサイン計算をしなくてもすむことになります。
    dim vsin,256
    dim vcos,256
    repeat 256
        emcos x,cnt
        emsin y,cnt
        vcos.cnt=x
        vsin.cnt=y
    loop
 例えば角度が64(固定小数表現では0.25、90度)の時のサイン、コサイン値はvsin.64、vcos.64です。(vはvalueの略です。)
 少しでも処理速度を上げるために、このような事を行います。アークタンジェント(ematan)は、その都度計算するしかありませんが。
 この処理は「プログラム起動時の初期設定」に追加します。

 ここから敵弾の出現処理の話です。
 敵は、最大10匹います。それぞれにつき、弾を撃つ処理を行います。毎回出していたら画面中が弾だらけになってしまいますから、敵が64回移動する毎に1回にします。何回移動したかは、敵の移動処理で、enemycntに入れています。enemycntが64の倍数の時にだけ撃つようにすれば、64回に1回となります。
 存在する敵だけが弾を撃ちます。敵の出現位置(y座標)は−32、消滅位置はysizeですから、一瞬だけ画面外に存在します。画面の外側から弾を撃ってくるのは嫌ですから、敵が完全に画面内に現れている時だけとします。
    repeat 10
        if (enemyf.cnt=1)&(enemyx.cnt>=0)&(enemyx.cnt<=(xsize-32))
            &(enemyy.cnt>=0)&(enemyy.cnt<=(ysize-32))&(enemycnt.cnt\64=0) {
            n=cnt
            n番目の敵が弾を撃つ処理
        }
    loop
 見栄えのために、if文の途中に改行を入れましたが、実際には途中に改行を入れるとエラーになってしまいます。今は敵を1種類しか作っておらず、x方向については画面の幅内におさまって動きますが、後々のことも考えてx座標についても制限しています。

 n番目の敵が弾を撃つ処理ですが、存在フラグを0番の要素から順に見ていき、空いている要素について必要な設定を行います。
    repeat 100
        if eshotf.cnt=0 {
            eshotf.cnt=1
            弾の座標を設定する。
            1回にx方向、y方向のそれぞれに何ドット動かせばいいかを算出する。
            break
        }
    loop

 絵のファイル中の敵弾です。
  敵の弾
 弾は、敵の真ん中の位置に出すことにします。敵弾のサイズが8×8ドット、敵のサイズが32×32ドットですから、(enemyx.n+12,enemyy.n+12)です。
    eshotx.cnt=enemyx.n+12
    eshoty.cnt=enemyy.n+12
 これを固定小数に直します。
    smeshotx.cnt=eshotx.cnt<<8
    smeshoty.cnt=eshoty.cnt<<8

 弾を撃つ時に、自機のどこをねらえばいいかというと、自機がそのまま動かなかったとしたら自機の真ん中に行くようにねらうのがいいと思います。敵弾のサイズが8×8ドット、自機のサイズが32×32ドットですから、(playerx+12,playery+12)です。
  弾の飛び先
 弾の座標を中心とすると、飛び先の相対的な座標は(playerx+12-eshotx.cnt,playery+12-eshoty.cnt)となります。
 水色の矢印の角度rを求めます。
    ematan r,playerx+12-eshotx.cnt,playery+12-eshoty.cnt
    r=192-r
 第2引数にx成分、第3引数にy成分を指定すると、アークタンジェントを求めてrに代入します。2行目はematanとemsin,emcosの角度の定義が違うのを補正するためのものです。これを行うとrが0〜255の範囲に収まらなくなってしまいます。
 例えば450度は90度に直す必要がありますし、405度は45度に直す必要がありますし、−90度は270度に直す必要があります。
  角度=角度\360
 という計算を行うと、一見そうなりそうです。ところが、これは−90度についてはうまくいきません。−90を360で割った余りは270ではありません。−90です。
 0が0度、256が360度に対応しますから、正の値だけを考えるならば、
  角度=角度\256
 とすればいいです。しかし、もっといい方法があります。
  角度=角度&255
 これですと、320(450度)は64(90度)に直りますし、288(405度)は32(45度)になりますし、−64(−90度)は192(270度)に直ります。mが2の何乗かである時だけ、"\m"を"&(m-1)"に変更できます。
 よって、2行目は
    r=(192-r)&255
 となります。

 水色の矢印上の、原点(0,0)から長さ1だけ離れた点の、x座標はcos r、y座標はsin rです。なお、単位はドットです。ここでは1度に長さ6ドット分だけ動かすことにします。そのx成分、y成分は以下のように求まります。
    smeshotdx.cnt=6*vcos.r
    smeshotdy.cnt=6*vsin.r

 以上より、敵の弾を射出する処理はこのようになります。
    repeat 10
        if (enemyf.cnt=1)&(enemyx.cnt>=0)&(enemyx.cnt<=(xsize-32))&
            (enemyy.cnt>=0)&(enemyy.cnt<=(ysize-32))&(enemycnt.cnt\64=0) {
            n=cnt
            repeat 100
                if eshotf.cnt=0 {
                    eshotf.cnt=1
                    eshotx.cnt=enemyx.n+12
                    eshoty.cnt=enemyy.n+12
                    smeshotx.cnt=eshotx.cnt<<8
                    smeshoty.cnt=eshoty.cnt<<8
                    ematan r,playerx+12-eshotx.cnt,playery+12-eshoty.cnt
                    r=(192-r)&255
                    smeshotdx.cnt=6*vcos.r
                    smeshotdy.cnt=6*vsin.r
                    break
                }
            loop
        }
    loop


[目次へ][前へ][次へ] inserted by FC2 system