:敵機の落下、自弾の敵弾衝突処理、テクスチャによる描画、自弾敵弾の衝突による爆散、など
演習40 敵機の落下(再掲載)
・自弾が当たった敵機もすぐに消えるのではなく、落下(縮小)しながら移動するようにしよう ・落下(縮小)中の敵機は敵弾の発射はできない。また、衝突判定もしない。 ・敵機の表示(enemya[i].v)も0/1ではなく0~100(%)の表示倍率にしよう
手順(再掲載)
① 描画処理:敵機の描画を表示倍率が0ではないときに、倍率付きで行うようにしよう ② タイマーイベント処理:敵機と自機の衝突判定処理は敵機の表示(倍率)も100の時のみにする。 また、ゲームオーバー処理後、その敵機の表示(倍率)を99にする処理を追加 ③ タイマーイベント処理:敵機と自弾の衝突判定処理は敵機の表示(倍率)も100の時のみにする。 また、その敵機の表示(倍率)を0から99に変更 ④ タイマーイベント処理:敵機の出現待ち時間がゼロになった時の敵機の表示を1ではなく100にする ⑤ タイマーイベント処理:敵弾の発射敵機を決める処理は100%表示の敵機のみからにする ⑥ タイマーイベント処理:敵機の移動処理の前に、その敵機の表示(倍率)が99以下(落下中)であればデクリメントする
作成例
//演習40 敵機の落下
using System; //汎用的に利用
using System.Windows.Forms; //フォームアプリケーションに必須
using System.Drawing; //Size、Image用
struct Item { //アイテムを表す構造体
public Image i; //画像
public int x; //中心X座標
public int y; //中心Y座標
public int hv; //左右方向の速度(左向きは負の数、右向きは正の数)
public int vv; //上下方向の速度(上向きは負の数、下向きは正の数)
public int v; //表示状態(0:非表示、1以上:表示)
}
class Program : Form { //Formクラスの派生クラス
[System.Runtime.InteropServices.DllImport("user32.dll")] //DLLインポート
private static extern short GetKeyState(int nVirtKey); //外部定義指定
int gamemode = 0; //モード(0:タイトル画面,1:プレイ画面,9:終了画面)
int score = 0; //スコア
int hiscore = 0; //ハイスコア
Image backi = Image.FromFile("backb.bmp"); //背景画像を読込む
Image playeri = Image.FromFile("player.gif"); //自機通常画像を読込む
Image playerl = Image.FromFile("playerl.gif"); //自機左寄画像を読込む
Image playerr = Image.FromFile("playerr.gif"); //自機右寄画像を読込む
Image bulleti = Image.FromFile("bullet.gif"); //自弾画像を読込む
Image bullet2i = Image.FromFile("bullet2.gif"); //自弾画像2を読込む
Image enemyi = Image.FromFile("enemy.gif"); //敵機画像を読込む
Image enemybi = Image.FromFile("ebullet.gif"); //敵弾画像を読込む
Pen pen1 = new Pen(Color.Red, 2); //赤色太さ2のペン
Brush brush1 = new SolidBrush(Color.FromArgb(63, 255, 0, 0)); //透明赤いブラシ
Font font1 = new Font("メイリオ", 20, FontStyle.Bold); //フォントを生成
Font fontt = new Font("メイリオ", 80, FontStyle.Bold); //フォントを生成
Font fontm = new Font("メイリオ", 25, FontStyle.Bold); //フォントを生成
Brush brushs = new SolidBrush(Color.Yellow); //黄色のブラシ
Timer timer = new Timer(); //タイマーの生成
int backy = 0; //1枚目の背景描画開始Y座標
Item player; //自機の構造体オブジェクト
const int maxpb = 10; //自弾の最大数
Item[] pba = new Item[maxpb]; //自弾の構造体オブジェクト配列
const int cold = 10; //自弾発射の冷却時間
int waitpb = 0; //自弾発射の待ち時間
const int maxenemy = 20; //敵機の最大数
Item[] enemya = new Item[maxenemy]; //敵機の構造体オブジェクト配列
int waitenemy = 0; //敵機の出現待ち時間
int enemyint = 50; //敵機の出現間隔
string hiscoremsg = ""; //ハイスコアメッセージ
Random rnd = new Random(); //乱数用のRandomクラスのインスタンスを生成
const int enemybint = 75; //敵弾の出現間隔
int waitenemyb = 200; //敵弾の出現待ち時間
const int maxenemyb = 20; //敵弾の最大数
Item[] enemyba = new Item[maxenemyb]; //敵弾の構造体配列
const int enemyvv = 5; //敵機移動速度
//各データをゲーム開始時の値にする
void initData() {
player.i = playeri; //自機の画像
player.x = 320; //自機の中心X座標
player.y = 410; //自機の中心Y座標
player.hv = 0; //自機の左右方向の速度
player.v = 100; //自機を100%表示
waitpb = 0; //自弾発射の待ち時間
waitpb = 0; //自弾発射の待ち時間
score = 0; //スコアクリア
waitenemyb = 200; //敵弾の出現待ち時間
}
//中央座標を用いる画像描画処理
private void DrawItem(PaintEventArgs e, Item it) {
int xx = it.x - it.i.Width / 2; //左上X座標を得る
int yy = it.y - it.i.Height / 2; //左上Y座標を得る
e.Graphics.DrawImage(it.i, xx, yy);
}
//中央座標を用いる画像描画処理(倍率指定)
private void DrawItem(PaintEventArgs e, Item it, int rate) {
if (rate == 100) { //等倍?
DrawItem(e, it); //通常描画を呼ぶ
} else {
int xx = it.x - it.i.Width * rate / 100 / 2; //左上X座標を得る
int yy = it.y - it.i.Height * rate / 100 / 2; //左上Y座標を得る
e.Graphics.DrawImage(it.i, xx, yy, it.i.Width * rate / 100, it.i.Height * rate / 100);
}
}
//楕円形の衝突判定(アイテムaとアイテムbが衝突しているかどうかを返す)
private bool isHit(Item a, Item b) {
int dest = (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y); //距離2乗
double ra = (a.i.Width < a.i.Height) ? a.i.Width / 2.0 : a.i.Height / 2.0; //内径a
double rb = (b.i.Width < b.i.Height) ? b.i.Width / 2.0 : b.i.Height / 2.0; //内径b
return dest < (ra + rb) * (ra + rb); //距離2乗と内径の和の2乗を比較
}
//描画処理のオーバライド
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e); //基本クラスの描画処理を呼ぶ
e.Graphics.DrawImage(backi, 0, backy); //背景画像を描画
e.Graphics.DrawImage(backi, 0, backy - backi.Height); //背景画像を描画
if (gamemode == 0) { //スタート画面?
e.Graphics.DrawString("GAME1", fontt, brushs, 100, 150); //タイトル表示
e.Graphics.DrawString("Hit Enter Key", fontm, brushs, 200, 300); //メッセージ表示
} else { //プレイ画面かゲームオーバー画面?
string s = String.Format("SCORE:{0:000,000}", score); //スコア文字列を作る
e.Graphics.DrawString(s, font1, brushs, 400, 10); //スコア表示
if (player.v != 0) { //自機が表示中?
switch (player.hv) { //自機の向きによって分岐
case 0: player.i = playeri; break; //通常画像にする
case -1: player.i = playerl; break; //左寄画像にする
case 1: player.i = playerr; break; //右寄画像にする
}
DrawItem(e, player, player.v); //自機を倍率描画
}
foreach (var pb in pba) { //全自弾について繰返す
if (pb.v == 1) { //自弾がある?
DrawItem(e, pb); //自弾を描画
}
}
foreach (var enemy in enemya) { //全敵機について繰返す
if (enemy.v != 0) { //【変更】敵機がある?
DrawItem(e, enemy, enemy.v); //【変更】敵機を倍率描画
}
}
foreach (var enemyb in enemyba) { //全敵弾について繰返す
if (enemyb.v == 1) { //敵弾がある?
DrawItem(e, enemyb); //敵弾を描画
}
}
}
if (gamemode == 9) { //ゲームオーバー画面?
e.Graphics.DrawString("GAME OVER", fontm, brushs, 210, 150); //メッセージ表示
e.Graphics.DrawString(hiscoremsg, fontm, brushs, 150, 200); //ハイスコアメッセージ表示
e.Graphics.DrawString("Hit Enter Key", fontm, brushs, 200, 300); //メッセージ表示
}
}
//キー入力時処理
void OnKeyDown(object o, KeyEventArgs e) {
if (e.KeyCode.ToString() == "Escape") { //Escキーが押されていたら
Close(); //フォーム終了
}
//タイトル画面でEnterキーが押されていたら
if (gamemode == 0 && e.KeyCode.ToString() == "Return") {
initData(); //各データをゲーム開始時の値にする
gamemode = 1; //プレイ動画に遷移
timer.Start(); //タイマー開始
}
//ゲームオーバー画面でEnterキーが押されていたら
else if (gamemode == 9 && e.KeyCode.ToString() == "Return") {
initData(); //各データをゲーム開始時の値にする
for (int i = 0; i < maxenemy; i++) { //全敵機について繰返す
enemya[i].v = 0; //無しにする
}
for (int i = 0; i < maxpb; i++) { //全自弾について繰返す
pba[i].v = 0; //無しにする
}
for (int i = 0; i < maxenemyb; i++) { //全敵弾について繰返す
enemyba[i].v = 0; //無しにする
}
gamemode = 1; //プレイ動画に遷移
}
Invalidate(); //画面再描画を依頼
}
//タイマーイベント処理
void Play(object o, EventArgs e) {
backy = (backy + 1) % backi.Height; //1枚目の背景描画開始Y座標を下げる
if (gamemode == 1) { //プレイ画面?
player.hv = 0; //自機の向きを無しにしておく
if (player.x > playeri.Width / 2 && GetKeyState((int)Keys.Left) < 0) { //範囲内で←キーが押されている?
player.x -= 10; //自機を左へ
player.hv = -1; //左向き
}
if (player.x < backi.Width - playeri.Width / 2 && GetKeyState((int)Keys.Right) < 0) { //範囲内で→キーが押されている?
player.x += 10; //自機を右へ
player.hv = 1; //右向き
}
}
for (int i = 0; i < maxenemy; i++) { //全敵機について繰返す
if (enemya[i].v == 100) { //【変更】敵機が100%存在?
if (player.v == 100 && isHit(enemya[i], player)) { //自機が100%存在し衝突?
GameOver(); //ゲームオーバー
enemya[i].v = 99; //【追加】敵機落下開始
break; //抜ける
}
for (int j = 0; j < maxpb; j++) { //全自弾について繰返す
if (pba[j].v != 0 && isHit(enemya[i], pba[j])) { //自弾が存在し衝突?
enemya[i].v = 99; //【変更】敵機落下開始
pba[j].v = 0; //この自弾を消す
score += 10; //スコアアップ
break; //次の敵機へ進む
}
}
}
}
for (int i = 0; i < maxenemyb; i++) { //全敵弾について繰返す
if (enemyba[i].v != 0 && player.v == 100 && isHit(enemyba[i], player)) { //敵弾と自機が衝突?
GameOver(); //ゲームオーバー
enemyba[i].v = 0; //敵弾を消す
break; //繰返しを抜ける
}
}
if (gamemode == 1) { //プレイ画面?
if (GetKeyState((int)Keys.Space) < 0) { //スペースキーが押されている?
if (waitpb <= 0) { //自弾発射待ち時間がゼロ?
for (int i = 0; i < maxpb; i++) { //全自弾について繰返す
if (pba[i].v == 0) { //自弾が非表示?
pba[i].v = 1; //表示にする
pba[i].i = bulleti; //画像
pba[i].x = player.x; //X座標は自機と同じ
pba[i].y = player.y - player.i.Height / 2 - pba[i].i.Height / 2; //Y座標は自機の直上
pba[i].vv = -5; //上移動速度
waitpb = cold; //自弾発射待ち時間をセット
break; //1発発射できればOK
}
}
}
} else { //スペースキーが押されていない?
waitpb = 0; //自弾発射待ち時間をゼロにして発射可能にする
}
}
if (waitenemy <= 0) { //敵機出現待ち時間がゼロ?
for (int i = 0; i < maxenemy; i++) { //全敵機について繰返す
if (enemya[i].v == 0) { //敵機が非表示?
enemya[i].v = 100; //【変更】100%表示にする
enemya[i].i = enemyi; //画像
enemya[i].x = enemyi.Width / 2 + rnd.Next(backi.Width - enemyi.Width);
enemya[i].y = -enemyi.Height; //Y座標は上端の直上
enemya[i].vv = enemyvv; //下移動速度
waitenemy = enemyint; //敵機出現待ち時間をセット
break; //1機出現できればOK
}
}
}
if (waitenemyb <= 0) { //敵弾出現待ち時間がゼロ?
int enemyi = -1; //発射敵機を-1(未確定)にしておく
for (int i = 0; i < maxenemy; i++) { //全敵機について繰返す
if (enemya[i].v == 100 && enemya[i].y > 0 && enemya[i].y < backi.Height / 2) { //【変更】該当する敵機?
enemyi = i; //発射敵機が確定
break;
}
}
if (enemyi != -1) { //発射敵機が未確定ではない?
for (int i = 0; i < maxenemyb; i++) { //全敵弾について繰返す
if (enemyba[i].v == 0) { //敵弾が非表示?
enemyba[i].v = 1; //表示にする
enemyba[i].i = enemybi; //画像
enemyba[i].x = enemya[enemyi].x; //X座標は発射敵機と同じ
enemyba[i].y = enemya[enemyi].y - 15; //Y座標は発射敵機中央の上
double rad = Math.Atan2(player.y - enemyba[i].y, player.x - enemyba[i].x); //角度を得る
enemyba[i].hv = (int)(Math.Cos(rad) * enemyvv * 1.5); //X移動量
enemyba[i].vv = (int)(Math.Sin(rad) * enemyvv * 1.5); //Y移動量
waitenemyb = enemybint; //敵弾発射待ち時間をセット
break; //1弾出現できればOK
}
}
}
}
for (int i = 0; i < maxpb; i++) { //全自弾について繰返す
if (pba[i].v != 0) { //自弾が存在?
pba[i].i = (pba[i].i == bulleti) ? bullet2i : bulleti; //画像を交互変更
pba[i].y += pba[i].vv; //上へ移動
if (pba[i].y + pba[i].i.Height / 2 < 0) { //画面上端より上に出たら
pba[i].v = 0; //自弾を消す
}
}
}
for (int i = 0; i < maxenemy; i++) { //全敵機について繰返す
if (enemya[i].v != 0) { //敵機が存在?
if (enemya[i].v <= 99) { //【以下追加】落下中?
enemya[i].v--; //落下する
}
enemya[i].y += enemya[i].vv; //下へ移動
if (enemya[i].y - enemya[i].i.Height / 2 > backi.Height) { //画面下端より下に出たら
enemya[i].v = 0; //敵機を消す
}
}
}
for (int i = 0; i < maxenemyb; i++) { //全敵弾について繰返す
if (enemyba[i].v != 0) { //敵弾が存在?
enemyba[i].x += enemyba[i].hv; //X移動量をX座標に加算
enemyba[i].y += enemyba[i].vv; //Y移動量をY座標に加算
if (enemyba[i].x < -enemybi.Width / 2 ||
enemyba[i].x - enemybi.Width / 2 > backi.Width ||
enemyba[i].y < -enemybi.Height / 2 ||
enemyba[i].y - enemybi.Height / 2 > backi.Height) {
enemyba[i].v = 0; //上下左右から外に出たら消す
}
}
}
if (waitpb > 0) { //自弾発射待ち時間がセットされていたら
waitpb--; //カウントダウンする
}
if (waitenemy > 0) { //敵機出現待ち時間がセットされていたら
waitenemy--; //カウントダウンする
}
if (waitenemyb > 0) { //敵弾出現待ち時間がセットされていたら
waitenemyb--; //カウントダウンする
}
if (gamemode == 9 && player.v > 0 && player.v < 100) { //ゲームオーバーで自機が縮小中?
player.v--; //表示倍率をダウンする
}
Invalidate(); //画面再描画を依頼
}
//ゲームオーバー処理
private void GameOver() {
player.v = 99; //自機の落下(縮小)を開始
gamemode = 9; //ゲームオーバーにする
if (score > hiscore) { //ハイスコア更新?
hiscore = score; //ハイスコア更新
hiscoremsg = "HiSCORE Update !"; //メッセージ
} else {
hiscoremsg = String.Format("HiSCORE is {0:000,000}", hiscore); //スコア文字列を作る
}
}
//コンストラクタ
Program() {
DoubleBuffered = true; //ダブルバッファリングを有効化
KeyDown += new KeyEventHandler(OnKeyDown); //キー入力イベント登録
timer.Tick += new EventHandler(Play); //タイマーイベント登録
timer.Interval = 10; //タイマーインターバル(ミリ秒)
}
public static void Main() {
Program f = new Program(); //自分のオブジェクトを生成
f.Size = new Size(660, 520); //フォームのサイズを設定
f.Text = "Game"; //フォーム名を設定
f.ControlBox = false; //コントロールボックスを非表示に
f.FormBorderStyle = FormBorderStyle.Fixed3D; //サイズ変更を抑止
Application.Run(f); //フォームを現出
}
}
演習41 自弾の敵弾衝突処理
・自弾と敵弾の衝突判定を行い、衝突していたら両方を消そう ・スコアに100点加算する
手順と仕様
①タイマーイベント処理:敵弾と自機の衝突判定に加えて、自弾と敵弾の衝突判定を行う。 衝突していたら両方を消し、スコアに100点加算し、次の敵弾のチェックに進む。
作成例
//演習41 自弾の敵弾衝突処理
using System; //汎用的に利用
using System.Windows.Forms; //フォームアプリケーションに必須
using System.Drawing; //Size、Image用
struct Item { //アイテムを表す構造体
public Image i; //画像
public int x; //中心X座標
public int y; //中心Y座標
public int hv; //左右方向の速度(左向きは負の数、右向きは正の数)
public int vv; //上下方向の速度(上向きは負の数、下向きは正の数)
public int v; //表示状態(0:非表示、1以上:表示)
}
class Program : Form { //Formクラスの派生クラス
[System.Runtime.InteropServices.DllImport("user32.dll")] //DLLインポート
private static extern short GetKeyState(int nVirtKey); //外部定義指定
int gamemode = 0; //モード(0:タイトル画面,1:プレイ画面,9:終了画面)
int score = 0; //スコア
int hiscore = 0; //ハイスコア
Image backi = Image.FromFile("backb.bmp"); //背景画像を読込む
Image playeri = Image.FromFile("player.gif"); //自機通常画像を読込む
Image playerl = Image.FromFile("playerl.gif"); //自機左寄画像を読込む
Image playerr = Image.FromFile("playerr.gif"); //自機右寄画像を読込む
Image bulleti = Image.FromFile("bullet.gif"); //自弾画像を読込む
Image bullet2i = Image.FromFile("bullet2.gif"); //自弾画像2を読込む
Image enemyi = Image.FromFile("enemy.gif"); //敵機画像を読込む
Image enemybi = Image.FromFile("ebullet.gif"); //敵弾画像を読込む
Pen pen1 = new Pen(Color.Red, 2); //赤色太さ2のペン
Brush brush1 = new SolidBrush(Color.FromArgb(63, 255, 0, 0)); //透明赤いブラシ
Font font1 = new Font("メイリオ", 20, FontStyle.Bold); //フォントを生成
Font fontt = new Font("メイリオ", 80, FontStyle.Bold); //フォントを生成
Font fontm = new Font("メイリオ", 25, FontStyle.Bold); //フォントを生成
Brush brushs = new SolidBrush(Color.Yellow); //黄色のブラシ
Timer timer = new Timer(); //タイマーの生成
int backy = 0; //1枚目の背景描画開始Y座標
Item player; //自機の構造体オブジェクト
const int maxpb = 10; //自弾の最大数
Item[] pba = new Item[maxpb]; //自弾の構造体オブジェクト配列
const int cold = 10; //自弾発射の冷却時間
int waitpb = 0; //自弾発射の待ち時間
const int maxenemy = 20; //敵機の最大数
Item[] enemya = new Item[maxenemy]; //敵機の構造体オブジェクト配列
int waitenemy = 0; //敵機の出現待ち時間
int enemyint = 50; //敵機の出現間隔
string hiscoremsg = ""; //ハイスコアメッセージ
Random rnd = new Random(); //乱数用のRandomクラスのインスタンスを生成
const int enemybint = 75; //敵弾の出現間隔
int waitenemyb = 200; //敵弾の出現待ち時間
const int maxenemyb = 20; //敵弾の最大数
Item[] enemyba = new Item[maxenemyb]; //敵弾の構造体配列
const int enemyvv = 5; //敵機移動速度
//各データをゲーム開始時の値にする
void initData() {
player.i = playeri; //自機の画像
player.x = 320; //自機の中心X座標
player.y = 410; //自機の中心Y座標
player.hv = 0; //自機の左右方向の速度
player.v = 100; //自機を100%表示
waitpb = 0; //自弾発射の待ち時間
waitpb = 0; //自弾発射の待ち時間
score = 0; //スコアクリア
waitenemyb = 200; //敵弾の出現待ち時間
}
//中央座標を用いる画像描画処理
private void DrawItem(PaintEventArgs e, Item it) {
int xx = it.x - it.i.Width / 2; //左上X座標を得る
int yy = it.y - it.i.Height / 2; //左上Y座標を得る
e.Graphics.DrawImage(it.i, xx, yy);
}
//中央座標を用いる画像描画処理(倍率指定)
private void DrawItem(PaintEventArgs e, Item it, int rate) {
if (rate == 100) { //等倍?
DrawItem(e, it); //通常描画を呼ぶ
} else {
int xx = it.x - it.i.Width * rate / 100 / 2; //左上X座標を得る
int yy = it.y - it.i.Height * rate / 100 / 2; //左上Y座標を得る
e.Graphics.DrawImage(it.i, xx, yy, it.i.Width * rate / 100, it.i.Height * rate / 100);
}
}
//楕円形の衝突判定(アイテムaとアイテムbが衝突しているかどうかを返す)
private bool isHit(Item a, Item b) {
int dest = (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y); //距離2乗
double ra = (a.i.Width < a.i.Height) ? a.i.Width / 2.0 : a.i.Height / 2.0; //内径a
double rb = (b.i.Width < b.i.Height) ? b.i.Width / 2.0 : b.i.Height / 2.0; //内径b
return dest < (ra + rb) * (ra + rb); //距離2乗と内径の和の2乗を比較
}
//描画処理のオーバライド
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e); //基本クラスの描画処理を呼ぶ
e.Graphics.DrawImage(backi, 0, backy); //背景画像を描画
e.Graphics.DrawImage(backi, 0, backy - backi.Height); //背景画像を描画
if (gamemode == 0) { //スタート画面?
e.Graphics.DrawString("GAME1", fontt, brushs, 100, 150); //タイトル表示
e.Graphics.DrawString("Hit Enter Key", fontm, brushs, 200, 300); //メッセージ表示
} else { //プレイ画面かゲームオーバー画面?
string s = String.Format("SCORE:{0:000,000}", score); //スコア文字列を作る
e.Graphics.DrawString(s, font1, brushs, 400, 10); //スコア表示
if (player.v != 0) { //自機が表示中?
switch (player.hv) { //自機の向きによって分岐
case 0: player.i = playeri; break; //通常画像にする
case -1: player.i = playerl; break; //左寄画像にする
case 1: player.i = playerr; break; //右寄画像にする
}
DrawItem(e, player, player.v); //自機を倍率描画
}
foreach (var pb in pba) { //全自弾について繰返す
if (pb.v == 1) { //自弾がある?
DrawItem(e, pb); //自弾を描画
}
}
foreach (var enemy in enemya) { //全敵機について繰返す
if (enemy.v != 0) { //敵機がある?
DrawItem(e, enemy, enemy.v); //敵機を倍率描画
}
}
foreach (var enemyb in enemyba) { //全敵弾について繰返す
if (enemyb.v == 1) { //敵弾がある?
DrawItem(e, enemyb); //敵弾を描画
}
}
}
if (gamemode == 9) { //ゲームオーバー画面?
e.Graphics.DrawString("GAME OVER", fontm, brushs, 210, 150); //メッセージ表示
e.Graphics.DrawString(hiscoremsg, fontm, brushs, 150, 200); //ハイスコアメッセージ表示
e.Graphics.DrawString("Hit Enter Key", fontm, brushs, 200, 300); //メッセージ表示
}
}
//キー入力時処理
void OnKeyDown(object o, KeyEventArgs e) {
if (e.KeyCode.ToString() == "Escape") { //Escキーが押されていたら
Close(); //フォーム終了
}
//タイトル画面でEnterキーが押されていたら
if (gamemode == 0 && e.KeyCode.ToString() == "Return") {
initData(); //各データをゲーム開始時の値にする
gamemode = 1; //プレイ動画に遷移
timer.Start(); //タイマー開始
}
//ゲームオーバー画面でEnterキーが押されていたら
else if (gamemode == 9 && e.KeyCode.ToString() == "Return") {
initData(); //各データをゲーム開始時の値にする
for (int i = 0; i < maxenemy; i++) { //全敵機について繰返す
enemya[i].v = 0; //無しにする
}
for (int i = 0; i < maxpb; i++) { //全自弾について繰返す
pba[i].v = 0; //無しにする
}
for (int i = 0; i < maxenemyb; i++) { //全敵弾について繰返す
enemyba[i].v = 0; //無しにする
}
gamemode = 1; //プレイ動画に遷移
}
Invalidate(); //画面再描画を依頼
}
//タイマーイベント処理
void Play(object o, EventArgs e) {
backy = (backy + 1) % backi.Height; //1枚目の背景描画開始Y座標を下げる
if (gamemode == 1) { //プレイ画面?
player.hv = 0; //自機の向きを無しにしておく
if (player.x > playeri.Width / 2 && GetKeyState((int)Keys.Left) < 0) { //範囲内で←キーが押されている?
player.x -= 10; //自機を左へ
player.hv = -1; //左向き
}
if (player.x < backi.Width - playeri.Width / 2 && GetKeyState((int)Keys.Right) < 0) { //範囲内で→キーが押されている?
player.x += 10; //自機を右へ
player.hv = 1; //右向き
}
}
for (int i = 0; i < maxenemy; i++) { //全敵機について繰返す
if (enemya[i].v == 100) { //敵機が100%存在?
if (player.v == 100 && isHit(enemya[i], player)) { //自機が100%存在し衝突?
GameOver(); //ゲームオーバー
enemya[i].v = 99; //敵機落下開始
break; //抜ける
}
for (int j = 0; j < maxpb; j++) { //全自弾について繰返す
if (pba[j].v != 0 && isHit(enemya[i], pba[j])) { //自弾が存在し衝突?
enemya[i].v = 99; //敵機落下開始
pba[j].v = 0; //この自弾を消す
score += 10; //スコアアップ
break; //次の敵機へ進む
}
}
}
}
for (int i = 0; i < maxenemyb; i++) { //全敵弾について繰返す
if (enemyba[i].v != 0) { //【移動】敵弾が存在?
if (player.v == 100 && isHit(enemyba[i], player)) { //【変更】敵弾と自機が衝突?
GameOver(); //ゲームオーバー
enemyba[i].v = 0; //敵弾を消す
break; //繰返しを抜ける
}
for (int j = 0; j < maxpb; j++) { //【以下追加】全自弾について繰返す
if (pba[j].v != 0 && isHit(enemyba[i], pba[j])) { //敵弾と自弾が衝突?
enemyba[i].v = 0; //敵弾を消す
pba[j].v = 0; //自弾を消す
score += 100;
break; //次の敵弾へ
}
}
}
}
if (gamemode == 1) { //プレイ画面?
if (GetKeyState((int)Keys.Space) < 0) { //スペースキーが押されている?
if (waitpb <= 0) { //自弾発射待ち時間がゼロ?
for (int i = 0; i < maxpb; i++) { //全自弾について繰返す
if (pba[i].v == 0) { //自弾が非表示?
pba[i].v = 1; //表示にする
pba[i].i = bulleti; //画像
pba[i].x = player.x; //X座標は自機と同じ
pba[i].y = player.y - player.i.Height / 2 - pba[i].i.Height / 2; //Y座標は自機の直上
pba[i].vv = -5; //上移動速度
waitpb = cold; //自弾発射待ち時間をセット
break; //1発発射できればOK
}
}
}
} else { //スペースキーが押されていない?
waitpb = 0; //自弾発射待ち時間をゼロにして発射可能にする
}
}
if (waitenemy <= 0) { //敵機出現待ち時間がゼロ?
for (int i = 0; i < maxenemy; i++) { //全敵機について繰返す
if (enemya[i].v == 0) { //敵機が非表示?
enemya[i].v = 100; //100%表示にする
enemya[i].i = enemyi; //画像
enemya[i].x = enemyi.Width / 2 + rnd.Next(backi.Width - enemyi.Width);
enemya[i].y = -enemyi.Height; //Y座標は上端の直上
enemya[i].vv = enemyvv; //下移動速度
waitenemy = enemyint; //敵機出現待ち時間をセット
break; //1機出現できればOK
}
}
}
if (waitenemyb <= 0) { //敵弾出現待ち時間がゼロ?
int enemyi = -1; //発射敵機を-1(未確定)にしておく
for (int i = 0; i < maxenemy; i++) { //全敵機について繰返す
if (enemya[i].v == 100 && enemya[i].y > 0 && enemya[i].y < backi.Height / 2) { //該当する敵機?
enemyi = i; //発射敵機が確定
break;
}
}
if (enemyi != -1) { //発射敵機が未確定ではない?
for (int i = 0; i < maxenemyb; i++) { //全敵弾について繰返す
if (enemyba[i].v == 0) { //敵弾が非表示?
enemyba[i].v = 1; //表示にする
enemyba[i].i = enemybi; //画像
enemyba[i].x = enemya[enemyi].x; //X座標は発射敵機と同じ
enemyba[i].y = enemya[enemyi].y - 15; //Y座標は発射敵機中央の上
double rad = Math.Atan2(player.y - enemyba[i].y, player.x - enemyba[i].x); //角度を得る
enemyba[i].hv = (int)(Math.Cos(rad) * enemyvv * 1.5); //X移動量
enemyba[i].vv = (int)(Math.Sin(rad) * enemyvv * 1.5); //Y移動量
waitenemyb = enemybint; //敵弾発射待ち時間をセット
break; //1弾出現できればOK
}
}
}
}
for (int i = 0; i < maxpb; i++) { //全自弾について繰返す
if (pba[i].v != 0) { //自弾が存在?
pba[i].i = (pba[i].i == bulleti) ? bullet2i : bulleti; //画像を交互変更
pba[i].y += pba[i].vv; //上へ移動
if (pba[i].y + pba[i].i.Height / 2 < 0) { //画面上端より上に出たら
pba[i].v = 0; //自弾を消す
}
}
}
for (int i = 0; i < maxenemy; i++) { //全敵機について繰返す
if (enemya[i].v != 0) { //敵機が存在?
if (enemya[i].v <= 99) { //落下中?
enemya[i].v--; //落下する
}
enemya[i].y += enemya[i].vv; //下へ移動
if (enemya[i].y - enemya[i].i.Height / 2 > backi.Height) { //画面下端より下に出たら
enemya[i].v = 0; //敵機を消す
}
}
}
for (int i = 0; i < maxenemyb; i++) { //全敵弾について繰返す
if (enemyba[i].v != 0) { //敵弾が存在?
enemyba[i].x += enemyba[i].hv; //X移動量をX座標に加算
enemyba[i].y += enemyba[i].vv; //Y移動量をY座標に加算
if (enemyba[i].x < -enemybi.Width / 2 ||
enemyba[i].x - enemybi.Width / 2 > backi.Width ||
enemyba[i].y < -enemybi.Height / 2 ||
enemyba[i].y - enemybi.Height / 2 > backi.Height) {
enemyba[i].v = 0; //上下左右から外に出たら消す
}
}
}
if (waitpb > 0) { //自弾発射待ち時間がセットされていたら
waitpb--; //カウントダウンする
}
if (waitenemy > 0) { //敵機出現待ち時間がセットされていたら
waitenemy--; //カウントダウンする
}
if (waitenemyb > 0) { //敵弾出現待ち時間がセットされていたら
waitenemyb--; //カウントダウンする
}
if (gamemode == 9 && player.v > 0 && player.v < 100) { //ゲームオーバーで自機が縮小中?
player.v--; //表示倍率をダウンする
}
Invalidate(); //画面再描画を依頼
}
//ゲームオーバー処理
private void GameOver() {
player.v = 99; //自機の落下(縮小)を開始
gamemode = 9; //ゲームオーバーにする
if (score > hiscore) { //ハイスコア更新?
hiscore = score; //ハイスコア更新
hiscoremsg = "HiSCORE Update !"; //メッセージ
} else {
hiscoremsg = String.Format("HiSCORE is {0:000,000}", hiscore); //スコア文字列を作る
}
}
//コンストラクタ
Program() {
DoubleBuffered = true; //ダブルバッファリングを有効化
KeyDown += new KeyEventHandler(OnKeyDown); //キー入力イベント登録
timer.Tick += new EventHandler(Play); //タイマーイベント登録
timer.Interval = 10; //タイマーインターバル(ミリ秒)
}
public static void Main() {
Program f = new Program(); //自分のオブジェクトを生成
f.Size = new Size(660, 520); //フォームのサイズを設定
f.Text = "Game"; //フォーム名を設定
f.ControlBox = false; //コントロールボックスを非表示に
f.FormBorderStyle = FormBorderStyle.Fixed3D; //サイズ変更を抑止
Application.Run(f); //フォームを現出
}
}
テーマ34 テクスチャによる図形の描画
・画像を用いてテクスチャブラシを生成することで、図形の塗りつぶしなどに利用できる ・生成書式例: Brush 参照変数 = new TextureBrush(Image); ・生成したTextureBrushは基本クラスであるBrushのオブジェクトとして、図形の塗りつぶしに利用できる ・テクスチャによる円の塗りつぶしの書式例: e.Graphics.FillEllipse(Brush, 左上X座標, 左上Y座標, 幅, 高さ); ・太さのある線をテクスチャで描くには、コンストラクタPen(Brush, 太さ)でPenオブジェクトを生成して用いると良い 例: Brush b = new TextureBrush(burni); Pen p = new Pen(b, 10); e.Graphics.DrawEllipse(p, 10, 10, 300, 300);
演習42 敵弾の自弾衝突による爆散
・自弾と敵弾が衝突していたら敵弾の中心から円を広げるように爆散するようにしよう ・爆散用画像ファイルを用いるburn.gif 60×60 ・これをテクスチャにした太さ15の円を用い、直径200まで拡大したら消そう ・爆散中の敵弾は動かず、衝突判定もしない
手順と仕様
※コメントのみ:Item構造体のメンバvを「0:非表示、1~100:表示、101~200:爆散中」とする ①データメンバ:Image変数burniを追加し、爆散用の炎画像burn.gifを読み込む ②データメンバ:Pen変数burnpを追加し、burniのテクスチャブラシと太さ15を指定して生成 ③描画処理:全敵弾について、enemy.vが爆散中であれば: ・爆散円の直径=(enemy.vー100)の2倍とする(つまり2から200まで) ・爆散円をDrawEllipse(burnp, X座標ー(enemy.vー100), Y座標ー(enemy.vー100), 直径, 直径)で描画 ④タイマーイベント処理:敵弾と自弾の衝突判定を敵弾.vが1の時のみ行うように変更 ⑤タイマーイベント処理:敵弾と自弾が衝突したら、敵弾.vを101にして爆散を開始するように変更 ⑥タイマーイベント処理:敵弾を移動する処理を敵弾.vが1の時のみ行うように変更。 また、敵弾.vが101以上(爆散中)であえば、200までインクリメントする処理を追加
作成例
//演習42 敵弾の自弾衝突による爆散
using System; //汎用的に利用
using System.Windows.Forms; //フォームアプリケーションに必須
using System.Drawing; //Size、Image用
struct Item { //アイテムを表す構造体
public Image i; //画像
public int x; //中心X座標
public int y; //中心Y座標
public int hv; //左右方向の速度(左向きは負の数、右向きは正の数)
public int vv; //上下方向の速度(上向きは負の数、下向きは正の数)
public int v; //表示状態(0:非表示、1~100:表示、101~200:爆散中)
}
class Program : Form { //Formクラスの派生クラス
[System.Runtime.InteropServices.DllImport("user32.dll")] //DLLインポート
private static extern short GetKeyState(int nVirtKey); //外部定義指定
int gamemode = 0; //モード(0:タイトル画面,1:プレイ画面,9:終了画面)
int score = 0; //スコア
int hiscore = 0; //ハイスコア
Image backi = Image.FromFile("backb.bmp"); //背景画像を読込む
Image playeri = Image.FromFile("player.gif"); //自機通常画像を読込む
Image playerl = Image.FromFile("playerl.gif"); //自機左寄画像を読込む
Image playerr = Image.FromFile("playerr.gif"); //自機右寄画像を読込む
Image bulleti = Image.FromFile("bullet.gif"); //自弾画像を読込む
Image bullet2i = Image.FromFile("bullet2.gif"); //自弾画像2を読込む
Image enemyi = Image.FromFile("enemy.gif"); //敵機画像を読込む
Image enemybi = Image.FromFile("ebullet.gif"); //敵弾画像を読込む
static Image burni = Image.FromFile("burn.gif"); //【追加】炎画像を読込む
Pen burnp = new Pen(new TextureBrush(burni), 15); //【追加】炎のテクスチャペン
Pen pen1 = new Pen(Color.Red, 2); //赤色太さ2のペン
Brush brush1 = new SolidBrush(Color.FromArgb(63, 255, 0, 0)); //透明赤いブラシ
Font font1 = new Font("メイリオ", 20, FontStyle.Bold); //フォントを生成
Font fontt = new Font("メイリオ", 80, FontStyle.Bold); //フォントを生成
Font fontm = new Font("メイリオ", 25, FontStyle.Bold); //フォントを生成
Brush brushs = new SolidBrush(Color.Yellow); //黄色のブラシ
Timer timer = new Timer(); //タイマーの生成
int backy = 0; //1枚目の背景描画開始Y座標
Item player; //自機の構造体オブジェクト
const int maxpb = 10; //自弾の最大数
Item[] pba = new Item[maxpb]; //自弾の構造体オブジェクト配列
const int cold = 10; //自弾発射の冷却時間
int waitpb = 0; //自弾発射の待ち時間
const int maxenemy = 20; //敵機の最大数
Item[] enemya = new Item[maxenemy]; //敵機の構造体オブジェクト配列
int waitenemy = 0; //敵機の出現待ち時間
int enemyint = 50; //敵機の出現間隔
string hiscoremsg = ""; //ハイスコアメッセージ
Random rnd = new Random(); //乱数用のRandomクラスのインスタンスを生成
const int enemybint = 75; //敵弾の出現間隔
int waitenemyb = 200; //敵弾の出現待ち時間
const int maxenemyb = 20; //敵弾の最大数
Item[] enemyba = new Item[maxenemyb]; //敵弾の構造体配列
const int enemyvv = 5; //敵機移動速度
//各データをゲーム開始時の値にする
void initData() {
player.i = playeri; //自機の画像
player.x = 320; //自機の中心X座標
player.y = 410; //自機の中心Y座標
player.hv = 0; //自機の左右方向の速度
player.v = 100; //自機を100%表示
waitpb = 0; //自弾発射の待ち時間
waitpb = 0; //自弾発射の待ち時間
score = 0; //スコアクリア
waitenemyb = 200; //敵弾の出現待ち時間
}
//中央座標を用いる画像描画処理
private void DrawItem(PaintEventArgs e, Item it) {
int xx = it.x - it.i.Width / 2; //左上X座標を得る
int yy = it.y - it.i.Height / 2; //左上Y座標を得る
e.Graphics.DrawImage(it.i, xx, yy);
}
//中央座標を用いる画像描画処理(倍率指定)
private void DrawItem(PaintEventArgs e, Item it, int rate) {
if (rate == 100) { //等倍?
DrawItem(e, it); //通常描画を呼ぶ
} else {
int xx = it.x - it.i.Width * rate / 100 / 2; //左上X座標を得る
int yy = it.y - it.i.Height * rate / 100 / 2; //左上Y座標を得る
e.Graphics.DrawImage(it.i, xx, yy, it.i.Width * rate / 100, it.i.Height * rate / 100);
}
}
//楕円形の衝突判定(アイテムaとアイテムbが衝突しているかどうかを返す)
private bool isHit(Item a, Item b) {
int dest = (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y); //距離2乗
double ra = (a.i.Width < a.i.Height) ? a.i.Width / 2.0 : a.i.Height / 2.0; //内径a
double rb = (b.i.Width < b.i.Height) ? b.i.Width / 2.0 : b.i.Height / 2.0; //内径b
return dest < (ra + rb) * (ra + rb); //距離2乗と内径の和の2乗を比較
}
//描画処理のオーバライド
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e); //基本クラスの描画処理を呼ぶ
e.Graphics.DrawImage(backi, 0, backy); //背景画像を描画
e.Graphics.DrawImage(backi, 0, backy - backi.Height); //背景画像を描画
if (gamemode == 0) { //スタート画面?
e.Graphics.DrawString("GAME1", fontt, brushs, 100, 150); //タイトル表示
e.Graphics.DrawString("Hit Enter Key", fontm, brushs, 200, 300); //メッセージ表示
} else { //プレイ画面かゲームオーバー画面?
string s = String.Format("SCORE:{0:000,000}", score); //スコア文字列を作る
e.Graphics.DrawString(s, font1, brushs, 400, 10); //スコア表示
if (player.v != 0) { //自機が表示中?
switch (player.hv) { //自機の向きによって分岐
case 0: player.i = playeri; break; //通常画像にする
case -1: player.i = playerl; break; //左寄画像にする
case 1: player.i = playerr; break; //右寄画像にする
}
DrawItem(e, player, player.v); //自機を倍率描画
}
foreach (var pb in pba) { //全自弾について繰返す
if (pb.v == 1) { //自弾がある?
DrawItem(e, pb); //自弾を描画
}
}
foreach (var enemy in enemya) { //全敵機について繰返す
if (enemy.v != 0) { //敵機がある?
DrawItem(e, enemy, enemy.v); //敵機を倍率描画
}
}
foreach (var enemyb in enemyba) { //全敵弾について繰返す
if (enemyb.v == 1) { //敵弾がある?
DrawItem(e, enemyb); //敵弾を描画
} else if (enemyb.v >= 101) { //【以下追加】爆散中?
int rr = (enemyb.v - 100) * 2; //直径を計算
e.Graphics.DrawEllipse(burnp, enemyb.x - (enemyb.v - 100),
enemyb.y - (enemyb.v - 100), rr, rr); //爆散円描画
}
}
}
if (gamemode == 9) { //ゲームオーバー画面?
e.Graphics.DrawString("GAME OVER", fontm, brushs, 210, 150); //メッセージ表示
e.Graphics.DrawString(hiscoremsg, fontm, brushs, 150, 200); //ハイスコアメッセージ表示
e.Graphics.DrawString("Hit Enter Key", fontm, brushs, 200, 300); //メッセージ表示
}
}
//キー入力時処理
void OnKeyDown(object o, KeyEventArgs e) {
if (e.KeyCode.ToString() == "Escape") { //Escキーが押されていたら
Close(); //フォーム終了
}
//タイトル画面でEnterキーが押されていたら
if (gamemode == 0 && e.KeyCode.ToString() == "Return") {
initData(); //各データをゲーム開始時の値にする
gamemode = 1; //プレイ動画に遷移
timer.Start(); //タイマー開始
}
//ゲームオーバー画面でEnterキーが押されていたら
else if (gamemode == 9 && e.KeyCode.ToString() == "Return") {
initData(); //各データをゲーム開始時の値にする
for (int i = 0; i < maxenemy; i++) { //全敵機について繰返す
enemya[i].v = 0; //無しにする
}
for (int i = 0; i < maxpb; i++) { //全自弾について繰返す
pba[i].v = 0; //無しにする
}
for (int i = 0; i < maxenemyb; i++) { //全敵弾について繰返す
enemyba[i].v = 0; //無しにする
}
gamemode = 1; //プレイ動画に遷移
}
Invalidate(); //画面再描画を依頼
}
//タイマーイベント処理
void Play(object o, EventArgs e) {
backy = (backy + 1) % backi.Height; //1枚目の背景描画開始Y座標を下げる
if (gamemode == 1) { //プレイ画面?
player.hv = 0; //自機の向きを無しにしておく
if (player.x > playeri.Width / 2 && GetKeyState((int)Keys.Left) < 0) { //範囲内で←キーが押されている?
player.x -= 10; //自機を左へ
player.hv = -1; //左向き
}
if (player.x < backi.Width - playeri.Width / 2 && GetKeyState((int)Keys.Right) < 0) { //範囲内で→キーが押されている?
player.x += 10; //自機を右へ
player.hv = 1; //右向き
}
}
for (int i = 0; i < maxenemy; i++) { //全敵機について繰返す
if (enemya[i].v == 100) { //敵機が100%存在?
if (player.v == 100 && isHit(enemya[i], player)) { //自機が100%存在し衝突?
GameOver(); //ゲームオーバー
enemya[i].v = 99; //敵機落下開始
break; //抜ける
}
for (int j = 0; j < maxpb; j++) { //全自弾について繰返す
if (pba[j].v != 0 && isHit(enemya[i], pba[j])) { //自弾が存在し衝突?
enemya[i].v = 99; //敵機落下開始
pba[j].v = 0; //この自弾を消す
score += 10; //スコアアップ
break; //次の敵機へ進む
}
}
}
}
for (int i = 0; i < maxenemyb; i++) { //全敵弾について繰返す
if (enemyba[i].v == 1) { //【変更】敵弾が存在?
if (player.v == 100 && isHit(enemyba[i], player)) { //敵弾と自機が衝突?
GameOver(); //ゲームオーバー
enemyba[i].v = 0; //敵弾を消す
break; //繰返しを抜ける
}
for (int j = 0; j < maxpb; j++) { //全自弾について繰返す
if (pba[j].v != 0 && isHit(enemyba[i], pba[j])) { //敵弾と自弾が衝突?
enemyba[i].v = 101; //【変更】敵弾を爆散状態へ
pba[j].v = 0; //自弾を消す
score += 100;
break; //次の敵弾へ
}
}
}
}
if (gamemode == 1) { //プレイ画面?
if (GetKeyState((int)Keys.Space) < 0) { //スペースキーが押されている?
if (waitpb <= 0) { //自弾発射待ち時間がゼロ?
for (int i = 0; i < maxpb; i++) { //全自弾について繰返す
if (pba[i].v == 0) { //自弾が非表示?
pba[i].v = 1; //表示にする
pba[i].i = bulleti; //画像
pba[i].x = player.x; //X座標は自機と同じ
pba[i].y = player.y - player.i.Height / 2 - pba[i].i.Height / 2; //Y座標は自機の直上
pba[i].vv = -5; //上移動速度
waitpb = cold; //自弾発射待ち時間をセット
break; //1発発射できればOK
}
}
}
} else { //スペースキーが押されていない?
waitpb = 0; //自弾発射待ち時間をゼロにして発射可能にする
}
}
if (waitenemy <= 0) { //敵機出現待ち時間がゼロ?
for (int i = 0; i < maxenemy; i++) { //全敵機について繰返す
if (enemya[i].v == 0) { //敵機が非表示?
enemya[i].v = 100; //100%表示にする
enemya[i].i = enemyi; //画像
enemya[i].x = enemyi.Width / 2 + rnd.Next(backi.Width - enemyi.Width);
enemya[i].y = -enemyi.Height; //Y座標は上端の直上
enemya[i].vv = enemyvv; //下移動速度
waitenemy = enemyint; //敵機出現待ち時間をセット
break; //1機出現できればOK
}
}
}
if (waitenemyb <= 0) { //敵弾出現待ち時間がゼロ?
int enemyi = -1; //発射敵機を-1(未確定)にしておく
for (int i = 0; i < maxenemy; i++) { //全敵機について繰返す
if (enemya[i].v == 100 && enemya[i].y > 0 && enemya[i].y < backi.Height / 2) { //該当する敵機?
enemyi = i; //発射敵機が確定
break;
}
}
if (enemyi != -1) { //発射敵機が未確定ではない?
for (int i = 0; i < maxenemyb; i++) { //全敵弾について繰返す
if (enemyba[i].v == 0) { //敵弾が非表示?
enemyba[i].v = 1; //表示にする
enemyba[i].i = enemybi; //画像
enemyba[i].x = enemya[enemyi].x; //X座標は発射敵機と同じ
enemyba[i].y = enemya[enemyi].y - 15; //Y座標は発射敵機中央の上
double rad = Math.Atan2(player.y - enemyba[i].y, player.x - enemyba[i].x); //角度を得る
enemyba[i].hv = (int)(Math.Cos(rad) * enemyvv * 1.5); //X移動量
enemyba[i].vv = (int)(Math.Sin(rad) * enemyvv * 1.5); //Y移動量
waitenemyb = enemybint; //敵弾発射待ち時間をセット
break; //1弾出現できればOK
}
}
}
}
for (int i = 0; i < maxpb; i++) { //全自弾について繰返す
if (pba[i].v != 0) { //自弾が存在?
pba[i].i = (pba[i].i == bulleti) ? bullet2i : bulleti; //画像を交互変更
pba[i].y += pba[i].vv; //上へ移動
if (pba[i].y + pba[i].i.Height / 2 < 0) { //画面上端より上に出たら
pba[i].v = 0; //自弾を消す
}
}
}
for (int i = 0; i < maxenemy; i++) { //全敵機について繰返す
if (enemya[i].v != 0) { //敵機が存在?
if (enemya[i].v <= 99) { //落下中?
enemya[i].v--; //落下する
}
enemya[i].y += enemya[i].vv; //下へ移動
if (enemya[i].y - enemya[i].i.Height / 2 > backi.Height) { //画面下端より下に出たら
enemya[i].v = 0; //敵機を消す
}
}
}
for (int i = 0; i < maxenemyb; i++) { //全敵弾について繰返す
if (enemyba[i].v == 1) { //【変更】敵弾が存在?
enemyba[i].x += enemyba[i].hv; //X移動量をX座標に加算
enemyba[i].y += enemyba[i].vv; //Y移動量をY座標に加算
if (enemyba[i].x < -enemybi.Width / 2 ||
enemyba[i].x - enemybi.Width / 2 > backi.Width ||
enemyba[i].y < -enemybi.Height / 2 ||
enemyba[i].y - enemybi.Height / 2 > backi.Height) {
enemyba[i].v = 0; //上下左右から外に出たら消す
}
} else if (enemyba[i].v >= 101) { //【以下追加】爆散中?
enemyba[i].v = (enemyba[i].v <= 200) ? enemyba[i].v + 1 : 0; //200までカウントアップ
}
}
if (waitpb > 0) { //自弾発射待ち時間がセットされていたら
waitpb--; //カウントダウンする
}
if (waitenemy > 0) { //敵機出現待ち時間がセットされていたら
waitenemy--; //カウントダウンする
}
if (waitenemyb > 0) { //敵弾出現待ち時間がセットされていたら
waitenemyb--; //カウントダウンする
}
if (gamemode == 9 && player.v > 0 && player.v < 100) { //ゲームオーバーで自機が縮小中?
player.v--; //表示倍率をダウンする
}
Invalidate(); //画面再描画を依頼
}
//ゲームオーバー処理
private void GameOver() {
player.v = 99; //自機の落下(縮小)を開始
gamemode = 9; //ゲームオーバーにする
if (score > hiscore) { //ハイスコア更新?
hiscore = score; //ハイスコア更新
hiscoremsg = "HiSCORE Update !"; //メッセージ
} else {
hiscoremsg = String.Format("HiSCORE is {0:000,000}", hiscore); //スコア文字列を作る
}
}
//コンストラクタ
Program() {
DoubleBuffered = true; //ダブルバッファリングを有効化
KeyDown += new KeyEventHandler(OnKeyDown); //キー入力イベント登録
timer.Tick += new EventHandler(Play); //タイマーイベント登録
timer.Interval = 10; //タイマーインターバル(ミリ秒)
}
public static void Main() {
Program f = new Program(); //自分のオブジェクトを生成
f.Size = new Size(660, 520); //フォームのサイズを設定
f.Text = "Game"; //フォーム名を設定
f.ControlBox = false; //コントロールボックスを非表示に
f.FormBorderStyle = FormBorderStyle.Fixed3D; //サイズ変更を抑止
Application.Run(f); //フォームを現出
}
}
演習43 敵機と爆散円の衝突判定
・敵機が爆散円に触れたら落下するようにしよう ・敵機が破壊されたことがわかりやすくなるように、落下中の画像に切り替えようmeteo3.gif ・スコアの上昇に合わせて、敵機の出現間隔が短くなるようにしよう(10まで)
手順と仕様
①データメンバ:Image変数meteoiを追加し、敵機落下用の画像meteo3.gifを読み込む ②タイマーイベント処理:敵機と自機が衝突したら敵機画像をmeteoiにする処理を追加 ③タイマーイベント処理:敵機と自弾が衝突したら敵機画像をmeteoiにする処理を追加 ④タイマーイベント処理:敵機と爆散円の中央の距離が(爆散円の半径+敵機幅)未満なら衝突とする。 そして、10点加算、敵機画像を差し替えて落下を開始する。 ※爆散円は存在中の敵弾の数だけチェックする必要がある
提出:演習41または42(未完成でも可)
burn.gif 60×60
・これをテクスチャにした太さ15の円を用い、直径200まで拡大したら消そう
・爆散中の敵弾は動かず、衝突判定もしない
meteo3.gif
・スコアの上昇に合わせて、敵機の出現間隔が短くなるようにしよう(10まで)