前回まででゲームオーバーまでの処理をプログラミングすることができました。今回はこれまで作ったゲームをゲームらしく仕上げます。
キャラクターイメージ
まずキャラクターイメージがイマイチなので改善します。敵キャラをうまく作成することができればいいのですが、デザイン的なセンスがないのでこれを使います。
Enemyクラス内に静的メンバーを3つ追加します。そして自作メソッドGetBitmapsを追加します。GetBitmapsメソッドは画像ファイルから必要な部分を切り出してリストに格納しているだけです。
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 |
public class Enemy { static List<Bitmap> Bitmaps1 = new List<Bitmap>(); static List<Bitmap> Bitmaps2 = new List<Bitmap>(); static List<Bitmap> Bitmaps3 = new List<Bitmap>(); void GetBitmaps() { Bitmap sourceBitmap = Properties.Resources.sprite2; List<Rectangle> rectangle1 = new List<Rectangle>(); rectangle1.Add(new Rectangle(4, 63, 25, 28)); rectangle1.Add(new Rectangle(36, 63, 25, 28)); rectangle1.Add(new Rectangle(68, 63, 25, 28)); rectangle1.Add(new Rectangle(100, 63, 25, 28)); rectangle1.Add(new Rectangle(132, 63, 25, 28)); List<Rectangle> rectangle2 = new List<Rectangle>(); rectangle2.Add(new Rectangle(4, 94, 25, 28)); rectangle2.Add(new Rectangle(36, 94, 25, 28)); rectangle2.Add(new Rectangle(68, 94, 25, 28)); rectangle2.Add(new Rectangle(100, 94, 25, 28)); rectangle2.Add(new Rectangle(132, 94, 25, 28)); List<Rectangle> rectangle3 = new List<Rectangle>(); rectangle3.Add(new Rectangle(4, 125, 25, 28)); rectangle3.Add(new Rectangle(36, 125, 25, 28)); rectangle3.Add(new Rectangle(68, 125, 25, 28)); rectangle3.Add(new Rectangle(100, 125, 25, 28)); rectangle3.Add(new Rectangle(132, 125, 25, 28)); int size = GameManager.CharctorSize; foreach (Rectangle rect in rectangle1) { Bitmap bitmap = new Bitmap(size, size); Graphics g = Graphics.FromImage(bitmap); g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; g.DrawImage(sourceBitmap, new Rectangle(0, 0, size, size), rect, GraphicsUnit.Pixel); g.Dispose(); Bitmaps1.Add(bitmap); } foreach (Rectangle rect in rectangle2) { Bitmap bitmap = new Bitmap(size, size); Graphics g = Graphics.FromImage(bitmap); g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; g.DrawImage(sourceBitmap, new Rectangle(0, 0, size, size), rect, GraphicsUnit.Pixel); g.Dispose(); Bitmaps2.Add(bitmap); } foreach (Rectangle rect in rectangle3) { Bitmap bitmap = new Bitmap(size, size); Graphics g = Graphics.FromImage(bitmap); g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; g.DrawImage(sourceBitmap, new Rectangle(0, 0, size, size), rect, GraphicsUnit.Pixel); g.Dispose(); Bitmaps3.Add(bitmap); } } } |
GetBitmapsメソッドはこれをコンストラクタ内で呼びます。ただし呼ぶのは最初のインスタンスを生成するときだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public class Enemy { public Enemy(int x, int y) { if (Bitmaps1.Count == 0) GetBitmaps(); // ここから下は前回までと同じ X = x; Y = y; InitX = x; InitY = y; if (Random.Next(2) == 0) MoveDirect = MoveDirect.Left; else MoveDirect = MoveDirect.Right; Timer.Tick += GetoutHole; LadderPositions = GameManager.GetLadderPositions(); LadderUpperPositions = GetLadderUpperPositions(LadderPositions); } } |
敵を描画する処理を示します。
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 class Enemy { int DrawCount = 0; public void Draw(Graphics graphics) { Point pt = GameManager.GetPointForShow(X, Y); List<Bitmap> bitmaps = Bitmaps1; if (Life == 2) bitmaps = Bitmaps2; if (Life == 3) bitmaps = Bitmaps3; DrawCount++; int surplus = DrawCount % 16; if (surplus < 4) graphics.DrawImage(bitmaps[0], pt); else if(surplus < 8) graphics.DrawImage(bitmaps[1], pt); else if (surplus < 12) graphics.DrawImage(bitmaps[2], pt); else if (surplus < 16) graphics.DrawImage(bitmaps[3], pt); } } |
次にプレイヤーですが、Unityちゃんを使います。
ユニティちゃん 2DデータのなかにあるPNGファイルを使います。
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 |
public class GameManager { List<Bitmap> PlayerRightImages = new List<Bitmap>(); List<Bitmap> PlayerLeftImages = new List<Bitmap>(); public GameManager(Form1 form) { Form = form; LadderPositions = GetLadderPositions(); GetPlayerImages(); // 最初の敵を適当につくる Enemies.Add(new Enemy(CharctorSize * 3, GetYFromNumberOfFloors(6))); Enemies.Add(new Enemy(CharctorSize * 6, GetYFromNumberOfFloors(5))); Enemies.Add(new Enemy(CharctorSize * 9, GetYFromNumberOfFloors(4))); } void GetPlayerImages() { Bitmap sourceImage = Properties.Resources.UnityChan; List<Rectangle> rectangle1 = new List<Rectangle>(); rectangle1.Add(new Rectangle(6, 79, 42, 40)); rectangle1.Add(new Rectangle(68, 79, 40, 40)); rectangle1.Add(new Rectangle(133, 79, 40, 40)); rectangle1.Add(new Rectangle(200, 79, 40, 40)); rectangle1.Add(new Rectangle(6, 143, 42, 40)); rectangle1.Add(new Rectangle(68, 143, 40, 40)); rectangle1.Add(new Rectangle(133, 143, 40, 40)); rectangle1.Add(new Rectangle(200, 143, 40, 40)); foreach (Rectangle rect in rectangle1) { Bitmap bitmap = new Bitmap(CharctorSize, CharctorSize); Graphics g = Graphics.FromImage(bitmap); g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; g.DrawImage(sourceImage, new Rectangle(0, 0, CharctorSize, CharctorSize), rect, GraphicsUnit.Pixel); g.Dispose(); PlayerRightImages.Add(bitmap); } foreach (Bitmap bitmap in PlayerRightImages) { Bitmap bmp = new Bitmap(bitmap); bmp.RotateFlip(RotateFlipType.RotateNoneFlipX); PlayerLeftImages.Add(bmp); } } } |
プレイヤーを描画する処理を示します。
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 |
public class GameManager { int DrawCount = 0; public void DrawPlayer(Graphics graphics) { Point point = GetPointForShow(PlayerX, PlayerY); // 敵に捕まったときはアニメーションを停止する if (!IsGameStop) DrawCount++; Bitmap bitmap = null; if (PlayerMoveDirect == MoveDirect.Right) { bitmap = PlayerRightImages[0]; int Surplus = DrawCount % 16; if (Surplus < 4) bitmap = PlayerRightImages[0]; else if (Surplus < 8) bitmap = PlayerRightImages[1]; else if (Surplus < 12) bitmap = PlayerRightImages[2]; else if (Surplus < 16) bitmap = PlayerRightImages[3]; } else { bitmap = PlayerLeftImages[0]; int Surplus = DrawCount % 16; if (Surplus < 4) bitmap = PlayerLeftImages[0]; else if (Surplus < 8) bitmap = PlayerLeftImages[1]; else if (Surplus < 12) bitmap = PlayerLeftImages[2]; else if (Surplus < 16) bitmap = PlayerLeftImages[3]; } // やや大きめに描画する graphics.DrawImage(bitmap, new Rectangle(point.X - 5, point.Y - 10, CharctorSize + 10, CharctorSize + 10)); } } |
敵を倒したときの点数表示
敵を倒したときに加点される点数を表示させます。
加点される点数は敵が落とされて死んだ場所の近くに表示されます。AdditionalPointsクラスは表示される点数と座標を管理するクラスです。加点表示は1秒間おこなわれます。そのためDrawメソッドのなかでLifeを減算しています。0になれば表示の必要なしということになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class AdditionalPoints { Point Point = Point.Empty; String str = ""; static Font Font = new Font("MS ゴシック", 24); public int Life = 60; public AdditionalPoints(int score, Point point) { Point = point; str = score.ToString(); } public void Draw(Graphics graphics) { if (Life > 0) { graphics.DrawString(str, Font, Brushes.White, Point); Life--; } } } |
OnEnemyDeadメソッドのなかで点数を計算してAdditionalPointsオブジェクトをリストのなかに格納します。そしてDrawAdditionalPointsメソッドで加点表示をしています。またスコア表示用のフィールド変数の値も変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class GameManager { public static List<AdditionalPoints> AdditionalPointsList = new List<AdditionalPoints>(); int Score = 0; void OnEnemyDead(Enemy enemy) { AdditionalPointsList.Add(new AdditionalPoints(100, enemy.DeadPoint)); Score += enemy.AddScore; } public void DrawAdditionalPoints(Graphics graphics) { AdditionalPointsList = AdditionalPointsList.Where(x => x.Life > 0).ToList(); foreach (AdditionalPoints ad in AdditionalPointsList) { ad.Draw(graphics); } } } |
これはスコアと残機数を表示させるメソッドです。
1 2 3 4 5 6 7 8 9 |
class GameManager { Font ScoreFont = new Font("MS ゴシック", 16); public void DrawScoreRest(Graphics graphics) { string str = String.Format("Score {0:00000} 残 {1}", Score, Rest); graphics.DrawString(str, ScoreFont, Brushes.White, new Point(10, 10)); } } |
ゲームオーバー表示
ゲームオーバーになったらゲームオーバー表示をします。
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 |
class GameManager { string GameOverString = "Game Over"; Font GameOverFont = new Font("MS ゴシック", 24, FontStyle.Bold); Point GameOverPoint = Point.Empty; string RetryString = "Press S key to Retry"; Font RetryFont = new Font("MS ゴシック", 18); Point RetryPoint = Point.Empty; public void DrawStringIfGameOver(Graphics graphics) { if (Rest > 0) return; if (GameOverPoint == Point.Empty) { int y = GetYFromNumberOfFloors(5); Point pt = GetPointForShow(0, y); Size gameOverSize = System.Windows.Forms.TextRenderer.MeasureText(GameOverString, GameOverFont); int gameOverX = (this.Form.ClientSize.Width - gameOverSize.Width) / 2; int gameOverY = pt.Y + CharctorSize; GameOverPoint = new Point(gameOverX, gameOverY); Size retrySize = System.Windows.Forms.TextRenderer.MeasureText(RetryString, RetryFont); int retryX = (this.Form.ClientSize.Width - retrySize.Width) / 2; int retryY = gameOverY + gameOverSize.Height + 20; RetryPoint = new Point(retryX, retryY); } System.Windows.Forms.TextRenderer.DrawText(graphics, GameOverString, GameOverFont, GameOverPoint, Color.White); System.Windows.Forms.TextRenderer.DrawText(graphics, RetryString, RetryFont, RetryPoint, Color.White); } } |
ゲームを再開するメソッドをつくります。ゲームオーバーになっているとき以外はなにもしません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class GameManager { public void Retry() { if (Rest == 0) { Score = 0; Rest = 3; Enemies.Clear(); Holes.Clear(); InitFloors(); IsGameStop = false; } } } |
Form1クラスのOnPaintメソッドに描画関連メソッド呼び出しを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class Form1 : Form { protected override void OnPaint(PaintEventArgs e) { GameManager.DrawFloor(e.Graphics); GameManager.DrawLadders(e.Graphics); GameManager.DrawHoles(e.Graphics); GameManager.DrawPlayer(e.Graphics); GameManager.DrawEnemies(e.Graphics); GameManager.DrawAdditionalPoints(e.Graphics); GameManager.DrawScoreRest(e.Graphics); GameManager.DrawStringIfGameOver(e.Graphics); base.OnPaint(e); } } |