Silverlight+WCF 新手實例 象棋 棋子移動-吃子(五)

上一節,我們的棋子就是一個Canvas,裏面add進了一個Ellipse圓圈和TextBlock字

想想我們是怎麼下棋的,要先選中棋子吧,選中後,隨便找個地方點,棋就會自動移過去。

所以,這裏就產生了兩件事,一是選中,二是移動。

要選中,其實就是選中棋子,選中棋子就是選中Canvas了。

於是,我們爲Canvas增加一個鼠標點擊事件。

讓我們回到棋子類Chessman的Draw方法裏,爲chessman添加一個MouseLeftButtonDown事件,於是代碼變成了

 

複製代碼
ExpandedBlockStart.gif
  private   void  Draw()
        {
            
// 這裏實現畫啦
            Ellipse elp  =   new  Ellipse()
            {
                Width 
=  Radius  *   2 ,
                Height 
=  Radius  *   2 ,
                Stroke 
=   new  SolidColorBrush(Color),
                Fill 
=   new  SolidColorBrush(Color),
                Opacity 
=   15
            };
            TextBlock text 
=   new  TextBlock()
            {
                TextAlignment 
=  TextAlignment.Center,
                Foreground 
=   new  SolidColorBrush(Colors.White),
                Text 
=  Name,
                FontFamily 
=   new  FontFamily( " 宋體 " ),
                FontSize 
=  Radius,
                FontWeight 
=  FontWeights.Bold,
                Margin 
=   new  Thickness(Radius  /   2   -   2 , Radius  /   2   -   2 0 0 )
            };
            chessman 
=   new  Canvas();
            
// ----這裏新加一個事件啦-----
            chessman.MouseLeftButtonDown  +=   new  MouseButtonEventHandler(chessman_MouseLeftButtonDown);
            
// ----這裏新加一個事件啦-----
            Point pixel  =  Parent.SwitchPixelArray(InitPoint);
            chessman.Margin 
=   new  Thickness(pixel.X  -  Radius, pixel.Y  -  Radius,  0 0 );
            chessman.Children.Add(elp);
            chessman.Children.Add(text);
            container.Children.Add(chessman);
        }
        
// 新加的事件方法
         void  chessman_MouseLeftButtonDown( object  sender, MouseButtonEventArgs e)
        {
            MessageBox.Show(
" 你選中的是: "   +  Name);
        }
複製代碼

 

其實就一共新增加了三行代碼,運行看看效果。

好了,選中是OK了,那我們怎麼移動?

Silverlight裏有幾種移動方法,這裏挑了Storyboard故事板來移動棋子。

按原始想法,棋子自己不會動,所以新建一個ChessAction類來實現棋子的移動。

啥也不說,對着類庫右鍵-》添加類—》ChessAction.cs就新建了。

 

複製代碼
  ///   <summary>
    
///  棋子動作類 by 路過秋天
    
///   </summary>
     public   class  ChessAction
    {

    }
複製代碼

 

想想棋子是怎麼動的?其實就兩個動作,一個是吃子,另一個沒子吃直接移動。於是呢,就先產生兩個方法:

 

複製代碼
ExpandedBlockStart.gif
  ///   <summary>
    
///  棋子動作類 by 路過秋天
    
///   </summary>
     public   class  ChessAction
    {
        
///   <summary>
        
///  吃子
        
///   </summary>
        
///   <param name="moveChessman"> 移動的棋子 </param>
        
///   <param name="eatChessman"> 被吃的棋子 </param>
         public   void  EatChessman(Chessman moveChessman, Chessman eatChessman)
        {

        }
         
///   <summary>
        
///  移動棋子
        
///   </summary>
        
///   <param name="chessman"> 棋子 </param>
        
///   <param name="toX"> 移動到X座標 </param>
        
///   <param name="toY"> 移動到Y座標 </param>
         public   bool  MoveTo(Chessman chessman, Point moveTo)
        {
            
return   true ;
        }
    }
複製代碼

 

再想想,其實吃子,就是移動棋子,讓後叫被吃的那個子離開自己的位置gotodead。

-_-..這裏順便爲Chessman棋子類自己加個方法叫GoToDead好了。

複製代碼
ExpandedBlockStart.gif
///   <summary>
        
///  銷亡
        
///   </summary>
         public   void  GoToDead()
        {
            container.Children.Remove(chessman);
// 從控件中移除
            Parent.ChessmanList.Remove( this ); // 從棋子列表移除
            
        }
複製代碼

 

OK,我們可以爲吃子方法寫完它了。先移動棋子,然後叫被吃的自己GoToDead了。

複製代碼
ExpandedBlockStart.gif
public   void  EatChessman(Chessman moveChessman, Chessman eatChessman)
        {
            
if  (MoveTo(moveChessman, eatChessman.MovePoint))
            {
                eatChessman.GoToDead();
            }
        }
複製代碼

 

說來說去,就剩下要完成MoveTo的時候,棋子要移動了。

我們把移動的動作封成一個函數叫PlayMove,所以在MoveTo裏輕鬆調用PlayMove就搞定了。

複製代碼
ExpandedBlockStart.gif
public   bool  MoveTo(Chessman chessman, Point moveTo)
        {
            PlayMove(chessman, moveTo);
            
return   true ;
        }
        
void  PlayMove(Chessman chessman, Point moveTo)
        {
            
// 這裏完成移動啦
        }
複製代碼

 

移動的代碼的故事版,先上代碼,再解說

複製代碼
ExpandedBlockStart.gif
  void  PlayMove(Chessman chessman, Point moveTo)
        {
            
// 這裏完成移動啦
            moveTo  =  Parent.SwitchPixelArray(moveTo);
            Point initPixel 
=  Parent.SwitchPixelArray(chessman.InitPoint);
            Point movePixel 
=  Parent.SwitchPixelArray(chessman.MovePoint);

            Storyboard sb 
=   new  Storyboard(); // 創建動畫版
            
// 創建X方向的動畫
            DoubleAnimation daX  =   new  DoubleAnimation();
            daX.Duration 
=   new  Duration(TimeSpan.FromMilliseconds( 200 ));
            daX.From 
=  movePixel.X  -  initPixel.X;
            daX.To 
=  moveTo.X  -  initPixel.X;
          
            
// 創建Y方向的動畫
            DoubleAnimation daY  =   new  DoubleAnimation();
            daY.Duration 
=   new  Duration(TimeSpan.FromMilliseconds( 200 ));
            daY.From 
=  movePixel.Y  -  initPixel.Y;
            daY.To 
=  moveTo.Y  -  initPixel.Y;
         
            
// 設置動畫版上的目標
            Storyboard.SetTarget(daX, chessman.chessman);
            Storyboard.SetTarget(daY, chessman.chessman);
            
// 設置動畫版上的目標要變化的屬性
            Storyboard.SetTargetProperty(daX,  new  PropertyPath( " (Canvas.Left) " ));
            Storyboard.SetTargetProperty(daY, 
new  PropertyPath( " (Canvas.Top) " ));

            sb.Children.Add(daX);
            sb.Children.Add(daY);
            sb.Begin();

            sb 
=   null ;
        }
複製代碼

 

其實Storyboard的類用法,比較死的,記住怎麼用就行了,記不住就Copy多幾次也就差不多了。

實在看不懂,多在博客園裏多搜搜該類的用法,看來看去基本就是這麼用,看的多了習慣了就也是這麼一回事了。

這裏說幾個注意點:

1。Parent哪來的?我們的棋子類不是也有一個Parent麼,其實就是Chess類對象了。

所以呢,我們要爲ChessAction添加一個屬性和構造函數,讓Chess對象傳進來啦。

複製代碼
ExpandedBlockStart.gif
///   <summary>
        
///  Chess對象
        
///   </summary>
         public  Chess Parent
        {
            
get ;
            
set ;
        }
        
public  ChessAction(Chess ownChess)
        {
            Parent 
=  ownChess;
        }
複製代碼

 

2。就是那個座標要先轉換成像素座標,比較滑動的時候是用像素來算的

3。這個重要了:故事板滑動的幾個from,to的像素,要減去棋子本身的初始相對座標,才能滑的正確。

關於這個,我調試了好久,才讓它滑的正確,默認博客園裏的相關文章都沒有相對物體一開始就相對存在的情況做說明。

4。最後一點就是,在Chess類的構造函數裏默認實例化一下這個ChessAction對象。

其實Chess類的構造函數加了一行代碼,同時多了一個屬性

複製代碼
ExpandedBlockStart.gif
///   <summary>
        
///  棋子動作,新加的屬性
        
///   </summary>
         public  ChessAction Action
        {
            
get ;
            
set ;
        }
        
public  Chess(Panel control)
        {
            container 
=  control;
            ChessmanList 
=   new  List < Chessman > ( 32 );
            Action 
=   new  ChessAction( this );//新增加的
        }
複製代碼

 

OK,該寫的都寫的差不多完了。可是目前運行的話,還是沒有效果可看。

我們還是趕緊弄一下吃子的效果出來先。吃子,就是選中一顆棋子,然後再點另一顆棋子。

所以在棋子被點擊的時候,我們要判斷,是不是已經有了一顆棋子被點擊了,如果有,就執行吃子動作了。

這裏要判斷,怎麼判斷有沒有已經被選中的,這裏我倒有一個方法:

爲棋子增加一個ReadyMove屬性

複製代碼
  ///   <summary>
        
///  待移動
        
///   </summary>
         public   bool  ReadyMove
        {
            
get ;
            
set ;
        }
複製代碼

 

這樣,我們被點擊的時候,只要設置一下自己的ReadyMove就行了。

那我們怎麼找哪一顆棋子曾經被選中??這個簡單了,遍歷棋子列表,找出ReadyMove=true的棋子就行了。

我們爲Chess類增加一個ReadyMoveChessman屬性,返回被激活ReadyMove爲true的棋子。

 

複製代碼
ExpandedBlockStart.gif
  ///   <summary>
        
///  激活的棋子,就是選中要下的棋子。
        
///   </summary>
         public  Chessman ReadyMoveChessman
        {
            
get
            {
                
foreach  (Chessman chessman  in  ChessmanList)
                {
                    
if  (chessman.ReadyMove)
                    {
                        
return  chessman;
                    }
                }
                
return   null ;
            }
        }
複製代碼

 

OK,現在我們可以回到棋子點擊事件裏,加if判斷如下:

 

複製代碼
ExpandedBlockStart.gif
  // 新加的事件方法
         void  chessman_MouseLeftButtonDown( object  sender, MouseButtonEventArgs e)
        {
            
if  (ReadyMove) // 取消激活
            {
                ReadyMove 
=   false ;
                chessman.Background 
=   null ;
            }
            
else   if  (Parent.ReadyMoveChessman  ==   null ) // 激活
            {
                ReadyMove 
=   true ;
                chessman.Background 
=   new  SolidColorBrush(Colors.Blue); // 選中時加個背景色
            }
            
else // 吃子
            {
                
if  (Parent.ReadyMoveChessman.Color  ==   this .Color) // 同顏色,切換
                {
                    Parent.ReadyMoveChessman.chessman.Background 
=   null ;
                    Parent.ReadyMoveChessman.ReadyMove 
=   false ;
                    ReadyMove 
=   true ;
                    chessman.Background 
=   new  SolidColorBrush(Colors.Blue);

                }
                
else
                {
                    Parent.Action.EatChessman(Parent.ReadyMoveChessman, 
this );
                }
            }
        }
複製代碼

 

OK,按F5執行。

1。點擊棋子,發現棋子背景色沒變?

2。點另一個棋子,發現吃棋子動作完成了。這個很好。

3。再點另一個棋子,還是繼續吃棋?

小小調試了一下發現:

1。背景色沒變,原來是忘記了給Canvas弄個寬和高。

到Chessman類裏,在Draw函數裏找到Canvas,設置一下寬和高就行了。

複製代碼
ExpandedBlockStart.gif
private   void  Draw()
        {
            
// 這裏實現畫啦
            Ellipse elp  =   new  Ellipse()
            {
                Width 
=  Radius  *   2 ,
                Height 
=  Radius  *   2 ,
                Stroke 
=   new  SolidColorBrush(Color),
                Fill 
=   new  SolidColorBrush(Color),
                Opacity 
=   15
            };
            TextBlock text 
=   new  TextBlock()
            {
                TextAlignment 
=  TextAlignment.Center,
                Foreground 
=   new  SolidColorBrush(Colors.White),
                Text 
=  Name,
                FontFamily 
=   new  FontFamily( " 宋體 " ),
                FontSize 
=  Radius,
                FontWeight 
=  FontWeights.Bold,
                Margin 
=   new  Thickness(Radius  /   2   -   2 , Radius  /   2   -   2 0 0 )
            };
            chessman 
=   new  Canvas()
            {
                Width 
=  elp.Width, // 新增加的寬
                Height  =  elp.Height // 新增加的高
            };
            chessman.MouseLeftButtonDown 
+=   new  MouseButtonEventHandler(chessman_MouseLeftButtonDown);
            Point pixel 
=  Parent.SwitchPixelArray(InitPoint);
            chessman.Margin 
=   new  Thickness(pixel.X  -  Radius, pixel.Y  -  Radius,  0 0 );
            chessman.Children.Add(elp);
            chessman.Children.Add(text);
            container.Children.Add(chessman);
        }
複製代碼

 

於是點擊時候的背景色出來了。

3.怎麼一直吃子?那是移動後呢?有手尾要做的

a.設置棋子的ReadyMove=false;

b.去掉棋子的背景色。

c.移動後,棋子的Move座標要換成被吃棋子的座標。

於是,我們回到MoveTo函數裏,新增加這幾個手尾:

複製代碼
ExpandedBlockStart.gif
public   bool  MoveTo(Chessman chessman, Point moveTo)
        {
            chessman.ReadyMove 
=   false ;
            chessman.chessman.Background 
=   null ;
            
            PlayMove(chessman, moveTo);
            chessman.MovePoint 
=  moveTo;
            
return   true ;
        }
複製代碼

 

好了,手尾弄好了,現在移動棋子就變成吃子了:

OK,到現在棋子終於可以走了,不過目前只是吃子,而且是隨便吃的。。。

下節說不吃子,讓棋子走到線的交叉點上。

OK,打完收工

作者博客:http://cyq1162.cnblogs.com/

版權聲明:本文原創發表於博客園,作者爲路過秋天,原文鏈接:

http://www.cnblogs.com/cyq1162/archive/2010/07/08/1773840.html