今回はカードが流れるように移動する演出を加えます。
MovingCardクラス
最初に移動するカードの座標とイメージを管理するためのクラスをつくります。コンストラクタの引数はカード、カードが移動を開始する座標、移動を終了する座標、カードの幅と高さです。
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 |
public class MovingCard { public MovingCard(Card card, Point start, Point end, int width, int height) { Card = card; Start = start; End = end; Width = width; Height = height; } public Card Card { private set; get; } public int Width { private set; get; } public int Height { private set; get; } public int X { private set; get; } public int Y { private set; get; } public Point Point { get { return new Point(X, Y); } } public Point Start { private set; get; } public Point End { private set; get; } } |
カードのイメージですがコンストラクタに引数として渡したカードのCardImageプロパティをコピーして使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class MovingCard { Bitmap _cardImage = null; public Bitmap CardImage { get { if (_cardImage != null) return _cardImage; Bitmap bitmap = new Bitmap(Width, Height); Graphics graphics = Graphics.FromImage(bitmap); graphics.DrawImage(Card.CardImage, new Rectangle(0, 0, Width, Height)); graphics.Dispose(); _cardImage = bitmap; return _cardImage; } } } |
カードを移動させるための処理を示します。最初に出発点と終着点は引数として渡されているので、ここからX、Y座標がどれだけ移動するかを求めます。4回の移動で終着点に移動させたいので全体の移動分の4分の1が1回の移動処理で移動する量となります。何回移動したかを数えてこれを最初の座標に加えれば現在の座標がわかります。
そして_updateCountとMaxUpdateCountが同じになったら移動は完了したことになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class MovingCard { int _updateCount = 0; const double MaxUpdateCount = 4; public void Move() { _updateCount++; int totalMoveX = End.X - Start.X; int totalMoveY = End.Y - Start.Y; X = (int)(Start.X + totalMoveX / MaxUpdateCount * _updateCount); Y = (int)(Start.Y + totalMoveY / MaxUpdateCount * _updateCount); } public bool MoveEnd() { return _updateCount >= MaxUpdateCount; } } |
移動中のカードを描画するメソッドを示します。これといって捻りはありません。
1 2 3 4 5 6 7 |
public class MovingCard { public void Draw(Graphics graphics) { graphics.DrawImage(CardImage, Point); } } |
カードを移動させるためのTimerとイベント処理
次にこのMovingCardクラスをForm1クラスで使います。
最初にタイマーをもうひとつ作ります。これはカードが滑るように移動して見える処理をするために必要です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public partial class Form1 : Form { Timer Timer2 = new Timer(); public Form1() { InitializeComponent(); BackColor = Color.Green; DoubleBuffered = true; Timer1.Interval = 2000; Timer1.Tick += Timer1_Tick; Timer2.Interval = 50; Timer2.Tick += Timer2_Tick; Timer2.Start(); LoadConfig(); } } |
Timer.Tickイベントが発生したらMovingCardsに格納されているオブジェクトを移動させます。すでに目的地に到着したオブジェクトはリストから取り除きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class Form1 : Form { List<MovingCard> MovingCards = new List<MovingCard>(); private void Timer2_Tick(object sender, EventArgs e) { // MovingCardsが空ならなにもする必要はない if (MovingCards.Count == 0) return; foreach (MovingCard movingCard in MovingCards) movingCard.Move(); MovingCards = MovingCards.Where(x => !x.MoveEnd()).ToList(); Invalidate(); } } |
カードを描画する処理ですが、MovingCardsに格納されているオブジェクトのイメージは最後に描画します。移動しているカードが一番手前にみえるようにしたいからです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class Form1 : Form { protected override void OnPaint(PaintEventArgs e) { DrawDeckCards(e.Graphics); DrawPutIntoPlayCards(e.Graphics); DrawLedgerCards(e.Graphics); DrawCardCount(e.Graphics); GetSizeSpeedText(); DrawSpeedIfNeed(e.Graphics); foreach (MovingCard movingCard in MovingCards) movingCard.Draw(e.Graphics); base.OnPaint(e); } } |
プレイヤーが場に出されているカードを出すときは、移動対象のカードと現在の座標と移動先の座標などを引数にMovingCardオブジェクトを生成します。そしてこれをMovingCardsに格納します。
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 |
public partial class Form1 : Form { bool PutCardFromIntoPlay(Point point) { int index = IsPointPutIntoCards1(point); if (index != -1) { Card card = CardsPutIntoPlay1[index]; if (card == null) return false; List<Card> ledger = CanPutCard(card.Number); if (ledger != null) { ledger.Add(card); CardsPutIntoPlay1[index] = null; // GetPutIntoPlayPoints1()[index]が移動元の座標 // 移動先は2つ考えられるので条件分岐させる if(ledger == this.CardsLedgerPlayer1) MovingCards.Add(new MovingCard(card, GetPutIntoPlayPoints1()[index], this.GetLedgerPoint1(), CardWidth, CardHeight)); else MovingCards.Add(new MovingCard(card, GetPutIntoPlayPoints1()[index], this.GetLedgerPoint2(), CardWidth, CardHeight)); // 効果音も鳴らしてみる(Sound2メソッドのコードは省略。ゴメンナサイ) Sound2(); } else { if (!CanPutNextCard()) { CallSpeed(); } } Invalidate(); return true; } return false; } } |
コンピュータがカードを出すときの処理も修正が必要です。あとTimer1.Intervalに大きめの値を入れて実際にプレイしてみましたが、山札から場へカードを移動させるときも遅くなるので、この処理はすぐに終わるようにしました。0.75秒とTimer1.Intervalのうち短い側を採用します。
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 partial class Form1 : Form { void CompPutCard() { // 山札がある場合 if (CardsDeckPlayer2.Count > 0) { // 場が空いているなら山札からカードを補充する処理はここではやらない // for (int i = 0; i < CardsPutIntoPlay2.Length; i++) // { // if (CardsPutIntoPlay2[i] == null) // { // CardsPutIntoPlay2[i] = CardsDeckPlayer2[0]; // CardsDeckPlayer2.RemoveAt(0); // Invalidate(); // return; // } // } // 場が空いていいないなら場から出せるカードを出す for (int i = 0; i < CardsPutIntoPlay2.Length; i++) { Card card = CardsPutIntoPlay2[i]; if (card == null) continue; List<Card> cardsLedgerPlayer = CanPutCard(card.Number); if (cardsLedgerPlayer != null) { cardsLedgerPlayer.Add(card); CardsPutIntoPlay2[i] = null; if(cardsLedgerPlayer == this.CardsLedgerPlayer1) MovingCards.Add(new MovingCard(card, GetPutIntoPlayPoints2()[i], GetLedgerPoint1(), CardWidth, CardHeight)); else MovingCards.Add(new MovingCard(card, GetPutIntoPlayPoints2()[i], GetLedgerPoint2(), CardWidth, CardHeight)); Invalidate(); // 0.75秒とTimer1.Intervalのうち短い時間が経過したら // 山札からカードを補充する処理をおこなう Timer timer = new Timer(); timer.Interval = Timer1.Interval < 750 ? Timer1.Interval : 750; timer.Tick += Timer_Tick; timer.Start(); return; // 山札からカードを補充する処理。関数内のこんなところに関数が書けてしまう void Timer_Tick(object sender, EventArgs e) { Timer t = (Timer)sender; t.Stop(); t.Dispose(); if (CardsDeckPlayer2.Count == 0) return; for (int k = 0; k < CardsPutIntoPlay2.Length; k++) { if (CardsPutIntoPlay2[k] == null) { CardsPutIntoPlay2[k] = CardsDeckPlayer2[0]; CardsDeckPlayer2.RemoveAt(0); Invalidate(); return; } } } } } } else { // 山札がない場合 for (int i = 0; i < CardsPutIntoPlay2.Length; i++) { Card card = CardsPutIntoPlay2[i]; if (card == null) continue; List<Card> cardsLedgerPlayer = CanPutCard(card.Number); if (cardsLedgerPlayer != null) { cardsLedgerPlayer.Add(card); CardsPutIntoPlay2[i] = null; if (cardsLedgerPlayer == this.CardsLedgerPlayer1) MovingCards.Add(new MovingCard(card, GetPutIntoPlayPoints2()[i], GetLedgerPoint1(), CardWidth, CardHeight)); else MovingCards.Add(new MovingCard(card, GetPutIntoPlayPoints2()[i], GetLedgerPoint2(), CardWidth, CardHeight)); Invalidate(); return; } } } } } |
移動しているカードを描画する処理
カードを出すときの処理を修正します。カードが移動している最中なのに出されたカードは台札の上に表示されるのはおかしいです。実際に動かしてみるまではカードの移動速度も速いしたいした問題ではないだろうと考えていたのですが、実際にテストをしてみると非常に違和感があります。
そこで台札の一番上のカードと同じカードがMovingCardsのなかにあるときは二番目のカードを表示させることにします。
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 |
public partial class Form1 : Form { void DrawLedgerCards(Graphics graphics) { if (CardsLedgerPlayer1.Count > 0) { int count = CardsLedgerPlayer1.Count; Card topCard = CardsLedgerPlayer1[count -1]; if (!MovingCards.Any(x => x.Card.Number == topCard.Number && x.Card.Suit == topCard.Suit)) { // 台札の一番上のカードと同じカードがMovingCardsのなかにないときだけ描画する topCard.Point = GetLedgerPoint1(); topCard.Draw(graphics); } else if (CardsLedgerPlayer1.Count > 1) { // MovingCardsのなかにカードが存在するときは上から2番目のカードを表示させる Card secondCard = CardsLedgerPlayer1[count - 2]; secondCard.Point = GetLedgerPoint1(); secondCard.Draw(graphics); } } if (CardsLedgerPlayer2.Count > 0) { int count = CardsLedgerPlayer2.Count; Card topCard = CardsLedgerPlayer2[count - 1]; if (!MovingCards.Any(x => x.Card.Number == topCard.Number && x.Card.Suit == topCard.Suit)) { topCard.Point = GetLedgerPoint2(); topCard.Draw(graphics); } else if (CardsLedgerPlayer2.Count > 1) { Card secondCard = CardsLedgerPlayer2[count - 2]; secondCard.Point = GetLedgerPoint2(); secondCard.Draw(graphics); } } } } |