C#入门学习-----推箱子游戏(WPF技术实现)

编译平台:VS2008 + .Net Framework 3.5

        语言: C#

使用工具:Expression Design 4

                    Expression Blend    

3、实现游戏用户界面
尽管程序员可以使用VS编写XAML代码的方式来构造用户界面,但是对于有设计爱好的用户来说,使用类似Photoshop一样的Expression套件能将

软件美工最大化。对于怪物与目标块的图形显示,示例使用了Expression Design来设计图形,然后将其导入到Expression Blend中进行布局处理。

 

3、1 使用Expression Design设计图案
Expression Design 是一个专业的图表和图形设计工具。该工具提供了矢量图形的绘制能力,强大之处可以像PhotoShop一样设计好用户界面或是所需要量的图形,

使用其导出功能导出为XAML资源或代码。Expression Design主界面如下:

 \

Design 通常是与Blend紧密相关的。美工人员使用Design强大的设计功能来设计界面元素,导出给Blend进行编辑。最后通过VS设计程序代码。

在示例中,使用Design设计了一个Cell图案,在其中添加了多个图层,每一层放置各自不同的图案,比如箱子、怪物、墙体。

然后使用Design的导出功能将这些图案导出为资源字典,以便于程序引用这些图形。导出窗口如下:

 \

然后在App.xaml中的资源字典的定义中使用<ResourceDictionary.MergedDictionaries>指定Cell.xaml作为整个应用程序级别的资源。

 \

3、2 实现用户主界面
在开始布局游戏区域前,主界面上需要一个Banner来显示游戏名称,为游戏主界面添加背景图片以增加界面的效果。

游戏创建了两个用户控件来实现这样的特效,位于游戏项目的Controls文件夹下。

BackgroundControl.xaml用户控件实现非常简单,通过使用Expression Design来设计背景图片,然后导出为独立的Xaml文件。

导出设置如下:

 \

 

 

图形将被置于一个Canvas画布中,在MainWindow.xaml中通过引用这个控件来设置背景色。

 \

Banner.xmal的实现与BackgroundControl.xaml的实现类似。


主界面的布局非常简单,主要分为四行:

 \

(1) 第一行

主要放了一个Border、一个Rectangle、一个Viewbox(里面放Banner)

 \

 

(2) 第二行

在一个Grid中加入一个Rectangle、显示关卡代码、关数、按钮

 \

(3) 第三行

           只是一个间隔,没放东西

(4)  第四行

 \

 

如上,位于Viewbox的Grid, x:Name为grid_Game在游戏启动时动态创建行和列定义,并在行列中放置多个Button按钮来实现游戏的方块。

在MainWindow.xaml的声明中,为窗口关联了Loaded事件,当该事件触发时,将执行Window_Load代码。这段代码在游戏窗口打开时,开始游戏第一关。

 \

Window_Loaded将调用在资源中实例化的Game类,在Window.Resource资源区,首先定义了Game类,代码如下:

[csharp]
<!-- 用于整个游戏的Game实例. --> 
lt;Sokoban:Game x:Key="sokobanGame"/> 

   <!-- 用于整个游戏的Game实例. -->
  <Sokoban:Game x:Key="sokobanGame"/>在MainWindow.xaml.cs中相应地定义了一个Game属性,该属性使用在资源中指定的x:Key键值查找Game对象实例。Game属性的声明如下:

[csharp]
/// <summary>  
        /// 获取定义在Window资源中的Game对象的实例  
        /// </summary>  
        /// <value>游戏实例.</value>  
        Game Game 
        { 
            get 
            { 
                return (Game)TryFindResource("sokobanGame"); 
            } 
        } 

/// <summary>
        /// 获取定义在Window资源中的Game对象的实例
  /// </summary>
  /// <value>游戏实例.</value>
  Game Game
  {
   get
   {
    return (Game)TryFindResource("sokobanGame");
   }
  }Game属性的get获取器使用TryFindResource() 方法,传入指定的查找对象实例,并转换为Game对象。因为TryFindResource()方法返回object类型的实例。

[csharp]
//  
       // 摘要:  
       //     搜索具有指定键的资源,如果找到,则返回该资源。  
       //  
       // 参数:  
       //   resourceKey:  
       //     要查找的资源的键标识符。  
       //  
       // 返回结果:  
       //     找到的资源;如果未找到具有所提供 key 的资源,则为 null。  
       public object TryFindResource(object resourceKey); 

 //
        // 摘要:
        //     搜索具有指定键的资源,如果找到,则返回该资源。
        //
        // 参数:
        //   resourceKey:
        //     要查找的资源的键标识符。
        //
        // 返回结果:
        //     找到的资源;如果未找到具有所提供 key 的资源,则为 null。
        public object TryFindResource(object resourceKey);
3、3  程序启动时加载关卡
Window_Loaded 将使用Game对象加载游戏关卡,并初始化用户界面。

[csharp]
void Window_Loaded(object sender, RoutedEventArgs e) 
        {   //当Game的Level属性发生变更时,会触发PropertyChanged事件  
            Game.PropertyChanged += game_PropertyChanged; 
            try 
            { 
                /* 加载并开始第一级游戏,Level属性变更,
                 * 触发Game.PropertyChanged */ 
                Game.Start(); 
            } 
            catch (Exception ex) 
            {   //异常处理消息。  
                MessageBox.Show("加载游戏出现异常. " + ex.Message); 
            } 
        } 

void Window_Loaded(object sender, RoutedEventArgs e)
  {   //当Game的Level属性发生变更时,会触发PropertyChanged事件
   Game.PropertyChanged += game_PropertyChanged;
   try
   {
                /* 加载并开始第一级游戏,Level属性变更,
                 * 触发Game.PropertyChanged */
    Game.Start();
   }
   catch (Exception ex)
   {   //异常处理消息。
    MessageBox.Show("加载游戏出现异常. " + ex.Message);
   }
  }Game.Start将开始游戏的每一关,Start() 方法调用的LoadLevel()方法内部触发了Game.PropertyChanged事件。

只要游戏的状态发生变化,game_PropertyChanged事件处理代码便会执行,该事件中的代码将开始游戏界面的更新工作。

[csharp]
/// <summary>  
        /// 通过加载第一关来开始游戏  
        /// </summary>  
        public void Start() 
        { 
            if (sokobanService != null) 
            {   //获取关卡数  
                LevelCount = sokobanService.LevelCount; 
            } 
            else 
            {   //得到关卡总数,通过获取关卡文件的个数来得到  
                string[] files = Directory.GetFiles(levelDirectory, "*.skbn"); 
                LevelCount = files.Length; 
            } 
            LoadLevel(0);//加载关卡  
        } 

/// <summary>
  /// 通过加载第一关来开始游戏
  /// </summary>
  public void Start()
  {
   if (sokobanService != null)
   {   //获取关卡数
    LevelCount = sokobanService.LevelCount;
   }
   else
   { //得到关卡总数,通过获取关卡文件的个数来得到
    string[] files = Directory.GetFiles(levelDirectory, "*.skbn");
    LevelCount = files.Length;
   }
   LoadLevel(0);//加载关卡
  }
 

[csharp]
void game_PropertyChanged(object sender,  
            System.ComponentModel.PropertyChangedEventArgs e) 
        {   //判断传入的属性名称  
            switch (e.PropertyName) 
            { 
                case "GameState"://如果为GameState变更  
                    UpdateGameDisplay();//更新游戏的界面显示  
                    break; 
            } 
        } 

void game_PropertyChanged(object sender,
            System.ComponentModel.PropertyChangedEventArgs e)
  {   //判断传入的属性名称
   switch (e.PropertyName)
   {
    case "GameState"://如果为GameState变更
     UpdateGameDisplay();//更新游戏的界面显示
     break;
   }
  }
在以上代码中,判断PropertyName是否为GameState,如果为GameState属性发生变更,则调用UpdateGameDisplay来更新游戏界面的显示。

 

 3、4  更新游戏界面的显示
 UpdateGameDisplay方法将根据游戏的状态显示不同的信息,使玩家理解游戏当前所在的状态。

如果游戏处于开始运行状态,将调用Initialiselevel() 方法来初始化游戏的关卡。UpdateGameDisplay的实现如下:

[csharp]
/// <summary>  
    /// 设置游戏进度状态显示,  
    /// </summary>  
    void UpdateGameDisplay() 
    { 
        switch (Game.GameState) 
        { 
            case GameState.Loading://如果游戏处于加载中  
                FeedbackControl1.Message = //在界面上显示响应消息  
                       new FeedbackMessage { Message = "正在加载..." }; 
                ContinuePromptVisible = false; 
                break; 
            case GameState.GameOver://如果游戏结束状态  
                FeedbackControl1.Message =//显示结束信息  
                       new FeedbackMessage { Message = "游戏结束" }; 
                ContinuePromptVisible = true; 
                break; 
            case GameState.Running: //如果游戏处于开始运行状态  
                ContinuePromptVisible = false; 
                FeedbackControl1.Message = new FeedbackMessage(); 
                   InitialiseLevel();//初始化游戏关卡界面  
                break; 
            case GameState.LevelCompleted:  //如果玩家玩过关  
                FeedbackControl1.Message =  //显示玩过关的消息  
                       new FeedbackMessage { Message = "恭喜您,成功过关!" }; 
                MediaElement_LevelComplete.Position = TimeSpan.MinValue; 
                MediaElement_LevelComplete.Play();//播放声音  
                ContinuePromptVisible = true; 
                break; 
            case GameState.GameCompleted://如果玩完了所有的关卡  
                FeedbackControl1.Message = new FeedbackMessage //显示最终信息  
                   { Message = "干得好. \n游戏完成! \n请在Codeproejct联系游戏作者" }; 
                MediaElement_GameComplete.Position = TimeSpan.MinValue; 
                MediaElement_GameComplete.Play();//播放完成音乐  
                break; 
        } 
    } 

 /// <summary>
  /// 设置游戏进度状态显示,
  /// </summary>
  void UpdateGameDisplay()
  {
   switch (Game.GameState)
   {
    case GameState.Loading://如果游戏处于加载中
     FeedbackControl1.Message = //在界面上显示响应消息
                        new FeedbackMessage { Message = "正在加载..." };
     ContinuePromptVisible = false;
     break;
    case GameState.GameOver://如果游戏结束状态
     FeedbackControl1.Message =//显示结束信息
                        new FeedbackMessage { Message = "游戏结束" };
     ContinuePromptVisible = true;
     break;
    case GameState.Running: //如果游戏处于开始运行状态
     ContinuePromptVisible = false;
     FeedbackControl1.Message = new FeedbackMessage();
                    InitialiseLevel();//初始化游戏关卡界面
     break;
    case GameState.LevelCompleted:  //如果玩家玩过关
     FeedbackControl1.Message =  //显示玩过关的消息
                        new FeedbackMessage { Message = "恭喜您,成功过关!" };
     MediaElement_LevelComplete.Position = TimeSpan.MinValue;
     MediaElement_LevelComplete.Play();//播放声音
     ContinuePromptVisible = true;
     break;
    case GameState.GameCompleted://如果玩完了所有的关卡
     FeedbackControl1.Message = new FeedbackMessage //显示最终信息
                    { Message = "干得好. \n游戏完成! \n请在Codeproejct联系游戏作者" };
     MediaElement_GameComplete.Position = TimeSpan.MinValue;
     MediaElement_GameComplete.Play();//播放完成音乐
     break;
   }
  }
UpdateGameDisplay()方法通过判断Game类中定义的GameState来向UI界面显示游戏状态消息。

FeedbackControl1是一个自定义的用于游戏界面显示消息的用户控件,该控件主要用于在用户控件上显示一些消息。

[csharp]
<Controls:FeedbackControl Grid.Row="3" x:Name="FeedbackControl1" Margin="10,10,10,10" Click="FeedbackControl1_Click"/> 

<Controls:FeedbackControl Grid.Row="3" x:Name="FeedbackControl1" Margin="10,10,10,10" Click="FeedbackControl1_Click"/>
UpdateGameDisplay初始化关卡界面是在Running状态,即游戏开始运行后,开始初始化关卡,

这是通过 InitialiseLevel();初始化游戏关卡界面。

InitialiseLevel();方法将初始化学Grid的行列定义,并在行列中放置Button控件作为方块的容器。

由于在调用InitialiseLevel();前,LoadLevel()方法已经加载了关卡数据到Level类中,因此可以将Cell对象与指定的Button控件相关联。

[csharp]
/// <summary>  
/// 使用游戏级别初始化Grid  
/// </summary>  
void InitialiseLevel() 

    commandManager.Clear();//清除命令集合  
          //清除Grid子元素集合,以及行列定义集合  
    grid_Game.Children.Clear(); 
    grid_Game.RowDefinitions.Clear(); 
    grid_Game.ColumnDefinitions.Clear(); 
          //根据关卡中的行数向Grid中添加行定义  
    for (int i = 0; i < Game.Level.RowCount; i++) 
    { 
        grid_Game.RowDefinitions.Add(new RowDefinition()); 
    } 
          //根据关卡中的列数向Grid中添加列定义  
    for (int i = 0; i < Game.Level.ColumnCount; i++) 
    { 
        grid_Game.ColumnDefinitions.Add(new ColumnDefinition()); 
    } 
    for (int row = 0; row < Game.Level.RowCount; row++) 
    {   //循环遍历行  
        for (int column = 0; column < Game.Level.ColumnCount; column++) 
        {   //循环遍历列  
            Cell cell = Game.Level[row, column];//得到行列中的Cell对象  
            cell.PropertyChanged += cell_PropertyChanged;//关联属性变更事件                   
            Button button = new Button();//实例化Button控件  
            button.Focusable = false; //该控件不允许获取焦点  
                  //将Button的DataContext指定为cell对象,以便在XAML中控制  
            button.DataContext = cell;  
            button.Padding = new Thickness(0, 0, 0, 0);//按钮无边框  
            button.Style = (Style)Resources["Cell"];//指定按钮样式  
            button.Click += Cell_Click;//关联按钮单击事件  
            //通过附加属性设置按锯位于Grid的行和列  
            Grid.SetColumn(button, column); 
            Grid.SetRow(button, row); 
            grid_Game.Children.Add(button);//将按钮添加到Grid控件列表中  
        } 
    } 
    textBox_LevelCode.Text = Game.LevelCode;//显示关卡号  
    label_LevelNumber.Content =  //显示当前关卡和总关卡信息  
              Game.Level.LevelNumber + 1 + "/" + Game.LevelCount; 
    grid_Main.DataContext = Game.Level;//设置主界面的DataContext为关卡             
    mediaElement_Intro.Position = TimeSpan.MinValue;//播放介绍音乐  
    mediaElement_Intro.Play(); //播放音乐  
    grid_Game.Focus();        //游戏区域得到焦点  

  /// <summary>
  /// 使用游戏级别初始化Grid
  /// </summary>
  void InitialiseLevel()
  {
   commandManager.Clear();//清除命令集合
            //清除Grid子元素集合,以及行列定义集合
   grid_Game.Children.Clear();
   grid_Game.RowDefinitions.Clear();
   grid_Game.ColumnDefinitions.Clear();
            //根据关卡中的行数向Grid中添加行定义
   for (int i = 0; i < Game.Level.RowCount; i++)
   {
    grid_Game.RowDefinitions.Add(new RowDefinition());
   }
            //根据关卡中的列数向Grid中添加列定义
   for (int i = 0; i < Game.Level.ColumnCount; i++)
   {
    grid_Game.ColumnDefinitions.Add(new ColumnDefinition());
   }
   for (int row = 0; row < Game.Level.RowCount; row++)
   {   //循环遍历行
    for (int column = 0; column < Game.Level.ColumnCount; column++)
    {   //循环遍历列
     Cell cell = Game.Level[row, column];//得到行列中的Cell对象
     cell.PropertyChanged += cell_PropertyChanged;//关联属性变更事件     
     Button button = new Button();//实例化Button控件
     button.Focusable = false; //该控件不允许获取焦点
                    //将Button的DataContext指定为cell对象,以便在XAML中控制
     button.DataContext = cell;
     button.Padding = new Thickness(0, 0, 0, 0);//按钮无边框
     button.Style = (Style)Resources["Cell"];//指定按钮样式
     button.Click += Cell_Click;//关联按钮单击事件
     //通过附加属性设置按锯位于Grid的行和列
     Grid.SetColumn(button, column);
     Grid.SetRow(button, row);
     grid_Game.Children.Add(button);//将按钮添加到Grid控件列表中
    }
   }
   textBox_LevelCode.Text = Game.LevelCode;//显示关卡号
   label_LevelNumber.Content =  //显示当前关卡和总关卡信息
                Game.Level.LevelNumber + 1 + "/" + Game.LevelCount;
   grid_Main.DataContext = Game.Level;//设置主界面的DataContext为关卡   
   mediaElement_Intro.Position = TimeSpan.MinValue;//播放介绍音乐
   mediaElement_Intro.Play(); //播放音乐
   grid_Game.Focus();        //游戏区域得到焦点
  }
 

InitialiseLevel首先清除命令管理器中的命令列表,清除Grid中的行列定义及子内容。

然后根据游戏关卡的行数和列数创建行列定义,循环遍历行列。先获取在Game.Level 实例中加载的cell 对象。以便与稍后将要创建的Button控件相关联。

在获取了Cell并设置了Cell的PropertyChanged事件为cell_PropertyChanged事件处理代码后实例化一个Buttons控件。

该Button控件将作为一个游戏方块,指定其不能得焦点,不能具有边界,并关联其Click单击事件。

最后将该Button添加到Grid中。

cell_PropertyChanged事件主要用于判断当CellContents属性发生变更,将根据方块的内容是一个箱子还是角色来播放不同的音乐。

 

3、5  处理方块单击事件
当用户单击某个按钮时,会执行Cell_Click事件处理代码。

该事件处理代码将根据鼠标单击的位置,让怪物转到当前单击的位置点,如果玩家是按下键盘的确Shit键并单击鼠标,将执行PushCommand命令,

否则JumpCommand命令。命令的执行是通过CommandManager这个命令管理器来实现的。Cell_Click事件代码如下:

[csharp]
void Cell_Click(object sender, RoutedEventArgs e) 
        { 
            Button button = (Button)e.Source;//得到当前单击的Button实例  
            Cell cell = (Cell)button.DataContext;//获取DataContext  
            CommandBase command;            //要执行的命令变量  
            if (Keyboard.IsKeyDown(Key.LeftShift) //如果按下左或右Shift  
                || Keyboard.IsKeyDown(Key.RightShift)) 
            {   //实例化一个PushCommand执行推动命令  
                command = new PushCommand(Game.Level, cell.Location); 
            } 
            else //如果没有按下Shift,则执行跳转命令  
            {   //实例化跳转命令  
                command = new JumpCommand(Game.Level, cell.Location); 
            }//使用CommandManager命令管理器执行命令  
            commandManager.Execute(command); 
        } 

void Cell_Click(object sender, RoutedEventArgs e)
  {
   Button button = (Button)e.Source;//得到当前单击的Button实例
   Cell cell = (Cell)button.DataContext;//获取DataContext
   CommandBase command;            //要执行的命令变量
   if (Keyboard.IsKeyDown(Key.LeftShift) //如果按下左或右Shift
                || Keyboard.IsKeyDown(Key.RightShift))
   {   //实例化一个PushCommand执行推动命令
    command = new PushCommand(Game.Level, cell.Location);
   }
   else //如果没有按下Shift,则执行跳转命令
   {   //实例化跳转命令
    command = new JumpCommand(Game.Level, cell.Location);
   }//使用CommandManager命令管理器执行命令
   commandManager.Execute(command);
  }
代码首先得到当前单击的Button对象,根据该Button对象获取到与其相关联的cell对象。

然后判断当前是否按下了左或右Shit键。

如理按下了Shit键,则实例化PushCommand命令,否则实例化JumpCommand命令,要求CommandManager命令

管理器来执行相应的推移或跳转操作。

 

3、6  使用Command模式发送命令请求
Command模式是为了让命令的请求与命令的执行者进行解耦。CommandManager只对抽象的CommandBase进行执行,而不与实际的

命令代码进行解耦。

在用户界面上,当用户执行命令时,传递实际的命令给CommandManager 执行,实际上这是利用了面向对象的多态技术。

当用户界面按下键盘上的按钮时,一系列具体的命令对象产生,然后交给CommandManager命令管理器进行执行。

在MainWindow.xaml的定义中,响应了主窗体的KeyDown事件,将执行Window_KeyDown事件处理代码。

[csharp]
/// <summary>  
       /// 处理Window控件的KeyDown事件  
    /// </summary>  
    void Window_KeyDown(object sender, KeyEventArgs e) 
    { 
        CommandBase command = null;//用于保存命令的变量  
        Level level = Game.Level;  //得到当前关卡对象实例  
        if (Game != null)          //如果己经初始化了Game  
        {   //判断当前游戏的状态是否在运行状态  
            if (Game.GameState == GameState.Running) 
            { 
                switch (e.Key) //获取按键  
                { 
                    case Key.Up://如果是向上方向键,则向上移动  
                        command = new MoveCommand(level, Direction.Up); 
                        break; 
                       case Key.Down://如果是向下方向键,则向下移动  
                        command = new MoveCommand(level, Direction.Down); 
                        break; 
                       case Key.Left://如果是向左方向键,则向左移动  
                        command = new MoveCommand(level, Direction.Left); 
                        break; 
                       case Key.Right://如果是向右方向键,则向右移动  
                        command = new MoveCommand(level, Direction.Right); 
                        break; 
                       case Key.Z://如果是Ctrl+Z键  
                        if (Keyboard.Modifiers == ModifierKeys.Control) 
                        {   //执行Undo操作,将从撤消堆栈中取上一次执行的命令  
                            commandManager.Undo(); 
                        } 
                        break; 
                    case Key.Y://如果是Ctrl+Y键  
                        if (Keyboard.Modifiers == ModifierKeys.Control) 
                        {  //执行Redo操作,将从命令堆栈中取上一次执行的命令  
                            commandManager.Redo(); 
                        } 
                        break; 
                } 
            } 
            else 
            { 
                switch (Game.GameState) //根据游戏的不同状态判断  
                { 
                    case GameState.GameOver://如果游戏结束  
                        Game.Start();       //按任意键重新开始  
                        break; 
                    case GameState.LevelCompleted://如果关卡玩过关  
                        Game.GotoNextLevel();//按任意键开始下一关  
                        break; 
                } 
            } 
        } 
        if (command != null)//根据己经赋好的命令对象  
        {   //使用命令管理器的Execute执行命令  
            commandManager.Execute(command); 
        } 
    } 

 /// <summary>
        /// 处理Window控件的KeyDown事件
  /// </summary>
  void Window_KeyDown(object sender, KeyEventArgs e)
  {
   CommandBase command = null;//用于保存命令的变量
   Level level = Game.Level;  //得到当前关卡对象实例
   if (Game != null)          //如果己经初始化了Game
   {   //判断当前游戏的状态是否在运行状态
    if (Game.GameState == GameState.Running)
    {
     switch (e.Key) //获取按键
     {
      case Key.Up://如果是向上方向键,则向上移动
       command = new MoveCommand(level, Direction.Up);
       break;
                        case Key.Down://如果是向下方向键,则向下移动
       command = new MoveCommand(level, Direction.Down);
       break;
                        case Key.Left://如果是向左方向键,则向左移动
       command = new MoveCommand(level, Direction.Left);
       break;
                        case Key.Right://如果是向右方向键,则向右移动
       command = new MoveCommand(level, Direction.Right);
       break;
                        case Key.Z://如果是Ctrl+Z键
       if (Keyboard.Modifiers == ModifierKeys.Control)
       {   //执行Undo操作,将从撤消堆栈中取上一次执行的命令
        commandManager.Undo();
       }
       break;
      case Key.Y://如果是Ctrl+Y键
       if (Keyboard.Modifiers == ModifierKeys.Control)
       {  //执行Redo操作,将从命令堆栈中取上一次执行的命令
        commandManager.Redo();
       }
       break;
     }
    }
    else
    {
     switch (Game.GameState) //根据游戏的不同状态判断
     {
      case GameState.GameOver://如果游戏结束
       Game.Start();       //按任意键重新开始
       break;
      case GameState.LevelCompleted://如果关卡玩过关
       Game.GotoNextLevel();//按任意键开始下一关
       break;
     }
    }
   }
   if (command != null)//根据己经赋好的命令对象
   {   //使用命令管理器的Execute执行命令
    commandManager.Execute(command);
   }
  }
代码中的CommandBase 抽象基类用来保存具体的Command命令,这是利用了多态的原理。

如果游戏全Game 不为null,将根据游戏是否在运行状态获取用户所按下的按钮。

 

3、7  使用MultiDataTrigger改变方块外观
尽管使用Grid和Button布好了游戏界面,但是默认情况下,所有的Button都使用Cell外观。

幸好WPF提供了强大的样式触发器,可以根据指定的条件变更其外观。

到目前为止,已经使用了Expression Design创建了用于不同方块的图案及角色形状,并且这些图形都以画刷的形式嵌入到了应用程序的资源中。

那么通过MultiDataTrigger就可以根据特定的Cell名称来应用不同的按钮填充,使得按钮可以显示不同的外观。

对于方块内容样式,比如方块中是否有一个箱子,或是当前角色移动到某个方块的位置,使用MultiDataTrigger多条件触发器定义以下XAML代码:

 \

通过WPF提供的强大的样式触发器,当移动角色或推动箱子是地,UI端会自动变更显示内容,不需要编程实现设置图形的位置。

 

上面讲到Button的Style是Rectangle,这是在InitialiseLevel()方法中,为每一个单元格创建按钮时,指定了按钮的默认模式为Cell时指定的。

[csharp]
button.Style = (Style)Resources["Cell"];//指定按钮样式 

button.Style = (Style)Resources["Cell"];//指定按钮样式


Cell样式指定了按钮的呈现外观,因为按钮是一个内容控件,可以通过指定其Template来改变按钮的默认呈现。Cell样式如下:

 \

方块显示的Rectangle指定的Style为"CellStyle",它会根据Button所关联的不同的Cell类型来显示不同的方块样式。如下:

 \

 

通过这些样式触发器的设置,游戏关卡内容一旦回去,就自动使用各自不同的方块外观来显示整个游戏的布局。

当鼠标或键盘移动时,会根据CellContents的Name属性自动设置移动,实现了游戏的运行显示效果。

 

 作者:chenyujing1234

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值