我是公众号线下派对游戏的作者HullQin(欢迎关注公众号,发送加微信、交友),转载需征得作者HullQin授权。我独立开发了《联机桌游合集》,这是一个网页,在这里你可以轻松地和朋友一起玩网络游戏,五子棋等游戏,不收费,也没有广告。还为GameJam2022开发了《Dice Crush》,喜欢的话可以关注我HullQin哦~有空我会分享制作游戏的相关技术。背景在上一篇《用177行代码写个体验超好的五子棋》中,我们用177行代码实现了一个本地五子棋游戏。现在,如果我们要做一个在线五子棋,我们应该怎么做呢?需求分析首先,我们需要一个后端服务。两个不同的玩家一起连接到这个后端服务,告诉后端要下的棋,后端转发给另一个玩家。当然,如果有观众,也要把当前的比赛转发给观众。另外,为了让两个玩家联机,还需要有“房号”的概念。只有同一房间的人才能在线玩。不同房间的人互不影响,可以让多个房间的人同时玩游戏。Process整个通信过程如下:玩家A请求进入房间1,玩家A将持有黑色。玩家B请求进入房间1。玩家B将持有白色棋子。这个时候人潮已经满了,其他人也会进来观战。玩家C请求进入房间1。玩家C是观众。玩家A请求下棋并将坐标告诉服务器。服务器通知玩家B和玩家C,并告诉大家A下棋的坐标。玩家B请求下棋并将坐标告诉服务器。服务器通知玩家A和玩家C,并告诉大家B下棋的坐标。然后重复步骤4-7。为了简化后端逻辑,所有的逻辑判断都放在了前端。比如在前端判断游戏是否结束(五连珠)。如果游戏结束,则不允许前端再发送请求。技术选择协议和方案涉及服务端主动向用户发送数据,所以有几种选择:Http轮询:如果在等待对方下棋,前端每隔1s发送一次请求,看对方是否下棋下过国际象棋。httplongpolling:如果在等待对方下棋,前端会每隔1s发送一个请求,看对方是否在下棋。但是后台不会马上返回结果,要等到界面超过一定时间才会返回结果。WebSocket:一旦浏览器与服务器建立连接,就可以随时主动向浏览器推送数据。这里我们选择WebSocket,因为Http协议在这种场景下确实浪费了很多资源。WebSocket虽然实现起来有点困难,但是节省了资源。具体实施方案只需要某种编程语言/框架支持WebSocket即可。因为以前经常用Django,用过Channels,知道它的底层依赖daphne,所以直接选择了daphne。它是ASGI标准的一个实现。Daphne是一个非常轻量级的选择,不像Django+Channels框架提供了一个重度的解决方案。daphne只提供基本的ASGI实现,没有其他多余的功能。就像:我在开发西洋双陆棋前端时,没有使用React框架,而是使用了SVG+DomAPI。开发基础daphne要求我们以这种格式定义服务:#server.pyasyncdefapplication(scope,receive,send):#handlewebsocketprotocolifscope['type']=='websocket':#receivefirstApackagemust是一个建立连接(connect)的包,否则服务会被拒绝表示ws连接建立成功awaitsend({'type':'websocket.accept'})#之后双方可以随时向对方发送消息。开启无限循环whileTrue:#接收一个包event=awaitreceive()#如果是断开连接的请求,则结束循环ifevent['type']=='websocket.disconnect':break#这个方法可以读取包的文本内容data=event['text']#该方法可以向浏览器发送一个包,这里是将浏览器发送的包原封不动的发送回去awaitsend({'type':'websocket.send','text':data})操作方法:pipinstalldaphnedaphne-b0.0.0.0-p8001server:application业务开发我们需要定义一个房间的集合,叫做househouse={}写入玩家的初始连接(进入房间)逻辑:importjsonasyncdefapplication(scope,receive,send):ifscope['type']=='websocket':event=awaitreceive()ifevent['type']!='websocket.connect':returnawaitsend({'type':'websocket.accept'})#连接建立后,要求前端发送一个EnterRoom事件,提供json格式的用户id和房间号roomevent=awaitreceive()data=json.loads(event['text'])ifdata['type']!='EnterRoom'ornotdata['id']ornotdata['room']:#如果前面发送的第一个事件end不是这个,然后报错,断开连接awaitsend({'type':'websocket.close','code':403})returnroom_id=data['room']user_id=data['id']#检查房间号是否在房子里,如果如果room_id不在房子里,则不创建房间:house[room_id]={'black':None,'white':None,'pieces':[],'sends':[],'users':[],}room=house[room_id]old=False#查看玩家是否是老玩家(掉线重连)ifroom['black']==user_idorroom['white']==user_id:old=Trueifuser_idinroom['users']:old_send=room['sends'][room['users'].index(user_id)]room['sends'].remove(old_send)room['users'].remove(user_id)awaitold_send({'type':'websocket.close','code':4000})else:#表示玩家第一次进入,如果room['black']是,给他黑棋或白棋无:room['black']=user_idelifroom['white']isNone:room['white']=user_id#如果玩家既没有黑旗也没有白旗,就是观众来访=room['black']!=user_idandroom['white']!=user_id#room中存放玩家的send函数,方便其他玩家下棋时调用,从而广播棋局事件room['sends'].append(send)#room中存放玩家ID['用户']。append(user_id)玩家进入房间后,我们需要通知他房间的基本信息,比如是否已经开始?目前场上情况如何?awaitsend({'type':'websocket.send','text':json.dumps({'type':'InitializeRoomState','pieces':room['pieces'],#场上的棋子'visiting':visiting,#Areyouaspectator'black':room['black']==user_idifnotvisitingbool(len(room['pieces'])%2),#如果你正在下棋:blackis你是吗?如果你是旁观者:谁是黑棋?'ready':bool(room['black']androom['white']),#房间准备好开始了吗?只要有2peopleatsametime,Youcanopenit})})#因为有人进了房间,所以需要广播这条消息ifnotoldand(room['black']==user_idorroom['white']==user_id):for_sendinroom['sends']:if_send==send:continueawait_send({'type':'websocket.send','text':json.dumps({'type':'AddPlayer','ready':bool(room['black']androom['white']),})})whileTrue:event=awaitreceive()#有人断线了,处理一下。如果房间是空的,删除房间,防止内存占用无限增加remove(send)room['users'].remove(user_id)iflen(room['pieces'])==0andlen(room['sends'])==0:delhouse[room_id]break#某人发送一个事件,接收它data=json.loads(event['text'])#如果是国际象棋事件,改变房间的棋子数据并广播给所有人ifdata['type']=='DropPiece':room['pieces'].append((data['x'],data['y']))for_sendinroom['sends']:if_send==send:#不需要通知自己,所以跳过自己继续await_send({'type':'websocket.send','text':json.dumps({'type':'DropPiece','x':data['x'],'y':data['y'],})})当然,写完这些之后,还需要进行测试。最好直接写前端,联合调试。在我们的下一篇文章中,我们将添加前端WebSocket逻辑。完整源码包括前后端源码(总共不到400行):https://github.com/HullQin/gobang是一个关于WebSocket的demo,值得学习。最后,我是公众号线下派对游戏的作者HullQin(欢迎关注公众号,发送加微信,交友),转载本文需作者HullQin授权。我独立开发了《联机桌游合集》,这是一个网页,在这里你可以轻松地和朋友一起玩网络游戏,五子棋等游戏,不收费,也没有广告。还为GameJam2022开发了《Dice Crush》,喜欢的话可以关注我HullQin哦~有空我会分享制作游戏的相关技术。
