上一节我们讲到了客户端发送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
[
8
,
8
];
//
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(
5
,
4
));
8
int
j
=
int
.Parse(checkbox.Name.Substring(
9
,
4
));
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
到这里,我们把游戏大厅的简单逻辑都处理了,下一节将介绍客户端棋盘的呈现。
源代码下载