zoukankan      html  css  js  c++  java
  • 基于Udp的五子棋对战游戏

    引言

    本文主要讲述在局域网内,使用c#基于Udp协议编写一个对战的五子棋游戏。主要从Udp的使用、游戏的绘制、对战的逻辑这三个部分来讲解。

    开发环境:vs2013,.Net4.0,在文章的末尾提供源代码下载的地址。
     

    Udp通信


    Udp是基于无连接的传输协议,特点是资源消耗小、处理速度快、使用方便,不需要与接收方建立连接即可发送消息,但是对方有可能会接受不到发送的消息。在.Net中提供了UdpClient类来实现基于Udp协议的通信,下面就讲解一下Udp的基本使用。
     
    1、发送信息
    首先声明一个UdpClient对象。
    UdpClient udpSend;
    然后建立一个方法去发送信息即可。
      public void Send(string sendMsg)
            {
                udpSend = new UdpClient();
                byte[] byteMsg = Encoding.Default.GetBytes(sendMsg);
                udpSend.Send(byteMsg, byteMsg.Length, this.sendIp, sendPort);
                udpSend.Close();
            }
     
    2、接受信息
    首先也声明一个UdpClient对象。
     UdpClient  udpReceive;
    然后建立一个方法初始化UdpClient对象,开辟一个线程去接受消息以及一个接受消息的方法
     
     public void StartReceive()
            {
                //接受端口5888的消息
                udpReceive = new UdpClient(5888);
                Thread threadReceive = new Thread(ReceiveMessages);
                threadReceive.IsBackground = true;
                threadReceive.Start();
            }
        private  void ReceiveMessages()
            {
                IPEndPoint remoteIPEndPoint = new IPEndPoint(IPAddress.Any, 0);//获取发送信息方的ip和端口信息
                while (true)
                {
                    try
                    {
                        //关闭receiveUdpClient时此句会产生异常
     
                        byte[] receiveBytes = udpReceive.Receive(ref remoteIPEndPoint);
     
                        string message = Encoding.Default.GetString(receiveBytes, 0, receiveBytes.Length);
                        MessageParse(message);//去分析消息,并处理
                    }
                    catch
                    {
                        break;
                    }
                }
            }
    到此,接受信息和发送信息就接受,相比Tcp协议来说,使用这个是更加简,。是在局域网内对战游戏通信的最佳选择。
    但是也有值得注意的地方。不能同时运行两个一样的程序,在接受信息的时候,不能有两个upd实例同时去监听相同的端口号。比如在写完程序之后需要用在本机上使用“127.0.0.1”这个ip去调试自己变成的程序。那么程序就会是这样。
    程序1:UdpReceive接受的端口号为5888。UdpSend发送的ip和端口号分别为“127.0.0.1”和“5888”。
    程序2:UdpReceive接受的端口号也是5888。UdpSend发送的ip和端口号分别为“127.0.0.1”和“5888”。
    这样运行程序2的时候就会出错,因为程序1和程序2监听的是同一个端口号,这是不被允许的(udp监听的端口号不能被其他程序占,用要唯一),所以要进行小小的修改。
    程序1:UdpReceive UdpReceive接受的端口号为5888。UdpSend发送的ip和端口号分别为“127.0.0.1”和“5887”。
    程序2:UdpReceive接受的端口号也是5887。UdpSend发送的ip和端口号分别为“127.0.0.1”和“5888”。
    这样稍微变动一下就不会出错了。
     
     

    游戏的绘制


    玩过五子棋的都知道,主要是绘制棋盘和棋子,稍微再人性化的就是把对方最后一个放置的棋子标注出来,让我们更加清楚的知道对方的刚下的棋子是哪一个。

    首先需要建立一个全局变量,保存棋盘中各个位置的状态。
      const int BOARDSIZE = 15;
      const int BOARDLENGTH = 800;
     int[,] chessMap = new int[BOARDSIZE, BOARDSIZE];//其中值为0:没有棋子,1:白棋,2:黑棋
    在棋盘控件PictureBox控件的Paint方法写如下代码:
     //paint方法
       private void picChessboard_Paint(object sender, PaintEventArgs e)
            {
                Graphics g = e.Graphics;
                //绘制棋盘
                chessBoard.DrawBoard(g);
                //绘制棋子
                chessBoard.DrawChess(g);
            }
     
      //初始化全局变量
            public void InitialChess()
            {
                for (int i = 0; i < BOARDSIZE; i++)
                {
                    for (int j = 0; j < BOARDSIZE; j++)
                    {
                        chessMap[i, j] = 0;
                    }
                }
                this.picChessboard.Invalidate();
            }
            //绘制棋盘
            public  void DrawBoard(Graphics g)
            {
                Pen p = new Pen(Brushes.Black, 3.0f);
                //  p.Width = 2f;
                //横线
                for (int i = 0; i < BOARDSIZE; i++)
                {
                    g.DrawLine(p, new Point(0, (i + 1) * 50), new Point(BOARDLENGTH, (i + 1) * 50));
                }
                //竖线
                for (int i = 0; i < BOARDSIZE; i++)
                {
                    g.DrawLine(p, new Point((i + 1) * 50, 0), new Point((i + 1) * 50, BOARDLENGTH));
                }
            }
     
            //绘制棋子
            public void DrawChess(Graphics g)
            {
     
                for (int i = 0; i < BOARDSIZE; i++)
                {
                    for (int j = 0; j < BOARDSIZE; j++)
                    {
                        if (chessMap[i, j] == 1)
                        {
                            g.DrawImage(Properties.Resources.whitechess, new Point(50 * (i + 1) - 20, 50 * (j + 1) - 20));
                        }
                        if (chessMap[i, j] == 2)
                        {
                            g.DrawImage(Properties.Resources.blackchess, new Point(50 * (i + 1) - 20, 50 * (j + 1) - 20));
                        }
                    }
                }
                if (pCurrent.X !=-1)
                {
                    //绘制最后落下棋子上的红色标注
                    g.FillEllipse(Brushes.Red, new Rectangle((pCurrent.X + 1) * 50-5, (pCurrent.Y + 1) * 50-5, 10, 10));
                }
            }
     
    到此游戏的界面完成了,效果如下。
     
     

    对战逻辑


    对战的逻辑简单的说就是两部分。1、自己下棋,然后设置全局变量chessMap 通知界面绘制棋子;接受对方下棋的消息,然后设置全局变量chessMap 通知界面绘制棋子;2、下棋之后检测输赢情况。

    在这里只贴出关键的代码,具体细节可以把我的源码下载下来,里面的注释写的还算详细。
     

    1、下棋的代码

       
     /// <summary>
            /// 下棋
            /// </summary>
            /// <param name="flag">设置全局变量chessMap的标志</param>
            /// <param name="x">棋盘x坐标</param>
            /// <param name="y">棋盘y坐标</param>
            public void PutOneChess(int flag, int x, int y)
            {
                if (isPut||myFlag !=flag)//判断是否是自己下棋,或者是别人下棋
                {
                    //计算鼠标点击位置在棋盘的中的行、列的位置
                    int tolerance = 8;
                    int row = y / 50;
                    int rows = y % 50;
                    int col = x / 50;
                    int cols = x % 50;
     
                    if (rows + tolerance >= 50)
                    {
                        row++;
                    }
                    else if (rows - tolerance <= 0)
                    {
                    }
                    else
                    {
                        return;//没有选中
                    }
     
                    if (cols + tolerance >= 50)
                    {
                        col++;
                    }
                    else if (cols - tolerance <= 0)
                    {
                    }
                    else
                    {
                        return;
                    }
     
                    col--;
                    row--;
     
                    if (col >= 0 && col < BOARDSIZE && row >= 0 && row < BOARDSIZE)
                    {
                        this.chessMap[col, row] = flag;
                        pCurrent = new Point(col, row);//保存最新放置棋子的位置,以便标注和悔棋
                        this.picChessboard.Invalidate();
                        if (myFlag == flag)//如果是自己下棋
                        {
                            this.isPut = false;//轮到对方走棋
                            UpdateRemoteChessBoardDelegate(flag, x, y);//将更新对方的棋盘
     
                            if (IsWin(col, row))//自己赢了
                            {
                                InformRemoteResultDelegate();
                            }
                        }
                        else
                        {
                            this.isPut = true;//轮到自己下棋了。
                        }
                    }
                }
            }

    2、检测输赢的代码 

     //横向
            private bool Is1(int c, int r)
            {
                int count = 1;
                for (int i = c+1; i <c+5; i++)
                {
                    if(i<BOARDSIZE)
                    {
                        if (chessMap[i, r] == myFlag)
                        {
                            count++;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        break ;
                    }
                }
                for (int i = c - 1; i > c - 5; i--)
                {
                    if (i >= 0)
                    {
                        if (chessMap[i, r] == myFlag)
                        {
                            count++;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                if (count > 4)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
     
            //纵向
            private bool Is2(int c, int r)
            {
                int count = 1;
                for (int i = r + 1; i < r + 5; i++)
                {
                    if (i < BOARDSIZE)
                    {
                        if (chessMap[c, i] == myFlag)
                        {
                            count++;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                for (int i = r - 1; i > r - 5; i--)
                {
                    if (i >= 0)
                    {
                        if (chessMap[c, i] == myFlag)
                        {
                            count++;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                if (count > 4)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
     
            //左上-右下
            private bool  Is3(int c,int r)
            {
                int count = 1;
                for (int i = 1; i < 5;i++ )
                {
                    if ((c - i) >= 0 && (r - i)>=0)
                    {
                        if (chessMap[c-i,r-i] == myFlag)
                        {
                            count++;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                for (int i = 1; i < 5; i++)
                {
                    if ((c + i) < BOARDSIZE && (r + i) < BOARDSIZE)
                    {
                        if (chessMap[c + i, r +i] == myFlag)
                        {
                            count++;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                if (count > 4)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
     
     
            //右下-左上
            private bool Is4(int c, int r)
            {
                int count = 1;
                for (int i = 1; i < 5; i++)
                {
                    if ((c - i) >=0&&(r+i)<BOARDSIZE)
                    {
                        if (chessMap[c - i, r + i] == myFlag)
                        {
                            count++;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                for (int i = 1; i < 5; i++)
                {
                    if ((c+i) < BOARDSIZE&&(r-i)>=0)
                    {
                        if (chessMap[c + i, r - i] == myFlag)
                        {
                            count++;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                if (count > 4)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
    其中如何通知对方自己下棋的位置信息、接收对方的信息更新自己棋盘、悔棋、聊天的代码都省略啦,可以自行看源码。
     
    3、游戏最终界面效果
     
    4、注意
    通信都是以字符串的形式发送的,以字符“|”分割的。信息分为信息头和信息内容。
    • 发送消息:Talk|"hello"
    • 通知对方下棋:Put|1|230|400
    • 通知对方输棋:Lose|
    • 申请悔棋:ReSet|
    • 对方同意:OkSet|
    • 对方退出游戏:Exit|
     

    总结


    通过写这一个小游戏,让我学会了Udp的具体用法以及体会到了委托在window程序设计中的方便,以往写的程序委托都没有用武之地,我们只是知道委托的的语法如何怎么使用,但是却不知道什么情况使用,在哪里是用。

    好了分享的就这么多了。大家要是对着这个程序有什么疑问的可以私信我;对这个程序有建议的也可以私信我。嘿嘿。
     下载链接:http://download.csdn.net/detail/mingge38/9387603
     
     
  • 相关阅读:
    支持复制粘贴word公式的wangEditor编辑器
    支持复制粘贴word公式的KindEditor编辑器
    支持复制粘贴word公式的CKEditor编辑器
    支持复制粘贴word公式的百度HTML编辑器
    支持复制粘贴word公式的百度Web编辑器
    PHP 大文件上传分享(500M以上)
    PHP 大文件上传问题(500M以上)
    SAP ABAP报表依赖设计原理详解
    获得某个时间段内修改过的所有ABAP对象列表
    FLINK实例(13):Flink的重启策略(一)
  • 原文地址:https://www.cnblogs.com/mingjiatang/p/5096017.html
Copyright © 2011-2022 走看看