zoukankan      html  css  js  c++  java
  • Silverlight+WCF 新手实例 象棋 主界面棋谱回放(三十九)

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

    本节完后,同时会更新Silverlight+WCF 新手实例 象棋 专题索引,并顺路提供第八阶段源码

    Silverlight+WCF 新手实例 象棋 主界面-棋谱-布局写谱(三十六)节中,我们完成了下棋双方的棋谱传递

    Silverlight+WCF 新手实例 象棋 主界面-棋谱-获取列表(三十八)节中,我们完成了观棋者获取棋谱列表

    在本节中,我们要进行最一步了,棋谱回放:

    首先,当用户进入列表后,获取完棋谱信息之后,第一个动作,就是要把棋谱按顺序播放一下,这样,用户看到棋局就是双方正在下的棋局了,之后,就跟下棋一样,一步步接收棋步,然后一步步的自动移动就行了。

    我们播放哪里的棋谱呢的?

    界面上显示的ListBox显然是不能用来循环读取的,毕竟那显示的是格式化后的字符串,从字符串解析回棋步是不现实的;

    一开始获取的列表不是可以循环?那也只有一部分,之后棋手双方下的呢?

    所以,说了这么多,其实不就是想找多个变量来存棋谱列表么,于是,我们在App.xaml.cs全局又添加一个全局变量了:

    public partial class App : Application
        {
         
    //...省略N行代码...
            public static List<GameService.MoveStep> stepList = new List<GameService.MoveStep>();//棋谱
            public static bool chessManualPlaying = false;//棋谱正回放中
            
    //...省略N行代码...
            public App()
            {
             
    //...省略N行代码...
            }
            
    //...省略N行代码...

    }

    这里又顺路添加了一个全局属性,用于设置棋谱是不是正在回放中,后面我们会用到[很后哦]。

    OK,全局棋谱存贮变量有了,我们就得为它增加棋谱数据了,我们会在两个地方添加棋谱,

    1:一个是用户下棋时传递时的棋谱:

    我们进入Chess.xaml.cs,找到收到棋谱通知的代码,只需要添加一行,第一行,把棋步添加一下就行了:

    void client_NotifyMoveStepReceived(object sender, NotifyMoveStepReceivedEventArgs e)
            {
                App.stepList.Add(e.player.Step);
    //只需要一行
                if (App.player.ID != e.player.ID)//非自己
                {
                   
    //...省略N行...
                }
                HelpSetChessManualEvent(e.player.Step);
            }

    2:进入页面时获取的棋谱列表读取到棋谱区时:

    我们进入ChessManual.xaml.cs,找到获取棋谱列表的代码,同样只需要一行:

     void client_GetMoveStepListCompleted(object sender, GameService.GetMoveStepListCompletedEventArgs e)
            {
                
    //获取完棋谱后,这里循环调用添加就可以了
                if (e.Result != null && e.Result.Count > 0)
                {
                    App.stepList 
    = e.Result;//这里只需要一行代码
                    foreach (GameService.MoveStep step in e.Result)
                    {
                        lbChessManual.Items.Add(step.ID 
    + ":" + step.Name);
                    }
                }
            }

    OK,我们对全局棋谱的传值,两行代码就搞定了,于是在播放棋谱时,理论上我们循环全局的stepList就行了:

    我们先产生一个方法,用于播放一个棋步,就是传进一个棋步,然后自动播放:

    void AutoMove(GameService.MoveStep step)
            {
                Point from 
    = new Point(step.FromX, step.FromY);
                Point to 
    = new Point(step.ToX, step.ToY);
                
    if (App.player.ColorValue + step.ColorValue == 3 || (step.ColorValue == 2 && App.player.ColorValue == 3))//旁观者 黑色棋子
                {
                    from 
    = App.chess.ReverseArray(from);
                    to 
    = App.chess.ReverseArray(to);
                }
                App.chess.Action.AutoMoveTo(from, to);
            }

    这个方法其实有点简单,只要调用AotoMoveTo就搞完了,唯一的复杂点,就是判断要不要反转坐标了。

    [两种情况:如果是下棋者,棋步是对手的,要反转坐标;观众默认是红色的,收到黑色棋步,也要反转坐标]

    好了,有了单步的方法,我们for一下step里,然后循环传值就OK了?No,如果这样循环,棋步刷的一下就没了,还看啥回放?

    因此,我们需要定间隔播放了,所以,我们需要先产生间隔的秒数,我们通过滑动块来设置文本框的数字:

    我们设置一下滑动块的属性[默认值为0,最大值为9,最小滑动为0.5,最大滑动为1]:

    我们同时添加滑动事件:

    而我们滑动时只需要一行代码,为文本框赋值就行了:

    private void slPlayerInternal_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
            {
                txtValue.Text 
    = e.NewValue.ToString();
            }

    我们增加一个方法,叫PlayStep,用于循环播放棋步:

    private void PlayStep()
    {     
       
    //待实现
    }

    好了,接着我们双击一下“回放”按钮,里面调用一下这个PlayStep就行了:

    private void btnPlay_Click(object sender, RoutedEventArgs e)
    {
      PlayStep();
    }

    默认我们加载完棋步列表后,也要定位棋步,所以也调用一下PlayStep:

    void client_GetMoveStepListCompleted(object sender, GameService.GetMoveStepListCompletedEventArgs e)
            {
                
    //获取完棋谱后,这里循环调用添加就可以了
                if (e.Result != null && e.Result.Count > 0)
                {
                    App.stepList 
    = e.Result;//这里只需要一行代码
                    foreach (GameService.MoveStep step in e.Result)
                    {
                        lbChessManual.Items.Add(step.ID 
    + ":" + step.Name);
                    }
                    PlayStep();
    //加完后马上调用播放棋步,默认0秒,所以刷一下就播放完了
                }
            }

    于是,所有的重点只剩下怎么实现这个PlayStep了。

    我们每一步棋步都需要间隔N秒走一步,一开始我竟然用线程的Sleep来操作,结果是,程序卡住了,直接播放完了才能动,那个汗-_-!

    于是,N久之后,非线程阻塞的timer出来了,让它定时每N秒走一步就行了:

    全局定义一下timer先,顺路加上移动的索引:

    public partial class ChessManual : UserControl
        {
            System.Windows.Threading.DispatcherTimer timer;
    //定义timer
            int moveStepIndex = 0;//移动到第几步了
             
             
    //...下面省略N行...
    }

    接着初始化timer及事件:

     public partial class ChessManual : UserControl
        {
            System.Windows.Threading.DispatcherTimer timer;
    //定义timer
            int moveStepIndex = 0;//移动到第几步了
            public ChessManual()
            {
                
    //...省略N行...
                timer = new System.Windows.Threading.DispatcherTimer();
                timer.Tick 
    += new EventHandler(timer_Tick);
            }

            
    void timer_Tick(object sender, EventArgs e)
            {
                
    //待实现
            }

                
    //...省略N行...
         }

    我们这里没有初始化timer的间隔时间,是因为把间隔时间设置放到PlayStep函数里了,说到PlayStep,我们可以实现它了:

    private void PlayStep()
            {
                
    if (App.stepList.Count > 0)
                {
                    
    //播放前复位棋子
                      App.chess.Reset();
                    timer.Interval 
    = TimeSpan.FromSeconds(slPlayerInternal.Value);
                    timer.Start();
                }
            }

    我们只用了小小几行代码,其实就是设置一下时间,然后让timer它Start起来就完事了,于是重心又一步转移到timer的tick事件里了:

     void timer_Tick(object sender, EventArgs e)
            {
                
    //待实现
                GameService.MoveStep step = App.stepList[moveStepIndex];//获取棋步
                lbChessManual.SelectedIndex = moveStepIndex;//定位棋步索引
                lbChessManual.UpdateLayout();//需要更新下布局
                lbChessManual.ScrollIntoView(lbChessManual.SelectedItem);//滚动到先中的项
                AutoMove(step);//移动棋步
                moveStepIndex++;//索引加1
                if (moveStepIndex == App.stepList.Count)//判断棋步结束没有
                {
                    moveStepIndex 
    = 0;//重置索引
                    timer.Stop();//停止timer
                }
            }

    这里每一步都有注释,相信不会看不懂的了。OK,至此,我们是可以F5看效果了:

    1:目前正在下棋,下到一半中:

    2:观众进来了,获取完棋步,并播放了一次,定位到最后一步:

    3:设置为2秒,点“回放”,自动播放了第一步:

    4:回放走到第二步:

    5:回放走到最后一步了:

    OK,到此,我们的棋谱回放功能基本完成了,只是有一些细节,我们是需要另外处理了:

    1:下棋者在下棋过程,要不要开放“回放”功能,如果开放,需要注意什么?

    2:观众在回放过程中,突然又传来一个棋步,需要注意什么?

    这两个小细节,下节处理,本系列到这里,所有基础区的功能都基本完成了差不多了,那本系列是不是也接近尾声了?

    OK,本节点到为止了,提供第八阶段源码:点击下载

    版权声明:本文原创发表于 博客园,作者为 路过秋天 本文欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。
    个人微信公众号
    创业QQ群:617713515
    Donation(扫码支持作者):支付宝:
    Donation(扫码支持作者):微信:
  • 相关阅读:
    ant-design-vue a-tree默认展开所有父节点不生效
    CSS模型简介
    一点BFC的看法
    css提高开发效率的必备代码
    CSS模型简介-逆战班
    CSS 样式:常用居中方法
    rem 自适应布局 js 代码
    CSS 样式 :position-absolute 绝对定位属性
    CSS 样式
    CSS样式字体初解
  • 原文地址:https://www.cnblogs.com/cyq1162/p/1795873.html
Copyright © 2011-2022 走看看