代码改变世界

基于TCP的网络游戏黑白棋系列(三):游戏大厅

2008-10-11 15:29  BAsil  阅读(2077)  评论(8编辑  收藏  举报

上一节我们讲到了客户端发送Login命令后,服务器返回欢迎信息,完成了一个简单的数据传输。这一节我们来完成游戏大厅的基本功能,我们首先思考一下游戏大厅的基本功能:

1 提供可供对弈的游戏桌,游戏大厅可供多桌玩家同时游戏,为了考虑游戏大厅服务器的负载能力,应该设置一个人数的上限和桌数的上限。实际上前面提到的功能抽象出来就是一些数据的状态集合。

2 当玩家登入大厅,应该直观的显示当前大厅的就座情况,方便玩家选择。此处应该考虑大厅的直观显示。

3 当玩家选择某一位置就坐,游戏大厅的相应状态数据应发生更改,任何玩家都能看到大厅的就座情况的变化,方便做出选择。比如a选择坐在第一桌的黑方位置,则b应该看到该位置不可落坐,只能选择其他位置就坐。

尽量从面向对象的角度考虑,我们应当把游戏大厅,游戏桌,玩家看做对象。建议大家使用面向对象的方法去思考,个人感觉服务器客户端通信的网络程序主要涉及通信协议(就是我们前面提到的命令,参数1,参数2等等)的分析,设计不好的话到最后你会发现逻辑复杂到难以控制的程度。

下面我们分别看一下这几个对象(有删减,具体请看源代码)

 

 1 //游戏大厅
 2 public FormServer
 3 {
 4     //basilwang 2008-09-06 
 5     //new to this version myGame2
 6     //游戏桌集合
 7     private GameTable[] gameTable;
 8     //玩家集合
 9     List<User> userList = new List<User>();
10     //游戏桌上限
11     private int maxTables;
12     //玩家上限
13     private int maxUsers;
14     //上一节列出的帮助类
15     private Service service;
16 }
17 //游戏桌
18 class GameTable
19 {
20         private const int None = -1//无棋子
21         private const int Black = 0//黑白棋子
22         private const int White = 1//白色棋子
23         private int[,] grid = new int[88];   //8*8的方格
24         public Player[] gamePlayer;
25         public GameTable()
26         {
27             gamePlayer = new Player[2];
28             gamePlayer[0= new Player();
29             gamePlayer[1= new Player();
30         }
31         
32  }
33 class Player
34 {
35         //是否开始
36     public bool started;
37     //己方棋子个数
38     public int grade;
39     //是否落座
40     public bool someone;
41     public Player()
42     {
43         someone = false;
44         started = false;
45         grade = 0;
46     }
47 }

 

当服务器程序启动的时候,需要初始化游戏大厅的数据;而当客户端登录到游戏大厅后,按照前面列出的逻辑,我们需要把游戏大厅的状态在客户端直观的显示出来。那怎么才能得到游戏大厅的状态数据呢?没错,也是利用的客户端服务器之间的通讯。

当客户端向服务器发送Login命令后,服务器处理完毕后,返回Tables命令返回状态数据

 

 1 private void ReceiveData(object obj)
 2 {
 3     //省略接受客户端协议代码
 4     
 5     //拆分接受到的协议      格式:  命令,参数1,参数2 .
 6     string[] splitString = receiveString.Split(',');
 7     string sendString = "";
 8     switch (splitString[0])
 9     {
10     case "Login":
11         user.userName = string.Format("[{0}--{1}]", splitString[1], client.Client.RemoteEndPoint);
12         //向客户端发送协议   
13         //格式 : Tables,参数1
14         //参数1为游戏大厅的游戏桌就座情况
15         sendString = "Tables," + this.GetOnlineString();        
16         service.SendToOne(user, sendString);        
17         break;
18     default:
19         break;
20     }
21  }
22  //返回如0100010101的字符串  奇数位表示游戏桌黑方的就座情况,偶数位相反,游戏桌按序号排列连接
23  private string GetOnlineString()
24 {
25     string str = "";
26     for (int i = 0; i < gameTable.Length; i++)
27     {
28     for (int j = 0; j < 2; j++)
29     {
30         str += gameTable[i].gamePlayer[j].someone == true ? "1" : "0";
31     }
32     }
33     return str;
34 }

 

客户端接受到Tables命令协议进行分析,直观显示游戏大厅的就座情况,这里为了简单,采用了动态生成若干组checkbox控件添加到Panel的方法,比较简单但能够说明问题。checkbox选中表明已有玩家就座,如果未选中表明可以在此处落座。

这部分代码就不列出来了,可以看一下原程序。

如果玩家选择在某一位置落座,将出发CheckBox的CheckedChanged事件,并向服务器发送SitDown命令

 

 1 void checkBox_CheckedChanged(object sender, EventArgs e)
 2 {
 3             CheckBox checkbox = (CheckBox)sender;
 4             if (checkbox.Checked)
 5             {
 6             //动态生成的CheckBox命名规则为checkXXXXYYYY, 第5-8位为桌号,不足0补齐;第9-12位为黑方或白方,不足0补齐
 7                 int i = int.Parse(checkbox.Name.Substring(54));
 8                 int j = int.Parse(checkbox.Name.Substring(94));
 9                 side = j;
10         //格式 SitDown,参数1,参数2
11         //参数1 桌号
12         //参数2 黑方或白方
13                 service.SendToServer(string.Format("SitDown,{0},{1}", i, j));
14             }
15  }

 

服务器分析SitDown命令

 1 private void ReceiveData(object obj)
 2 {
 3         //省略接受客户端协议代码
 4     
 5         //拆分接受到的协议      格式:  命令,参数1,参数2 .
 6         string[] splitString = receiveString.Split(',');
 7     string sendString = "";
 8     int tableIndex = -1;  //桌号
 9     int side = -1;        //座位号
10     int anotherSide = -1//对方座位号
11 
12     switch (splitString[0])
13     {
14         case "Login":
15         //省略部分
16         break;
17         case "SitDown":
18         tableIndex = int.Parse(splitString[1]);
19         side = int.Parse(splitString[2]);
20         gameTable[tableIndex].gamePlayer[side].user = user;
21         gameTable[tableIndex].gamePlayer[side].someone = true;
22         service.SetListBox(string.Format(
23             "{0}在第{1}桌第{2}座入座", user.userName, tableIndex + 1, side + 1));
24         //得到对家座位号
25         anotherSide = (side + 1% 2;
26         //判断对方是否有人
27         if (gameTable[tableIndex].gamePlayer[anotherSide].someone)
28         {
29             //先告诉该用户对家已经入座
30             //发送格式:SitDown,座位号,用户名
31             sendString = string.Format("SitDown,{0},{1}", anotherSide,
32             gameTable[tableIndex].gamePlayer[anotherSide].user.userName);
33             service.SendToOne(user, sendString);
34         }
35         //同时告诉两个用户该用户入座(也可能对方无人)
36         //发送格式:SitDown,座位号,用户名
37         sendString = string.Format("SitDown,{0},{1}", side, user.userName);
38         service.SendToBoth(gameTable[tableIndex], sendString);
39         //重新将游戏室各桌情况发送给所有用户
40         service.SendToAll(userList, "Tables," + this.GetOnlineString());
41         break;
42         default:
43         break;
44     }
45  }
46 

 

到这里,我们把游戏大厅的简单逻辑都处理了,下一节将介绍客户端棋盘的呈现。

源代码下载