UFOを出現させる
UFOを出現させます。これは本家スクランブルの動画です。
UFOが出現する地形クラスを作成する
UFOが出現する地形は洞窟のような場所で下だけでなく上に天井のようなものがあります。また地上にミサイルも設置されていますが、発射されることはありません。
TopographyUFOクラスはTopographyクラスを継承しています。違いは地形をつくるためのフィールド変数が違うだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
public class TopographyUFO : Topography { string topographyUFO0 = "■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■"; string topographyUFO1 = "□□□□□□□▼▽□□□□□□□□□□□□□□□□□□▼▽□□□□□□□□□□□□□□□□"; string topographyUFO2 = "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□"; string topographyUFO3 = "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□"; string topographyUFO4 = "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□"; string topographyUFO5 = "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□"; string topographyUFO6 = "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□"; string topographyUFO7 = "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□"; string topographyUFO8 = "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□"; string topographyUFO9 = "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□"; string topographyUFO10 = "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□"; string topographyUFO11 = "□□□□□□□▲△□□□□□□□□□□□□□□□□□□▲△□□□□□□□□□□□□□□□□"; string topographyUFO12 = "□□□□□□▲■■△□□□□□□□□□□□□□□□□▲■■△□□□□□□□□□□□□□□□"; string topographyUFO13 = "□□□□□▲■■■■△□M□MM□MM□MMM□□▲■■■■△□M□M□M□M□M□MMM"; string topographyUFO14 = "■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■"; public TopographyUFO(Form1 form) { MainForm = form; } protected override List<string> GetListTopography() { List<string> vs = new List<string>(); vs.Add(topographyUFO0); vs.Add(topographyUFO1); vs.Add(topographyUFO2); vs.Add(topographyUFO3); vs.Add(topographyUFO4); vs.Add(topographyUFO5); vs.Add(topographyUFO6); vs.Add(topographyUFO7); vs.Add(topographyUFO8); vs.Add(topographyUFO9); vs.Add(topographyUFO10); vs.Add(topographyUFO11); vs.Add(topographyUFO12); vs.Add(topographyUFO13); vs.Add(topographyUFO14); return vs; } } |
Form1クラスではTopographyUFOクラスのインスタンスを生成してUFO出現時の地形を描画できるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public partial class Form1 : Form { Topography Topography = null; public Form1() { InitializeComponent(); // TopographyクラスではなくTopographyUFOクラスのインスタンスを生成する //Topography = new Topography(this); Topography = new TopographyUFO(this); InitPanelEx(); InitTimer(); } } |
UFOクラスを作成する
次にUFOを表示したり移動させるためのUFOクラスを作成します。
プロパティ
Bitmap
UFOのビットマップです。
X
Y
UFOが存在する場所の座標です。
Line
UFOの初期位置(地形を構成するブロック単位で指定)です。
HeightMax
HeightMin
UFOが存在できるY座標の最大値と最小値です。
DeadRectangle
UFOの当たり判定のために必要な矩形を返します。
Tick
UFOが生成されて以降のTickイベントの回数です。
IsDead
UFOが撃墜されたかどうかを示します。
Move()
Draw(Graphics g)
UFOは同じ場所を上下に移動します。自機が移動することでだんだん自機の方向に近づいてきます。
コンストラクタの第二引数はUFOが存在する地形の場所です。引数のheightMaxとheightMinは上下に移動するときの上限と下限です。ここでHeightMax、HeightMin、Lineプロパティの値が決まります。乱数をつかってTickプロパティの値も決まります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class UFO { Form1 MainForm = null; static Random Random = new Random(DateTime.Now.Millisecond); public UFO(Form1 form1, int line, int heightMax, int heightMin) { MainForm = form1; Line = line; HeightMax = heightMax * Topography.BlockSize.Height; HeightMin = heightMin * Topography.BlockSize.Height; Tick = Random.Next(100); } public UFO() { } } |
UFOの動きは三角関数を利用しています。Sin、Cosの引数はUFOオブジェクトが生成されてから経過したTickイベントの回数を加工して使っています。またUFOのY座標が同じにならないように乱数をつかって初期値を分散させています(コンストラクタを参照)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class UFO { public void Move() { Tick++; double rad = (double)Tick / 6; double a = (HeightMax - HeightMin)/2d; int center = (HeightMax + HeightMin) / 2; X = Topography.BlockSize.Height * Line + (int)(Math.Cos(rad*2) * 4); Y = (int)(a * Math.Sin(rad)) + center; } public void Draw(Graphics g) { if(!IsDead) { int displacement = MainForm.Jiki.Displacement; g.DrawImage(Bitmap, new Point(X - displacement, Y)); } } } |
ミサイル同様、スクロールされて左側から外に出たUFOはIsDeadプロパティをtrueにして描画対象から外します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class UFO { bool isDead = false; public bool IsDead { get { if(isDead) return true; int displacement = MainForm.Jiki.Displacement; if((X - displacement < -10)) isDead = true; return isDead; } set { isDead = value; } } } |
それ以外のプロパティはときに説明することはありません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
public class UFO { public static Bitmap Bitmap { get { return Scramble.Properties.Resources.ufo; } } public int X { get; set; } public int Y { get; set; } public int Line { get; set; } public int HeightMax { get; set; } public int HeightMin { get; set; } public Size Size { get { return new Size(Bitmap.Width, Bitmap.Height); } } public Rectangle DeadRectangle { get { int displacement = MainForm.Jiki.Displacement; return new Rectangle(new Point(X - displacement, Y), Size); } } public int Tick { get; set; } = 0; } |
ゲームが始まったらミサイルの初期化と同時にUFOの初期化もおこないます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public partial class Form1 : Form { private void GameStartMenuItem_Click(object sender, EventArgs e) { JikiInit(); InitMissiles(); InitUFOs(); Timer.Start(); } void InitUFOs() { uFOs.Clear(); uFOs.Add(new UFO(this, 12, 1, 12)); uFOs.Add(new UFO(this, 16, 1, 12)); uFOs.Add(new UFO(this, 20, 1, 12)); uFOs.Add(new UFO(this, 24, 1, 12)); uFOs.Add(new UFO(this, 28, 3, 10)); uFOs.Add(new UFO(this, 32, 1, 12)); uFOs.Add(new UFO(this, 36, 1, 12)); uFOs.Add(new UFO(this, 38, 1, 12)); uFOs.Add(new UFO(this, 40, 1, 12)); uFOs.Add(new UFO(this, 42, 1, 12)); } } |
Tickイベントが発生したらUFOを動かし、自機から発射された弾丸との当たり判定をおこないます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
public partial class Form1 : Form { private void Timer_Tick(object sender, EventArgs e) { TimerTick_JikiMove(); // UFO出現時はミサイルは飛ばない //FlyingMissilesMove(); UfosMove(); panelEx1.Invalidate(); // 弾丸を前へ移動させる(前述) bulletList = bulletList.Select(x => new Point(x.X + 10, x.Y)).ToList(); // 自機が発射した弾丸との当たり判定をして命中した弾丸は取り除く RemoveBalletsHitTopography(); RemoveBalletsOutOfField(); RemoveBalletsHitMissile(); // 今回はこれを追加 RemoveBalletsHitUfo(); foreach(Bomb bomb in bombList) bomb.Fall(); // 自機から投下された爆弾との当たり判定をして命中した爆弾は取り除く RemoveBombsHitTopography(); RemoveBombsOutOfField(); RemoveBombsHitMissile(); // 今回はこれを追加 RemoveBombsHitUFO(); CheckIsDead(); } public event EventHandler HitUFO; void RemoveBalletsHitUfo() { foreach(UFO ufo in uFOs) { if(ufo.IsDead) continue; int hitIndex = -1; foreach(Point pt in bulletList) { Rectangle rect = new Rectangle(pt, GetBulletSize()); if(rect.Bottom < ufo.DeadRectangle.Top || rect.Top > ufo.DeadRectangle.Bottom || rect.Right < ufo.DeadRectangle.Left || rect.Left > ufo.DeadRectangle.Right) { // 自機から放たれた弾丸は命中していない } else { ufo.IsDead = true; hitIndex = bulletList.IndexOf(pt); HitUFO?.Invoke(this, new EventArgs()); break; } } if(hitIndex != -1) bulletList.RemoveAt(hitIndex); } } void RemoveBombsHitUFO() { foreach(UFO ufo in uFOs) { if(ufo.IsDead) continue; foreach(Bomb bomb in bombList) { Rectangle rect = bomb.DeadRectangle; if(rect.Bottom < ufo.DeadRectangle.Top || rect.Top > ufo.DeadRectangle.Bottom || rect.Right < ufo.DeadRectangle.Left || rect.Left > ufo.DeadRectangle.Right) { // 自機から投下された爆弾は命中していない } else { ufo.IsDead = true; bomb.IsDead = true; HitMissile?.Invoke(this, new EventArgs()); break; } } } bombList = bombList.Where(x => !x.IsDead).ToList(); } } |
自機とUFOの当たり判定をするメソッドを示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public partial class Form1 : Form { void CheckIsDead() { if(isMuteki) return; bool isDead = Jiki.IsDead(Topography.GetRectangles()); if(!isDead) { Rectangle[] rectangles = Missiles.Where(x => !x.IsDead).Select(x => x.DeadRectangle).ToArray(); isDead = Jiki.IsDead(rectangles); } if(!isDead) { Rectangle[] rectangles = uFOs.Where(x => !x.IsDead).Select(x => x.DeadRectangle).ToArray(); isDead = Jiki.IsDead(rectangles); } if(isDead) { Timer.Stop(); MessageBox.Show("Miss"); } } } |
PanelEx1_Paintハンドラが呼び出されたら撃墜されていないUFOを描画します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public partial class Form1 : Form { private void PanelEx1_Paint(object sender, PaintEventArgs e) { Jiki.Show(e.Graphics); Topography.Show(e.Graphics); foreach(Missile missile in Missiles) missile.Draw(e.Graphics); foreach(UFO ufo in uFOs) ufo.Draw(e.Graphics); foreach(Point pt in bulletList) e.Graphics.FillRectangle(new SolidBrush(Color.White), new Rectangle(new Point(pt.X, pt.Y), GetBulletSize())); foreach(Bomb bomb in bombList) e.Graphics.FillRectangle(new SolidBrush(Color.Orange), new Rectangle(new Point(bomb.X, bomb.Y), bomb.Size)); } } |