【C# + XNA4.0】敵の重複出現

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
goma

【C# + XNA4.0】敵の重複出現

#1

投稿記事 by goma » 11年前

はじめまして、gomaと申します。
現在、C# + XNA4.0で弾幕シューティングを製作中なのですが、CSVファイルに基づいて敵を出現させる所で躓いてしまいました。
CSVファイルから敵データを読み込むことは出来たのですが、いざ敵を表示させようとすると、指定されたタイミングに1体だけ敵が出て欲しいところに、たくさんの敵が出てしまいます。
恐らく、高速でゲームが更新され、タイミングが合致しているわずかな間に何度も条件文が実行されているためだと思うのですが、これを改善するにはどうすれば良いでしょうか?
よろしくお願い致します。
(もし、同様な質問が過去にもあるようでしたら、その時のログを教えていただけると幸いです。)

コード:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;

namespace ShootingGame
{
    class Enemy
    {
        //
        // 敵プログラム
        //

        const float frameTime = 1.0f / 60.0f; // ゲーム内時間

        const int enemySize = 32;  // 敵の大きさ

        const int ENEMY_MAX = 30;   // 敵の同時表示最大数

        //const int ENEMY_POS = enemySize + 10;   // 敵の出現間隔

        Texture2D enemyImg;        // 敵画像

        struct EnemyData
        {
            public int enemyFlag;         // 敵の出現フラグ
            public float enemyPosX;     // 敵のX座標
            public float enemyPosY;     // 敵のY座標
            public float enemyDeg;      // 敵の角度
            public float enemySpeed;    // 敵の速さ
            public int hitPoint;       // 敵の耐久力

            public int pattern;        // 敵の移動パターン

            public float wait;         // 待機時間

            public int pointFlag;         // 敵の軌道の切り替え点

            public float bulletTimer;   // 弾の出現時間
        }
        EnemyData[] enemy = new EnemyData[ENEMY_MAX];

        //int count; // 敵が消えた数をカウント


        // 初期化
        public void Initialize()
        {
            for (int i = 0; i < ENEMY_MAX; i++)
            {
                enemy[i].enemyFlag = 0;   // 敵の出現フラグ
                enemy[i].enemyPosX = Main.fieldLeft;
                enemy[i].enemyPosY = 100;
                enemy[i].enemyDeg = 0;
                enemy[i].enemySpeed = frameTime * 300.0f;
                enemy[i].hitPoint = 2;

                enemy[i].pattern = 0;
                enemy[i].wait = 0;

                enemy[i].pointFlag = 0;

                enemy[i].bulletTimer = 0.5f + (i * 0.25f); // 初弾発射タイミング + 次の敵の弾発射タイミング
            }
        }

        // 敵の出現初期化
        public void EnemyApp()
        {
            for (int i = 0; i < ENEMY_MAX; i++)
            {
                enemy[i].enemyFlag = 1;   // 敵の出現フラグ
            }
        }


        // コンテンツの読み込み
        public void Load(ContentManager Content)
        {
            enemyImg = Content.Load<Texture2D>("敵");
        }

        // ゲームロジック
        public void Update()
        {
            //count = 0; // 敵が消えた数を初期化

            //EnemyNumSearch();
            EnemyEnter();

            for (int i = 0; i < ENEMY_MAX; i++)
            {
                // 敵が画面に出ている間だけ描画
                if (enemy[i].enemyFlag == 1)
                {
                    EnemyPath(i);   // 敵の軌道処理
                    EBulletApp(i);  // 弾の出現処理
                }

                EnemyVanish(i);  // 敵の消失処理
                BulletHit(i);    // 自機弾との当たり判定処理
                PlayerHit(i);    // 自機との当たり判定処理
            }
        }

        // 描画処理
        public void Draw(SpriteBatch spriteBatch)
        {

            for (int i = 0; i < ENEMY_MAX; i++)
            {
                // 敵が画面に出ている間だけ描画
                if (enemy[i].enemyFlag == 1)
                {
                    spriteBatch.Draw(
                        enemyImg,
                        new Vector2((int)enemy[i].enemyPosX + (enemySize / 2), (int)enemy[i].enemyPosY + (enemySize / 2)),
                        null,
                        Color.White,
                        0,
                        new Vector2(enemySize / 2, enemySize / 2),
                        new Vector2(1.0f, 1.0f),
                        SpriteEffects.None,
                        0.8f
                        );
                }
            }
        }

        // 空いている敵番号を探す
        int EnemyNumSearch()
        {
            for (int i = 0; i < ENEMY_MAX; i++)
            {
                if (enemy[i].enemyFlag == 0) // 出現していない敵を探す
                {
                    return i; // 使用可能番号を返す
                }
            }
            return -1; // 全部埋まっていたらエラーを返す
        }

        // 敵情報の登録
        void EnemyEnter()
        {
            int i;

            for (int t = 0; t < Appear.ENEMY_TOTAL_MAX; t++)
            {
                if (DataLoad.enemyOrder[t].count == (int)Appear.scrollPos) // 出現タイミングが合致したとき
                {
                    if ((i = EnemyNumSearch()) != -1) // 空いている敵
                    {
                        enemy[i].enemyFlag = 1; // 出現フラグ
                        enemy[i].pattern = DataLoad.enemyOrder[t].pattern; // 行動パターン
                        //enemy[i].count = 0;   // 敵出現時間
                        //enemy[i].kind = DataLoad.enemyOrder[t].kind;     // 敵の種類
                        enemy[i].enemyPosX = Main.fieldLeft + DataLoad.enemyOrder[t].posX;   // 敵のX座標
                        enemy[i].enemyPosY = Main.fieldUpper + DataLoad.enemyOrder[t].posY;   // 敵のY座標
                        //enemy[i].speed = DataLoad.enemyOrder[t].speed;   // 敵のスピード
                        //enemy[i].stime = DataLoad.enemyOrder[t].stime;   // 弾発射時間
                        //enemy[i].skind = DataLoad.enemyOrder[t].skind;     // 弾幕の種類
                        //enemy[i].scolor = DataLoad.enemyOrder[t].scolor;     // 弾の色
                        enemy[i].hitPoint = DataLoad.enemyOrder[t].hp;     // 体力
                        //enemy[i].skind2 = DataLoad.enemyOrder[t].skind2;    // 弾の種類
                        //enemy[i].wait = DataLoad.enemyOrder[t].wait; // 待機時間
                        /*
                        for (int j = 0; j < DataLoad.ITEM_KIND; j++)
                        {
                            enemy[i].item[j] = DataLoad.enemyOrder[t].item[j];    // アイテム
                        }
                        */

                    }
                }
            }
        }

        // 敵の消失処理
        void EnemyVanish(int i)
        {
            // 敵が画面外へ行った
            if (enemy[i].enemyPosX < -enemySize + Main.fieldLeft || enemy[i].enemyPosX > (Main.fieldWidth + Main.fieldLeft)
                || enemy[i].enemyPosY < -enemySize + Main.fieldUpper || enemy[i].enemyPosY > Main.fieldHeight + Main.fieldLower)
            {
                enemy[i].enemyFlag = 0; // 敵を消す
                //count++;    // 敵が消えたらカウントアップ
            }

            if (enemy[i].enemyFlag == 2) // 敵を倒した
            {
                enemy[i].enemyFlag = 0; // 敵を消す

                //count++;    // 敵が消えたらカウントアップ
            }

            /*
            if (count == ENEMY_MAX)
            {
                Initialize();   // 敵が全部消えたら初期化
            }
            */
        }

        
        // 弾の出現処理
        void EBulletApp(int i)
        {
            enemy[i].bulletTimer -= frameTime;

            if (enemy[i].bulletTimer < 0)
            {
                // EBulletApp(弾の種類, X座標, Y座標, 画像サイズ)
                EBullet.EBulletApp(2, enemy[i].enemyPosX + (enemySize / 4),
                    enemy[i].enemyPosY + (enemySize / 4), enemySize);

                enemy[i].bulletTimer = 0.5f;  // 二発目以降(敵1体からの弾発射間隔)
            }
        }


        // 敵の軌道処理
        void EnemyPath(int i)
        {
            switch (enemy[i].pattern)
            {
                case 0:
                    {
                        if (enemy[i].enemyPosY <= 400)
                        {
                            enemy[i].enemyPosY += enemy[i].enemySpeed;
                        }
                        else if (enemy[i].enemyPosY > 400)
                        {
                            enemy[i].enemyPosX += enemy[i].enemySpeed;
                        }
                        
                    }
                    break;
            }
            /*
            switch (pointFlag[i])
            {
                case 0: // 左へ直進
                    enemyPosX[i] -= enemySpeed[i]
                        * (float)Math.Cos(MathHelper.ToRadians(enemyDeg[i]));
                    enemyPosY[i] -= enemySpeed[i]
                        * (float)Math.Sin(MathHelper.ToRadians(enemyDeg[i]));

                    // 軌道切り替え
                    if (enemyPosX[i] < 200)
                    {
                        pointFlag[i] = 1;
                    }
                    break;

                case 1: //下へカーブ移動
                    enemyDeg[i] += frameTime * 180;

                    enemyPosX[i] -= enemySpeed[i]
                        * (float)Math.Cos(MathHelper.ToRadians(enemyDeg[i]));
                    enemyPosY[i] += enemySpeed[i]
                        * (float)Math.Sin(MathHelper.ToRadians(enemyDeg[i]));

                    if (enemyDeg[i] > 180)
                    {
                        pointFlag[i] = 2;
                    }
                    break;

                case 2: // 右へ直進
                    enemyDeg[i] = 180;

                    enemyPosX[i] -= enemySpeed[i]
                        * (float)Math.Cos(MathHelper.ToRadians(enemyDeg[i]));
                    enemyPosY[i] += enemySpeed[i]
                        * (float)Math.Sin(MathHelper.ToRadians(enemyDeg[i]));

                    break;
            }
            */
        }


        // 敵と自機弾の当たり判定
        void BulletHit(int i)
        {
            float X_Dist;   //X座標の距離差 
            float Y_Dist;   //Y座標の距離差
            float XY_Dist;  //X+Y座標の距離差


            if (enemy[i].enemyFlag != 1) return;// 敵が画面に出ている時

            for (int j = 0; j < Bullet.BULLET_MAX; j++)
            {
                if (Bullet.bulletFlag[j] == 1) // 弾が画面に出ている時
                {
                    // 距離差を絶対値で求める
                    X_Dist = Math.Abs((enemy[i].enemyPosX + enemySize / 2)
                        - (Bullet.bulletPosX[j] + Bullet.bulletSize / 2));
                    Y_Dist = Math.Abs((enemy[i].enemyPosY + enemySize / 2)
                        - (Bullet.bulletPosY[j] + Bullet.bulletSize / 2));

                    // 線分を平方根で求める
                    XY_Dist = (float)Math.Sqrt((X_Dist * X_Dist) + (Y_Dist * Y_Dist));

                    if (XY_Dist < (enemySize / 2) + (Bullet.bulletSize / 2))
                    {
                        enemy[i].hitPoint -= 1; // 当たったら体力を1減らす
                        if (enemy[i].hitPoint <= 0)
                        {
                            enemy[i].enemyFlag = 2; // 敵を倒したと判定

                            Item.ItemApp(enemy[i].enemyPosX + (enemySize / 4),
                            enemy[i].enemyPosY + (enemySize / 4)); // アイテム出現
                        }
                        Bullet.bulletFlag[j] = 0; // 当たった弾を消す
                        Explosion.ExplApp(enemy[i].enemyPosX - (enemySize / 4),
                            enemy[i].enemyPosY - (enemySize / 4));  // 撃破エフェクトと敵を重ねる

                        Explosion.explSE.Play(0.2f, 0.0f, 0.0f);  // 撃破音
                    }
                }
            }
                
        }

        // 敵と自機の当たり判定
        void PlayerHit(int i)
        {
            float X_Dist;   //X座標の距離差 
            float Y_Dist;   //Y座標の距離差
            float XY_Dist;  //X+Y座標の距離差


            if (enemy[i].enemyFlag != 1 || Player.playerFlag == 2) return;// 敵が画面に出ている時

            // 距離差を絶対値で求める
            X_Dist = Math.Abs((enemy[i].enemyPosX + enemySize / 2)
                - (Player.playerPos.X + Player.playerSize / 2));
            Y_Dist = Math.Abs((enemy[i].enemyPosY + enemySize / 2)
                - (Player.playerPos.Y + Player.playerSize / 2));

            // 線分を平方根で求める
            XY_Dist = (float)Math.Sqrt((X_Dist * X_Dist) + (Y_Dist * Y_Dist));

            if (XY_Dist < (enemySize / 2) + (Player.playerSize / 4))
            {
                enemy[i].enemyFlag = 2; // 敵を倒したと判定
                Player.playerFlag = 2; // 自機がやられたと判定
                Explosion.ExplApp(enemy[i].enemyPosX - (enemySize / 4),
                    enemy[i].enemyPosY - (enemySize / 4));  // 撃破エフェクトと敵を重ねる
                Explosion.ExplApp(Player.playerPos.X - (Player.playerSize / 4),
                    Player.playerPos.Y - (Player.playerSize / 4));  // 撃破エフェクトと自機を重ねる

                Player.explSE.Play(1.0f, 0.0f, 0.0f);
                Player.PlayerInit(); // 自機の初期化
            }

        }

    }
}


goma

追記

#2

投稿記事 by goma » 11年前

すみません、若干情報が不足していたと思われるので追記します。
CSVファイルの読み込みは、別のクラス(DataLoadクラス)で行い、敵の各情報をenemyOrderという構造体に格納しています。
また、ステージごとの最大出現数は別のAppearクラスで定義しており、ENEMY_TOTAL_MAX=500となっています。
タイミングの判定を行うAppear.scrollPosはfloat型で、約0.5秒に1増加し、敵の出現タイミングはcount=10,15,20,25,30に設定しています。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 【C# + XNA4.0】敵の重複出現

#3

投稿記事 by みけCAT » 11年前

例えば、DataLoad.enemyOrder[t]に「まだ出ていない」というフラグを用意し、
「まだ出ていない」フラグが立っている場合のみ出現処理を行い、出現したら「まだ出ていない」フラグを折る、
というのはどうでしょうか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

goma

Re: 【C# + XNA4.0】敵の重複出現

#4

投稿記事 by goma » 11年前

みけCAT さんが書きました:例えば、DataLoad.enemyOrder[t]に「まだ出ていない」というフラグを用意し、
「まだ出ていない」フラグが立っている場合のみ出現処理を行い、出現したら「まだ出ていない」フラグを折る、
というのはどうでしょうか?
みけCATさんのアドバイスの通りに、DataLoad.enemyOrder[t]に、その敵が出現したかどうかを表すフラグを用意し、
初期状態でfalseにしておき、出現タイミングになったらtrueにするようにしたところ、敵が1体ずつ出現するようになりました!
ありがとうございました!

閉鎖

“C言語何でも質問掲示板” へ戻る