ASP.NET Core版 マッピー(MAPPY)をつくる(3)の続きです。
AspNetCore.SignalR.Hubクラスを継承してMappyHubクラスを定義します。
1 2 3 4 5 6 7 8 9 10 |
using System.Collections.Generic; using Microsoft.AspNetCore.SignalR; using System.Timers; namespace Mappy { public class MappyHub : Hub { } } |
以降は名前空間を省略して以下のように書きます。
1 2 3 |
public class MappyHub : Hub { } |
Contents
接続時の処理
接続時の処理を示します。初回だけタイマーの初期化をおこないます。またこのとき接続しているクライアント数が1のとき停止しているタイマーを動作させます。
接続のたびにMappyGameオブジェクトを生成して辞書に追加します。切断された場合は辞書から削除します。MappyGameオブジェクトが生成されたらそのなかにPlayerオブジェクトも生成されるので、イベントハンドラを追加してイベント発生時にはクライアントサイドに送信できるようにしておきます。
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 MappyHub : Hub { static bool IsFirstConnection = true; static System.Timers.Timer Timer = new System.Timers.Timer(); static Dictionary<string, IClientProxy> ClientProxyMap = new Dictionary<string, IClientProxy>(); static Dictionary<string, MappyGame> Games = new Dictionary<string, MappyGame>(); public override async Task OnConnectedAsync() { if (IsFirstConnection) { IsFirstConnection = false; Timer.Interval = 1000 / MappyGame.UPDATES_PER_SECOND; Timer.Elapsed += Timer_Elapsed; } await base.OnConnectedAsync(); ClientProxyMap.Add(Context.ConnectionId, Clients.Caller); MappyGame game = new MappyGame(); // Context.ConnectionIdをPlayer.ConnectionIdにセットする game.Player.SetConnectionId(Context.ConnectionId); // イベントハンドラの追加(後述) game.Player.JumpEvent += Player_JumpEvent; game.Player.OpenCloseDoorEvent += Player_OpenCloseDoorEvent; game.Player.EnemyDownEvent += Player_EnemyDownEvent; game.Player.FireBullet += Player_FireBullet; game.Player.EnemyDeadEvent += Player_EnemyDeadEvent; game.Player.GetItemEvent += Player_GetItemEvent; game.Player.AddScoreEvent1 += Player_AddScoreEvent1; game.Player.AddScoreEvent2 += Player_AddScoreEvent2; game.Player.StageCleared += Player_StageCleared; game.Player.NewStageEvent += Player_NewStageEvent; game.Player.PlayerDeadEvent += Player_PlayerDeadEvent; game.Player.GameOverEvent += Player_GameOverEvent; Games.Add(Context.ConnectionId, game); if (ClientProxyMap.Count == 1) Timer.Start(); await SendMap(Context.ConnectionId); // 後述 // 接続に成功したことをクライアントサイドに伝える await Clients.Caller.SendAsync("SuccessfulConnectionToClient", "接続成功", Context.ConnectionId); } } |
切断時の処理
切断時の処理を示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class MappyHub : Hub { public override async Task OnDisconnectedAsync(Exception? exception) { await base.OnDisconnectedAsync(exception); // 辞書からContext.ConnectionIdに対応するものを削除 if (ClientProxyMap.ContainsKey(Context.ConnectionId)) ClientProxyMap.Remove(Context.ConnectionId); if (Games.ContainsKey(Context.ConnectionId)) Games.Remove(Context.ConnectionId); // 接続されているクライアントが0になったらタイマー停止 if (ClientProxyMap.Count == 0) Timer.Stop(); try { System.GC.Collect(); } catch { Console.WriteLine("GC.Collect失敗"); } } } |
イベントハンドラ
プレイヤーがトランポリンでジャンプしたとき、このことをクライアントサイドに送信する処理を示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class MappyHub : Hub { private void Player_JumpEvent(object? sender, EventArgs e) { SendEventToClient(sender, "PlayerJumpEventToClient"); } void SendEventToClient(object? sender, string sendString) { Player? player = (Player?)sender; if (player == null) return; if (ClientProxyMap.ContainsKey(player.ConnectionId)) { Task.Run(async () => { if (ClientProxyMap.ContainsKey(player.ConnectionId)) await ClientProxyMap[player.ConnectionId].SendAsync(sendString); }); } } } |
それ以外のイベント発生時の処理も以下に示します。基本的にSendEventToClientメソッドを使い回していますが、引数を別に渡す必要がある場合は別途定義しています。また新しいステージが開始されたときはクライアントサイドに送信したマップを新しいものに更新しないといけないので、後述するSendMapメソッドを実行しています。
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 |
public class MappyHub : Hub { // ドアを開閉したとき private void Player_OpenCloseDoorEvent(object? sender, EventArgs e) { SendEventToClient(sender, "OpenCloseDoorEventToClient"); } // 敵が気絶したとき private void Player_EnemyDownEvent(object? sender, EventArgs e) { SendEventToClient(sender, "EnemyDownEventToClient"); } // パワードアを開いて弾丸を発射したとき private void Player_FireBullet(object? sender, EventArgs e) { SendEventToClient(sender, "FireBulletEventToClient"); } // 敵が弾丸によって死亡したとき private void Player_EnemyDeadEvent(object? sender, EventArgs e) { SendEventToClient(sender, "EnemyDeadEventToClient"); } // プレイヤーがアイテムを回収したとき private void Player_GetItemEvent(object? sender, EventArgs e) { SendEventToClient(sender, "GetItemEventToClient"); } // 加点されたとき(追加された点数をその場に表示する) private void Player_AddScoreEvent1(Player sender, int value, int x, int y) { if (ClientProxyMap.ContainsKey(sender.ConnectionId)) { Task.Run(async () => { await ClientProxyMap[sender.ConnectionId].SendAsync("PlayerAddScoreEvent1ToClient", value, x, y); }); } } // 加点されたとき(追加された点数を流れるように移動させる) private void Player_AddScoreEvent2(Player sender, int value, int y, bool isFromLeft) { if (ClientProxyMap.ContainsKey(sender.ConnectionId)) { Task.Run(async () => { await ClientProxyMap[sender.ConnectionId].SendAsync("PlayerAddScoreEvent2ToClient", value, y, isFromLeft); }); } } // ステージクリアしたとき private void Player_StageCleared(object? sender, EventArgs e) { SendEventToClient(sender, "StageClearEventToClient"); } // ステージクリアしたあと新しいステージが開始されたとき private void Player_NewStageEvent(object? sender, EventArgs e) { Player? player = (Player?)sender; if (player == null) return; Task.Run(async () => { await SendMap(player.ConnectionId); }); } // プレイヤーが死亡したとき private void Player_PlayerDeadEvent(object? sender, EventArgs e) { SendEventToClient(sender, "PlayerDeadEventToClient"); } // ゲームオーバーになったとき private void Player_GameOverEvent(object? sender, EventArgs e) { Player? player = (Player?)sender; if (player == null) return; SaveHiscore(player); // 後述 if (ClientProxyMap.ContainsKey(player.ConnectionId)) { Task.Run(async () => { if (ClientProxyMap.ContainsKey(player.ConnectionId)) await ClientProxyMap[player.ConnectionId].SendAsync("GameOverEventToClient"); }); } } } |
マップデータをクライアントに送信する処理
ステージが始まるときにマップデータをクライアントに送信する処理を示します。
MappyGame.MapArray[row, col]を調べてCell.FloorとCell.Ceilingになっている部分が床になります。またドアが設置されている部分は開閉方向が左右どちらなのかわかるように表示させたいのでCell.LeftOpenDoorとCell.LeftOpenPowerDoor、Cell.RightOpenDoorとCell.RightOpenPowerDoorの座標もクライアントサイドに送信します。それから左右の外壁についてもその座標をクライアントサイドに送信します。
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 |
public class MappyHub : Hub { static async Task SendMap(string connectionId) { if (!ClientProxyMap.ContainsKey(connectionId) || !Games.ContainsKey(connectionId)) return; MappyGame game = Games[connectionId]; List<int> floorPositionXs = new List<int>(); List<int> floorPositionYs = new List<int>(); List<int> leftOpenDoorPositionXs = new List<int>(); List<int> leftOpenDoorPositionYs = new List<int>(); List<int> rightOpenDoorPositionXs = new List<int>(); List<int> rightOpenDoorPositionYs = new List<int>(); List<int> wallPositionXs = new List<int>(); List<int> wallPositionYs = new List<int>(); List<int> rightWallPositionXs = new List<int>(); List<int> rightWallPositionYs = new List<int>(); for (int row = 0; row < game.RowMax; row++) { for (int col = 0; col < game.ColMax; col++) { if (game.MapArray[row, col] == Cell.Floor || game.MapArray[row, col] == Cell.Ceiling) { floorPositionXs.Add(col * MappyGame.CHARACTER_SIZE); floorPositionYs.Add(row * MappyGame.CHARACTER_SIZE); } if (game.MapArray[row, col] == Cell.LeftOpenDoor || game.MapArray[row, col] == Cell.LeftOpenPowerDoor) { leftOpenDoorPositionXs.Add(col * MappyGame.CHARACTER_SIZE); leftOpenDoorPositionYs.Add(row * MappyGame.CHARACTER_SIZE); } if (game.MapArray[row, col] == Cell.RightOpenDoor || game.MapArray[row, col] == Cell.RightOpenPowerDoor) { rightOpenDoorPositionXs.Add(col * MappyGame.CHARACTER_SIZE); rightOpenDoorPositionYs.Add(row * MappyGame.CHARACTER_SIZE); } if (game.MapArray[row, col] == Cell.LeftWall) { wallPositionXs.Add(col * MappyGame.CHARACTER_SIZE); wallPositionYs.Add(row * MappyGame.CHARACTER_SIZE); } if (game.MapArray[row, col] == Cell.RightWall) { rightWallPositionXs.Add(col * MappyGame.CHARACTER_SIZE); rightWallPositionYs.Add(row * MappyGame.CHARACTER_SIZE); } } } // 床 string xs1 = String.Join(",", floorPositionXs.Select(pos => pos.ToString()).ToArray()); string ys1 = String.Join(",", floorPositionYs.Select(pos => pos.ToString()).ToArray()); // ドアが設置されている部分の上側の床(形状が微妙に違うので別に送信する) string xs2 = String.Join(",", leftOpenDoorPositionXs.Select(pos => pos.ToString()).ToArray()); string ys2 = String.Join(",", leftOpenDoorPositionYs.Select(pos => pos.ToString()).ToArray()); string xs3 = String.Join(",", rightOpenDoorPositionXs.Select(pos => pos.ToString()).ToArray()); string ys3 = String.Join(",", rightOpenDoorPositionYs.Select(pos => pos.ToString()).ToArray()); int xMax = game.ColMax * MappyGame.CHARACTER_SIZE; await ClientProxyMap[connectionId].SendAsync("SendFloorPositionsToClient", xs1, ys1, xs2, ys2, xs3, ys3, xMax); // 外壁 string xs4 = String.Join(",", wallPositionXs.Select(pos => pos.ToString()).ToArray()); string ys4 = String.Join(",", wallPositionYs.Select(pos => pos.ToString()).ToArray()); string xs5 = String.Join(",", rightWallPositionXs.Select(pos => pos.ToString()).ToArray()); string ys5 = String.Join(",", rightWallPositionYs.Select(pos => pos.ToString()).ToArray()); await ClientProxyMap[connectionId].SendAsync("SendWallPositionsToClient", xs4, ys4, xs5, ys5); } } |
ゲーム開始の処理
ゲームを開始するときの処理を示します。
ゲームを開始するときはクライアントサイドからGameStartメソッドが呼び出されるので、ここからMappyGame.GameStartメソッド(既出)を呼び出してゲームを開始します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class MappyHub : Hub { public void GameStart(string id, string playerName) { if (!ClientProxyMap.ContainsKey(id) || !Games.ContainsKey(id)) return; if(!Games[id].Player.IsGameOver) return; Games[id].Player.Name = playerName; Games[id].GameStart(); Task.Run(async () => { if (ClientProxyMap.ContainsKey(id)) await ClientProxyMap[id].SendAsync("EventGameStartToClient"); }); } } |
更新処理
更新処理がおこなわれるときはMappyGame.Updateメソッド(後述)が呼び出されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class MappyHub : Hub { static private void Timer_Elapsed(object? sender, ElapsedEventArgs e) { Task.Run(async () => { foreach (MappyGame game in Games.Values) game.Update(); foreach (string id in ClientProxyMap.Keys) await SendUpdateToClient(id); }); } } |
MappyGame.Updateメソッド
ゲームオーバーのときは何もしません。TimeToStartが0より大きいときはステージクリアと次のステージ開始時の中間の状態なのでTimeToStartをデクリメントして0になったら次のステージを生成してイベントを発生させます。TimeToPlayerReviveが0より大きいときはプレイヤー死亡時なのでTimeToPlayerReviveをデクリメントして0になったら残機ありのときは復活させてゲーム続行、残機0のときはゲームオーバーのイベントを発生させます。
上記以外のときはプレイヤーと敵、火花を更新したあと当たり判定(後述)をおこない、ステージクリア判定をおこないます。
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 |
public class MappyGame { public void Update() { if (Player.IsGameOver) return; if (TimeToStart > 0) { TimeToStart--; if (TimeToStart <= 0) { // 次のステージを生成 Init(); // NewStageEventイベントを発生させる Player.NewStage(); } return; } if (TimeToPlayerRevive > 0) { TimeToPlayerRevive--; // プレイヤー死亡時は更新処理はしないが火花に関しては更新処理が必要 foreach (Spark spark in Sparks) spark.Update(); if (TimeToPlayerRevive <= 0) { if (Player.Rest > 0) { Player.Reset(); foreach (Enemy enemy in Enemies) enemy.Reset(); } else { // 残機0のときはGameOverEventイベントを発生させる Player.GameOver(); } } return; } // 上記以外のときはプレイヤーと敵、火花を更新する Player.Update(); foreach (Enemy enemy in Enemies) enemy.Update(); foreach (Spark spark in Sparks) spark.Update(); // 当たり判定 HitCheck(); // ステージクリア判定 if (Items.Count(_ => !_.Passed) == 0) { TimeToStart = TIME_TO_START_MAX; Player.StageClear(); } } } |
当たり判定
当たり判定の処理を示します。これはMappyGame.HitCheckメソッドを呼び出しておこないます。
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 |
public class MappyGame { void HitCheck() { // プレイヤーはアイテムを回収したか? foreach (Item item in Items) { // 未回収のアイテムのなかで当たり判定をする // 該当するものが見つかったらPassedフラグをfalseにして加点処理とGetItemEventイベントを発生させる if (!item.Passed && item.X == Player.X && item.Y == Player.Y) { item.Passed = true; Player.GetItem(item); } } // 弾丸が敵に命中したか? foreach (Bullet bullet in Player.Bullets) { // フロア上の敵だけでなくトランポリンで上下に移動している敵も対象とする List<Enemy> enemies = Enemies.Where(_ => !_.IsDead && bullet.X - 16 <= _.X && _.X <= bullet.X + 16 && bullet.Y - 16 <= _.Y && _.Y <= bullet.Y + 16).ToList(); for (int i = 0; i < enemies.Count; i++) { // 死亡する敵が見つかった場合 // 火花を生成 SetSparks(enemies[i].X, enemies[i].Y); // Enemy.IsDeadフラグをセット、復活までの時間をセットする enemies[i].Dead(i); // その弾丸で何体の敵を倒したかを記憶させる bullet.KillCount++; // EnemyDeadEventイベントを発生させる Player.EnemyDead(); } } // 弾丸が画面の外に移動したら点数計算をする // 同時に倒した敵の数と得点はadds配列のとおり(10体以上のときは一律5000点) Bullet? deadBullet = Player.Bullets.FirstOrDefault(_ => _.X < -CHARACTER_SIZE || _.X > ColMax * CHARACTER_SIZE); if (deadBullet != null) { if (deadBullet.KillCount > 0) { bool fromLeft = deadBullet.X < 0 ? true : false; int[] adds = { 0, 200, 400, 800, 1200, 1600, 2000, 3000, 4000, 5000 }; if (deadBullet.KillCount < adds.Length) Player.AddScore2(adds[deadBullet.KillCount], deadBullet.Y, fromLeft); else Player.AddScore2(5000, deadBullet.Y, fromLeft); } } // 画面の外に移動した弾丸はPlayer.Bulletsから外す Player.Bullets = Player.Bullets.Where(_ => _.X >= -MappyGame.CHARACTER_SIZE && _.X <= ColMax * CHARACTER_SIZE).ToList(); // 自機の死亡判定 // 自機がトランポリンで上下に移動しているときは死亡判定をしない(無敵) if (Player.MoveDirect == Direct.Up || Player.MoveDirect == Direct.Down) return; // 自機と敵が重なっている場合 // 少しでも重なっている場合も対象にすると当たり判定が厳しすぎるので調整する Enemy? enemy = Enemies.FirstOrDefault(_ => !_.IsDead && _.CantMoveCount <= 0 && _.MoveDirect != Direct.Up && _.MoveDirect != Direct.Down && Player.X - CHARACTER_SIZE / 2 <= _.X && _.X <= Player.X + CHARACTER_SIZE / 2 && _.Y == Player.Y); // 自機と重なっている敵(一定のラインを超えて重なっている敵)が存在する場合、自機死亡の処理をする if (enemy != null) { TimeToPlayerRevive = TIME_TO_START_MAX; SetSparks(Player.X, Player.Y); Player.Dead(); } } } |
更新結果をクライアントサイドに送信する
更新処理の結果をクライアントサイドに送信する処理を示します。
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 |
public class MappyHub : Hub { static async Task SendUpdateToClient(string id) { if (!ClientProxyMap.ContainsKey(id) || !Games.ContainsKey(id)) return; // プレイヤーの座標と向いている方向をクライアントサイドに送信する Player player = Games[id].Player; string lastDirect = player.LastDirect == Direct.Left ? "L" : "R"; await ClientProxyMap[id].SendAsync( "UpdatePlayerToClient", player.X, player.Y, lastDirect, player.Name, player.IsDead, player.Score, player.Rest, Games[id].StageNumber); // 敵の座標と向いている方向をクライアントサイドに送信する List<Enemy> enemies = Games[id].Enemies.Where(_=> !_.IsDead).ToList(); string enemyXs = String.Join(",", enemies.Select(_ => _.X).ToArray()); string enemyYs = String.Join(",", enemies.Select(_ => _.Y).ToArray()); string enemyDirects = String.Join(",", enemies.Select(_ => { return _.LastDirect == Direct.Left ? "L" : "R"; }).ToArray()); string isEnemyDowns = String.Join(",", enemies.Select(_ => { return _.CantMoveCount > 0 ? "true" : "false"; }).ToArray()); await ClientProxyMap[id].SendAsync( "UpdateEnemiesToClient", enemyXs, enemyYs, enemyDirects, isEnemyDowns); // 未回収のアイテムの座標と種類をクライアントサイドに送信する List<Item> items = Games[id].Items.Where(_ => !_.Passed).ToList(); string itemXs = String.Join(",", items.Select(_ => _.X).ToArray()); string itemYs = String.Join(",", items.Select(_ => _.Y).ToArray()); string itemNums = String.Join(",", items.Select(_ => _.Number).ToArray()); await ClientProxyMap[id].SendAsync( "UpdateItemsToClient", itemXs, itemYs, itemNums); // ドアの座標と状態をクライアントサイドに送信する List<Door> doors = Games[id].Doors; string doorXs = String.Join(",", doors.Select(_ => _.X).ToArray()); string doorYs = String.Join(",", doors.Select(_ => _.Y).ToArray()); string isDoorOpens = String.Join(",", doors.Select(_ => { return _.IsOpen ? "true" : "false"; }).ToArray()); string isPowerDoors = String.Join(",", doors.Select(_ => { return _.IsPowerDoor ? "true" : "false"; }).ToArray()); string doorDirects = String.Join(",", doors.Select(_ => { return _.OpenDirect == Direct.Left ? "L" : "R"; }).ToArray()); await ClientProxyMap[id].SendAsync( "UpdateDoorsToClient", doorXs, doorYs, isDoorOpens, isPowerDoors, doorDirects); // トランポリンの座標と状態をクライアントサイドに送信する List<Trampoline> trampolines = Games[id].Trampolines; string trampolineXs = String.Join(",", trampolines.Select(pos => pos.X).ToArray()); string trampolineYs = String.Join(",", trampolines.Select(pos => pos.Y).ToArray()); string trampolineLifes = String.Join(",", trampolines.Select(pos => pos.Life).ToArray()); await ClientProxyMap[id].SendAsync("UpdateTrampolinesToClient", trampolineXs, trampolineYs, trampolineLifes); // 弾丸の座標と状態をクライアントサイドに送信する List<Bullet> bullets = Games[id].Player.Bullets; string bulletXs = String.Join(",", bullets.Select(_ => _.X).ToArray()); string bulletYs = String.Join(",", bullets.Select(_ => _.Y).ToArray()); string bulletDirects = String.Join(",", bullets.Select(_ => { return _.VelocityX < 0 ? "L" : "R"; }).ToArray()); await ClientProxyMap[id].SendAsync( "UpdateBulletsToClient", bulletXs, bulletYs, bulletDirects); // 火花の座標と状態をクライアントサイドに送信する List<Spark> sparks = Games[id].Sparks; string sparkXs = String.Join(",", sparks.Select(fire => fire.X).ToArray()); string sparkYs = String.Join(",", sparks.Select(fire => fire.Y).ToArray()); string sparkLifes = String.Join(",", sparks.Select(fire => fire.Life).ToArray()); await ClientProxyMap[id].SendAsync( "UpdateSparksToClient", sparkXs, sparkYs, sparkLifes); // クライアントサイドへのデータ送信終了 await ClientProxyMap[id].SendAsync("EndUpdateToClient"); } } |
スコアランキングへの登録
ゲームオーバーのときにスコアランキングに登録する処理を示します。これはAspNetCore.SignalRにおける処理 ASP.NET Core版 ボンバーマンのような対戦型ゲームをつくる(3)の終わりのように示しているものとほぼ同じで、違うのはテキストファイルを保存するパスが違うだけです。Zero.Pages.Mappy.HiscoreクラスもASP.NET Core版 ボンバーマンで使用しているHiscoreクラスと同じです。
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 MappyHub : Hub { void SaveHiscore(Player player) { string path = "../hiscore-mappy.txt"; List<Zero.Pages.Mappy.Hiscore> hiscores = new List<Zero.Pages.Mappy.Hiscore>(); if (System.IO.File.Exists(path)) { System.IO.StreamReader sr = new StreamReader(path); string text = sr.ReadToEnd(); string[] vs1 = text.Split('\n'); foreach (string str in vs1) { try { string[] vs2 = str.Split(','); Zero.Pages.Mappy.Hiscore hiscore = new Zero.Pages.Mappy.Hiscore(vs2[0], long.Parse(vs2[1]), vs2[2]); hiscores.Add(hiscore); } catch { } } sr.Close(); } DateTime now = DateTime.Now; string time = String.Format("{0}-{1:00}-{2:00} {3:00}:{4:00}:{5:00}", now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second); hiscores.Add(new Zero.Pages.Mappy.Hiscore(player.Name, player.Score, time)); hiscores = hiscores.OrderByDescending(x => x.Score).ToList(); if (hiscores.Count > 30) hiscores = hiscores.Take(30).ToList(); System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (Zero.Pages.Mappy.Hiscore hiscore in hiscores) sb.Append(String.Format("{0},{1},{2}\n", hiscore.Name, hiscore.Score, hiscore.Time)); System.IO.StreamWriter sw = new StreamWriter(path); sw.Write(sb.ToString()); sw.Close(); } } |
キー操作時の処理
キーが押されたときの処理をするDownKeyメソッドと離されたときの処理をするUpKeyメソッドを示します。
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 MappyHub : Hub { public void DownKey(string key, string name) { // 長大なデータが送りつけられるかもしれないので対策 if (key.Length > 16) return; if (!Games.ContainsKey(Context.ConnectionId)) return; Player player = Games[Context.ConnectionId].Player; // プレイヤー名が長すぎる場合は16文字に切り詰める // カンマが入っているとスコアランキング登録時に問題がおきるので別の文字に置換する player.Name = name.Length > 16 ? name.Substring(0, 16) : name; player.Name = player.Name.Replace(",", "_"); // このゲームで使用するキーは3つだけ 遠隔操作でドアを開閉するときはスペースキー if (key == "ArrowLeft") player.IsLeftKeyDown = true; else if (key == "ArrowRight") player.IsRightKeyDown = true; else if (key == " ") player.OpenCloseDoor(); } public void UpKey(string key) { // 長大なデータが送りつけられるかもしれないので対策 if (key.Length > 16) return; if (!Games.ContainsKey(Context.ConnectionId)) return; Player player = Games[Context.ConnectionId].Player; if (key == "ArrowLeft") player.IsLeftKeyDown = false; if (key == "ArrowRight") player.IsRightKeyDown = false; } } |
最後にMappy.MappyHubをProgram.csに追加します。
Program.cs
1 2 3 |
// app.Run();の直前あたりに以下を追加 app.MapHub<Mappy.MappyHub>("/MappyHub"); |