• Silverlight+WCF 新手实例 象棋 该谁下棋B下A停(三十)


    在线演示地址:Silverlight+WCF 新手实例 象棋 在线演示

    上上一节,就是二十八节:Silverlight+WCF 新手实例 象棋 该谁下棋-A下B停(二十八)

    我们实现了“开始”游戏后,对棋子的限制,A下B停

    这节,我们要实现:B下A停,[同时,传递棋步,对方收到棋步,要反转棋步坐标,自动移动棋子]

    所以呢,这节我们要实现的东西是比上上一节相对多一点。

    少废话,开始了:

    按流程来了,A移动棋子之后,要干点什么事呢?

    //-------这是一个AB同样的循环流程-----

    1:自己不能再动了,IsCanMove=false;

    2:要记录移动坐标

    3:传递坐标给对方

    4:对方要接收坐标->反转坐标[对方的坐标对自己来说,肯定是相反的啦,自己把头反过来看下棋盘就有点清楚了]

    5:对方系统自动移动棋子

    6:对方的IsCanMove=true

    7:然后到对方下棋了。

    8:对方下棋之后呢?Go to 1

    //-----又回到开始,不断的循环------

    我们先来增加一个用于传递棋步类,既然是传递的,当然得在WCF端新建了,回到和Player一样位置[就是DataContract文件夹下了]:

    添加文件类名:MoveStep.cs

    namespace GameService
    {
        
    /// <summary>
        
    /// WCF 传递的棋步 by 路过秋天
        
    /// http://cyq1162.cnblogs.com
        
    /// </summary>
        public class MoveStep
        {

        }
    }

    当了棋步传递使者,自然得属性加身了,看看加了什么属性:

    ID:这个用于标识是第几步,好像没怎么用到

    Name:名称,是马还是炮

    ColorValue:什么颜色的

    下面四个一看就知,为什么不用Point传递,和那个ColorValue一样,WCF的Point和Silverlight客户端的名称空间不一样[马走一下]

    FromX

    FromY

    ToX

    ToY

    于是一个一个的敲完就像下面这样了:


    using System.Runtime.Serialization;
    namespace GameService
    {
        
    /// <summary>
        
    /// WCF 传递的棋步 by 路过秋天
        
    /// http://cyq1162.cnblogs.com
        
    /// </summary>
        public class MoveStep
        {
            
    /// <summary>
            
    /// 棋的步数
            
    /// </summary>
            [DataMember]
            
    public int ID
            {
                
    get;
                
    set;
            }
            
    /// <summary>
            
    /// 棋的原始X位置
            
    /// </summary>
            [DataMember]
            
    public double FromX
            {
                
    get;
                
    set;
            }
            
    /// <summary>
            
    /// 棋的原始Y位置
            
    /// </summary>
            [DataMember]
            
    public double FromY
            {
                
    get;
                
    set;
            }
            
    /// <summary>
            
    /// 棋的移动X位置
            
    /// </summary>
            [DataMember]
            
    public double ToX
            {
                
    get;
                
    set;
            }
            
    /// <summary>
            
    /// 棋的移动X位置
            
    /// </summary>
            [DataMember]
            
    public double ToY
            {
                
    get;
                
    set;
            }
            
    /// <summary>
            
    /// 棋的名称
            
    /// </summary>
            [DataMember]
            
    public string Name
            {
                
    get;
                
    set;
            }
            
    /// <summary>
            
    /// 棋的移颜色值
            
    /// </summary>
            [DataMember]
            
    public int ColorValue
            {
                
    get;
                
    set;
            }
        }
    }

    我们习惯了一直都传递Player,所以,为Player加个属性了:

    namespace GameService
    {
        
    /// <summary>
        
    /// 游戏玩家 by 路过秋天
        
    /// </summary>
        [DataContract]
        
    public class Player
        {
            
    //...省略其它属性...
            [DataMember]
            
    public MoveStep Step
            {
                
    get;
                
    set;
            }
           
        }
    }

    同时啊,同时啊,刚刚想起来-_-...,我们要为房间添加一个棋子列表,记录每步棋步,不然刚进房间的人看西北风的啊。

    同时添加了构造函数,初始化一下List,不然Null魂就会老跟着你。

    namespace GameService
    {
        [DataContract]
        
    public class Room
        {
            
    public Room()
            {
                StepList 
    = new List<MoveStep>();
            }
            
    /// <summary>
            
    /// 房间的棋谱
            
    /// </summary>
            [DataMember]
            
    public List<MoveStep> StepList
            {
                
    get;
                
    set;
            }
            
    //...省略下面N个属性...
          }
    }

    OK,传递使者和两个XX都有了,那我们要在WCF端建立传递和接收的接口了,这下我们只要传递Player来来去去的就行了:

    IService.cs添加接口:


    namespace GameService
    {
        [ServiceContract(CallbackContract 
    = typeof(ICallBack))]//头顶上这里写明了回调是ICallBack
        public interface IService
        {
           
    //...省略上面N个接口...
            [OperationContract(IsOneWay = true)]
            
    void MoveStep(Player player);

        }
    }

    ICallBack.cs添加接口:

    namespace GameService
    {
        
    interface ICallBack
        {
            
    //...省略上面N个接口...
            [OperationContract(IsOneWay = true)]
            
    void NotifyMoveStep(Player player);//通知接收棋步
        }
    }

    OK,接着我们一如既往的实现MoveStep接口方法

    Service.svc.cs,轻轻松松就完工,四行代码搞定。

     public void MoveStep(Player player)
            {
                Room room 
    = roomList[player.RoomID];
                player.Step.ID 
    = room.StepList.Count + 1;
                room.StepList.Add(player.Step);
                Notify.Game(player, GameType.Move);
            }

    那个Notify.Game我们上节都有的了,我们回到Notify里补一个Switch里的Case GameType.Move的方法就行了:

     internal static void Game(Player player, GameType type)
            {
                
    switch (type)
                {
                    
    case GameType.Start://通知对方玩家开始游戏
                        
    //...上上节实现了...
                        break;
                    
    case GameType.Move://通知移动了,房间内人手一份
                        foreach (KeyValuePair<Guid, Player> item in Service.playerList[player.RoomID])
                        {
                           item.Value.CallBack.NotifyMoveStep(player);
                        }
                        
    break;
                    
    case GameType.End:
                        
    break;
                }
            }

    OK,到此,服务端完成了,编绎,更新引用:

    接着我们回到客户端,要开始发送和接收了:

    哪里加发送呢?我们棋步在哪里移动,就在哪里发送了

    哪里移动呢?想啦啦找啦啦:棋子移动类ChessAction里的MoveTo方法,我们要在里面添加一个移动后触发的事件

    可是怎么触发?单独的类里,是拿不到App.Client对象,更别说传递了到WCF了,于是,大哥啊,代理快出来:

    还记得以前Silverlight+WCF 新手实例 象棋 主界面-控件消息传递(二十六),不记得回去看看了。

    我们在ChessAction里添加一个代理事件:

    看,我们定义代理事件之后只增加一句代码,在移动后直接调用,至于怎么实现的,我们全不理,反正有人帮我干这事。

     /// <summary>
        
    /// 棋子动作类 by 路过秋天
        
    /// </summary>
        public class ChessAction
        {
            
    public delegate void HelpMoveStep(Chessman chessman, Point movePoint);
            
    public event HelpMoveStep HelpMoveStepEvent;
           
            
            
    public bool MoveTo(Chessman chessman, Point moveTo)
            {
                
    if (Rule.IsCanMove(chessman, moveTo))
                {
                    chessman.ReadyMove 
    = false;
                    chessman.chessman.Background 
    = null;
                    PlayMove(chessman, moveTo);
                    chessman.MovePoint 
    = moveTo;
                    HelpMoveStepEvent(chessman, moveTo);
    //这里增加一句
                    return true;
                }
                
    return false;
            }
             
    //... 其它省略N多...
     }

    OK,我们回到Chess.xaml.cs里,我们要实现做下代理人:

    public Chess()
            {
               
    //..省略N行...
                chess.Action.HelpMoveStepEvent += new ChessNewInstance.ChessAction.HelpMoveStep(Action_HelpMoveStepEvent);
                App.chess 
    = chess;//为全局对象赋值
            }

            
    void Action_HelpMoveStepEvent(ChessNewInstance.Chessman chessman, Point moveTo)
            {
                MoveStep step 
    = new MoveStep();
                step.FromX 
    = chessman.MovePoint.X;
                step.FromY 
    = chessman.MovePoint.Y;
                step.ToX 
    = moveTo.X;
                step.ToY 
    = moveTo.Y;
                step.ColorValue 
    = chessman.Color == Colors.Red ? 1 : 2;
                step.Name 
    = chessman.Name;
                App.player.Step 
    = step;//附加棋步
                App.client.MoveStepAsync(App.player);
                chess.IsCanMove 
    = false;
            }

    设置完杂七杂八的参数后,把Step放到Player身上,就传递到服务端了,然后设置一下IsCanMove=false;

    发送棋步就搞完了,接下来要接收棋步了,不过在接收棋上之前,我们要先完成一个函数,反转坐标:

    我们回到Chess.cs象棋类里,添加方法,"马走一步",太简单了:

     /// <summary>
            
    /// 反转棋子坐标
            
    /// </summary>
            public Point ReverseArray(Point point)
            {
                point.X 
    = 8 - point.X;
                point.Y 
    = 9 - point.Y;
                
    return point;
            }

    别急,我们还要添加一个自动移动的方法:

    回到ChessAction.cs里:

    需要解释代码么?不需要吧

    解释:既然是系统自动移动,就不用判断什么规则了,直接把棋子移过去,如果移动到的另一个点有棋子,就移掉,然后设置一下坐标。

     /// <summary>
            
    /// 系统自动移动棋子
            
    /// </summary>
            public void AutoMoveTo(Point from, Point to)
            {
                Chessman chessman 
    = Parent.FindChessman(from);
                Chessman eatchessman 
    = Parent.FindChessman(to);
                
    if (chessman != null)
                {
                    PlayMove(chessman, to);
                    chessman.MovePoint 
    = to;
                    
    if (eatchessman != null)
                    {
                        eatchessman.GoToDead();
                    }
                }
            }

    好了,可以接收了,要实现了,眼睛睁大点,回到Chess.xaml.cs:

     public partial class Chess : UserControl
        {
            ChessNewInstance.Chess chess;
    //这里我们同时把它提到全局对象
            public Chess()
            {
               
    //...省略N行...
                App.client.NotifyMoveStepReceived += new EventHandler<NotifyMoveStepReceivedEventArgs>(client_NotifyMoveStepReceived);
                App.chess 
    = chess;//为全局对象赋值

              
            }

            
    void client_NotifyMoveStepReceived(object sender, NotifyMoveStepReceivedEventArgs e)
            {
                
    if (App.player.ID != e.player.ID)//非自己
                {
                    GameService.MoveStep step 
    = e.player.Step;
                    Point from 
    = new Point(step.FromX, step.FromY);
                    Point to 
    = new Point(step.ToX, step.ToY);
                    
    //转换坐标
                    if (e.player.ColorValue == 2 || App.player.ColorValue != 3)//旁观者 黑色棋子
                    {
                        from 
    = chess.ReverseArray(from);
                        to 
    = chess.ReverseArray(to);
                    }
                    chess.Action.AutoMoveTo(from, to);
                    
    if (App.player.ColorValue != 3)//下棋者
                    {
                        chess.IsCanMove 
    = true;
                    }
                }
            }
            
    //....省略N行...
        }

    看清楚,就是转换坐标,然后移动棋子,设置一下IsCanMove。

    OKOKOK,代码终于全部写完了,可以F5运行看效果了:

    “马再走一步”,上面代码棋子没有自动移动,又要调试了,不截图先:

    断点一调试,发现接收的点都是一样的,一步步回去查,终于发现在MoveTo方法里添加的一行事件位置不对:

    看有位置的那两行,看清楚了。

    public bool MoveTo(Chessman chessman, Point moveTo)
            {
                
    if (Rule.IsCanMove(chessman, moveTo))
                {
                    chessman.ReadyMove 
    = false;
                    chessman.chessman.Background 
    = null;
                    PlayMove(chessman, moveTo);
                    HelpMoveStepEvent(chessman, moveTo);
    //这一行要在上
                    chessman.MovePoint = moveTo;//这一行要在下
                    
                    
    return true;
                }
                
    return false;
            }

    OK,现在可以F5看效果了,截图:

    OK,本节到此,打完收工!

    顺逢周五,打包源码:第六阶段源码:点击下载

    版权声明:本文原创发表于 博客园,作者为 路过秋天 本文欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。
    个人微信公众号
    创业QQ群:617713515
    Donation(扫码支持作者):支付宝:
    Donation(扫码支持作者):微信:
  • 相关阅读:
    Is It A Tree?(并查集)(dfs也可以解决)
    【转】python中查询某个函数的使用方法
    python2和python3 print输出不换行
    Python在Windows下列出所有的安装包和模块
    构建打包发布模块及导入使用
    Python保存时提示“SyntaxError: Non-ASCII character 'xe8' in file”
    python基础学习(一)
    命令别名设定:alias,unalias 历史命令:history
    变量内容的删除、取代与替换(optional)
    变量键盘读取、数组与宣告:read,array,declare
  • 原文地址:https://www.cnblogs.com/cyq1162/p/1788879.html
Copyright © 2020-2023  润新知