毕设
2d动画效果
1.创建一个实现游戏引擎的基类,每当创建该基类时,将他放入一个全局的数组内,游戏内每一个会动的物体都需要继承该基类才可以有动画效果。
2.js提供的API:requestAnimationFrame(回调函数),该方法会在下一帧执行括号内的回调函数,如果在回调函数内递归的调用requestAnimationFrame,即可实现每一帧调用一次全局数组内的对象。该方法会自动匹配浏览器帧率来展示动画。
3.因为会出现玩家的显示器帧率不同的情况,因此不用帧率衡量动画,而用时间差去衡量,记录上一帧的时间戳,与当前的时间戳相减即可获得时间差。每一帧的变化,通过乘时间差来实现动画效果
实现鼠标点击移动与火球发射
- cnavas.on(contextmenu)返回false,移除鼠标右键的菜单效果
- 用直角公式计算原坐标与鼠标点击坐标之间的距离
- 用atan2(偏移量y,偏移量x)计算出角度。即arctan的意思。
- 横向速度vx=cos(角度) * 1(即斜边初始速度),纵向速vy=sin(角度) * 1(即斜边初始速度)
- 再后续的update_move()中x = x + vx * move表示:
move指在一帧中移动的距离,vx与vy严格意义上不叫速度,而是指在该角度下,x轴或y轴的移动距离占实际move的百分之多少 - 每一帧的移动距离=速度 * 每一帧的时间差
- 发射火球即在按下技能的时候,创建一个火球类,然后把鼠标的坐标传入火球的构造器。
粒子效果
同释放火球类似,在判定碰撞的时候,循环10次像周围随机方向释放小圆球,增加一个0.9的摩擦力,使其速度每一帧都乘以摩擦力,这样会产生向四周弹射的视觉效果。
AI随机发射炮弹的概率实现
每秒钟调用60次,5秒种调用300次,random < 1 / 300,random在300次内大概率会有好几次小于1 / 300,小于则发射炮弹
登陆实现
前端用ajax向后端发送请求与数据,后端url接收请求并跳转到对应的view函数,view函数用request接收请求与数据,然后用JsonResponse向前端返回json格式的字典。ajax内的success参数下的方法接收json。
后端的view函数内不能直接验证密码,因为密码是哈希值,可以用django的authentic方法验证密码哈希值是否相等。判断
成功后用django的login方法,可以直接将账号密码存到cookie里
退出登陆可用django的logout方法退出,并删除cookie
注册时验证账户是否已存在:filter(username=).exists()
1. 将账号密码加入django默认的user类
2. 再数据库中创建对应的记录:Player.object.create(user=user,photo=)
联机对战
统一度量
1.固定游戏背景长宽比16:9,为了防止不超出最大窗口,尺度单位unit取Min(width/16,height/9),然后width=unit16;height=unit9。
2.让窗口内物体随窗口大小变化,渲染物体时,不用绝对值,用相对值,将所有绝对值除以height即可实现相对值,相当于所有绝对值变成了height的**%,height改变,他们也会随之改变。
联机匹配
1.http是单项通信协议,用户先向服务器发送请求,服务器再返回。服务器无法再没有请求的情况下返回数据。因此需要用到websocket协议实现双向通信。WebSocket是一种在单个TCP连接上进行全双工通信的协议。Django想用到websocket需要django_channel的支持。并用channels_redis记录对局的实时信息。
2.操作同步流程:玩家移动,调用前端websocket类里的对应函数,该函数把操作信息发送到后端服务器,服务器接收信息后群发给组内其他成员,其他成员的前端会接收信息并路由到对应函数做出对应操作
3.A击中B,A的窗口判断成功,然后把成功的消息传给其他窗口,击中与否以A窗口的判断为准。这个击中如果交给服务器,可能会有很大的延迟,为了提升用户体验,所以以本地判断为主,但是任意有外挂。
4.建立consumer文件夹,其功能类似与view,负责逻辑业务。
5.内部有三个函数,创建连接、断开连接、就收请求。建立连接:new Websocket(ws:***) 在创建连接时用group_add将连接加入一个组内,系统提供了一个群发的API,group_send,可以对组内的所有连接群发消息,后端有一个接收群发消息的函数,将接收到的群发信息发给前端的receive,再通过判断event调用对应接收函数。前端:ws.onopen():刚建立连接时自动运行的api,类似于start。ws.onmessage()接收后端传过来的数据。ws.send()向后端发送信息。
6.总共4个动作需要同步,创建玩家,玩家移动,释放技能,收到攻击。每个动作都分一个发送函数与一个接收函数。发送函数:因服务器接收的是字符串,因此需要将JSON格式的转换成string格式(JSON.stringify)。接收函数:
7.添加一个8位标识标记每个物体,方便识别。
8.不同步游戏内所有物体坐标而只同步4个动作是为了防止丢帧,如果每秒钟同步20次,只要有一次出错,结果都是天差地别。但是动作是玩家控制的,玩家操作再多也会有一个上限,服务器的压力会小很多。
9.因为玩家的坐标是用sin,cos计算的角度,随着时间的增加,角度误差会越来越大,因此在玩家被击中的过程中做一个补偿,在A击中B时,将B的位置广播,然后同步其他窗口内B的位置
10.技能冷却实现:moveTo使笔尖从圆心移到圆的边缘,然后用arc画圆的边,画到一定角度之后,用lineTo从圆边缘画直线到圆心
游戏聊天室
1.滚动条:overflow:auto实现滚动条
2.默认会显示最上面的记录 ,新输入的记录会被挤在下面要用滚动条才能看到。scrollTop(滚动条总长度)表示将滚动条移到对应数值的地方,参数设为总长度表示滚动条移动最下面
胜利与失败触发条件
1.胜利:在update中添加判断函数,判断条件为1.是自己 2.在fighting中 3.player数组长度为1
2.失败:死亡时,需要销毁该player,在销毁函数中,判断1.是自己 2.在fighting
负载均衡:
js文件负载均衡
用户端渲染:各种html代码都写在js内,然后通过脚本语言打包成一个js传给用户端,让用户的本地浏览器渲染html页面,服务器端只需要处理数据
服务器端渲染:服务器端会拼接html与各种js和css,然后传给浏览器,浏览器再渲染即可
thrift负载均衡(模块化,结构更清晰,减少单个服务器的压力)
1.用户匹配时会出现延迟,延迟的时候,服务器单线程会被阻塞,在完成匹配前不能干别的事情,此时需要一个外部独立的模块去帮助完成匹配,此时可以用thrift(远程过程调用)来完成两个服务器或进程之间的通信,因为是通过IP地址与端口号的方式通信,因此可以装在不同的服务器上。本项目计算量不大,1s左右匹配一次,可以在同一台服务器上运行。服务器负责将匹配信息传递给thrift模块,自己则做别的事情。
2.开两个线程,一个做成匹配池,一个用来接收消息队列里的信息。这就是一个生产者与消费者模型,单开一个线程然后死循环,不断吸收消息队列里的信息,然后将信息扔给匹配池,该操作每1s执行一次,不然死循环会把CPU占100%,浪费资源。死循环相当于生产者,匹配池相当于消费者。
3.Django为了获取外部进程的通知,提供了一个channels的API,外部进程可以通过channels_name通知到channels里。
4.python3的队列是一个同步队列,即线程安全的队列,不需要用锁去维护。而C++提供的是不安全的队列。线程不安全:出现多个进程同时修改同一个变量的情况,会出现数据错误,这就是不安全。
5.python语言可以调用c++的动态链接库,以弥补python语言低性能的缺点。
动态链接库:某个程序在运行中要调用某个动态链接库函数的时候,操作系统首先会查看所有正在运行的程序,看在内存里是否已有此库函数的拷贝了。如果有,则让其共享那一个拷贝;只有没有才链接载入。
静态链接库:当要使用时,连接器会找出程序所需的函数,然后将它们拷贝到执行文件,由于这种拷贝是完整的,所以一旦连接成功,静态程序库也就不再需要了。
6.匹配时,匹配的分数范围会随时间扩大,玩家自己的分数+时间*50分,当a能碰到b,b能碰到a时,双方同时满足要求才匹配
7.匹配时用到了贪心的原则,如果暴力匹配,3个人就需要三重循环,n个人就n重循环。在贪心的原则下,默认优先匹配分数相近的n个人,先排序,然后再匹配。
算法
动态规划dynamic programming
分阶段求解决策问题的数学思想,它通过把原问题分解为简单的子问题来解决复杂问题。适用于解决带有最优子结构和子问题重叠性质的问题.
动态规划与分治算法很相似,但分治算法一般都是使用递归自顶向下实现,动态规划使用迭代自底向上实现或带有记忆功能的递归实现。分治算法分解的子问题是相对独立的.动态规划分解的子问题是互相带有关联且有重叠的.
1. 状态表示dp[i]或dp[i,j]
2. dp[i,j]这个集合i,j对应的含义
3. dp[i,j]这个集合内的数字对应的属性
4. 状态计算:计算dp[i][j]的递推公式
二分查找(Binary research)
有单调性,一定可以二分,但可以二分的题目,不一定非得要有单调性。二分的本质不是单调性,而是边界问题。
在目标区间内寻找某种性质,可以将整个区间一分为二,一半满足一半不满足,二分可以寻找这个性质的边界。
为什么不三分、n分? 以三分为例,如果运气好,一次就可以扔掉2/3的数,但如果运气不好,就只能排除1/3的数,在这种情况下的最坏时间复杂度是不如二分的,而且代码会冗长且难以理解。
贪心greedy algorithm
在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。贪心最难的地方在于如何证明局部最优解等于整体最优解。在一些情况下,即使贪心算法不能得到整体最优解,其最终结果却是最优解的很好近似。
django
最出名的是其全自动化的管理后台,只需要使用起ORM,做简单的对象定义,它就能自动生成数据库结构、以及全功能的管理后台。ORM中的“O”就是object,也就是我们说的对象;R指的是relations关系;M指的是mapping也就是映射。所以ORM是对象-关系-映射的简称。Django的卖点是超高的开发效率,Django适用的是中小型的网站。Django框架的MTV设计模式借鉴了MVC框架的思想,三部分为:Model(业务对象与数据库对象)、Template(页面展示)和View(负责业务逻辑,并在适当的时候调用Model和Template)
修改model数据库之后,要运行makemigrations 和 migrate
数据库用法: 表名.Objects.get()或create()。在websocket中函数是异步的,在异步内使用数据库需要封装成函数使用。
django内的redis API
django.core.cache
cache.get() cache.set() cache.has_key() cache.keys(‘*’) cache.delete()
面向对象
将客观事物的具体特性和变化规律 抽象 为 数据模型 的一中思维方式。这中方法可以使程序员更集中更深刻的描述客体的特征。其内部继承、多态、封装的思想提高代码的重用,使代码更易于维护。
抽象类主要用作对象系列的基类,共享某些主要特性,例如共同的目的和结构。接口则主要用于类,这些类在基础水平上有所不同,但仍然可以完成某些相同的任务。
封装:将各种逻辑私有,仅对外留一个公共的接口供使用
继承:子类可以使用父类的非私有方法和属性并进行扩展
多态:不同对象可以对同一个消息作出响应
面试题:
https://blog.csdn.net/qq_56751933/article/details/121415190
https://blog.csdn.net/weixin_44985880/article/details/119390659
docker
镜像和容器的区别类似于代码和进程。
镜像是我们写的代码,而容器则是运行这个代码发起的进程。
所以镜像以文件形式保存在硬盘中,可以独立存在。而容器是个进程,只能靠运行镜像而存在,没有能够脱离于镜像而存在的容器。
一份代码可以执行多次产生多个进程,而一个进程只能由确定的一份代码产生。同样的从一个镜像可以发起多个容器,而一个容器肯定只能从一个镜像产生。
机器学习
K近邻算法
给一个新的数据时,离它最近的 k 个点中,哪个类别多,这个数据就属于哪一类。
K均值算法
先要将一组数据,分为K类,对每个类选一个初始数据作为中心点,然后其他数据,归类到离他最近的那个类。归类完之后,将类对应的平均数作为新一轮的中心点。几轮之后,分组不再变化了,就可以停止了
修改访问时acwing域名或服务器地址
ball_game/game/views/settings/acwing/web/apply_code.py
14: redirect_uri = quote("https://app4861.acapp.acwing.com.cn/settings/acwing/web/receive_code/”)
ball_game/game/views/settings/acwing/acapp/apply_code.py
14: redirect_uri = quote("https://app4861.acapp.acwing.com.cn/settings/acwing/acapp/receive_code/”)
ball_game/game/static/js/src/playground/socket/multiplayer/zbase.js
6: this.ws = new WebSocket("wss://app4861.acapp.acwing.com.cn/wss/multiplayer/”);
ball_game/game/static/js/src/settings/zbase.js
150:url: "https://app4861.acapp.acwing.com.cn/settings/acwing/web/apply_code/”,
167:url: "https://app4861.acapp.acwing.com.cn/settings/login/”,
191:url: "https://app4861.acapp.acwing.com.cn/settings/register/”,
213:url: "https://app4861.acapp.acwing.com.cn/settings/logout/”,
250:url: "https://app4861.acapp.acwing.com.cn/settings/acwing/acapp/apply_code/”,
264:url: "https://app4861.acapp.acwing.com.cn/settings/getinfo/”,
wss 改成 ws