1

我正在尝试编写一个纸牌游戏,玩家可以在其中堆叠纸牌。例如王牌,二,三。

我想想象一叠牌,其中 A 牌部分被两张牌覆盖,而两张牌被三张牌部分覆盖。三张牌是完全可见的。

容易,我想。我在添加卡片的位置创建了一个用户控件: Controls.Add(ace); Controls.Add(二); 等等

然后我需要一些能够布局我的控件的东西,所以我编写了我的自定义 LayoutEngine(派生自 LayoutEngine)。我的第一个测试只是将控件移动 50 像素。

运行解决方案后,我注意到 Z 排序错误。不是三张牌在上面,而是 A 牌在上面,看起来像这样:

王牌 > 两张牌 > 三张牌 其中: A 牌在上面 两张牌在 A 牌下面 三张牌在两张牌下面。

所以我开始寻找一种方法来改变 WinForms 中的 Z 顺序,结果发现它根本就是“不可用”。就像.. 嗯?!

另一种方法(由 MS 提供)是可以通过设置控件的 ChildIndex 来更改 Z 顺序。Jikes,这意味着在列表中四处寻找会改变我的应用程序的行为。好样的MS...

无论如何,我尝试了所有类型的东西,但似乎不可能编写一个可以做到这一点的布局引擎。

我整天用google-d,没有发现任何有用的东西。我不是 GUI 专家,所以我被困在这个蹩脚的问题上。谁能帮帮我?

非常感激!

巴斯

4

1 回答 1

5

您最好的选择是完全避免使用控件。它们将 A) 导致较差的性能和 B) 使命中测试/绘图复杂化。

只需创建对象来表示表格的状态(我使用 CardContainer 对象)并使用 Graphics.DrawImage 来绘制它们在绘制事件期间所在的所有卡片。如果您还需要添加其他 UI 元素,则可以对整个表格使用单个控件。

如果您决定添加动画,这也将使动画卡片移动更简单。

更新

我打算扩大这个答案,但被叫走了,只是发布了我所拥有的。以下是一些您可能会觉得有用的细节。我创建了一个“纸牌游戏引擎”。该引擎一次承载一个纸牌游戏(克朗代克、蜘蛛、策略等)。它跟踪每个游戏的统计数据,并允许玩和编辑单个游戏。这些游戏是 IronPython 脚本,这使得添加新游戏相对容易。

My CardContainer 是一个包含零个或多个卡片的对象。

它有一个 LieDirection (None, Up, Down, Left, Right),它决定了它的卡片是如何布局的。

它有一个限制在 LieDirection 中绘制的卡片数量的 MaximumDepth。这对于像克朗代克这样的游戏来说很方便,您只想显示前 3 张浪费的牌。

它具有用于间隔卡片的属性。面朝上和面朝下的卡片有单独的间距值。它可以将卡片自动打包到由最大长度定义的区域中。它有一个“额外的”值,每张卡片一个——无论该索引处是否有卡片。后者在模拟鼠标悬停期间用于“发现”指向的卡片,以便用户可以清楚地看到可能被上面的卡片遮挡的卡片。这是通过在悬停卡顶部设置卡的“额外垫”来实现的。这可以通过具有“悬停卡”和“悬停间距”属性来简化,但是每张卡具有额外的填充允许奇数种类的纸牌游戏,这些纸牌游戏突出显示具有间距的画面堆中的特定“行”。

它有一个 HitTest 方法可以从给定的 X、Y 位置返回一张卡片。

所有这一切都意味着 Card 对象不知道它在桌子上的位置。我有一个复杂的动画系统,所以卡片的位置最终来自动画引擎。如果卡片当前没有动画,动画系统从它的容器中获取它的位置。

请注意,上面提到的卡片位置仅用于绘制。所有的卡片总是附在一个 CardContainer 上,并且简单地从一张卡移动到另一张卡。有一个“特殊”容器,称为甲板,最初包含每张卡片。它最初位于桌子之外。容器具有 Visible 属性。仅当将卡片从可见容器移动到另一个可见容器时才会播放动画。这允许您在必要时在没有动画的情况下移动卡片,并且卡片可以“飞出”到/从放置在桌子外的容器中。

该引擎还有一个基本的布局系统,用于相对于彼此定位 CardContainer。我做的一件非常方便的事情是使用卡片大小相对坐标系。表格的“宽度”正好是 11 个卡片宽度。无论用户的桌子尺寸有多大,宽度始终是 11 卡宽度。这意味着卡片大小(如用户所见)会增长和缩小。高度是可变的,但由固定的卡片大小比例决定(由卡片位图决定)。如果您给 CardContainer 一个 X 值 1.0,这意味着它将位于距离表格左侧一个卡片宽度的位置。这些值是浮点数,因此您可以使用 0.5 指定 1/2 卡片宽度。这使得在脚本中定位元素变得非常容易,而不必担心屏幕坐标。无论用户如何改变屏幕的大小,

该引擎还具有无限的撤消和重做功能。这意味着不仅要记录卡片移动(从一个容器到另一个容器),而且还要记录所有属性更改(卡片和容器属性)。如果从一开始就没有计划好,撤消和重做可能难以实施。脚本可以访问 Game.LogVariableChange 方法,以便它们可以通过记录机制更改全局变量的值。这对于像克朗代克的“三重交易”功能这样的东西是必要的。该脚本必须存储使用的重新交易次数,但如果用户取消了重新交易,则该变量的值更改也必须撤消。

这对纸牌非常有效,但几乎适用于任何类型的纸牌游戏。显然,您不必第一次去实施所有这些。我提供这些信息只是为了给你一些想法。

于 2010-12-29T16:30:24.380 回答