現在、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(); // 自機の初期化
}
}
}
}