これまではカードを出すと出したプレイヤーのカードが1枚減り、中央に表示されていたカードが変化しました。今回はプレーヤーによって出されたカードが中央へむかって流れるように移動します。そのため誰がカードを出したのかがわかりやすいです。
ではどのようにすればいいのでしょうか?
フォーム上をカードが流れるように移動させるには?
まず出されたカードが表示されるPanelを作成します。これを移動させます。
ただループ文をつくって座標を変えていく方法ではすぐに終わってしまうのでカードが流れるように移動してみえません。そこでループ文のなかに
1 |
await Task.Delay(20).ConfigureAwait(false); |
を入れます。
これでカードが流れるように中央にむかって移動してみえるようになります。
デザイナを使って新しいPanelを追加します。新しく追加されたパネルの名前はpanelPutCardとします。
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 |
public partial class Form1 : Form { async Task PutCardMoveAsync(Card card, Player player) { if(player == null) return; Panel playerPanel = player.PlayerPanel; Point startPt = new Point(0, 0); startPt.Y = playerPanel.Location.Y; startPt.X = (playerPanel.Width - card.Size.Width) / 2 + playerPanel.Location.X; // 最初の座標を決める panelPutCard.Location = startPt; panelPutCard.Visible = true; Graphics g = Graphics.FromHwnd(panelPutCard.Handle); g.DrawImage(card.Bitmap, new Point(0, 0)); panelPutCard.Size = card.Size; g.Dispose(); // 左右からは中央まで距離があるのでループの回数を変える int roopCount = 4; if(player == Players[1] || player == Players[3]) roopCount = 7; for(int i = 0; i < roopCount; i++) { Point pt = panelPutCard.Location; if(playerPanel == panelSouth) pt.Y -= 20; if(playerPanel == panelNorth) pt.Y += 20; if(playerPanel == panelWest) pt.X += 20; if(playerPanel == panelEast) pt.X -= 20; panelPutCard.Location = pt; await Task.Delay(20).ConfigureAwait(false); } panelPutCard.Visible = false; panelPutCard.Invalidate(); } } |
CardExクラス
移動するためにはそのカードがどこから出てきたかを知る必要があります。
今回はカードにそのカードはどこから出てきたのかを知る変数を持たせることにしました。
しかしCardクラスを変更することはできません。自作のライブラリだから変更してもいいのだが・・・
ライブラリはそのままにしたいので、ここではこれを継承してCardExというクラスを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 |
using CardLibrary; public class CardEx : CardLibrary.Card { public CardEx(Suit suit, int number) { Suit = suit; Number = number; } public Player player = null; } |
CardExクラスをつくった以上は生成するカードもCardExクラスからつくることになります。ただし格納先はこれまでと同じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public partial class Form1 : Form { List<Card> Cards = new List<Card>(); void CreateCards() { for(int i = 1; i <= 13; i++) Cards.Add(new CardEx(Suit.Spade, i)); for(int i = 1; i <= 13; i++) Cards.Add(new CardEx(Suit.Hart, i)); for(int i = 1; i <= 13; i++) Cards.Add(new CardEx(Suit.Dia, i)); for(int i = 1; i <= 13; i++) Cards.Add(new CardEx(Suit.Club, i)); } } |
次にカードを出すときにそのプレーヤーが出したことがわかるようにフィールド変数に代入します。
どのカードを出すのかを決めるメソッドはPlayerクラスのPutCardメソッドでした。戻り値をCardからCardExに変更しています。
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 |
public class Player { public CardEx PutCard(Card card, List<Card> shuffledCards) { List<Card> canPutCards = GetAblePutCards(card); // 実際に出されるカード CardEx putCard = null; if(canPutCards.Count > 0) putCard = (CardEx)OnFindCanPutCard(canPutCards); // キャストが必要 else putCard = (CardEx)OnFindCanPutNoCard(shuffledCards, card); // 同 // Qを出したあと一緒に出すかどうか if(putCard.Number == 12) { Card ret = OnAfterPutQueen(); if(ret != null) putCard = (CardEx)ret; } putCard.player = this; // 誰が出したカードかわかるようにする return putCard; } } |
CenterCardプロパティを変更する
それから出されたカードが移動する場所は中央なので、CenterCardプロパティに手を入れれば簡単にすみそうです。
ところで・・・
Taskを極めろ!async/await完全攻略 – Qiita
要点を引用すると
TaskをWaitしてはいけないと聞いたことはありますね?
理由はもちろんデッドロック。ちょっとWaitを呼んでみるだけで、予想よりもずっと簡単にデッドロックを発生させることができてしまいます。しかし!対策はあります!
特定のスレッドに戻りたくても戻れないために発生するデッドロックならば、「特定のスレッドに戻らなくても良い」ようにすればいいのではないでしょうか。そのためのメソッドが、ConfigureAwait(false)です。
await後に戻る先のスレッドを指定しないようにできます。そうすれば、続きの実行は適当な空いているスレッドに割り当てられ、上のようなデッドロックが発生することはなくなります。
長くなってしまいましたが、ConfigureAwait(false)があれば問題ないということです。なのでTaskをWaitすることにします。ConfigureAwait(false)をつけないとどうなるか実験してみたら動かなくなってしまった。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public partial class Form1 : Form { Card CenterCard { get { return centerCard; } set { // if文の部分が追加部分 if(value != null) { CardEx cardEx = (CardEx)value; Task task = PutCardMoveAsync(value, cardEx.player); task.Wait(); // TaskをWaitしてはいけないのだが、この場合は問題ない } centerCard = value; panelCenter.Invalidate(); } } } |
自分のカードを出したときの処理
自分がカードを出すときはカードに自分が出したことがわかるようにフィールド変数に値をいれておく必要があります。
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 |
public partial class Form1 : Form { private void panelSouth_MouseDown(object sender, MouseEventArgs e) { Card card = GetClickedCard(e); if(CheckUnjustClick(card)) return; Players[0].Cards.Remove(card); ((CardEx)card).player = Players[0]; // 追加部分 CenterCard = card; if(Players[0].Cards.All(x => x.Number == 12)) Players[0].Cards.Clear(); Card QueenCard = OnMyPutCardQueen(); if(QueenCard != null) { ((CardEx)QueenCard).player = Players[0]; CenterCard = QueenCard; } if(CenterCard.Number == 1) isLastCardA = true; if(CenterCard.Number == 2) drawTwoCount = 1; if(CenterCard.Number == 11) { if(!isFromBack) isFromBack = true; else isFromBack = false; Text = "逆回転"; System.Threading.Thread.Sleep(500); } panelSouth.Invalidate(); // ドボンされる可能性があるのでチェックする if(DobonIfCan(Players[0])) return; // カードがなくなったらあがり if(Players[0].Cards.Count == 0) { OnMyFinish(); return; } RivalsTurnAsync(); } } |