基於Udp的五子棋對戰游戲

来源:http://www.cnblogs.com/mingjiatang/archive/2016/01/03/5096017.html
-Advertisement-
Play Games

引言本文主要講述在區域網內,使用c#基於Udp協議編寫一個對戰的五子棋游戲。主要從Udp的使用、游戲的繪製、對戰的邏輯這三個部分來講解。開發環境:vs2013,.Net4.0,在文章的末尾提供源代碼下載的地址。Udp通信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    
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 看重構6.4Replace Temp with Query(以查詢取代臨時變數)中提到Replace Temp with Query往往是你運用Extract Method之前必不可少的一個步驟,局部變數會使代碼難以被提煉, 其中Extract Method是VS自帶的功能,我從VS200.....
  • 新建項目à新建一個空白解決方案 在Model新建一個實體類 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; name...
  • 一、將WCF服務部署到IIS上 【轉載自簡單笑容——http://www.cnblogs.com/skdsxx/p/5072726.html】1.首先檢測電腦上是否安裝了IIS,一般來說Win7以上系統自帶IIS2.下麵進行IIS服務的開啟設置控制面板=》打開或關閉Windos功能3.勾選該視窗中的...
  • 引言本文主要講述在區域網內,使用c#基於Udp協議編寫一個對戰的五子棋游戲。主要從Udp的使用、游戲的繪製、對戰的邏輯這三個部分來講解。開發環境:vs2013,.Net4.0,在文章的末尾提供源代碼下載的地址。Udp通信Udp是基於無連接的傳輸協議,特點是資源消耗小、處理速度快、使用方便,不需要與接...
  • 一:故事背景 以前在寫WebApi2的時候,一直是用作前後端分離(WebApi2 +angularjs),可是最近自己在給WebApp寫介面的時候遇到了很多坑,總結一下就是跨域問題。而跨域問題在WebApi2中配置也是有點麻煩,不知道在2中是否有對jsonp跨域問題更好解決方案,如果有,跪求各位博....
  • 《重構》這本書的代碼都是java,我準備用C#來一遍。而今天我的主要任務是寫一大段垃圾代碼出來,然後重構(僅限於函數的,不涉及到其它方面的重構)。程式界面:功能介紹:俠客名字自己取,然後點擊按鈕隨機角色的屬性,根骨,經脈,柔韌,悟性等四項屬性值都是隨機而來。其他的都是由這四個屬性計算而來:根骨:影響...
  • Technorati 標記: http 代理驗證及測試Technorati 標記: C#參考了網上很多資料,綜合整理出來最終的代碼:using System; using System.Collections; using System.Collections.Generic; using Syst...
  • 使用NuGet安裝Nancy和直接引用源碼項目存在一些差異,如序列化,授權驗證問題。如果引用源碼的話,自定義JsonSerializer,如下:註意,需要使用NuGet安裝Newtonsoft.Json public class CustomJsonNetSerializer : JsonSeria...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...