無理矢理流れに乗せてみると意外とうまくいって無理矢理ではなかったというオチ

list このエントリーをはてなブックマークに追加

あの日はこう考えていたけど、今はこう考えていて、それを実装しようとするときに、以前の他人の自分を「他人」として捉えるのも良し。いやいや、同一人物なんだから同じ考えでオーケーでは?と再び考えてみて、それなりにリスペクトしたコードを書いてみれば、なるほど、そう考えていたのか、と自分に納得する、という現象なのか。

■2枚を選択させるUIを場コントロールに追加する

2つの選択肢をしめすために、RectangleShape を使って枠線を描画する、ってのはいいとして、もともとのメイン画面からの呼び出しが、こんな風にコールバックになっているわけです。

/// <summary>
/// 2枚から選択する
/// </summary>
/// <param name="c1"></param>
/// <param name="c2"></param>
/// <returns></returns>
Card Player1_EventSelCard(Card c1, Card c2)
{
	string msg = string.Format(
		"{0} と {1} があります。{2} を選択しますか?",
		c1.ID, c2.ID, c1.ID);
	var btn = MessageBox.Show(msg, "", MessageBoxButtons.YesNo);
	if (btn == System.Windows.Forms.DialogResult.Yes)
	{
		return c1;
	}
	else
	{
		return c2;
	}
}

これを場コントロールで選択できる、という風に仮定すると、こんな感じなるのが理想的…つーか、最初はコールバック関数を作るとか、モロモロ変な感じなコードになっていたのですが、Player1_EventSelCard メソッド自体が、コールバック関数なので、この中で処理をしないといけないんですよね。非同期の async/await が使えればそれはそれでいいのだけど、今回の場合は Windows フォームなので、それを使うのも変な話だし。

/// <summary>
/// 2枚から選択する
/// </summary>
/// <param name="c1"></param>
/// <param name="c2"></param>
/// <returns></returns>
Card Player1_EventSelCard(Card c1, Card c2)
{
	return baControl1.SelCard(c1, c2);
}

という訳で、場コントロールには、次のロジックを追加。

PictureBox _selPic1;
PictureBox _selPic2;
Card _selCard1;
Card _selCard2;
Card _selCard;

/// <summary>
/// 2枚から選択する
/// </summary>
/// <param name="c1"></param>
/// <param name="c2"></param>
/// <returns></returns>
public Card SelCard(Card c1, Card c2)
{
	// 指定の2枚に枠を付ける
	var pic1 = _pics.Find( p => p.Image == CardUI.GetResName(c1.ID));
	var pic2 = _pics.Find( p => p.Image == CardUI.GetResName(c2.ID));
	this.rectangleShape1.Location = new Point(pic1.Location.X - 3, pic1.Location.Y - 3);
	this.rectangleShape2.Location = new Point(pic2.Location.X - 3, pic2.Location.Y - 3);
	this.rectangleShape1.Visible = true;
	this.rectangleShape2.Visible = true;

	pic1.Click += selPic_Click;
	pic2.Click += selPic_Click;

	_selPic1 = pic1;
	_selPic2 = pic2;
	_selCard1 = c1;
	_selCard2 = c2;

	// 選択待ち
	_selCard = null;
	while (_selCard == null)
	{
		Application.DoEvents();
	}
	return _selCard;
}

private void selPic_Click(object sender, EventArgs e)
{
	_selPic1.Click -= selPic_Click;
	_selPic2.Click -= selPic_Click;
	this.rectangleShape1.Visible = false;
	this.rectangleShape2.Visible = false;

	PictureBox pic = (PictureBox)sender;
	if (pic == _selPic1)
	{
		_selCard = _selCard1;
	} else {
		_selCard = _selCard2;
	}
}

メソッド内でユーザーの選択待ちの動作が入ってしまうので、Application.DoEvents() でクリック待ちをするという荒業です。これを使わない場合には、メイン画面のほうにコールバック関数を作って、という具合になるわけですが、そうなるとロジックと UI の部分の分離がうまくできない。ここは鬼っ子なのか?というのは後で検証するとして、まあ、これで2枚の花札を選択するという動作ができます。

■お次は「動的なデータ表現」

大体、静的なデータ表現ができたので、今度は動的なデータ表現を作ります。「動的な」ってのは、アニメーションとか、一連の動作のところですね。手札がから1枚場に捨てる場合、一連の動作としては、

  1. 手札から場に1枚出す。
  2. 場にマッチさせる。
  3. 山から1枚引いて、場に出す。
  4. 場にマッチさせる。
  5. マッチした札を取り札にする。

というところまで1連の動作なわけで、これを「静的なデータ」だけで表現すると、1の手順を行ったときに、一瞬で5になってしまって「何が起こったのかさっぱりわからない」状況になります。なので、このあたりを「時間」を差し込んで、アニメーションで表す、ってところですね。この時間を差し込むところは、業務アプリではほとんど使わないかと。強いて言えば、ドラッグ&ドロップのところぐらいでしょうか。リストから選んだり、テキストを入力してボタンを押した直後に、何か結果が出てくるのが業務アプリの特徴。ゲームの場合は、この間にアクションを差し込んで「わかりやすく」≒「エンターテインメント性を持たせる」ってことになります。

Windows メトロアプリの場合、XAML を使って storyboard を使うと、このゲーム性の部分が比較的簡単にできるのでは?と思っています。このあたりの実験も兼ねて。そう、まずは、Windows フォーム版を、XAML 版に移植するところから始めます。WPF を作ってから Windows メトロのほうがいいのかな?ちょっと思案中。

カテゴリー: C#, 花札ゲーム パーマリンク

無理矢理流れに乗せてみると意外とうまくいって無理矢理ではなかったというオチ への1件のフィードバック

コメントは停止中です。