程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#入門學習-----推箱子游戲(WPF技術實現)

C#入門學習-----推箱子游戲(WPF技術實現)

編輯:C#入門知識

編譯平台: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
 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved