ASP.NET Coreで戦車の対戦ゲームをつくります。以前、戦車をつかった対戦型ではないゲームをつくりました。
今回は似たような感じでオンラインで対戦できるゲームをつくります。
大雑把な仕様は以下のとおりです。
複数人でプレイするのでマップを広くする
壁は砲撃で破壊できる
外周の壁は破壊できない
撃破されたら外周の壁がある位置をランダムに選んでそこで復活する
残機制。残機ゼロになったらゲームオーバー
プレイできるのは最大7。それに満たない場合はNPC(non player character)を充てる
プレイしているユーザーがゼロになったら破壊された壁はすべて復元される
ではさっそくつくってみましょう。
TankGame名前空間
このゲームで使用するクラスはTankGame名前空間で定義します。
WallクラスとPositionクラス
まず壁の位置と破壊されているかを管理するためのクラスを定義します。また戦車の位置を管理するためのクラスも定義します。
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 |
namespace TankGame { public class Wall { public Wall(int x, int z) { X = x; Z = z; IsExist = true; } // 壁の中心の座標 public int X { private set; get; } public int Z { private set; get; } // 壁は存在するか? public bool IsExist { private set; get; } // 壁を破壊する public void Break() { IsExist = false; } } public class Position { public Position(int x, int z) { X = x; Z = z; } public int X { private set; get; } public int Z { private set; get; } } } |
壁の位置の定義
次に初期状態で配置されている壁の位置を定義します。これは tank-map.txt に0と1で書かれています。1は壁がある部分、0はない部分です。これをリソースに追加しておきます。プロジェクト名はZeroにしているのでZero.Properties.Resources.tank_mapとすれば文字列を読み出すことができます。
tank-map.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
00101000001110110 00101010111000000 11100010000011100 00001011101110000 11011000001000110 00000110111010000 01110100010011000 00010001010000010 01010100010100000 01000111100111100 11011100000100000 10010000111000110 10110110010011100 00100010010010000 10101000010110000 10001001000100000 00000000000000001 |
ひとつひとつの壁は戦車の半分の大きさです。したがってひとつの「1」がある部分には4つの壁が存在します。壁は砲撃されると砲撃された側の壁がふたつ破壊されます。そのためひとつの「1」がある部分にある壁をすべて破壊するには最低2回、最大3回の砲撃が必要です。
Gameクラス
次にGameクラスを定義します。ここでおこなわれる処理は壁の初期化です。
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 |
namespace TankGame { public class Game { static List<Wall> _walls = new List<Wall>(); public const int CHARACTER_SIZE = 24; // 引数の座標を中心とする4個の壁を生成する static void AddWalls(int x, int z) { int halfSizeWall = CHARACTER_SIZE / 4; _walls.Add(new Wall(x * CHARACTER_SIZE + halfSizeWall, z * CHARACTER_SIZE + halfSizeWall)); _walls.Add(new Wall(x * CHARACTER_SIZE + halfSizeWall, z * CHARACTER_SIZE - halfSizeWall)); _walls.Add(new Wall(x * CHARACTER_SIZE - halfSizeWall, z * CHARACTER_SIZE + halfSizeWall)); _walls.Add(new Wall(x * CHARACTER_SIZE - halfSizeWall, z * CHARACTER_SIZE - halfSizeWall)); } // 壁を初期化する public static void Init() { _walls.Clear(); string mapText = Zero.Properties.Resources.tank_map; string[] vs1 = mapText.Split("\n", StringSplitOptions.RemoveEmptyEntries); int rowMax = vs1.Length; int colMax = vs1[0].Length; for (int row = 0; row < rowMax; row++) { string str = vs1[row]; char[] vs2 = str.ToArray(); for (int col = 0; col < colMax; col++) { if (vs2[col] == '1') { if (row != 0 && col != 0) { AddWalls(row, col); AddWalls(-row, -col); AddWalls(row, -col); AddWalls(-row, col); } if (row == 0) { AddWalls(0, col); AddWalls(0, -col); } if (col == 0) { AddWalls(row, 0); AddWalls(-row, 0); } } } } // 破壊できない壁を追加する // 破壊できない壁の状態は変化しないので処理は最初の1回だけおこなう if (_indestructibleWalls.Count == 0) { int wallSize = CHARACTER_SIZE / 2; int minX = _walls.Min(_ => _.X); int maxX = _walls.Max(_ => _.X); int minZ = _walls.Min(_ => _.Z); int maxZ = _walls.Max(_ => _.Z); for (int x = minX - wallSize; x <= maxX + wallSize; x += wallSize) _indestructibleWalls.Add(new Wall(x, minZ - wallSize)); for (int x = minX - wallSize; x <= maxX + wallSize; x += wallSize) _indestructibleWalls.Add(new Wall(x, maxZ + wallSize)); for (int z = minZ - wallSize; z <= maxZ + wallSize; z += wallSize) _indestructibleWalls.Add(new Wall(minX - wallSize, z)); for (int z = minZ - wallSize; z <= maxZ + wallSize; z += wallSize) _indestructibleWalls.Add(new Wall(maxX + wallSize, z)); } } } } |
以下は生成された壁のリストを返すメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
namespace TankGame { public class Game { public static List<Wall> Walls { get { return _walls.ToList(); } } public static List<Wall> ExistingWalls { get { return _walls.Where(_=> _.IsExist).ToList(); } } public static List<Wall> IndestructibleWalls { get { return _indestructibleWalls.ToList(); } } } } |