1

プレイヤーがカードを積み重ねることができるカードゲームを書こうとしています。例: エース、ツー、スリー。

エース カードが 2 枚のカードで部分的に覆われ、2 枚のカードが 3 枚のカードで部分的に覆われているカードのスタックを視覚化したいと思います。スリーカードは完全に見えます。

簡単だと思いました。カードを追加するユーザー コントロールを作成します。 Controls.Add(ace); Controls.Add(2); 等

次に、コントロールをレイアウトできるものが必要なので、カスタム LayoutEngine (LayoutEngine から派生) を作成しました。私の最初のテストでは、コントロールを 50 ピクセルシフトするだけです。

ソリューションを実行した後、Z オーダーが間違っていることに気付きました。3 枚のカードが一番上にある代わりに、Ace カードが次のように一番上にありました。

エース カード > ツー カード > スリー カード ここで、エース カードが上にあり、ツー カードがエース カードの下にあり、スリー カードがツー カードの下にあります。

そこで、WinForms で Z オーダーを変更する方法を探し始めたところ、単に「利用できない」ことがわかりました。のように..え?

別の方法 (MS が提供) は、コントロールの ChildIndex を設定することにより、Z オーダーを変更できることです。つまり、リストをいじってみると、アプリケーションの動作が変わるということです。さすがMS...

とにかく、あらゆることを試しましたが、そのトリックを行うレイアウト エンジンを作成することは不可能のようです。

私は一日中グーグルで検索しましたが、何も役に立ちませんでした。私は GUI の専門家ではないので、この不自由な問題で立ち往生しています。誰が私を助けることができますか?

とても有難い!

バス

4

1 に答える 1

5

最善の策は、コントロールの使用を完全に避けることです。A) パフォーマンスが低下し、B) ヒット テスト/描画が複雑になります。

テーブルの状態を表すオブジェクトを作成し (私は CardContainer オブジェクトを使用します)、Graphics.DrawImage を使用して、ペイント イベント中に配置されているすべてのカードを描画します。他の UI 要素も追加する必要がある場合は、テーブル全体に 1 つのコントロールを使用できます。

これにより、アニメーションを追加することにした場合に、カードの動きのアニメーション化も簡単になります。

更新しました

私はこの回答を拡張するつもりでしたが、呼び出されて、単に私が持っていたものを投稿しました. ここでは、役に立つと思われる詳細をいくつか示します。「ソリティアゲームエンジン」を作りました。エンジンは、一度に 1 つのソリティア ゲーム (クロンダイク、スパイダー、戦略など) をホストします。各ゲームの統計を追跡し、個々のゲームのプレイと編集の両方を可能にします。ゲームは、新しいゲームの追加を比較的簡単にする IronPython スクリプトです。

My CardContainer は、0 個以上のカードを保持するオブジェクトです。

カードの配置方法を決定する LieDirection (None、Up、Down、Left、Right) があります。

LieDirection で描かれたカードの数をクランプする MaximumDepth があります。これはクロンダイクのような無駄カードの上位 3 枚だけを表示したいゲームに便利です。

カードの間隔をあけるプロパティがあります。表向きと裏向きのカードには、別々の間隔の値があります。MaximumLength で定義された領域にカードを自動パックできます。また、そのインデックスにカードがあるかどうかに関係なく、カードごとに 1 つの「追加パッド」値があります。後者は、シミュレートされたマウスのホバー中に使用され、指しているカードを「明らかに」して、ユーザーがカードの上にあるカードによって隠されている可能性のあるカードをはっきりと見ることができるようにします。これは、カードの「追加パッド」をホバー カードの上に設定することによって実現されます。これは、「ホバー カード」と「ホバー間隔」プロパティを使用することで単純化できますが、カードごとに追加のパディングを使用すると、タブロー パイルの特定の「行」を間隔で強調表示する奇妙な種類のソリティア ゲームが可能になります。

特定の X、Y 位置から Card を返す HitTest メソッドがあります。

つまり、Card オブジェクトには、テーブル上のどこに描画されるかという概念がありません。複雑なアニメーション システムを使用しているため、カードの位置は最終的にアニメーション エンジンから取得されます。カードが現在アニメーション化されていない場合、アニメーション システムはコンテナからその位置を取得します。

上記のカードの場所は、厳密にドロー用であることに注意してください。すべてのカードは常に 1 つの CardContainer に接続され、単純に別のカードに移動されます。デッキと呼ばれる 1 つの「特別な」コンテナがあり、最初はすべてのカードが含まれています。最初はテーブルから離れた場所に配置されています。コンテナには Visible プロパティがあります。アニメーションは、Visible コンテナから別の Visible コンテナにカードを移動する場合にのみ再生されます。これにより、必要に応じてアニメーションなしでカードを移動することができ、カードはテーブルの外にあるコンテナに出入りすることができます。

このエンジンには、CardContainers を相互に相対的に配置するための基本的なレイアウト システムもあります。私が行った非常に便利な方法の 1 つは、カード サイズに相対的な座標系を使用することでした。テーブルの「幅」はちょうど 11 カードの幅です。ユーザーがテーブルのサイズをどれだけ大きくしても、幅は常に 11 カード幅です。これは、(ユーザーから見た) カードのサイズが増減することを意味します。高さは可変ですが、固定のカード サイズの比率 (カード ビットマップから決定) によって決定されます。CardContainer に 1.0 の X 値を指定すると、テーブルの左から 1 カード幅に配置されることを意味します。値は浮動小数点であるため、0.5 でカード幅の 1/2 を指定できます。これにより、画面座標を気にすることなく、スクリプト内の要素を非常に簡単に配置できます。ユーザーが画面のサイズをどのように変更しても、

エンジンには、無制限の取り消しとやり直しもあります。これは、(あるコンテナから別のコンテナへの) カードの移動を記録する必要があるだけでなく、すべてのプロパティの変更も記録する必要があることを意味します (カードとコンテナのプロパティの両方)。元に戻すとやり直しは、最初から計画していない場合、実装が難しい場合があります。スクリプトは Game.LogVariableChange メソッドにアクセスできるため、記録メカニズムを通じてグローバル変数の値を変更できます。これは、クロンダイクの「3 回のリディール」機能などに必要です。スクリプトは使用された再取引の回数を保存する必要がありますが、ユーザーが再取引を元に戻した場合、その変数の値の変更も元に戻す必要があります。

これは、ソリティアでは非常にうまく機能しますが、ほぼすべての種類のカード ゲームで機能します。もちろん、最初からすべてを実装する必要はありません。アイデアを提供するためだけに情報を提示します。

于 2010-12-29T16:30:24.380 に答える