技术开发 频道

Windows游戏:Silverlight3开发空当接龙

    【IT168 专稿】在本系列文章中,我们将编写一个Silverlight 3版本的空当接龙游戏。与前款纸牌游戏类似,编写这款游戏的主要目的仍然是为了探讨Siverlight 3游戏开发中鼠标有关功能的支持。同时,在本游戏中,你还将学习Siverlight 3新引入的定制行为编程知识。

  注意,空当接龙游戏的开发及测试环境与前面介绍的纸牌游戏完全一致。而且,为了简化讨论,我们假设读者已经阅读了前面的纸牌游戏系列文章。

  一、空当接龙游戏简介

  空当接龙游戏是Windows平台上一款极具吸引力的单人智力扑克游戏,老少皆宜。据说,大约99.99%的牌面布局都可以解开。

  游戏区由右上方四个回收单元,左上方四个可用单元和下部一副牌组成。游戏开始时,牌的正面朝上,排成八列,自左边起前四列各7张,后四列各6张。

  本游戏的目标是主要利用左上方可用单元和下面可能存在的空位为中转单元,将下部所有的纸牌都移到右上方回收单元中。如果能在回收单元中叠放四叠从A到K升序排列的、每叠只有一种花色的牌,就赢得此局。

  具体游戏规则有:

  (1)如要移动纸牌,请先单击这张牌,然后单击要移到的位置。

  (2)将牌移动到下部某一列时,必须按照从大(K)到小(A)的顺序移动,并且红黑花色交替。

  (3)将牌移动到回收单元时,必须按照从小(A)到大(K)的顺序移动,并且花色相同。

  (4)每列最上面那张牌可以移到中转单元、另一列的最下面或者回收单元中。

  (5)可用单元中的牌可以移到下部一列的最上面或回收单元中。

  (6)如果在某一列的底部按序排好了两张或多张牌,只要有足够的可用单元,就可以将这整个序列的牌移到另一列。要移动一个列,请单击要移动列的最上面一张,再单击要移到的目标列。

  (7)双击纸牌,可以将其快速移到可用单元(本游戏中未实现)。

  (8)在每次移牌后,空当接龙会将废牌自动送到回收单元。当游戏区中没有相反颜色的更小的牌时,这张牌即为废牌。

  二、类似于纸牌游戏的设计方案

  由于本游戏与纸牌游戏中采用了许多相类似的解决方案;所以,后面的讨论中进行了许多简化。

  (1)处理扑克的坐标问题

  为了简化问题,仍然使用Canvas控件作为所有扑克子控件的容器控件。主要调用Canvas类的GetLeft、GetTop、SetLeft、SetTop四个方法来控制扑克子控件的Left和Top属性,从而实现扑克的位置控制。

  (2)处理扑克控件的zIndex属性

  在空当接龙游戏中,我们仍然采用与纸牌游戏相同的策略来处理扑克控件的zIndex属性—主要通过调用GetZIndex和SetZIndex两个方法来调整被移动扑克的zIndex属性值。

  (3)使用Silverlight Menu菜单控件

  使用http://slmenu.codeplex.com/开源网站上提供的菜单控件。

  (4)鼠标相关事件

  乍看起来,在空当接龙游戏中涉及的鼠标相关事件要比纸牌游戏中简单,例如不再需要进行鼠标拖动操作—在本游戏中,我们主要操作两个鼠标事件MouseLeftButtonDown和MouseLeftButtonUp来确定哪一张扑克是当前扑克,哪一张扑克刚刚操作过的前面那张扑克。但是,事情并非想像得那么简单。有关鼠标事件编程,将在后文中详细讨论。

  (5)取消支持鼠标双击操作

  在原来Windows空当接龙游戏中,通过双击鼠标能够把下部扑克移动到左上方可用单元或右上方回收单元,从而加速了扑克移动操作。但是,在本游戏中我们引入了一个特殊行为—InverseColorClickBehavior,由它控制实现扑克的反色。由于此行为组件的内部实现了内置了一个MouseLeftButtonDown事件处理器,从而妨碍了我们在最外层的双击鼠标实现方案。

  基于上述原因,我们决定放弃对于双击鼠标的支持。有兴趣的读者可以考虑通过取消应用上面的反色行为组件InverseColorClickBehavior而选择其他适当的双击鼠标实现方案。

  (6)一个更新版本的GetCurrentCard方法

  读者应当记得在纸牌游戏中,我们不断通过调用GetCurrentCard方法来定位当前鼠标。在本游戏中,我们继续使用这个方法,但是进行了如下改进:

  显然,上面代码的主体并没有更改—主要通过调用VisualTreeHelper类中的静态方法FindElementsInHostCoordinates来遍历Silverlight视觉树中的对象关系。但请注意,此处在从查询结果集中移除父结点后,判断条件更改为collidedElements.Count() >1。为什么呢?这是因为在我们把所有扑克控件添加到父控件Canvas中之前,我们提前在其中添加了几个Path和Rectangle控件。

  此外,传递给方法GetPosition的参数不是cardContainer而是null),而传递给方法FindElementsInHostCoordinates的参数不是null而是cardContainer。

  (7)定义新的扑克控件Card

  在本游戏中,为了管理扑克的方便,我们也定义了一个独立的Silverlight用户控件—Card。这个控件类的总体实现逻辑与纸牌游戏中是一致的。显然,在本游戏中,我们只需要定义扑克的点数和花色即可,因为所有扑克都是正面向上的。

public partial class Card : UserControl

  {

  
public int Number { get; set; }

  
public Suits CurrentSuit { get; set; }

  
private Uri[] uriPokerImages = {

  
new Uri("/SLFreeCell;Component/images/cards/cl1.PNG", UriKind.RelativeOrAbsolute),

  
new Uri("/SLFreeCell;Component/images/cards/cl2.PNG", UriKind.RelativeOrAbsolute),

  
//…omitted to save space

  
new Uri("/SLFreeCell;Component/images/cards/spk.PNG", UriKind.RelativeOrAbsolute)

  };

  
//define an index pointer

  
public Uri this[int nIndex]

  {

  
get

  {

  return uriPokerImages[nIndex];

  }

  
set

  {

  uriPokerImages[nIndex]
=value;

  }

  }

  
public Card(int number, Suits currSuit)

  {

  InitializeComponent();

  this.Number
= number;

  this.CurrentSuit
= currSuit;

  this.imageHolder.Source
= new BitmapImage(this[(int)currSuit * 13 + (number - 1)]);

  }

  }

  此外,我们只需定义除去大王和小王之外的52张图像,因为我们不再需要特殊的图像去担当占位符。总之,在本游戏中使用了一个简化版本的用户控件Card。

  三、创建定制的反色行为组件

  Silverlight 3中一个强大的功能是提供了对行为和触发器的支持。借助于行为和触发器等新特征,你可以以更加面向对象的方式来实现Silverlight编程。

  一般情况下,在Silverlight 3编程环境下,你可以使用四种类型的行为—Behavior、TriggerAction、TriggerBase和TargetedTriggerAction。其中,在Microsoft Expression Blend 3中,你可以添加三种定制的行为类,分别是Behavior类、Trigger类和Action类。

  在本文中,我们将创建这样一个定制的行为组件:当你单击某个图像时,由于与此图像相关联的行为的作用,图像的颜色能够以反色显示。

  (1)开发定制行为组件—InverseColorClickBehavior

  对于简单的应用情形,使用从泛型Behavior类派生的方式是创建一个定制行为的首要选择。这个类只有两个可重载的方法—OnAttached和OnDetaching,分别用于通知当前的行为某个对象何时附加于其上以及何时与其分离。

  上面的泛型类Behavior中的T是一个依赖对象。在大多数情况下,你只需要从Behavior或Behavior加以派生即可。

  为了简化编程,本游戏直接使用了开源网站http://www.codeplex.com/wpffx中提供的一个现成的WPF Pixel Shader 效果库的一部分。为了适应本游戏,我们还作了一些简单修改。

  我们的定制行为组件名字是InverseColorClickBehavior,其关键代码如下:

//…省略其他命名空间引用

  using ShaderEffectsLibrary;

  namespace SLFreeCell.Behaviors{

  
public class InverseColorClickBehavior : Behavior{

  InverseColor inverseColor;

  
public InverseColorClickBehavior() :base(){

  this.inverseColor
= new InverseColor();

  }

  protected override void OnAttached(){

  base.OnAttached();

  this.AssociatedObject.MouseLeftButtonDown
+= new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown);

  }

  protected override void OnDetaching(){

  base.OnDetaching();

  this.AssociatedObject.MouseLeftButtonDown
-= new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown);

  }

  
private void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) {

  this.AssociatedObject.Effect
=

  this.AssociatedObject.Effect
== null ?

  this.AssociatedObject.Effect
= this.inverseColor :

  this.AssociatedObject.Effect
= null;

  }

  }

  }

  显然,针对我们想实现的反色效果而言,我们仅对鼠标单击事件感兴趣。

  因此,在OnAttached方法中,我们需要订阅行为的关联对象的MouseLeftButtonDown事件。对应地,在OnDetaching方法中,我们还要实现取消订阅这个MouseLeftButtonDown事件。

  接下来,剩下的就是在关联对象的MouseLeftButtonDown事件处理器中进行编程,以便实现我们想要的反色效果。

  上述代码中的反色效果类inverseColor是我们自定制的一个效果类。效果组件(Effect)是自Silverlight 3始引入的一种控制图像效果的组件。有关如何创建定制效果组件是一个非常复杂的问题,请参考文章所附源码及MSDN有关资料,在此不再赘述。

  在上面代码中的关联对象将指向游戏中的Card控件。根据游戏需要,我们只要控制这个控件的Effect属性值即可。根据单击扑克的次数是奇数次还是偶数次,相应地设置或清除控件的Effect属性值,从而实现扑克外观的反色效果。

  (2)使用定制行为InverseColorClickBehavior

  关于使用行为组件的一个重要特征是,一次只能把一个行为关联到一个控件上,而一个控件可以被关联到多个行为上。

  首先,因为我们总共有52张扑克,所以可以定义一个数组变量来存储对应的52个反色行为:

  InverseColorClickBehavior[] InverseBehaviorArray;

  然后,要初始化这个数组(在InitAndStartGame方法中实现):

InverseBehaviorArray = null;

  InverseBehaviorArray
= new InverseColorClickBehavior[52];

  
for (int i = 0; i < 52; i++)

  InverseBehaviorArray[i]
= new InverseColorClickBehavior();

  最后,看一下如何使用上述行为数组(在方法GenerateAndDealCards中):

 arrPoker[RandomI[i]] = OrderPoker[i];

  InverseBehaviorArray[i].Attach(arrPoker[RandomI[i]]);

  每当创建一张扑克时,我们把一个对应的反色行为关联到其上。

0
相关文章