统一标准:
联机对战,多人模式要求所有的玩家在同一居游戏中, 所以 地图大小, 玩家大小等等游戏界面的所有东西都应该按照玩家的电脑界面比例来设定, 不能够直接确定出实际大小,否则的话玩 某些玩家屏幕大的话,游戏中的各种实际元素大小就偏大, 传输数据到小屏幕玩家电脑上就会造成不协调,同时如果玩家将网页界面缩小的话, 里面的元素也应该缩小,所以要将游戏界面的大小变为按照页面大小大小来确定实际大小。
python3中的async与await
并发: 是指两个或多个事件在同一时间间隔内发生。 是指一个时间段中有有多个 程序都处在运行状态到运行完毕之间,并且这几个程序都是在同一个处理机上运行。
并行:是指两个 或多个事件在同一时刻发生, 即在任何时间点上, 有多个程序运行在多个CPU上。
同步: 是指代码调用IO操作时, 必须等待IO操作完成才能返回的调用方式
异步: 是指代码调用IO操作时, 不必须等待I操作完成就能返回的调用方式
协程: 协程可以理解为纯用户态的线程, 其通过协作而不是抢占来进行切换, 相对于线程或者进程,协程所有的操作都可以在用户态完成, 创建和切换的效率更低。 传统函数调用过程为A->B->C->D,若需要一个可以暂停的函数,并且可以在适当的时候恢复该函数继续执行,这便是协程, 当程序中的方法需要等待时间的话,就可以用协程。
函数正在执行的 过程中是不能中断的, 所以如果需要写一个能够中断的函数, 则需要添加async关键字。
async 用来声明一个函数为异步函数, 异步函数的特点是能在函数执行的过程中挂起, 去执行其他的异步函数,等到挂起条件消失后,也就是1秒到了再回来执行被挂起的函数
await 用来声明程序挂起, 比如异步程序执行到某一步时需要等待的时间很长,就将此挂起, 去执行其他的异步程序。
def demo():
async def wash1():
do
do
await______
do
do
函数在执行过程中,发生了请求而我们等待得到回复,而在这个过程中,程序是不能运行的比较浪费时间,可以采用协程也就是异步去解决此问题,在等待的时候去做其他的事情,由此来节省时间提高效率, 只有被async函数声明了的方法才能发生协程, 并且只有在await后面的被async声明的函数或者是asyncio库里的函数发生了等待在会发生协程挂起该异步函数去执行其他程序。
利用django_channels来进行前端与后端的通信来维护我们游戏的实时对局信息。http协议是单向通信,也就是前端向后端发出请求,后端接受请求,进行处理并返回结果给前端,但是不能后端主动给前端发送信息,而https则是对http进行加密的协议,ws就是WebSocket协议是双向通信,前端可以给后端发送信息,同样后端也可以给前端发送信息,而wss就是对ws进行加密的协议,django要支持wss协议的话就需要django_channels来实现。同时在我们游戏的逻辑中,我们通信之后要使同一局玩家游戏界面情况相同,就要将游戏数据存储起来,这里可能你会想存储到普通数据库中不就可以了,但是我们是一个多人对战游戏对实时性要求比较高,采用redis来存储数据,这样我们存取效率就会比较高,为了时django_channels能够连接redis并使用它需要下载channels_redis
前端之间的每个窗口之间是如何通信的当前端通过链接试图与我们建立通道时,我们会执行connect()函数,然后我们假定房间号是0~999,那么遍历每一个房间,当某个房间没有被创建或者说房间内人数不满的时候就确定该连接的玩家房间号,并执
行accept()函数建立连接,然后,如果房间不存在的话就创建,然后遍历房间内其他玩家信息通过send()函数向前端发送信息,channel_layer.group_add(self.room_name, self.channel_name)此函数作用是,将该通道加到组名为room_name的组中,并且该通道的名字是channel_name,receive(self, text_data)函数作用是接收前端发送来的信息,
wss协议传输的是字符串,所以我们前后端不断要执行字典与字符串相互转化的操作
python中
json.dumps(字典) #将字典转化成字符串
json.loads(字符串) #将字符串转化成字典
js中
JSON.parse(字符串) //将字符串转化成字典
JSON.stringify(字典) //将字典转化成字符串
前端——写自定义send函数和自定义receive函数,负责与服务器的信息交互
后端——写对应的自定义receive函数
无论前端还是后端都要再写一下路由
什么路由——意思就是,在前后端信息交互的时候真正负责通信的是框架提供的api函数,而我们为了代码可读性更高所以会根据接收的信息标志而用不同的自定义函数去处理信息,但是框架又不晓得我们的自定义函数,所以我们就会在api函数中调用我们写的自定义函数。这就是写路由。
如何同步玩家信息
整合django_channels,用websocket实现:
每个客户端需要给主机实时发送请求,“主机”的建立会在后面详解
主机也需要实时向每个客户端发送广播。
客户端在收到广播后要更新自己的信息
广播移动玩家就要移动目标点, 对于玩家移动的话, 为了避免传递方向和移动距离
move_to”
y总采用的方法是广播移动玩家将要移动的目标点,其实由于网络延迟原因,只传递目标将要移动的目标点的话,如果玩家快速并且多次点击移动事件,那么就有可能出现误差,比如一个玩家在两个屏幕同一位置,此时玩家向右移动,然后另一块屏幕接收到传递信息后进行移动,但是此次接收信息的延迟比较大,此玩家还未到达目的地的时候玩家更改移动目的地的话,就会再次传递信息,另一块屏幕接收信息后换方向移动,此次延迟比较小,由于两次延迟造成,坐标出现差错,为了同步信息而且我们视角是以自己为中心,传递目的地坐标比较麻烦(需要反复计算实际坐标),所以我们采用传递方向和移动距离,但是与上面同理,同样会造成坐标偏差(两个屏幕同一玩家坐标不同),我们采用每三次移动事件就更新下玩家坐标,减少偏差。
“shoot_fireball”
同步发射火球函数,传递发射火球玩家,火球uuid与方向,接收函数只要调用玩家发射火球函数就可以
“attack”
击中玩家事件,我们设定只有自己发射的火球才能有判定是否击中的能力,其他玩家的火球,在其他玩家电脑中进行判定,这样设计不会导致同一火球有的玩家屏幕中火球击中,有的玩家屏幕中火球未击中,当发生了击中函数时,传递被击中玩家的uuid,和击中玩家的火球,还有击中产生的后退方向
“shoot_shield”
释放护盾事件,与发射火球事件一样,传递玩家信息,这里由于误差小就不在采用,火球击中事件的判定方法,没个护盾自行判断消除火球
“eat_dot”
吃”小豆豆”事件,传递被吃小豆豆的uuid与新坐标,还有玩家的uuid,这里采用跟火球一样的机制,自己吃的豆豆才回去发送事件,保持统一