ポールセザンヌ、カードプレーヤー
昔々、Windows 95にはMicrosoft Heartsゲームがありました。 世界中の対戦相手とオンラインでトランプ。 メモリが適切に機能する場合、Windows for Workgroups 3.11(はい、これらすべてのアーティファクトが見つかりました!)いわゆるNetDDEを使用してローカルネットワークで再生するためのバージョンがありました。
長い間、カードゲームを選ぶ必要はありませんでした。 ことわざにあるように、豊かなもの...高い湾の橋と好みは、完全な無知のために消えました。 ただ一つだけが
これまで私が「バックエンド」の開発に関与したことがないという事実により、状況は複雑でした。 グーグルは正しい場所、 SignalRに私を導いた。
ここでSignalRにいくつかの熱狂的な言葉を言いたいです。 私のニーズに完全に適合する、よく文書化されたライブラリ。 ボアズは、それはWindows専用だと言うでしょう。 iOS用の集合的なファームクライアントを持っているようですが、この問題については詳しく調査しませんでした。
次の質問はホスティングです。 それから私は、Azureが私の腕の下にあるとは思わなかった。
それで私は何が欲しかった?
- プレイヤーの接続方法は、Microsoft Heartsに似ている必要があります。 プレイヤーは、適切な金額が得られるとすぐに1つずつ接続し、ゲームが開始されます。 私自身は、1対1のゲームに限定することにしました-誰も引き分けません!
- 最初の段階ではプレイヤーはほとんどいないでしょう-彼らはお互いをどのように見つけますか? 次に、誰かがアプリケーションを起動してゲームサーバーに接続した瞬間にプッシュ通知を再生したいすべての人を送信するというアイデアが生まれました。 プッシュでユーザーを抑制しないために、彼は「N分に1回まで」という制限を設けました。
- 詳細な統計情報、賞品、実績などが必要です。
- 異なるデザインのカードが欲しい
- 「誰が神を遣わしたのか」ではなく、友達と遊びたい。
Azureで何を使用しますか?
- 実際、AppServiceはすべてのクライアントが接続するアプリケーションです。
- SQLサーバー+ SQLデータベース-ゲームの統計を保存します。
それだけです
最近、私は彼らのプッシュ通知配信サービスも使用しました。 しかし、それは高価なようで(月に10ドル)、さらに、Microsoftの請求の不具合のために、これら2つのサービスに1年以上支払っていたことがわかりました! 突合せのサポートにより、彼らは間違いを認識し、1か月もの補償を提供しました。 しばらくして、このサービスを完全に中止し、プッシュ用の署名者を保存するための別のテーブルをデータベースに追加し、メインアプリケーションから自分で送信しました。
現時点では、1か月あたりのホスティングコストは約400 pです。 これは、SQLサーバーのコストのみです。 発信トラフィックが少なく、1か月あたり5 GBの空き容量に収まります。
開発
開発はVisual Studio 2015で行われ、クライアントにはMVVMフレームワークMVVMライトが使用されました。
小さなサーバー「キッチン」(審美眼と弱気な人は見ないほうがいい)
ユーザー接続、プッシュ
public override Task OnConnected() { if (((DateTime.Now - LastPush).TotalSeconds) > 360) { LastPush = DateTime.Now; Task.Run(() => SendNotifications()); } return base.OnConnected(); }
匿名ゲームを作成する
/// <summary> /// . /// </summary> /// <returns>ID </returns> async public Task<String> ConnectAnonymous(PlayerInformation pi) { MainSemaphore.WaitOne(); try { string res = String.Empty; string p_ip = Context.Request.GetHttpContext().Request.UserHostAddress; if (NextAnonymGame == null) { NextAnonymGame = new FoolGame(Context.ConnectionId, pi, p_ip, false); res = NextAnonymGame.strGameID; } else { await NextAnonymGame.Start(Context.ConnectionId, pi, p_ip); ActiveGames.Add(NextAnonymGame.strGameID, NextAnonymGame); res = NextAnonymGame.strGameID; NextAnonymGame = null; } return res; } finally { MainSemaphore.Release(); } }
ゲームルームの作成
/// <summary> /// /// </summary> /// <returns> </returns> public String CreatePrivateGame(PlayerInformation pi) { MainSemaphore.WaitOne(); try { string p_ip = Context.Request.GetHttpContext().Request.UserHostAddress; FoolGame game = new FoolGame(Context.ConnectionId, pi, p_ip, true); WaitingPrivateGames.Add(game.strGameID, game); return game.PrivatePass; } finally { MainSemaphore.Release(); } }
デッキの一番上のカードをのぞきます
/// <summary> /// - /// </summary> /// <param name="gameid"></param> /// <returns></returns> async public Task PeekCard(string gameid) { FoolGame game = null; game = ActiveGames.FirstOrDefault(games => games.Value.strGameID == gameid).Value; if (game != null) { game.GameSemaphore.Wait(); await Task.Delay(35); try { await Clients.Caller.PeekedCard(game.Deck.Peek()); } finally { game.GameSemaphore.Release(); } } }
相手にメッセージを送る
/// <summary> /// /// </summary> /// <param name="gameid">ID ( )</param> /// <param name="ChatMessage"> </param> /// <returns></returns> async public Task ChatMessage(string gameid, string ChatMessage) { FoolGame game = null; game = ActiveGames.FirstOrDefault(games => games.Value.strGameID == gameid).Value; if (game != null) { game.GameSemaphore.Wait(); await Task.Delay(35); try { await Clients.OthersInGroup(gameid).ChatMessage(ChatMessage); } finally { game.GameSemaphore.Release(); } } }
顧客について
プレーヤーを識別するために、最初にLiveId機能が使用され、次にGraph APIが使用されました。 アプリケーションの最初の起動時に、プレーヤーは自分のアカウントへのアクセスを提供するように招待されます(このアカウントからは、名前と、「ed4dd29dda5f982a」のような匿名IDのみを取得します)。 ただし、プレイヤーは匿名でプレイできますが、ゲームの統計は保持されません。
非匿名プレーヤーごとに保存されます:
1.最初のゲームの日付/最後のゲームの日付
2.プレイヤー名/ニックネーム
3.プレイしたゲームの数/勝った数
4.最後のIPアドレス
5.受け取った賞品
2人の非匿名プレイヤーがゲームでプレイする場合、ゲームを開始する前に、これらのプレイヤーのゲームの統計情報を取得します(お互いにプレイしたゲームの数と誰が勝ったか)。 このため、受信した匿名IDがSQLクエリで使用されます。
左上のスクリーンショットでは、例を見ることができます(クリック可能):
一般統計のスクリーンショット(クリック可能):
さらに、「競争」が各国で開催されています(匿名プレイヤーもここに参加します。情報はIPアドレスから取得されます)。
プレイヤーは短いメッセージを交換できます。
FoolHubProxy.On<string>("ChatMessage", (chatmessage) => synchrocontext.Post(delegate { PlayChatSound(); ShowMessageToast(chatmessage); }, null));
シチュエーションハンドラーの例:「カードを取りますが、対戦相手がもう少し私を追加します」:
FoolHubProxy.On<byte, bool>("TakeOneMoreCard", (addedcard, lastcard) => synchrocontext.Post(delegate { CardModel card = new CardModel(addedcard, DeckIndex); CardsOnTable_Low.Add(card); OpponentsCards.Remove(OpponentsCards.Last()); if (lastcard) { AppMode = AppModeEnum.defeated; } }, null));
賞品、クッキーなどについて
毎月、最も多くの勝利を収めた最初の5人のプレイヤーには、
追加機能について
アプリケーションは無料ですが、InApp Purchasesとして装飾されたさまざまな追加の「バン」があります。
- ゲーム中にスーツとヒントでカードをグループ化する(ヒットまたはトスする必要がある場合、適切なカードが押し上げられます)
- 手に持っているカードの枚数を対戦相手に見せない能力。 通常の状況では、彼はそれを見ます
- 常に最初にゲームを開始する機会。 それ以外の場合、最初の動きはランダムに再生されます。
- ゲームで打ち負かされたカードを見る能力
- ゲームごとにデッキを覗いて次のカードを見つける機会
- チートする能力。 通常、ゲームごとに3回回避するか、間違ったカードを投げることができます。 しかし、相手はこれに気づいて、間違ったカードをあなたに返す機会があります。
おわりに
このアプリケーションを開発した結果、私は:
- SignalRに会った
- メモリ内の更新されたSQL(クエリ、ストアドプロシージャ、関数)
- Azureでアプリをホストする方法を学びました
- 「愚か者」を演じることにD然とした。
質問
プロモーションコードを個人の利害関係者に配布すること
更新する
YouTube:いくつかのゲームを記録する