53 章

では52章で作った漢字弾幕データを表現してみましょう。

とりあえず、下準備しましょう。また、今回新しい弾を2つ追加しました。


--- define.h に以下を追加 ---

//文字弾幕に使う弾の最大数(53)
#define FONT_BULLET_MAX 1000
//文字弾幕に使う文字の最大数(53)
#define FONT_NUM_MAX 10


--- GV.h に以下を追加 ---

GLOBAL BlPoint_t BlPoint[FONT_NUM_MAX];//漢字弾幕用変数(53)


--- func.h に以下を追加 ---

extern void boss_shot_bulletH000();
extern void boss_shot_bulletH001();
(中略)
extern void boss_shot_bulletH011();
extern void boss_shot_bulletH012();//(53)
extern void boss_shot_bulletH013();//(53)

void (*boss_shot_bullet[DANMAKU_MAX])() =
{
        boss_shot_bulletH012,//漢字弾幕(53)
        boss_shot_bulletH013,//漢字弾幕(53)

//中ボス
        boss_shot_bulletH000,//ノーマル
(中略)
};


--- struct.h に以下を追加 ---

//漢字弾1つ分の情報(53)
typedef struct{
    int Knd;//弾の種類
    int Col;//弾の色
    float Angle;//弾の角度
    float x,y;
}Bl_t;

//漢字弾全体の情報(53)
typedef struct{
    int Num;//登録した個数
    Bl_t Bl[ FONT_BULLET_MAX ];//登録する弾情報
}BlPoint_t;


--- function.h に以下を追加 ---

        //漢字弾幕の弾座標データロード(53)
        GLOBAL int load_font_dat(char name[32], BlPoint_t *Bp);


--- ini.cpp の以下を変更 ---

        boss.appear_count[0]=50;//中ボスが出現する時刻(42)(47)(53)


--- load.cpp の load() 内に以下を追加 ---

        LoadDivGraph( "../dat/img/bullet/b12.png", 10 , 10 , 1 , 12 ,  12 , ImgBullet[12]) ;
        LoadDivGraph( "../dat/img/bullet/b13.png", 10 , 10 , 1 , 22 ,  22 , ImgBullet[13]) ;


では、弾幕データ読み込み、表現をしてみましょう。

まず、読み込みです。fwriteで構造体の中身一括書き出しをしたデータはfreadで構造体の中身一括読み込み可能です。

上で、漢字弾幕座標データの格納場所はBlPointという配列で用意しました。

読み込ませたい漢字弾幕データと、そのファイル名を渡せばその格納庫に漢字弾幕データを格納してくれる関数を作ってみます。


//文字弾幕の弾座標データロード(53)
int load_font_dat(char name[64], BlPoint_t *Bp){
    int i;
    char fname[128];
    FILE *fp;
    sprintf(fname,"../dat/font/%s.dat",name);//受け取ったファイル名をパスに入れる
    fp = fopen( fname , "rb" );//そのパスとファイル名を使ってファイルを開く
    if( fp == NULL )
        return -1;
    fread( Bp, sizeof(BlPoint_t), 1, fp );
    fclose(fp);
    return 0;
}


漢字弾幕データは「dat/font/龍.dat」に入っています。

渡されたname文字列には例えば「龍」が入っています。それを「dat/font/龍.dat」のように変換するには、

sprintf関数を使って上記のように書いてやればいいわけです。

後はそのパスにあるファイルをfreadで読み込めばいいだけ。読み込みエラーの場合は-1を返します。

では次は弾幕を作ってみましょう。

今回は基本的な2つ弾幕を作ってみます。

まず、弾幕データはこのように作りました。



今回は色がわかりやすいようにカラフルにしましたが、まぁここまでセンスのない色使いをする人はいないと思います(ぇ

こんな風に色んな色や弾の種類が同時に扱えますよということがわかっていただければ嬉しいです。

これを使って以下のような2つの弾幕を作ってみました。


一つ目は中心から広がるように描画しています。

これは、まず-1〜1で座標が表されていることを利用して、中心からどれ位離れているかでスピードを決定しています。

最初は中心ピクセル-1〜1に弾が集まっているので、中心に重なって見えます。

そこから、中心からの距離に応じたスピードで中心から外に広がっていきます。

中心からどれ位離れているかは普通にピタゴラスの定理で、求めます。 √(x*x+y*y) の奴ですね。

弾の向きの計算は、これも座標が-1〜1で表現されていることを利用してそのまま角度を求める関数であるatan2に放り込んでいるだけです。

また、当然以下のように、弾の色や種類は弾幕データのものを利用する必要はありません。

プログラム内で変更してもいいわけです。

ここで、一つ注意なのが、(0,0)に座標データがあると、atan2関数を呼んだ時に不具合が出るのと、

中心からの距離でスピードを決定しているのに、(0,0)に弾があったのでは、永遠に静止してしまいますので、この時はエラーとします。

広がる系の弾幕データを作るときは、ピッタリ中心に書かないようにしましょう。

また、中心に近い弾は加速してやらないとなっかなか外に移動しないので、加速してやる必要があります。


--- boss_shotH.cpp に以下を追加 ---

//漢字弾幕(53)
void boss_shot_bulletH012(){
#define TM012 200
    int i,k,t=boss_shot.cnt%TM012,t2=boss_shot.cnt;
    static int num;
    if(t2==0){
        input_phy_pos(FMX/2,FMY/2, 50);//真ん中へ移動
        num=0;//回数初期化
        if(load_font_dat("龍", &BlPoint[0])!=0){
            printfDx("文字弾幕データロードエラー\n");
        }
    }
    if(t==50){
        for(i=0;i<BlPoint[0].Num;i++){//弾の個数分
            if((k=search_boss_shot())!=-1){
                float sx = BlPoint[0].Bl[i].x, sy = BlPoint[0].Bl[i].y;
                if(sx==0 && sy==0){
                    printfDx("漢字座標データの中に(0,0)の座標データがあります\n");
                    return ;
                }
                int col=BlPoint[0].Bl[i].Col, knd=BlPoint[0].Bl[i].Knd;
                if(num==1){
                    col = 0;
                    knd = 4;
                }
                boss_shot.bullet[k].col   = col;//弾の色
                boss_shot.bullet[k].x     = boss.x+BlPoint[0].Bl[i].x;//座標
                boss_shot.bullet[k].y     = boss.y+BlPoint[0].Bl[i].y;
                boss_shot.bullet[k].knd   = knd;//弾の種類
                boss_shot.bullet[k].angle = atan2(BlPoint[0].Bl[i].y, BlPoint[0].Bl[i].x);//角度
                boss_shot.bullet[k].flag  = 1;
                boss_shot.bullet[k].cnt   = 0;
                boss_shot.bullet[k].spd   = 
                    sqrt(BlPoint[0].Bl[i].x*BlPoint[0].Bl[i].x+BlPoint[0].Bl[i].y*BlPoint[0].Bl[i].y);//スピード
                boss_shot.bullet[k].eff   = 0;
                boss_shot.bullet[k].state = 0;
            }
        }
    }
    for(i=0;i<BOSS_BULLET_MAX;i++){//加速
        if(boss_shot.bullet[i].flag>0){
            if(boss_shot.bullet[i].state==0){
                boss_shot.bullet[i].spd *= 1.01;
            }
        }
    }
    if(t==TM012-1){
        num = (num+1)%2;
    }
}

 

先ほどはせっかく弾幕座標データで作ったAngleを使いませんでした。

それは移動する時のAngleは進行方向だと決まっているからです。

ですから、弾幕座標データで作った弾の角度は上の映像の2つ目の弾幕のような場合に使います。

それを以下に示します。


--- boss_shotH.cpp --- に以下の部分を追加

//漢字弾幕(53)
void boss_shot_bulletH013(){
    int i,n,k,t=boss_shot.cnt,t2=boss_shot.cnt;
    if(t2==0){
        input_phy_pos(FMX/2,FMY/2, 50);//真ん中へ移動
        if(load_font_dat("龍", &BlPoint[0])!=0){
            printfDx("文字弾幕データロードエラー\n");
        }
    }
    if(t==50){
        for(i=0;i<BlPoint[0].Num;i++){
            if((k=search_boss_shot())!=-1){
                float sx = BlPoint[0].Bl[i].x, sy = BlPoint[0].Bl[i].y;
                if(sx==0 && sy==0){
                    printfDx("漢字座標データの中に(0,0)の座標データがあります\n");
                    return ;
                }
                boss_shot.bullet[k].col   = BlPoint[0].Bl[i].Col;//弾の色
                boss_shot.bullet[k].x     = boss.x+BlPoint[0].Bl[i].x*200;//座標
                boss_shot.bullet[k].y     = boss.y+BlPoint[0].Bl[i].y*200;
                boss_shot.bullet[k].knd   = BlPoint[0].Bl[i].Knd;//弾の種類
                boss_shot.bullet[k].angle = BlPoint[0].Bl[i].Angle;//角度
                boss_shot.bullet[k].flag  = 1;
                boss_shot.bullet[k].cnt   = 0;
                boss_shot.bullet[k].spd   = 0;//スピード
                boss_shot.bullet[k].eff   = 0;
                boss_shot.bullet[k].state = 0;
            }
        }
    }
    for(i=0;i<BOSS_BULLET_MAX;i++){
        if(boss_shot.bullet[i].flag>0){
            if(boss_shot.bullet[i].state==0){
                int cnt = boss_shot.bullet[i].cnt;
                if(60<cnt && cnt<=120){
                    boss_shot.bullet[i].x += 1;
                }
                if(120<cnt && cnt<=240){
                    boss_shot.bullet[i].x -= 1;
                }
                if(240<cnt && cnt<=300){
                    boss_shot.bullet[i].x += 1;
                }
                if(350==cnt){
                    boss_shot.bullet[i].spd = 1;
                }
            }
        }
    }
}


spdを0にし、強制的にspdを使わずに移動させるか、Angleの方向にspdを使って移動させるかという方法が利用できます。

強制移動は横移動だけじゃなく、縮小拡大や回転を加えても面白いでしょう。

ただし、spdが0で移動する方法は想定された移動ではない為、

軌跡を計算して当たり判定を計算しないので、あまり速く動かしすぎ内容に注意しましょう(すり抜ける場合がある)。

また、2つ目の弾幕の一番最後のように、決められたAngle方向に飛んでいくような弾を作れば、単純な計算式では飛ばせないような

パターンで飛ばすことが可能です。

後は発想次第ですね☆

- Remical Soft -