GoogleAdsence

弾幕五重奏

本日はオリジナル弾幕五重奏を、やんちゃでかわいい感じをイメージして作ってみました。

以上の弾幕です!1つずつソースコードを解説していきます。

寂しがりのソロ

public class CSabisigarinosoro : CBossBulletFactory
{
    public CSabisigarinosoro(MOVE_BOSS_POS move_boss_pos) : base(move_boss_pos)
    {
        SpelName = "寂しがりのソロ";
    }
    public override void Run(double x, double y)
    {
        int k, n, t = Cnt % 300, t2 = Cnt;
        if (t == 0)
            MoveBossPos(40, 30, FIELD_MAX_X - 40, 120, 60, 60);
        if (t2 == 0)
        {
            SGP.CreateBullet(x: x, y: y, angle: PI / 2, speed: 4.0, graph_type: 0, color: 0, state:0, pi2or1: 1.0);
            //se_flag[0] = 1;
        }
        for (int i = 0; i < SGP.Bullet.Count; i++)
        {
            int a_cnt = SGP.Bullet[i].Cnt;
            int state = SGP.Bullet[i].State;
            double bx = SGP.Bullet[i].X;
            double by = SGP.Bullet[i].Y;

            if (a_cnt == 80 && state == 0)
            {
                SGP.Bullet[i].Speed = 0;
            }
                
            switch (state)
            {
                case 0:
                    if (t == 120 || t == 270)
                    {
                        for (n = 0; n < 7; n++)
                        {
                            for (int j = 0; j < 6; j++)
                            {
                                SGP.CreateBullet(x: bx, y: by, angle: PI / 5 * j + PI, speed: 1 * n + 2, graph_type: 7, color: 0, state: -1, pi2or1: 1.0)
                            }
                        }
                        //se_flag[0] = 1;
                    }
                    break;
            }
            
        }
        if (t == 180 || t == 210 || t == 240)
        {
            for (int j = 0; j < 4; j++)
            {
                for (int i = 0; i < 5; i++)
                {
                    SGP.CreateBullet(x: x, y: y, angle: PI / 2 + PI / 2 / 5 * (i - 5 / 2), speed: j * 0.5 + 2.2, graph_type: 4, color: 0, state: -1, pi2o1.0);
                }
            }
            //se_flag[0] = 1;
        }
        ++Cnt;
    }
}

アルゴリズムは簡単で、最初のカウント(14行目)で真下弾を発射して、80カウント後にストップし、アシストから横6方向縦7方向に弾幕を発射します。この時アシスト弾幕のstateとアシスト弾幕から発射したい弾幕のstateを同じにするのがポイントです。
それとは別にボス本体からも120カウントと210カウントと240カウントの時に横5縦4の弾幕を発射します。

愛しき2人のデュエット

この弾幕は二つのアシストが発射されて、その間にアシストから花の弾幕が発射されます。

public class CItosikiFutarinoDuet : CBossBulletFactory
{
    public CItosikiFutarinoDuet(MOVE_BOSS_POS move_boss_pos) : base(move_boss_pos)
    {
        SpelName = "愛しき2人のデュエット";
    }
    public override void Run(double x, double y)
    {
        const int C_TIME = 60;
        const int C_TIME2 = 200;

        int t = Cnt % 300;
        if (t % 16 == 0 && t > 200)
        {
            for (int i = 0; i < 11; i++)
            {
                SGP.CreateBullet(x: x, y: y, angle: PI / 10 * i, speed: 3.0, graph_type: 7, color: 0, state: 0, pi2or1: 1.0);
            }
        }
        if (t == 0)
        {
            MoveBossPos(40, 30, FIELD_MAX_X - 40, 120, 60, 60);
            // アシスト弾幕
            for (int i = 0; i < 2; i++)
            {
                SGP.CreateBullet(x: x, y: y, angle: PI / 2 - PI / 12 + PI / 6 * i, speed: 5.0, graph_type: 0, color: 1, state: 1, pi2or1: 1.0);
            }
            //se_flag[0] = 1;
        }
        for (int i = 0; i < SGP.Bullet.Count; i++)
        {
            int state = SGP.Bullet[i].State;
            int a_cnt = SGP.Bullet[i].Cnt;
            double bx = SGP.Bullet[i].X;
            double by = SGP.Bullet[i].Y;

            if (state == 1 && a_cnt % 20 == 0 && 15 < a_cnt && a_cnt < 70)
            {
                for (int j = 0; j < 10; j++)
                {
                    SGP.CreateBullet(x: bx, y: by, angle: PI2 / 10 * j, speed: 1.5, graph_type: 15, color: 0, state: 2, pi2or1: 1.0, kaiten: 1);
                }
                SGP.PlaySound(13);
            }          
        }
        for (int i = 0; i < SGP.Bullet.Count; i++)
        {
            if (SGP.Bullet[i].State == 2)
            {
                int cnt = SGP.Bullet[i].Cnt;
                //ボスの座標-10まできたら減速
                if (cnt < C_TIME2 && SGP.Bullet[i].Y < y - 10)
                {
                    SGP.Bullet[i].Speed = 0.5;
                }
                if (C_TIME == cnt)
                {
                    SGP.Bullet[i].Color = 1;
                    SGP.Bullet[i].Angle = Math.Atan2(y - SGP.Bullet[i].Y, x - SGP.Bullet[i].X);
                }
                if (C_TIME2 == cnt)
                {// || boss_shot.bullet[i].y < boss.y-20){
                    SGP.Bullet[i].Color = 2;
                    SGP.Bullet[i].Angle = SGP.GetPlayerAnglePI2(x, y);
                    SGP.Bullet[i].Speed = 5.0;
                    SGP.Bullet[i].State = -1;
                    SGP.PlaySound(13);
                }
            }
        }
        ++Cnt;
    }
}

この弾幕は26行目のアシスト弾幕を斜め左下と右下に1つずつ発射して、そのアシスト弾幕から花弁の弾幕を発射します。(38行目)そして59行目で花弾幕をボスの方へ角度を設定します。
その後C_TIME2 == cntが満たされたされた時には、64行目のGetPlayerAnglePI2でプレイヤーへのなす角度を取得しその方向へ花弾幕を移動させます。

騒がしのトリオ

public class CSawagasinoTrio : CBossBulletFactory
{
    public CSawagasinoTrio(MOVE_BOSS_POS move_boss_pos) : base(move_boss_pos)
    {
        SpelName = "騒がしのトリオ";
    }
    public override void Run(double x, double y)
    {
        int t = Cnt;
        if (t == 0)
        {
            for (int i = 0; i < 3; i++)
            {
                SGP.CreateBullet(x: x, y: y, angle: PI / 2 + PI / 2 / 2 * (i - 3 / 2), state: i, speed: 4.5, graph_type: 0, color: 0, pi2or1: 1.0);
            }
            //se_flag[0] = 1;
        }
        for (int i = 0; i < SGP.Bullet.Count; i++)
        {
            int a_cnt = SGP.Bullet[i].Cnt;
            int state = SGP.Bullet[i].State;
            double bx = SGP.Bullet[i].X;
            double by = SGP.Bullet[i].Y;

            if (a_cnt == 30 && state != -1)
            {
                SGP.Bullet[i].Speed = 0.0;
            }
            if (a_cnt % 2 == 0 && 30 < a_cnt && state != -1)
            {
                SGP.CreateBullet(x: bx, y: by, angle: a_cnt * 0.2, state: -1, speed: 3.0, graph_type: 4, color: 1, pi2or1: 1.0);
            }  
        }
        ++Cnt;
    }
}

この弾幕はすごく単純でアシスト弾幕を14行目で3つ発射し30カウントで止まります。
そしてその位置から、回転させながら弾幕を発射させます。(31行目)

麗しのカルテット

public class CUruwasinoQuartet : CBossBulletFactory
{
    public CUruwasinoQuartet(MOVE_BOSS_POS move_boss_pos) : base(move_boss_pos)
    {
        SpelName = "麗しのカルテット";
    }
    public override void Run(double x, double y)
    {
        int t = Cnt % 380, t2 = Cnt;
        double bx, by;
        if (t2 == 0)
        {
            //input_phy_pos(FMX / 2, FMY / 2, 50);
        }

        if (t2 == 50)
        {
            // アシスト弾幕
            for (int i = 0; i < 4; i++)
            {
                SGP.CreateBullet(x: x, y: y, angle: PI / 4 + PI / 2 * i, speed: 4.5, graph_type: 0, color: 0, state: 2, pi2or1: 1.0);
            }
            //se_flag[0] = 1;
        }

        if (t == 80)
        {
            for (int j = 0; j < 2; j++)
            {
                for (int i = 0; i < 20; i++)
                {
                    SGP.CreateBullet(x: x, y: y, angle: PI2 / 20 * i, speed: 0.6 * j + 1, graph_type: 3, color: 4, state: 3, pi2or1: 1.0);
                }
            }
            //se_flag[0] = 1;
        }

        // アシスト弾幕から発射する弾幕
        for (int i = 0; i < SGP.Bullet.Count; i++)
        {
            int a_cnt = SGP.Bullet[i].Cnt;
            int state = SGP.Bullet[i].State;
            bx = SGP.Bullet[i].X;
            by = SGP.Bullet[i].Y;

            if (a_cnt == 30 && state == 2)
            {
                SGP.Bullet[i].Speed = 0;
            }
            if (t % 80 == 0 && state == 2)
            {
                for(int k = 0; k < 2; ++k)
                {
                    for(int j = 0; j < 16; ++j)
                    {
                        SGP.CreateBullet(x: bx, y: by, angle: PI2 / 16 * (j - 16 / 2), speed: 1.0, graph_type: 8, color: k, state: k, pi2or1: 1.0);
                    }
                }
            }
        }

        for (int i = 0; i < SGP.Bullet.Count; i++)
        {
            int state = SGP.Bullet[i].State;

            switch (state)
            {
                case 0:
                    SGP.Bullet[i].Angle -= PI / 10 / 100;
                    break;
                case 1:
                    SGP.Bullet[i].Angle += PI / 10 / 100;
                    break;
                default:
                    break;
            }
        }

        for (int i = 0; i < SGP.Bullet.Count; i++)
        {
            if (SGP.Bullet[i].State == 3)
            {
                int cnt = SGP.Bullet[i].Cnt;
                bx = SGP.Bullet[i].X;
                by = SGP.Bullet[i].Y;
                if (cnt == 60)
                {
                    SGP.Bullet[i].Angle = Math.Atan2(0 - SGP.Bullet[i].Y, FIELD_MAX_X / 2+40 - SGP.Bullet[i].X);
                    SGP.Bullet[i].Speed = 2.5;
                    SGP.PlaySound(13);
                }
                if (cnt == 120)
                {
                    SGP.Bullet[i].Angle = SGP.GetPlayerAnglePI2(bx,by);
                    SGP.Bullet[i].Speed = 3.0;
                    SGP.Bullet[i].State = -1;
                    SGP.PlaySound(13);
                }
            }
        }
        ++Cnt;
    }
}

21行目のCreateBulletでアシスト弾幕を生成し、30カウントになったら止めます。
4つのアシストの位置から32方向に細かい弾幕を発射します。

それとは別に32行目にボスの位置から20方向に弾幕を発射してます。60カウントになったらフィールドの上まで移動し、120カウントになったら、プレイヤー目指して弾幕が富んできます。

幻聴のクインテット

public class CGentyounoQuintet : CBossBulletFactory
{
    public CGentyounoQuintet(MOVE_BOSS_POS move_boss_pos) : base(move_boss_pos)
    {
        SpelName = "幻聴のクインテット";
    }
    public override void Run(double x, double y)
    {
        int t = Cnt % 830, t2 = Cnt;

        if (t == 0)
        {
            //input_phy_pos(FMX / 2, FMY / 2 - 20, 50);
        }
        if (t == 60 || t == 230 || t == 400 || t == 520)
        {
            int color = DX.GetRand(9);
            for (int i = 0; i < 20; i++)
            {
                SGP.CreateBullet(x: x, y: y, speed: 3.0,
                    angle: SGP.GetPlayerAnglePI2(x, y) + PI2 / 20 * (i - 10),
                    graph_type: 7, color: color, state: 0, pi2or1: 1.0);
            }
            //se_flag[0] = 1;
        }

        if (t2 == 50)
        {
            for (int i = 0; i < 5; i++)
            {
                SGP.CreateBullet(x: x, y: y, speed: 4.0,
                    angle: PI2 / 5 * i - PI / 2,
                    graph_type: 0, color: 0, state: 1, pi2or1: 1.0);
             }
            //se_flag[0] = 1;
        }
        for (int i = 0; i < SGP.Bullet.Count; i++)
        {
            int a_cnt = SGP.Bullet[i].Cnt;
            int state = SGP.Bullet[i].State;
            double bx = SGP.Bullet[i].X;
            double by = SGP.Bullet[i].Y;

            if (t == 80 && state == 1)
            {
                SGP.Bullet[i].Speed = 0.0;
            }
            if (state == 1)
            {
                //螺旋ショット
                if (90 < t && t < 200 && t % 8 == 0)
                {
                    SGP.CreateBullet(x: bx, y: by, speed: 3.0,
                           angle: PI2 / 100 * t,
                           graph_type: 1, color: 0, state: -1, pi2or1: 1.0);
                    //se_flag[0] = 1;
                }
                //螺旋ショット
                if (260 < t && t < 370 && t % 8 == 0)
                {
                    SGP.CreateBullet(x: bx, y: by, speed: 3.0,
                           angle: -PI2 / 100 * t,
                           graph_type: 1, color: 2, state: -1, pi2or1: 1.0);
                    //se_flag[0] = 1;
                }
                //全方向ショット
                if (t == 400)
                {
                    for (int j = 0; j < 20; j++)
                    {
                        SGP.CreateBullet(x: bx, y: by, speed: 3.0, angle: PI2 / 20 * (j - 10), graph_type: 7, c: 4, state: -1, pi2or1: 1.0);
                    }
                    //se_flag[0] = 1;
                }
                //6方向途中で自機に向かう
                if (t == 460)
                {
                    for (int j = 0; j < 6; j++)
                    {
                        SGP.CreateBullet(x: bx, y: by, speed: 2.0,
                            angle: PI2 / 6 * (j - 3),
                            graph_type: 3, color: 2, state: 2, pi2or1: 1.0);
                    }
                    //se_flag[0] = 1;
                }
                //螺旋ショット
                if (600 < t && t < 800 && t % 6 == 0)
                {
                    SGP.CreateBullet(x: bx, y: by, speed: 3.0,
                            angle: PI2 / 100 * t,
                            graph_type: 1, color: 1, state: -1, pi2or1: 1.0);
                    
                    //se_flag[0] = 1;
                }
            }
            if(state == 2 && a_cnt == 30)
            {
                SGP.Bullet[i].Angle = SGP.GetPlayerAnglePI2(bx, by);
                SGP.PlaySound(13);
            }
        }
        ++Cnt;
    }
}

まず20~22行目までのCreateBulletはボスが20方向に弾幕を放ちます。
31~33行目はアシスト弾幕を発射します。この時にstateに1を渡してアシスト弾幕であることをわかるようにしてます。

そしてアシスト弾幕から発射される弾幕は79~100行目までに定義されてます。
螺旋ショット→逆方向螺旋ショット→全方向ショット→6方向の刃弾幕(途中で自機狙い)→螺旋ショット

といった感じになります。まだまだボスの弾幕は作るのでこの講座は続きます。