做程序猿,情商比智商重要。
——茂叔
上一篇我们创建了角色,现在,是时候让我们的角色进入游戏世界了。
角色进入游戏世界
所以现在,我们在服务器端增加一个通信指令JOINGAME
,对于通信指令的维护,如果大家从头开始看到这里的话,应该是非常熟悉了,修改我们的Type.cs
:
public enum GameCommand : byte
{
ERROR = 0,
CONNECTED = 1,
LOGIN,
MAKENAMES,
CREATECHARACTER,
JOINGAME,//这条
REFRESH,//还有这条,顺便一起了
MOVE,
ATTACK,
SEARCH,
TAKE,
QUIT
}
我们顺便也把游戏进行中数据刷新的指令也添加好,等会儿会用到。
然后,在我们的客户端里面,对于角色创建指令回传处理代码这里,对角色创建成功回传的消息做如下处理:
……
if (_g.Player.Name === "@") {
let nextUI = require('/UI/makename.js')
nextUI.initialize()
} else {
var data = {
command: _g.COMMAND.JOINGAME,
data: ""
}
wx.sendSocketMessage({
data: JSON.stringify(data)
})
}
……
这样就把加入游戏的指令传给了服务端。
在服务端,我们添加对于这个指令的解析和处理:
GameServer.cs
……
case GameCommand.JOINGAME:
{
uint playerID = ((GameConnection)connection).PlayerID;
((GameConnection)connection).SendMessage(cmd, gGame.JoinGame(playerID));
}
break;
……
Game.cs
……
public string JoinGame(uint PlayerID)
{
GamePlayer Player = PlayerPool.Find(x => x.PlayerID == PlayerID);
if (Player == null || Player.Name == "@") return "FAIL";
Player.X = 7;
Player.Y = 7;
gWorld.AddObjectIntoMap(Player, Player.MapEID);
return "OK";
}
……
GameWorld.cs
……
internal bool AddObjectIntoMap(GameObject Obj, ExistenceID MapEID)
{
GameMap map = GetMap(MapEID);
if (map == null)
{
map = gMaps[0];
}
map.AddObject(Obj);
return true;
}
……
GameMap.cs
……
internal void AddObject(GameObject obj)
{
obj.MapEID = EID;
lock (gObjects)
{
gObjects.Add(obj);
}
}
……
我么一共修改了4个地方。代码解析不用说了,这个是标配,然后由我们的Game
对象来统一处理指令与游戏世界的交互,游戏世界GameWorld
再告知相应的地图GameMap
来添加这个角色,最后一次向上反馈结果。
这一个流程也是所有与角色相关的通信指令处理流程的标准流程。所有的实际指令执行都是GameWorld
来实施的。
这样的机制保证了各个层次的相对封闭性,各个层次的逻辑在只在内部产生效果,对外隔绝,便于今后的扩展(如果有这个必要的话)。例如,我们一个服务器可以提供多个游戏,而一个游戏可以有多个平行世界,这些策略导致今后的扩展会相对容易很多。
有进,就有出,所以,我们要把QUIT
指令也顺便实现了。按照上面的思路:
GameServer.cs
……
case GameCommand.QUIT:
gGame.PlayerQuit(uint.Parse(data));
((GameConnection)connection).mSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "{\"result\":\"fail\",\"msg\":\"离线断开\"}", CancellationToken.None);
ConnectionPool