クラッシュローラーではローラーを使える範囲は限定されていますが、これをつかって敵を撃退することができます。またプレイヤーがローラーを使っているときは移動速度が若干アップし、敵の追尾を振り切ることにも使えます。ただ実演動画をみているとローラーを使っているときはUターンはできないようです。
今回はローラーで敵を踏みつぶす処理を実装します。
Rollerクラス
まずローラーの位置情報を管理するためのクラスを作成します。上下、左右に移動できるローラーがフィールド上にひとつずつ存在します。プロパティとして初期座標と現在座標、移動可能範囲を設定します。前2者は同じなので基底クラスRollerをつくってこれを継承してRollerNSクラスとRollerWEクラスをつくります。
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 class Roller { public Roller() { } public int InitX { protected set; get; } public int InitY { protected set; get; } public int X { set; get; } public int Y { set; get; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class RollerWE : Roller { public RollerWE(int initX, int initY, int leftEndX, int rightEndX) { X = InitX = initX; Y = InitY = initY; LeftEndX = leftEndX; RightEndX = rightEndX; } public int LeftEndX { get; } public int RightEndX { get; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class RollerNS : Roller { public RollerNS(int initX, int initY, int topEndY, int bottomEndY) { X = InitX = initX; Y = InitY = initY; TopEndY = topEndY; BottomEndY = bottomEndY; } public int TopEndY { get; } public int BottomEndY { get; } } |
Rollerオブジェクトを生成する
次にForm1クラスのコンストラクタ内でRollerクラスのインスタントを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public partial class Form1 : Form { RollerWE rollerWE = null; RollerNS rollerNS = null; public Form1() { InitializeComponent(); BackColor = Color.Black; CreateMapFromBitmap(); InitTimer(); InitEnemies(); // 追加 rollerNS =CreateRollerNS(); rollerWE = CreateRollerWE(); } } |
CreateRollerNSメソッドとCreateRollerWEメソッドは以下のとおりです。ローラーの初期位置を求めコンストラクタに渡しています。
ローラーの初期位置は東西にかかる橋の西側と南北にかかる橋の南側なので、二次元配列 Mapを調べればわかります。Map(x, y)がPOSITION_BRIDGE_EDGEであり、その右または上がPOSITION_BRIDGE_XXならそこが初期配置をする座標となります。
またローラーの厚み(ROLLER_THICKNESS)からローラーの可動範囲もわかります。
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 partial class Form1 : Form { const int ROLLER_THICKNESS = 10; RollerWE CreateRollerWE() { for (int y = 0; y < MapSourceHeight; y++) { int left = -1; int right = -1; for (int x = 0; x < MapSourceWidth; x++) { if (Map[y, x] == POSITION_BRIDGE_EDGE && Map[y, x + 1] == POSITION_BRIDGE_WE) left = x; if (Map[y, x] == POSITION_BRIDGE_EDGE && Map[y, x - 1] == POSITION_BRIDGE_WE) right = x; } if (left != -1 && right != -1) return new RollerWE(left + ROLLER_THICKNESS / ExpansionRate, y, left + ROLLER_THICKNESS / ExpansionRate, right - ROLLER_THICKNESS / ExpansionRate); } return null; } RollerNS CreateRollerNS() { for (int x = 0; x < MapSourceWidth; x++) { int top = -1; int bottom = -1; for (int y = 0; y < MapSourceHeight; y++) { if (Map[y, x] == POSITION_BRIDGE_EDGE && Map[y + 1, x] == POSITION_BRIDGE_NS) top = y; if (Map[y, x] == POSITION_BRIDGE_EDGE && Map[y - 1, x] == POSITION_BRIDGE_NS) bottom = y; } if (top != -1 && bottom != -1) return new RollerNS(x, bottom - ROLLER_THICKNESS / ExpansionRate, top, bottom - ROLLER_THICKNESS / ExpansionRate); } return null; } } |
ローラーの移動処理
必要があればローラーを移動させます。ローラーを移動させるのはプレイヤーがローラーを押すことができる位置にいて、ローラーが移動可能範囲内にある場合です。
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 |
public partial class Form1 : Form { // プレイヤーはローラーをこの方向に押している bool IsHoldRollerE = false; bool IsHoldRollerW = false; bool IsHoldRollerN = false; bool IsHoldRollerS = false; void MoveRollerIfPlayerHold() { if ( PlayerDirect == Direct.Right && rollerWE.Y == PlayerY && rollerWE.X == PlayerX + CharactorSize / ExpansionRate && rollerWE.X + ExpansionRate < rollerWE.RightEndX ) { rollerWE.X += 1; IsHoldRollerE = true; } else IsHoldRollerE = false; if ( PlayerDirect == Direct.Left && rollerWE.Y == PlayerY && rollerWE.X == PlayerX - ROLLER_THICKNESS / ExpansionRate && rollerWE.X - ExpansionRate > rollerWE.LeftEndX ) { rollerWE.X -= 1; IsHoldRollerW = true; } else IsHoldRollerW = false; if ( PlayerDirect == Direct.Up && rollerNS.X == PlayerX && rollerNS.Y == PlayerY - ROLLER_THICKNESS / ExpansionRate && rollerNS.Y - ExpansionRate > rollerNS.TopEndY + CharactorSize / ExpansionRate ) { rollerNS.Y -= 1; IsHoldRollerN = true; } else IsHoldRollerN = false; if ( PlayerDirect == Direct.Down && rollerNS.X == PlayerX && rollerNS.Y == PlayerY + CharactorSize / ExpansionRate && rollerNS.Y - ExpansionRate < rollerNS.BottomEndY - ROLLER_THICKNESS / ExpansionRate ) { rollerNS.Y += 1; IsHoldRollerS = true; } else IsHoldRollerS = false; } // ローラーを追加させたのでTimer1_Tickにローラーを移動させる処理を追加する private void Timer1_Tick(object sender, EventArgs e) { MovePlayer(); MoveEnemies(); MoveRollerIfPlayerHold(); // 追加 Invalidate(); } } |
ローラーを持っているときは速度がアップし、UターンはできなくなるのでIsHoldRollerW、IsHoldRollerE、IsHoldRollerN、IsHoldRollerSのいずれかがtrueの場合は方向転換処理ができないようにします。
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 partial class Form1 : Form { void SetPlayerDirect() { if (IsInMap(PlayerY, PlayerX) && Map[PlayerY, PlayerX] != POSITION_BRIDGE_NS_CROSS && Map[PlayerY, PlayerX] != POSITION_BRIDGE_WE_CROSS && !IsHoldRollerW && !IsHoldRollerE && !IsHoldRollerN && !IsHoldRollerS ) { if (IsKeyUp) { if (Map[PlayerY - 1, PlayerX] > 0) PlayerDirect = Direct.Up; } if (IsKeyDown) { if (Map[PlayerY + 1, PlayerX] > 0) PlayerDirect = Direct.Down; } if (IsKeyLeft) { if (Map[PlayerY, PlayerX - 1] > 0) PlayerDirect = Direct.Left; } if (IsKeyRight) { if (Map[PlayerY, PlayerX + 1] > 0) PlayerDirect = Direct.Right; } } } } |
Timerを2種類セットすることでローラーを持っているときはもうひとつのTimer.Tickイベントでもプレイヤーの移動処理をおこない、速度をアップさせています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class Form1 : Form { private void Timer2_Tick(object sender, EventArgs e) { if (IsInMap(PlayerY, PlayerX)) { if (IsHoldRollerN && PlayerDirect == Direct.Up) PlayerY--; if (IsHoldRollerS && PlayerDirect == Direct.Down) PlayerY++; if (IsHoldRollerW && PlayerDirect == Direct.Left) PlayerX--; if (IsHoldRollerE && PlayerDirect == Direct.Right) PlayerX++; } MoveRollerIfPlayerHold(); } } |
ローラーの描画処理
ローラーを追加したので描画に関する処理も追加しています。
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 partial class Form1 : Form { // ローラーを描画する const int ROLLER_THICKNESS = 10; void DrawRollers(Graphics graphics) { graphics.FillRectangle( Brushes.Blue, new Rectangle(new Point(rollerWE.X * ExpansionRate + LEFT_MARGIN, rollerWE.Y * ExpansionRate + TOP_MARGIN), new Size(ROLLER_THICKNESS, CharactorSize))); graphics.FillRectangle( Brushes.Blue, new Rectangle(new Point(rollerNS.X * ExpansionRate + LEFT_MARGIN, rollerNS.Y * ExpansionRate + TOP_MARGIN), new Size(CharactorSize, ROLLER_THICKNESS))); } protected override void OnPaint(PaintEventArgs e) { DrawRoad(e.Graphics); DrawPlayer(e.Graphics); DrawEnemies(e.Graphics); // 橋を描画する DrawBridgeNS(e.Graphics); DrawBridgeWE(e.Graphics); // 橋の始点ですでに通過した部分が橋を描画する過程で白く描画されている場合は塗りなおす ReDrawBridgeEdgeIfNeed(e.Graphics); // 橋の上(周辺も含む)を通過中のキャラクタは最後に描画する ReDrawPlayerIfNeed(e.Graphics); ReDrawEnemies(e.Graphics); // ローラーを描画する DrawRollers(e.Graphics); // デバッグ用 // DrawEnemyPath(e.Graphics); base.OnPaint(e); } } |