引言本文主要講述在區域網內,使用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