在线演示地址:Silverlight+WCF 新手实例 象棋 在线演示
上上一节,就是二十八节:Silverlight+WCF 新手实例 象棋 该谁下棋-A下B停(二十八)
我们实现了“开始”游戏后,对棋子的限制,A下B停
这节,我们要实现:B下A停,[同时,传递棋步,对方收到棋步,要反转棋步坐标,自动移动棋子]
所以呢,这节我们要实现的东西是比上上一节相对多一点。
少废话,开始了:
按流程来了,A移动棋子之后,要干点什么事呢?
//-------这是一个AB同样的循环流程-----
1:自己不能再动了,IsCanMove=false;
2:要记录移动坐标
3:传递坐标给对方
4:对方要接收坐标->反转坐标[对方的坐标对自己来说,肯定是相反的啦,自己把头反过来看下棋盘就有点清楚了]
5:对方系统自动移动棋子
6:对方的IsCanMove=true
7:然后到对方下棋了。
8:对方下棋之后呢?Go to 1
//-----又回到开始,不断的循环------
我们先来增加一个用于传递棋步类,既然是传递的,当然得在WCF端新建了,回到和Player一样位置[就是DataContract文件夹下了]:
添加文件类名:MoveStep.cs
namespace
GameService
{
///
<summary>
///
WCF 传递的棋步 by 路过秋天
///
http://cyq1162.cnblogs.com
///
</summary>
public
class
MoveStep
{
}
}
当了棋步传递使者,自然得属性加身了,看看加了什么属性:
ID:这个用于标识是第几步,好像没怎么用到
Name:名称,是马还是炮
ColorValue:什么颜色的
下面四个一看就知,为什么不用Point传递,和那个ColorValue一样,WCF的Point和Silverlight客户端的名称空间不一样[马走一下]
FromX
FromY
ToX
ToY
于是一个一个的敲完就像下面这样了:
using
System.Runtime.Serialization;
namespace
GameService
{
///
<summary>
///
WCF 传递的棋步 by 路过秋天
///
http://cyq1162.cnblogs.com
///
</summary>
public
class
MoveStep
{
///
<summary>
///
棋的步数
///
</summary>
[DataMember]
public
int
ID
{
get
;
set
;
}
///
<summary>
///
棋的原始X位置
///
</summary>
[DataMember]
public
double
FromX
{
get
;
set
;
}
///
<summary>
///
棋的原始Y位置
///
</summary>
[DataMember]
public
double
FromY
{
get
;
set
;
}
///
<summary>
///
棋的移动X位置
///
</summary>
[DataMember]
public
double
ToX
{
get
;
set
;
}
///
<summary>
///
棋的移动X位置
///
</summary>
[DataMember]
public
double
ToY
{
get
;
set
;
}
///
<summary>
///
棋的名称
///
</summary>
[DataMember]
public
string
Name
{
get
;
set
;
}
///
<summary>
///
棋的移颜色值
///
</summary>
[DataMember]
public
int
ColorValue
{
get
;
set
;
}
}
}
我们习惯了一直都传递Player,所以,为Player加个属性了:
namespace
GameService
{
///
<summary>
///
游戏玩家 by 路过秋天
///
</summary>
[DataContract]
public
class
Player
{
//
...省略其它属性...
[DataMember]
public
MoveStep Step
{
get
;
set
;
}
}
}
同时啊,同时啊,刚刚想起来-_-...,我们要为房间添加一个棋子列表,记录每步棋步,不然刚进房间的人看西北风的啊。
同时添加了构造函数,初始化一下List,不然Null魂就会老跟着你。
namespace
GameService
{
[DataContract]
public
class
Room
{
public
Room()
{
StepList
=
new
List
<
MoveStep
>
();
}
///
<summary>
///
房间的棋谱
///
</summary>
[DataMember]
public
List
<
MoveStep
>
StepList
{
get
;
set
;
}
//
...省略下面N个属性...
}
}
OK,传递使者和两个XX都有了,那我们要在WCF端建立传递和接收的接口了,这下我们只要传递Player来来去去的就行了:
IService.cs添加接口:
namespace
GameService
{
[ServiceContract(CallbackContract
=
typeof
(ICallBack))]
//
头顶上这里写明了回调是ICallBack
public
interface
IService
{
//
...省略上面N个接口...
[OperationContract(IsOneWay
=
true
)]
void
MoveStep(Player player);
}
}
ICallBack.cs添加接口:
namespace
GameService
{
interface
ICallBack
{
//
...省略上面N个接口...
[OperationContract(IsOneWay
=
true
)]
void
NotifyMoveStep(Player player);
//
通知接收棋步
}
}
OK,接着我们一如既往的实现MoveStep接口方法
Service.svc.cs,轻轻松松就完工,四行代码搞定。
public
void
MoveStep(Player player)
{
Room room
=
roomList[player.RoomID];
player.Step.ID
=
room.StepList.Count
+
1
;
room.StepList.Add(player.Step);
Notify.Game(player, GameType.Move);
}
那个Notify.Game我们上节都有的了,我们回到Notify里补一个Switch里的Case GameType.Move的方法就行了:
internal
static
void
Game(Player player, GameType type)
{
switch
(type)
{
case
GameType.Start:
//
通知对方玩家开始游戏
//
...上上节实现了...
break
;
case
GameType.Move:
//
通知移动了,房间内人手一份
foreach
(KeyValuePair
<
Guid, Player
>
item
in
Service.playerList[player.RoomID])
{
item.Value.CallBack.NotifyMoveStep(player);
}
break
;
case
GameType.End:
break
;
}
}
OK,到此,服务端完成了,编绎,更新引用:
接着我们回到客户端,要开始发送和接收了:
哪里加发送呢?我们棋步在哪里移动,就在哪里发送了
哪里移动呢?想啦啦找啦啦:棋子移动类ChessAction里的MoveTo方法,我们要在里面添加一个移动后触发的事件
可是怎么触发?单独的类里,是拿不到App.Client对象,更别说传递了到WCF了,于是,大哥啊,代理快出来:
还记得以前Silverlight+WCF 新手实例 象棋 主界面-控件消息传递(二十六),不记得回去看看了。
我们在ChessAction里添加一个代理事件:
看,我们定义代理事件之后只增加一句代码,在移动后直接调用,至于怎么实现的,我们全不理,反正有人帮我干这事。
///
<summary>
///
棋子动作类 by 路过秋天
///
</summary>
public
class
ChessAction
{
public
delegate
void
HelpMoveStep(Chessman chessman, Point movePoint);
public
event
HelpMoveStep HelpMoveStepEvent;
public
bool
MoveTo(Chessman chessman, Point moveTo)
{
if
(Rule.IsCanMove(chessman, moveTo))
{
chessman.ReadyMove
=
false
;
chessman.chessman.Background
=
null
;
PlayMove(chessman, moveTo);
chessman.MovePoint
=
moveTo;
HelpMoveStepEvent(chessman, moveTo);
//
这里增加一句
return
true
;
}
return
false
;
}
//
... 其它省略N多...
}
OK,我们回到Chess.xaml.cs里,我们要实现做下代理人:
public
Chess()
{
//
..省略N行...
chess.Action.HelpMoveStepEvent
+=
new
ChessNewInstance.ChessAction.HelpMoveStep(Action_HelpMoveStepEvent);
App.chess
=
chess;
//
为全局对象赋值
}
void
Action_HelpMoveStepEvent(ChessNewInstance.Chessman chessman, Point moveTo)
{
MoveStep step
=
new
MoveStep();
step.FromX
=
chessman.MovePoint.X;
step.FromY
=
chessman.MovePoint.Y;
step.ToX
=
moveTo.X;
step.ToY
=
moveTo.Y;
step.ColorValue
=
chessman.Color
==
Colors.Red
?
1
:
2
;
step.Name
=
chessman.Name;
App.player.Step
=
step;
//
附加棋步
App.client.MoveStepAsync(App.player);
chess.IsCanMove
=
false
;
}
设置完杂七杂八的参数后,把Step放到Player身上,就传递到服务端了,然后设置一下IsCanMove=false;
发送棋步就搞完了,接下来要接收棋步了,不过在接收棋上之前,我们要先完成一个函数,反转坐标:
我们回到Chess.cs象棋类里,添加方法,"马走一步",太简单了:
///
<summary>
///
反转棋子坐标
///
</summary>
public
Point ReverseArray(Point point)
{
point.X
=
8
-
point.X;
point.Y
=
9
-
point.Y;
return
point;
}
别急,我们还要添加一个自动移动的方法:
回到ChessAction.cs里:
需要解释代码么?不需要吧
解释:既然是系统自动移动,就不用判断什么规则了,直接把棋子移过去,如果移动到的另一个点有棋子,就移掉,然后设置一下坐标。
///
<summary>
///
系统自动移动棋子
///
</summary>
public
void
AutoMoveTo(Point from, Point to)
{
Chessman chessman
=
Parent.FindChessman(from);
Chessman eatchessman
=
Parent.FindChessman(to);
if
(chessman
!=
null
)
{
PlayMove(chessman, to);
chessman.MovePoint
=
to;
if
(eatchessman
!=
null
)
{
eatchessman.GoToDead();
}
}
}
好了,可以接收了,要实现了,眼睛睁大点,回到Chess.xaml.cs:
public
partial
class
Chess : UserControl
{
ChessNewInstance.Chess chess;
//
这里我们同时把它提到全局对象
public
Chess()
{
//
...省略N行...
App.client.NotifyMoveStepReceived
+=
new
EventHandler
<
NotifyMoveStepReceivedEventArgs
>
(client_NotifyMoveStepReceived);
App.chess
=
chess;
//
为全局对象赋值
}
void
client_NotifyMoveStepReceived(
object
sender, NotifyMoveStepReceivedEventArgs e)
{
if
(App.player.ID
!=
e.player.ID)
//
非自己
{
GameService.MoveStep step
=
e.player.Step;
Point from
=
new
Point(step.FromX, step.FromY);
Point to
=
new
Point(step.ToX, step.ToY);
//
转换坐标
if
(e.player.ColorValue
==
2
||
App.player.ColorValue
!=
3
)
//
旁观者 黑色棋子
{
from
=
chess.ReverseArray(from);
to
=
chess.ReverseArray(to);
}
chess.Action.AutoMoveTo(from, to);
if
(App.player.ColorValue
!=
3
)
//
下棋者
{
chess.IsCanMove
=
true
;
}
}
}
//
....省略N行...
}
看清楚,就是转换坐标,然后移动棋子,设置一下IsCanMove。
OKOKOK,代码终于全部写完了,可以F5运行看效果了:
“马再走一步”,上面代码棋子没有自动移动,又要调试了,不截图先:
断点一调试,发现接收的点都是一样的,一步步回去查,终于发现在MoveTo方法里添加的一行事件位置不对:
看有位置的那两行,看清楚了。
public
bool
MoveTo(Chessman chessman, Point moveTo)
{
if
(Rule.IsCanMove(chessman, moveTo))
{
chessman.ReadyMove
=
false
;
chessman.chessman.Background
=
null
;
PlayMove(chessman, moveTo);
HelpMoveStepEvent(chessman, moveTo);
//
这一行要在上
chessman.MovePoint
=
moveTo;
//
这一行要在下
return
true
;
}
return
false
;
}
OK,现在可以F5看效果了,截图:

OK,本节到此,打完收工!
顺逢周五,打包源码:第六阶段源码:点击下载