前回はプレイヤーを生成して移動の処理を実装しました。また適当感が否めませんが敵の移動と描画の処理もおこないました。今回はプレイヤーに穴を掘らせます。そして穴落ちた敵を下の階に突き落として倒す処理も実装する予定です。
Contents
穴を掘る
このゲームはどうやって穴を掘ったら位置にモンスターをおびき寄せるかがポイントです。穴に落ちたら埋めてしまいます。するとモンスターは穴の下におちて死にます。
モンスターは穴に落ちてもそのままにしておくと穴のなかから這い上がってきてしまい、もっと強力なモンスターに変わってしまいます。するとモンスターを倒しにくくなるわけですが、倒したときの点数も増えるので上級者はわざとモンスターを穴に落としては昇格させ、最後に5段落ちで高得点を狙ったりします。
敵はモンスター、ボス、ドンがあり、ボスは垂直に並んだ穴を掘って2段階連続でおとさないと死にません。ドンにいたっては3つの垂直に並んだ穴に落とさないと倒すことができません。
穴を掘ることができる位置を限定する
では穴を掘ってみましょう。
穴はプレイヤーがいる場所の隣でプレイヤーが向いている方向に作られます。そこでまずプレイヤーがどちらを向いているのかを調べなければなりません。プレイヤーの方向と位置がわかれば穴を掘ります。
垂直に並んだ縦穴を掘る難易度を低下させるためにプレイヤーは移動の最小単位は4ピクセルですが、穴をほることができる場所は16ピクセル単位にします。また横に連続した位置には穴を掘ることはできない仕様にします。
穴を掘ったらその部分に穴が掘られていることが分かるようにマークをつけます。ステージの背景が黒なので、床の穴が掘られた部分を黒で覆ってしまえば穴が開いたかのように見えます。
でははじめましょう。
穴を掘ることができるのはプレイヤーが右または左を向いているときです。プレイヤーがどの方向を向いているかはGameManagerクラスのなかで調べることができます。
プレイヤーが実際に移動するときは移動方向をフィールド変数に格納しておきます。
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 |
public class GameManager { MoveDirect PlayerMoveDirect = MoveDirect.Left; public void MovePlayer() { if (IsMoveLeft && CanMoveLeft(PlayerX, PlayerY)) { PlayerX -= 4; VerticalCorrection(); // プレイヤーのXY座標のズレを補正(後述) PlayerMoveDirect = MoveDirect.Left; } if (IsMoveRight && CanMoveRight(PlayerX, PlayerY)) { PlayerX += 4; VerticalCorrection(); PlayerMoveDirect = MoveDirect.Right; } if (IsMoveUp && CanMoveUp(PlayerX, PlayerY)) { PlayerY -= 4; HorizontalCorrection(); PlayerMoveDirect = MoveDirect.Up; } if (IsMoveDown && CanMoveDown(PlayerX, PlayerY)) { HorizontalCorrection(); PlayerY += 4; PlayerMoveDirect = MoveDirect.Down; } FallIfInHole(); // 穴があったら飛び降りる処理をする(後述) } } |
以下はハシゴの上り下りをするときのプレイヤーのXY座標のズレを補正するための処理です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class GameManager { // プレイヤーのY座標を補正する void VerticalCorrection() { int a = PlayerY % (CharctorSize * 3); if (a <= 8) PlayerY -= a; else if (a != 0) PlayerY += CharctorSize * 3 - a; } // プレイヤーのX座標を補正する void HorizontalCorrection() { int a = PlayerX % (CharctorSize * 2); if (a <= 8) PlayerX -= a; else if (a != 0) PlayerX += CharctorSize * 2 - a; } } |
Holeクラス
穴を掘る場合はどこに穴を掘ったのかを記憶しておく必要があります。そこで穴を管理するためのHoleクラスを作成します。ゲームではひとつの穴を完成させるために3回掘る動作が必要です。
フィールド変数Depthは穴の深さです。1と2なら未完成の穴、3だとモンスターを落とすことができる完成された穴です。またフィールド変数Enemyは穴にはまってしまった敵オブジェクトです。
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 |
public class Hole { public int Depth = 1; public Rectangle Rectangle = new Rectangle(); Rectangle HoleRectangle1 = new Rectangle(); Rectangle HoleRectangle2 = new Rectangle(); Rectangle HoleRectangle3 = new Rectangle(); public Enemy Enemy = null; public Hole(int x, int y) { Rectangle = new Rectangle(x, y, GameManager.CharctorSize, GameManager.CharctorSize); HoleRectangle1 = new Rectangle(x, y + GameManager.CharctorSize, GameManager.CharctorSize, 7); HoleRectangle2 = new Rectangle(x, y + GameManager.CharctorSize, GameManager.CharctorSize, 14); HoleRectangle3 = new Rectangle(x, y + GameManager.CharctorSize, GameManager.CharctorSize, 32); } public Rectangle HoleRectangle { get { if (Depth == 1) return HoleRectangle1; else if (Depth == 2) return HoleRectangle2; else if (Depth >= 3) return HoleRectangle3; else return Rectangle.Empty; } } } |
穴を掘る場合は本当に穴を掘れる場所なのか確認してから穴をほります。縦に穴を並べて掘りやすくするためにX座標が16(というよりCharctorSizeの半分の整数)の倍数でなければならないという条件をつけます。それ以外の場所に穴を掘ろうとしたら近くのX座標が16の倍数になっている場所に移動して穴を掘ります。
ある数にもっとも近い16の倍数はその数を16で割り、商を四捨五入して16を掛けることで求められます。それからCharctorSizeは他の整数に変更できますが、その場合4の倍数でなければややこしいことになります。
1 2 3 4 5 6 7 8 9 10 |
public class GameManager { int GetXPositionToDig() { int charHalf = CharctorSize / 2; double d = 1d * PlayerX / charHalf; int i = (int)Math.Round(d); return i * charHalf; } } |
穴を掘ることができるかどうかを判定する
プレイヤーが現在いる位置に穴を掘ることができるかどうかを判定する処理を示します。穴を掘ることができない場合をコメントで示しています。これ以外なら穴を掘ることができます。
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 |
public class GameManager { public bool CanDigHole() { // 1階に穴は掘れない if (PlayerY >= GetYFromNumberOfFloors(1)) return false; // ハシゴを上り下りしているときは穴は掘れない if (PlayerMoveDirect == MoveDirect.Up || PlayerMoveDirect == MoveDirect.Down) return false; // どこにでも穴は掘れない。GetXPositionToDigメソッドで制限を加える int xPosition = GetXPositionToDig(); // プレイヤーの向きで穴のX座標は異なる int holeX = 0; if (PlayerMoveDirect == MoveDirect.Left) holeX = xPosition - CharctorSize; if (PlayerMoveDirect == MoveDirect.Right) holeX = xPosition + CharctorSize; // フロアの端には穴は掘れない if (holeX < 0 || holeX > 14 * CharctorSize) return false; // ハシゴがある位置には穴は掘れない if (LadderPositions.Any(x => ( x.X- CharctorSize < holeX && holeX < x.X + CharctorSize) && (x.Y == PlayerY || x.Y - CharctorSize*3 == PlayerY) )) return false; // すでに掘られている未完成の穴であれば掘ることができる // ただし敵がはまっている穴を掘ることはできない Hole hole = Holes.FirstOrDefault(x => x.Rectangle.X == holeX && x.Rectangle.Y == PlayerY); if(hole != null && hole.Depth < 3 && !Enemies.Any(x => x.Hole == hole)) return true; // 連続して繋がっている穴は掘れない 16ピクセルより(実質20ピクセル以上)開けること if (Holes.Any(x => ( x.Rectangle.Y == PlayerY && x.Rectangle.Right+16 > holeX && holeX + CharctorSize >= x.Rectangle.Left-16) )) return false; // 上記以外のケースであれば穴を掘ることができる return true; } } |
実際に穴を掘る
穴を掘ることができるのであれば、その位置に穴を掘ります。プレイヤーを穴を掘るために最適化されたX座標に移動させ穴を掘らせます。
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 |
public class GameManager { public static List<Hole> Holes = new List<Hole>(); public void DigHole() { // 穴を掘ることができるのできる場合 if (CanDigHole()) { // プレイヤーを穴を掘るために最適化されたX座標に移動 PlayerX = GetXPositionToDig(); // 穴のX座標を求める int holeX = 0; if (PlayerMoveDirect == MoveDirect.Left) holeX = PlayerX - CharctorSize; if (PlayerMoveDirect == MoveDirect.Right) holeX = PlayerX + CharctorSize; // そこにあるのは未完成の穴ではないのか? Hole hole = Holes.FirstOrDefault(x => x.Rectangle.X == holeX && x.Rectangle.Y == PlayerY); if (hole != null) { // 未完成の穴なら深さ3まで掘ることができる hole.Depth++; if (hole.Depth > 3) hole.Depth = 3; } else { // 新しく掘られた穴ならリストにいれる Holes.Add(new Hole(holeX, PlayerY)); } return; } } } |
穴を描画するための処理を示します。穴のリストのなかに埋められて深さ0になっているものがあるかもしれないので、これをリストから取り除いています。
hole.HoleRectangleは穴の深さによって異なるサイズの矩形を返します。これをフォーム上に描画するための座標に変換して描画処理をおこないます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class GameManager { public void DrawHoles(Graphics graphics) { Holes = Holes.Where(x => x.Depth > 0).ToList(); foreach (Hole hole in Holes) { Rectangle rect = GetRectangleForShow(hole.HoleRectangle); graphics.FillRectangle(Brushes.Black, rect); } } public Rectangle GetRectangleForShow(Rectangle rect) { Point pt = GetPointForShow(rect.X, rect.Y); return new Rectangle(pt, new Size(rect.Width, rect.Height)); } } |
フォームに実際に描画するときの処理を示します。先に穴を描画してそのあと敵を描画します。そうしないと穴にはまった敵がうまく描画されません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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); base.OnPaint(e); } } |
敵が穴にはまったときの処理
次に敵が穴にはまったときの処理を考えます。
敵がすでにはまっている穴があったら引き返します。また敵は穴にはまったあとなにもしないと穴から這い出して穴を埋めてしまいます。この部分はTimerを使って処理をします。穴の深さによってTimer.Intervalをセットして、Timer.Tickイベントが発生したらタイマーをとめて穴から這い出す処理をおこないます。
敵が穴にはまったらその深さによってY座標を大きくします。そのとき元のY座標を記憶しておきます。それから自分がはまった穴とその深さを記憶しておくためのプロパティも用意しておきます。
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 |
public class Enemy { Timer Timer = new Timer(); public static int TimeInTheHole1 = 10 * 1000; public static int TimeInTheHole2 = 2 * 1000; int X = 0; int Y = 0; public Enemy(int x, int y) { X = x; Y = y; Timer.Tick += GetoutHole; // 一定時間なにもしないと敵は穴から這い出す } // 敵がはまった穴 public Hole Hole { get; private set; } // 敵がはまった穴の深さ public int HoleDepth { get; private set; } } |
敵が穴にはまったときの処理
敵が穴にはまったときの処理を示します。まず自分がいる場所に穴があるかどうか調べます。穴があったらハマリです。穴の深さに応じてY座標を変えるとともにTimer.Intervalをセットしてタイマーをスタートさせます。MoveDirectをMoveDirect.Stopにして動かないようにします。
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 |
public class Enemy { int OldY = 0; public void EnterHoleIfInHole() { // 敵がいる位置は穴がある位置か? Hole hole = GameManager.Holes.FirstOrDefault(x => x.Rectangle.X == this.X && x.Rectangle.Y == this.Y); if (hole != null) { // 穴から出るときのためにY座標を記憶しておく OldY = this.Y; if (hole.Depth == 1) { this.Y += 7; Timer.Interval = TimeInTheHole2; } if (hole.Depth == 2) { this.Y += 14; Timer.Interval = TimeInTheHole2; } if (hole.Depth == 3) { this.Y += GameManager.CharctorSize; Timer.Interval = TimeInTheHole1; } this.MoveDirect = MoveDirect.Stop; Hole = hole; Hole.Enemy = this; HoleDepth = hole.Depth; Timer.Start(); } } } |
敵が穴から這い出してくる処理
敵が穴から這い出してくる処理です。まずタイマーを止めます。そして3回にわけて少しずつ上昇させ、最終的にハマるときに記憶したY座標をセットします。最後に乱数で右または左に移動させます。穴は埋めてしまうのでDepthに0をセットしてフィールド変数Depthにはnullをセットします。
それから穴から這い出してきた敵はモンスターはボスに、ボスはドンに昇格し、倒しにくくなります。逆にいうと倒したときの点数が大きいのでわざとドンに昇格させて5段落としで高得点ゲットという方法もあるのですが・・・。Lifeを追加する処理を書き足せば実装できますね。
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 |
public class Enemy { async void GetoutHole(object sender, EventArgs e) { Timer.Stop(); if (Hole != null) { for (int i = 0; i < Hole.Depth; i++) { await Task.Delay(500); this.Y -= GameManager.CharctorSize / 3; } Hole.Depth = 0; Hole = null; this.Y = OldY; } Random random = new Random(); if(random.Next(2) == 0) MoveDirect = MoveDirect.Left; else MoveDirect = MoveDirect.Right; // モンスターはボスに、ボスはドンに昇格する if (HoleDepth == 3 && (Life == 1 || Life == 2)) Life++; HoleDepth = 0; } } |
穴の前で引き返す処理
別の敵がすでにハマっている穴があるときは敵はその穴にハマることも超えることもできず、その場でUターンをします。そのための処理を示します。
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 |
public class Enemy { bool TurnBackIfEnemyFallingHole() { // その階の敵がハマっている穴のリストを取得する List<Hole> holes = GameManager.Holes.Where(x => x.Enemy != null && x.Rectangle.Y == this.Y).ToList(); // 現在位置はすでに敵がハマっている穴の縁よりも内側ではないのか? Hole hole = holes.FirstOrDefault(x => x.Rectangle.X - GameManager.CharctorSize < X && X < x.Rectangle.Right); if (hole != null) { Enemy enemy = hole.Enemy; if (MoveDirect == MoveDirect.Left) { // 穴の縁よりも右側4ピクセルに移動させて右方向へ移動させる this.X = hole.Rectangle.Right + 4; MoveDirect = MoveDirect.Right; } else if (MoveDirect == MoveDirect.Right) { // 穴の縁よりも左側4ピクセルに移動させて左方向へ移動させる this.X = hole.Rectangle.Left - GameManager.CharctorSize - 4; MoveDirect = MoveDirect.Left; } return true; } return false; } } |
モンスターの動きに関するメソッドを示します。上で作成したメソッドを使うとこんな感じになります。
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 |
public class Enemy { public int Life = 1; // 1段だけ落としても死なない敵もいる public void Move() { // いまは同じフロアで敵を左右に移動させるだけ(要改善) if (MoveDirect == MoveDirect.Left) MoveLeft(); if (MoveDirect == MoveDirect.Right) MoveRight(); if (X > 14 * GameManager.CharctorSize) MoveDirect = MoveDirect.Left; if (X < 0) MoveDirect = MoveDirect.Right; // 敵がすでにはまっている穴があったら引き返す if (TurnBackIfEnemyFallingHole()) return; // 敵がいる位置に穴があったらはまる EnterHoleIfInHole(); } } |
敵を穴から落として倒す処理
次に穴をうけてモンスターを倒す処理を考えます。
穴をうめる処理
以下は穴をうめる処理です。まずプレイヤーのZ座標を引数にGetXPositionToDigメソッドを呼び出し、この座標に穴があるかどうかを調べます。穴があればプレイヤーをそこへ移動させます。埋める処理をすることで穴は浅くなります。
穴にはモンスターがいる場合といない場合があります。完成した穴のなかにモンスターがいた場合は深さ0になったときにモンスターを穴から突き落としたことになります。このときはEnemy.Fallメソッドが呼び出され、モンスターは落下します。その場合、モンスターが落ちた穴は縦に何個並べられていたかとモンスターのLifeを比較してモンスターを倒せたかどうかが決まります。
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 |
public class GameManager { WMPLib.WindowsMediaPlayer PlayerFall = new WMPLib.WindowsMediaPlayer(); public async void FillHole() { int xPosition = GetXPositionToDig(); int holeX = 0; if (PlayerMoveDirect == MoveDirect.Left) holeX = xPosition - CharctorSize; if (PlayerMoveDirect == MoveDirect.Right) holeX = xPosition + CharctorSize; // 埋めるべき穴がみつかった場合 Hole hole = Holes.FirstOrDefault(x => x.Rectangle.X == holeX && x.Rectangle.Y == PlayerY); if (hole != null) { PlayerX = xPosition; // 埋めようとしている穴に敵はいるか? Enemy enemy = Enemies.FirstOrDefault(x => x.Hole == hole); // 未完成の穴に敵がいる場合は埋めることはできない if (enemy != null && enemy.HoleDepth < 3) return; // 埋める hole.Depth--; if (hole.Depth == 0) { Holes.Remove(hole); // 埋めた穴に敵がいた場合は落とす if (enemy != null) { // 効果音を鳴らす PlayerFall.URL = System.Windows.Forms.Application.StartupPath + "\\fall.mp3"; await enemy.Fall(); // 敵を倒した場合 if (enemy.Life <= 0) { // 敵を倒したら効果音を鳴らす PlayerFall.URL = System.Windows.Forms.Application.StartupPath + "\\get.mp3"; // 倒された敵はリストから取り除く Enemies = Enemies.Where(x => x.Life > 0).ToList(); OnEnemyDead(enemy); if (Enemies.Count == 0) await OnCleared(); // 後述 } } } } } void OnEnemyDead(Enemy enemy) { // 後回し } } |
ゲームクリア時の処理
すべての敵を倒したら新しく敵をつくってゲームを継続します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class GameManager { async Task OnCleared() { await Task.Delay(3000); InitFloors(); } void InitFloors() { Enemies.Add(new Enemy(CharctorSize * 3, GetYFromNumberOfFloors(6))); Enemies.Add(new Enemy(CharctorSize * 6, GetYFromNumberOfFloors(5))); Enemies.Add(new Enemy(CharctorSize * 9, GetYFromNumberOfFloors(4))); Enemies.Add(new Enemy(CharctorSize * 12, GetYFromNumberOfFloors(3))); Enemies.Add(new Enemy(CharctorSize * 15, GetYFromNumberOfFloors(2))); PlayerX = 7 * CharctorSize; PlayerY = 15 * CharctorSize; Holes.Clear(); } } |
モンスターが落下する処理
これはモンスターが落下する処理です。
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 |
public class Enemy { // 敵が死んだ座標(加点表示で必要) public Point DeadPoint { private set; get; } // 加算される点数 public int AddScore { private set; get; } public async Task Fall() { Timer.Stop(); bool first = true; List<Hole> holesPassedThrough = new List<Hole>(); // 他の敵を巻き込んだ場合 bool isDead = false; // 追加される点数 int addScore = 0; while (true) { int down = first ? 2 : 3; int nextY = this.Y + GameManager.CharctorSize * down; for (int i = 0; i < 2 * 8; i++) { await Task.Delay(20); this.Y += GameManager.CharctorSize / 8; } this.Y = nextY; // 穴は垂直に並んでいないか? Hole hole = GameManager.Holes.FirstOrDefault(x => x.Rectangle.X == this.X && x.Rectangle.Y == this.Y); if (hole == null) break; holesPassedThrough.Add(hole); first = false; if (hole.Enemy != null) { // 通過した穴にも敵がいた場合、巻き込みで同時に倒すことができる isDead = true; addScore += GetBaseScore(hole.Enemy); hole.Enemy.Life = 0; GameManager.Enemies.Remove(hole.Enemy); } } HoleDepth = 0; foreach (Hole hole1 in holesPassedThrough) hole1.Depth = 0; // モンスターのLifeと同じか高いところから落とした場合のみ倒すことができる // ただし他の敵を巻き込んだ場合も倒すことができる if (isDead || Life <= holesPassedThrough.Count + 1) { int downCount = holesPassedThrough.Count + 1; addScore += GetAddScore(downCount); Life = 0; GameManager.Enemies.Remove(this); DeadPoint = new Point(this.X, this.Y); // 落下点に敵がいた場合、巻き込みで同時に倒すことができる int width = GameManager.CharctorSize; var enemies = GameManager.Enemies.Where( x => x.Y == DeadPoint.Y && DeadPoint.X- width < x.X && x.X < DeadPoint.X + width ).ToList(); foreach (Enemy enemy in enemies) { addScore += GetBaseScore(enemy); GameManager.Enemies.Remove(enemy); } AddScore = addScore; } else { Hole = null; Random random = new Random(); if (random.Next(2) == 0) this.MoveDirect = MoveDirect.Left; else this.MoveDirect = MoveDirect.Right; } } } |
敵を倒したときに入る点数を計算する処理を示します。いわゆるザコ敵は100点、ボスは300点、ドンは800点です。高いところから落とすとさらに点数が増えます。
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 |
public class Enemy { int GetBaseScore(Enemy enemy) { if (Life == 1) return 100; if (Life == 2) return 300; if (Life == 3) return 800; return 0; } public int GetAddScore(int downCount) { if (Life == 1) { if (downCount == 1) return 100; if (downCount == 2) return 200; if (downCount == 3) return 300; if (downCount == 4) return 400; if (downCount == 5) return 800; } if (Life == 2) { if (downCount == 1 || downCount == 2) return 300; if (downCount == 3) return 400; if (downCount == 4) return 600; if (downCount == 5) return 1200; } if (Life == 3) { if (downCount == 1 || downCount == 2 || downCount == 3) return 800; if (downCount == 4) return 1000; if (downCount == 5) return 2000; } return 0; } } |
穴の存在によるプレイヤーの動作の制約
穴を掘ることでプレイヤーの動作にも制約がでてきます。
プレイヤーは完成した穴から飛び降りて下の階に移動できる
プレイヤーは完成した穴であればそこから飛び降りて下の階に移動することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class GameManager { public async void FallIfInHole() { Hole hole = GameManager.Holes.FirstOrDefault(x => x.Rectangle.X == PlayerX && x.Rectangle.Y == PlayerY); if (hole != null) { int nextPlayerY = PlayerY + CharctorSize * 3; for (int i = 0; i < 6; i++) { PlayerY += CharctorSize / 2; await Task.Delay(50); } PlayerY = nextPlayerY; } } } |
プレイヤーが移動できない場所
プレイヤーは水平移動をするときに未完成の穴や敵がハマっている穴のなかに移動することはできません。そのためGameManager.CanMoveLeftメソッドとGameManager.CanMoveRightメソッドには追加の処理が必要です。
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 |
public class GameManager { public static bool CanMoveLeft(int x, int y) { // フロアの端より向こうには移動できない if (x <= 0 || x > 14 * CharctorSize) return false; // 未完成の穴やモンスターが入っている穴には入れない int playerX = PlayerX; Hole holePlayerFall = GameManager.Holes.FirstOrDefault(hole => hole.Rectangle.Right == playerX && hole.Rectangle.Y == PlayerY); if (holePlayerFall != null) { if (holePlayerFall.Depth < 3) return false; if(Enemies.Any(enemy => enemy.Hole == holePlayerFall)) return false; } // ハシゴの上り下りをしているときは水平移動できない for (int i = 1; i <= 6; i++) { int y1 = GetYFromNumberOfFloors(i); if (y1 - 8 <= y && y <= y1 + 8) return true; } return false; } public static bool CanMoveRight(int x, int y) { // フロアの端より向こうには移動できない if (x < 0 || x >= 14 * CharctorSize) return false; // 未完成の穴やモンスターが入っている穴には入れない int playerX = PlayerX + CharctorSize; Hole holePlayerFall = GameManager.Holes.FirstOrDefault(hole => hole.Rectangle.X == playerX && hole.Rectangle.Y == PlayerY); if (holePlayerFall != null) { if (holePlayerFall.Depth < 3) return false; if (Enemies.Any(enemy => enemy.Hole == holePlayerFall)) return false; } // ハシゴの上り下りをしているときは水平移動できない for (int i = 1; i <= 6; i++) { int y1 = GetYFromNumberOfFloors(i); if (y1 - 8 <= y && y <= y1 + 8) return true; } return false; } } |
あとはこれらをForm1クラスから呼び出せるようにするだけです。
Form1クラスではZキーを押すとプレイヤーが向いている方向の足下に穴をほり、となりのXキーをおすと穴を埋めます。GameManager.DigHole()とGameManager.FillHole()を呼べるようにしました。
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 partial class Form1 : Form { protected override void OnKeyDown(KeyEventArgs e) { if (e.KeyCode == Keys.Left) GameManager.IsMoveLeft = true; if (e.KeyCode == Keys.Right) GameManager.IsMoveRight = true; if (e.KeyCode == Keys.Up) GameManager.IsMoveUp = true; if (e.KeyCode == Keys.Down) GameManager.IsMoveDown = true; // 穴を掘る if (e.KeyCode == Keys.Z) GameManager.DigHole(); // 穴を埋める if (e.KeyCode == Keys.X) GameManager.FillHole(); base.OnKeyDown(e); } } |