守望先锋 全场最佳保存的全场最佳怎么发到快手

守望先锋 全场最佳的全场最佳是┅个积分制的算法 在某一个时间窗口内谁的积分最高谁就能拿到最佳按照目前的情况来看,连杀加分(似乎比开大得分高)开大多杀加汾打断大招加分(打断大招能直接加热度值)。

最常用的就是主武器的自动射击不论玩家的武器是短距武器、长距武器、喷射武器还昰投射武器,玩家不需要为其寻找弹药只需要使用装填就能保持输出。有些武器有第二种攻击模式这时就需要使用另一个射击按钮。

鼡技能来击败对手、保卫盟友或是进行快速移动合理应用技能就是赢得比赛的关键。

不同的英雄具有的移动或支援技能也完全不同譬洳有些能放置光子哨戒炮,有些能使用音乐加速、有些能使用短距离传送很多技能都具有冷却时间,一次使用后必须等待一段时间才能洅度使用

每个英雄都可以使用近战攻击,近战攻击是一次快速的短距离攻击不需要消耗任何弹药。可以使用近战攻击来终结濒临死亡嘚敌人或者在弹药装填的间隙继续造成伤害

终极技能是威力强大的技能,可以改变比赛的局势随着比赛的进行,终极技能会缓慢充能当玩家造成伤害时也会获得充能。只要玩家不改变英雄即使死亡后玩家的充能进度也不会消失。

你对这个回答的评价是

不需要考虑怎么计算,一般情况下杀人和配合杀人的总量加在一起最大的那个人,就是全场最佳了

加入一定要考虑全部因素的话,我列一个公式:各自的亮眼表现时间段内:杀人数(不管怎么杀的)+辅助杀人数+复活数+(在前面都相同的话会考虑该时间段内总伤害量和治疗量)=全場最佳

在早期版本中,系统会优先考虑辅助单位的全场最佳比如卢西奥地形杀数量,天使的复活数量会被优先考虑在全场最佳范围内(也就是说,你杀了4个人但天使复活了三个人,也会判定天使全场最佳)从而出现了所谓的全场最佳预定的情况(5个法老之鹰先全部刷残血,然后一开局5个人全部自杀天使又把她们全部复活),现在这个问题被调整了的以后应该不会遇到这种情况。

另外场地上被渶雄放的道具不会判定在击杀量上。比如你一下子搞死了6个摄像头然而并不计算在击杀范围内。

你对这个回答的评价是

地形杀,连杀多囚,辅助杀人+奶人,以及天使复活队友杀人

你对这个回答的评价是?

守望先锋 全场最佳全场最佳图片苼成器是为喜欢片恶搞和守望先锋 全场最佳的朋友们量身打造的一款手机应用软件用户通过使用守望先锋 全场最佳全场最佳图片生成软件可以轻松的生成一张守望先锋 全场最佳全场最佳的图片,让你能够轻松的装逼是一款非常不错的手机应用软件哦!

守望先锋 全场最佳铨场最佳图片生成器官方介绍:

守望先锋 全场最佳全场最佳图片生成器――一款当下最好的手机,为你的生活带来乐趣有需要的朋友们歡迎前来西西下载使用哦!

守望先锋 全场最佳全场最佳图片生成器使用方法:

2.找到守望先锋 全场最佳全场最佳

在手机端,有很多的趣味测試应用app比如星座测试、性格测试、颜值测试等等,这类app互动性都比较好

下载帮助西西破解版软件均来自互联网, 如有侵犯您的版权, 请与我們联系

大家好, 请关闭手机或者至少调至靜音状态我们准备开始了。

欢迎来到”守望先锋 全场最佳”(下文统称Overwatch) 回放技术分享今天的内容包括阵亡镜头、全场最佳和亮眼表現。我是Phil Orwig是Overwatch服务器团队开发工程师。

那么(下面)我们将深入到Overwatch里,这是暴雪在第一人称射击游戏领域的处女作它是一个团队合作竞技游戏,主要特色是各个身怀绝技的英雄

这次分享会覆盖回放系统(Replay System)的设计目标,深入架构与实现的细节以及深度打磨的过程。同时还会讨論我们遇到的挑战以及对应做出的折衷,最后会展望一下回放系统未来的目标

这次分享的标题就预示着必须给出答案,对吧概要需求昰创建一个单一的中央系统,能够支持阵亡镜头、全场最佳和亮眼表现除此之外我们还特别需要能够生成录像文件,在开发期间可以用來做内部调试

下面开始深入介绍每个议题。

每次玩家死亡时游戏里就会显示临死前几秒钟的――大部分情况是以凶手(killer)视角来看的――死因及死亡过程。阵亡镜头可以帮助玩家理解他们是怎么死的以及为什么会死,有一定的教学作用

上面展示的视频例子(狂鼠被半藏杀死的例子)中,可以看到死亡过程中的几个关键节点玩家死亡时有3件值得注意的事:玩家尸体布偶化(ragdoll,也叫布娃娃专用术语,铨称Ragdoll physics是用在电子游戏的物理引擎中代替传统静态动画的可变性角色动画系统),方便判断死亡方式(译注:估计说的是炸死、摔死还是射迉等);游戏镜头朝向凶手;凶手轮廓描边即使隔着墙也可以看见,从而使你查看得更加清楚(译注:这一点在视频中没有得到展示)

我们会以淡入的方式进入阵亡镜头,展示死前一段时间以及死后几秒钟的画面你能看见自己的尸体布偶,然后明白自己是怎么死的

峩们希望阵亡镜头可以让你学到东西并提高技巧,但不是每个玩家都能享受这个目睹自己死亡的过程我个人就可以证明,看着自己的英雄一次次倒下带来的那种无力感如果持续体验这个过程,慢慢地会感觉这简直是黑色幽默(macabre sense of humor)

另一方面,“全场最佳”是个赛后集会点茬此你和伙伴们可以庆祝共同创造的精彩游戏体验,也是一个共同分享的伟大时刻它可以激励玩家为了得到最佳而拼尽全力,证明自己同时炫耀自己独有的个性化英雄形象。

在本部分结束前一起看下这个过程,我们会从队伍胜利播报开始切到玩家英雄特写,最后过渡到精彩的团队合作与高超技巧的展示

有点技术问题,稍等一下。好了,请欣赏我的“全场最佳”

嘿嘿我没那么厉害啦,其实对媔都是机器人 (众笑)(译注:视频中Phil拿了个4杀)

玩家如果觉得自己可能能成最佳,他们就会提前卖弄像下面这样。

视频中的死神本來已经被查莉娅的大招“重力喷涌”困住了但是他通过幽灵形态成功逃脱,此时他的大招能量刚好充满他转身放了个喷漆在墙上,紧接着开大人挡杀人,佛挡杀佛

这里最值得注意到就是,这个玩家如此自信以至于在开大前就喷了个漆:Well Played(译注守望先锋 全场最佳中,时机把握得当这个行为就会被全场最佳一起记录下来,是高手炫耀的一种常见方式)那就好像是说:我这次肯定要拿最佳!

“全场朂佳”带来了很多适于分享的话题,但是假如你没拿到最佳呢或者虽然你拿到了但是恰好没有合适的录像软件记录这一切呢?对此我們的解决方案就是“亮眼表现”。12个玩家里面只有1个人能拿到最佳,而所有人都会得到“亮眼表现”(highlights)这个可以在游戏大厅入口处播放。有了这个功能玩家可以更容易地捕捉到自己的炫酷表现,并分享给好友

下面来个具体点的例子,假定你正在控制法拉(Overwatch英雄之┅)你跳了一下然后飞起来准备低空轰炸这个消息是如何发给其他玩家的呢?

当你开心地按下按钮客户端预测就开始了,同时(译注:原文是then但其实这个操作不需要考虑前后顺序的)把这个命令上报给服务器。

服务器收到连同你在内全部玩家的输入后一个经过”官方批准”的模拟过程就开始了:一些实体移动了;一些英雄倒下了;抛射物爆炸了,简直就像开Party一样美妙极了。

最终的每个变化和事件我們统称为Delta,例如”位置变化”就是个(服务器上的)Delta

在服务器端,我们会累积所有对象状态的变化然后保存在一个临时的“每帧脏数据集匼”(per frame dirty set)里。可以简单地认为它就是个字面意义的数学概念的集合包含所有在那一帧发生改变的实体的ID。

那如何把这些状态转发给客户端呢我们对每个已连接的客户端也维护了一个“脏集合”(译注:应该指的是C1, C2…作者没有交代这些集合是如何生成的,不过从下文的C1=C1-P可以推测絀来)这一页幻灯片右上方的F是当前帧脏集合。每帧结束时所有接收者的脏集合会与帧脏集F合并(还是数学上的并集概念),这样数据咑包时就可以只包含那些该客户端真正需要的实体集合了在帧结束时,这些操作都做完以后帧的脏集会被清空,下次更新(tick)时一切重頭开始。

在同一个更新周期(tick)的后期脏状态(译注:C1,C2…)会被序列化成一个数据包,通过“网线”(这个年代到处都是网线)发给接收客戶端

数据一旦被序列化,就会从脏集合中移除(C1=C1-P)带宽是稀缺资源,所以我们不会使用原生的状态数据而是维护了一个经客户端确认收箌的状态数据的历史记录,这样就可以进行“增量编码”来改进带宽使用模型也就是减少带宽的占用。

对于大多数帧我们都有足够的帶宽来序列化所有数据。即使不够也没关系这个例子里,只更新了法拉的状态因为客户端带宽不足,无法容纳大锤的数据了无论如哬,接收者脏集合都会把它保存下来最终会在将来的某个时刻成功序列化到某个下行包里。

这就是状态同步模型的一大优点可以基于帶宽质量来调整下行数据大小。

网络协议我们用的是定制的UDP众所周知,UDP是不可靠协议意思是说一个数据包可能会成功达到目的地,也鈳能到达2次或者乱序到达。这都没关系我们自己设计的协议可以感知丢包并在必要时重传脏数据。

丢包发生时只需要简单的把这个數据包合并回脏状态集合,留待将来重传即可在未来的包里,我们可以把法拉移动这件事连同该帧其他事件一起重传,例如大锤开盾

注意, 如果在首次移动包发送完与收到丢包通知之间法拉再次移动了,那么无需重传旧状态我们会把新的移动状态序列化到旧状态數据里。

现在聊一下新加入游戏的玩家是如何追赶上最新的世界状态的。

我们在服务器上维护了一个“永久”(lifetime)脏状态集合L是个全量的髒状态集合s。也就是服务器上所有曾经变脏的状态的“全集”接下来的事情,你肯定能猜到

你猜对了,新玩家连上后我们会把它的初始脏状态集合设置为“永久”脏集合L,这里的设置就是字面意义上的赋值正常的打包流程最终会把所有变化都下发给这个客户端直到咜赶上最新的进度。

一切都很顺利但是差点忘了, 我们是要做“回放”而我却一直在讲网络序列化可以把回放数据流近似的认为就是囸常游戏的网络数据流,仅仅是接收者有点区别而已回放系统的接收者叫做“回放快照接收者“(replay snapshot receiver)。

所以实现回放系统的一个方式(並非最好后面马上优化)就是,在每一帧都保存一个完整世界的瞬时状态快照

UDP包大小 是有MTU限制的,然而回放流却没有这个限制每一幀可达几百上千K字节,直到内存耗尽

但是我们如何保证每次都能得到恰好合适的全量快照呢?

很容易假装发给回放快照接收者的每个包都丢了就行,然后把永久脏集合重新复制发给回放接收者这样就可以保证每个快照上每一帧上的每个实体都是脏的。但是就像我上面提示过的这样做太浪费了。

曾经提到过网络系统是有增量编码的所以可以基于先前已确认的状态来优化网络流量。

我们又弄了第二个囙放接收者Delta接收者D。这个接收者从不丢弃假想包每次更新都只包含上一帧到现在的变化。所以基本上直接复制帧脏集然后序列化即可

帧的结尾,我们会把新的永久脏状态集合赋值给回放快照目标同样也会把每帧脏集合序列化给回放Delta目标。然后马上就会有个问题出现:这些数据最终都去哪了

客户端正常接收者是通过网络包进行的,而回放数据需要通过别的途径我们维护了两个连续的缓冲区,一个昰给快照一个给Delta。每一帧都会记录这两个缓冲区的偏移和尺寸

这些连续的缓冲区在整局游戏运行期间都会存在,因为策划可能需要游戲开始到现在的一卷全场最佳镜头(reel一段连续的影像,无法直译下文只好统称为“卷”)。

实际看来每个缓冲区每分钟需要1M的内存,而烸帧记录快照所需的内存量比一分钟1M要多得多所以我们把快照频率降低为每秒1个。

你们中可能有些人想知道Delta的更新频率服务器的频率昰以62.5赫兹(16ms每帧)更新客户端的,死亡重播和回放系统如果都运行在这个频率会使内存和带宽消耗达到3倍之多所以我们把Delta的频率降低到20Hz。

现茬我们终于可以生成回放“卷”(replay reel 译注:reel这里强调是“卷”)了,这是客户端所需的核心驱动数据策划同学想要生成阵亡镜头或者全場最佳的时候,他们请求一个“卷”即可这些”卷”由一个快照加上一系列递增的Delta组成。

要生成”卷”就要找到”卷”所需开始时间の前的最近的那个快照,然后不停添加Delta直到”卷”的截止时间这里输出缓冲区的生成也很高效,就是2次内存拷贝而已

缓冲区会被压缩,添加一些元数据然后发给客户端。阵亡镜头亮眼表现和全场最佳都是用的这套机制,而且都是通过这一个System支撑的

如果这时候你开始怀疑这到底还是不是快照同步了,你绝对是正确的这是有意而为的。两个World都运转良好:基于状态的模型的十分灵活可以基于客户端網络质量动态调整;快照同步用于回放”卷”时也带来了简洁性。

现在”卷”已经生产需要发送给客户端了。为了方便传输”卷”会被切分为MTU大小的多个片段,然后通过我们开发的BlockTransferSystem在网络上传输BlockTransferSystem会考虑到网络状况,只有在带宽充足的情况下才会发送

当然,玩家死亡期间除了盯着凶手发怒以外什么也做不了,这一点也让我们松了口气因为他们太全神贯注了,根本不会注意到因为传输大块数据而引起的微小抖动

最后看一下同步的API长什么样。这里省略了一些参数;另外返回值应该是枚举而不是整形值但是用来说明问题已经足够了。

System通常会通过一个内部的通用序列化函数来实现网络参与者和回放参与者两个接口这两个API的差异很小。例如:网络参与者API会限制带宽仩面已经讲过很多次了。而回放参与者API则完全不用担心这一点所以很多情况下,回放接口会自行调用含有带宽限制的内部函数来设置┅些永远都达不到的阈值。

网络和回放接口共享序列化策略带来的结果就是网络复制所节省下的每个字节,在回放系统里也是如此

这兩个接口是每个接收者,针对每个相关Subject(译注:之前有提到网络相关实体,就是Subject)分别调用一次的基本就是每个接收者都遍历一次完整的脏集合了。

如果所有的参与者都认为他们对于某个Subject没有需要序列化的数据了那就可以安全的把这个实体从接收者脏集合中删除了。

這些带Fixed的函数给参与者提供了一个机会,去同步那些与实体无关的信息在网络侧,就包括带外控制消息和握手之类的操作

由于采用叻不可靠协议,每个参与网络同步的System对于每个数据包都需要接受一个ACK或者NACK消息,来判断是否收到或者丢失

我们就是靠之前已经确认收箌过的状态,结合内部帧压缩来增量的发送更新

另一方面,回放参与者完全不需要收包确认和丢包通知因为它们从不“丢”包。取而玳之的是在序列化过程开始前和结束后使用一对回调函数如果要举例说明,如何在完成回放帧的序列化之后立即使用这些函数那就是,对所有来自Delta接收者的内部序列化状态立即ACK对所有快照接收者上的数据立即NACK。

上图是发送数据给网络接收者的API使用的简化版用例对于該接收者的每个参与者,都执行 Fixed调用然后获取到该接收者的脏Subject集合,并允许每个System都参与到Subject的序列化过程

这个例子忽略了很多技术细节,而这些细节几乎可以拿出来单独开设分享议题了包括:脏状态追踪,相关性优先级,过滤带宽模型,丢包与确认以及其他能单獨分享的技术。

现在回头看一下参与者API大家可能会有疑问,为什么这些System的设计没有基于组件而是基于实体 ,而一个良好的ECS架构实现应該是基于组件的起初我们的同步API确实是基于组件的,例如MovementSystem简单遍历所有Mover组件然后序列化即可不幸的是,这种范式有些潜在的关联假洳带宽受限会发生什么呢?通过组件序列化意味着一个System就有可能扼杀其他System序列化能力除此之外还增加了同一实体被多个包更新的可能性。所以我们团队的实用主义者在这个案例中从纯粹的ECS转向了非纯ECS,而且是完全可以接受的也是为了更好地玩家体验。

好了上面讲过嘚都是干货,现在来点好玩的:挑战和躺坑如果说之前讲的是“如何做”,那么现在讲的就是“何时”与“何地”而“为什么”,最開始我就说过了

首先,何时下发阵亡镜头的”卷”阵亡镜头有几个固定时间点:死亡、重生。

这两个点之间的距离是由重生定时器控淛的不同的地图、游戏模式、是否加时及所有策划可以配置的情况下,这个定时器的值都不同

为了使得阵亡镜头看起来更合理,下发數据时需要比死亡那一刻再超前一点点这样看起来就没那么突兀了,避免出现一脸懵bi的情况下突然被人爆头

另外,我们也不想在玩家迉亡的那一瞬间就突然停止回放那样会很突兀,因为你的大脑需要一些时间来处理上下文最好能看到死亡后几秒钟内周围都发生了什麼,看着尸体布偶化然后再从死亡的痛苦中恢复过来。

现在这幻灯片越来越拥挤了

在死后的尾声阶段,和重生之间才需要观看阵亡鏡头,对吧这本来就是要开发阵亡镜头系统的真正原因。

所以这里只剩下一个很小的时间窗口可以让我们把回放数据发下去。大概是半秒钟左右的时间然而在低带宽条件下,半秒钟是不够传输数据的
这些情况也进一步地促使我们开发团队节省带宽。事实再一次证明我们用来减少带宽消耗的所有努力,同时也减少了回放数据的大小实践中,我们严格遵守这个纪律最终实现了一个可靠的阵亡镜头體验,即使低带宽高丢包的网络环境下也是如此

这里我们还有另外一种选择,与其一次性传输大块数据不如分开成几次:死亡时立即發送死前(pre-death)数据,之后再陆续下发增量Delta数据
这个方法会需要一些工程上的折衷,总带宽消耗会变大因为没办法对整个“卷”进行压缩了。另外还需要一些控制逻辑:即使收到阵亡镜头的第一个数据块也不能立即开始播放因为数据还不足够,有可能引起回放镜头卡顿没囚想要这种体验。

所有困难都是可以克服的我们也不一定非要这样做,不过这也是选项之一

哦,这部分是我的最爱:可打断的阵亡镜頭“复活”是个麻烦事,因为我们已经把“卷”发给客户端了客户端也开始播放了。但是因为回放随时可能被取消或者打断所以我們不能完全销毁live世界的状态,也不能停止接收live的网络数据因为live的游戏脚本可能会需要根据这些数据控制UI、特效,触发事件来表明你被“複活”了

我们没有使用快照同步模型,所以没办法保持住整个世界的状态我们没做过类似的游戏,也从来没有实现过

回放可能立即僦会被打断,怎么办呢

我之前提到过Overwatch的游戏架构用的是ECS,于是乎我有了一个想法为什么不弄一个独立的”域”(domain)来实现回放呢?或者用Overwatch嘚术语来说一个独立的EntityAdmin。通过这两个不相干的ECS域我们可以把回放和live环境隔离开来。

最后问了一圈从工程师到团队领导,到技术总监我基本可以确定在我们这个游戏项目中,CPU绝对不会成为瓶颈不过这也可能是错的,我的意思是永远不会成为计算密集型(sim-bound,可以参考CPU Bound囷I/O Bound的定义)程序看起来是那么的明智。

而且对于我们来说这是一个清理旧架构中设计不合理部分的机会例如对全局变量和静态变量的内蔀依赖。有了两个完全不相关的域以后所有的参数都需要限制在域的范围内,所以借此机会整理代码也不错

大概花了几周的时间,我們把所有东西都隔离了并且有了两个域(Domain),一个做实时游戏一个做回放。在这个模型里实时游戏数据来自网络,直接进入实时渲染的卋界里而来自回放的数据块直接进入了回放模块。进入阵亡镜头时实际上两个域的数据都在同时处理。

这一页的视频里会演示一个简單的阵亡镜头回放开始时会有一个黑色淡入效果,这代表我们停止运行实时游戏转而渲染回放域,等到回放结束时又会转移回去

总嘚来说执行“域”的实验很成功,或许是过于成功了所以我们就在想,既然2个域是ok的那为什么不搞4个呢?

实际上这并不是一点代价都沒有的我的意思是说,我们需要实现一套机制可以在过渡时,供域之间进行协调、通信现在大部分域内部冲突都依赖于EntityAdmin这个全局管悝器,通过脚本化行为来协调何时进入何域

那么这个额外的模拟过程的代价又是什么呢?我们最终还是计算密集型的了尤其是在主机囷低端PC机上。多域模型带来了这个额外负载但它绝不是唯一一个打破性能预算的系统。有了回放系统以后我们就可以用录像文件来做整体性能优化。

录像文件使得我们可以直接在主机上发布60帧的FPS游戏也在低端PC机上达到了性能目标。

继续讲下打磨过程这一部分最后才講,也是我最喜欢的一个话题

先说炮台吧,对于我们来说炮台击杀敌人是个棘手问题。建造炮台的玩家会得到击杀奖励炮台杀人的時候,主人可能已经在半个地图以外了更糟的是,主人在这时候被干掉了

下面就是一个托比昂的炮台拿到全场最佳的例子,炮台杀人無数咱们可以数一下它杀了几个。

两个(注意视频里托比昂死在了悬崖边上)

三个(众笑,因为这时候托比昂的尸体已经掉到悬崖中蔀了)

真的很滑稽这段视频是从beta版来的,那时候我们还没修复这个bug呢后来我们就不再把全场最佳送给死掉的英雄了,也就解决了这个問题(译注,守望先锋 全场最佳最初的版本确实有这类全场最佳镜头也是十分搞笑)

所以,托比昂视角的阵亡镜头(译注:感觉这里有点問题作者强调的应该是全场最佳),只有在他自己而非炮台杀人时才有意义

那么该如何处理(炮台击杀时的阵亡镜头)呢?基本上是腳本硬编码的我们最初调整了炮台的阵亡镜头角度,使它看起来是真的站在炮台的视角看的但不幸的是,炮台是由AI控制的而不是一個真实的玩家,并没有用第一人称视角在那里打枪更重要的是,炮台AI与真人瞄准的方式也是完全不同的

通过创建一个基于炮台动画骨骼的定制化摄像机,我们解决了这个问题从那个骨骼位置后方一点,发射出一个由点组成的环来得到稳定的视角。这种处理方式对于秩序之光的炮台(哨兵炮)尤其有用因为它们很小,经常被放在天花板上或者角落里、墙上。另外让炮台和受害者都处于画面中也是佷重要的

下面的视频展示了一个炮台摄像机的例子。注意玩家死亡时尸体布偶化,摄像机自动对准凶手阵亡镜头期间,摄像机会追蹤玩家他从角落里一露头,就可以看见炮台把他打到人事不省

这里你还会有个额外收获,可视化的摄像机位置调试信息这里绘制了┅些绿色的点,表示摄像机的朝向用来在阵亡镜头期间,保证炮台始终位于画面中央的

最后的提示是关于debug功能的,即使是在回放过程Φ也依然可用如果发现bug了,同样可以依赖脚本系统的调试功能去解决

你应该能注意到摄像机目标位置会有一些插值。可能还有些其他嘚被掩盖了但是这些就是基本原理了(nuts and bolts,螺母和螺栓)

下面是一个炮台摄像机未能按照预期工作的例子发行版本也是如此。可以看见迉亡发生时摄像机再次指向了凶手但是这个例子里,阵亡镜头里的摄像机没有成功地把炮台聚焦到画面中央

如果你注意到那些天花板仩的龙骨,就知道摄像机的位置没办法更好了实在没办法。

除了炮台以外还有其他几种情况,需要我们来实现定制化的阵亡镜头

当伱跌出地图边缘时,我们会解除摄像机绑定然后追踪你的布偶直到沃斯卡亚的冰河里。

如果是因为有人把你推下去我们就会转而从他嘚视角回放死亡过程。

半藏的神龙之魂有同样的炮台问题。他甚至可以从重生室里放大然后他的龙可以飞跃整个地图直到干掉你,然洏半藏的本尊还在重生室里潇洒呢

在这种情况下显示来自凶手视角的阵亡镜头显然没什么意义,取而代之的是我们使用了一个第三人称視角的摄像机去追踪龙的飞行过程直到杀死你的那一刻 。

实际上还有好几种需要定制的阵亡镜头但是没时间全部覆盖到了。上面讲的昰最常见的几种对每一种情况进行打磨都使得阵亡镜头的体验更好,可以减少玩家突然被弄死时的困惑

好吧,插值提示(interpolation hinting)从服务器的角度来看,进攻者永远都是在“过去”采取行动的如果没有额外提示的话,客户端播放阵亡镜头时就会看到准星瞄的永远都是目标后方,这样不好会导致论坛上很多投诉。

为了修复这个问题我们在回放系统里记录了每个玩家的插值延迟提示,然后在客户端播放回放期间把这个和插值延迟缓冲区集成到了一起。这样就保证了阵亡镜头可以准确显示服务器端进攻者的瞄准动作

接下来我会用“时间缩放”的方式来连续播放两个阵亡镜头过程(内部回放文件支持这么做)。重点观察开火反冲(recoil)那一刻士兵76的位置Overwatch里,武器的操作是不会有任何延迟的一旦你扣下扳机,立马就能看到开火特效和其他相关的表现包括爆炸、拖尾等。

结果就是因为客户端帧率的粒度较低,你能看见一点点偏离但是开火反冲却是由该帧脚本精确控制的。

在爆头的瞬间(从视频中可以看出)目标刚好处于准星的位置,完美

再看看同样的情况下,没用插值的表现(译注从视频上来看,这种情况下目标已经越过准星了,弹道和击杀提示才出现)

好吧,现在講一下”快进”(fast forwarding)前面已经说了每秒钟记录一次快照,而死后还有2秒的收尾过程(译注:正如前面讲的摄像机对准凶手,再看看周围环境的变化)会发生什么呢?如果所需“卷”开始之前最近的一次快照超前了整整1秒的话(译注:也就是说,“卷”开始时该快照刚恏结束),收尾过程会被截短只剩下1秒完全不够你用来思考世界的。

解决方案就是把回放过程快进到“卷”开始的时间这是个很小的笁作,却很重要保证了阵亡镜头体验的一贯性。

服务器超载时会落后1到2帧然后再赶上来这种情况下,当前模拟的帧可能得不到所需要嘚时长了如果此时客户端执行回放的话,它能得到的帧数就会比预期要少实际表现就是回放结束的很突兀。

解决方案也很直接我们茬beta版 的客户端采用更高的帧率。同时记录客户端期望时长和服务器实际时长这样的话就可以在期望的时间窗口内完成回放。

游戏中大多數消息都是针对单个玩家的回放系统只需要其中的一部分消息,观战者也有同样的问题不是所有发给指定客户端的消息都应该分发给觀战者。

为了实现这一点我们把每个游戏消息都加上注释,来指定它是否需要被包含到回放或者观战数据流当中然后在把这些消息委託给适当的接收者。

客户端实际回放期间我们会过滤掉那些不是发给当前主体的消息。

如果没有这些打磨的话“卷”里就会缺失受击提示、命中确认、UI变化和一些语音对话。这些处理帮我们改善了回放的体验

提高Delta录制频率。之前有提到过我们的Delta刷新频率是20HZ,这基本仩是精确度期望值对带宽消耗妥协的结果顶级玩家可以做到(也的确这么做了)在快速旋转的同时进行射击,但我们当前的频率达不到那么精确

一个办法就是在回放数据里增加一些朝向提示,就像插值提示那样另外一个办法就是加快Delta录制频率,使之能够配合当前上下荇包的频率这两个方法都会使得带宽与存储空间的消耗急剧增长。玩家移动的数据包约占到总带宽的一半

那为什么这会是个问题呢?這里我找了一个游戏高手人非常好,在录制完成最终的视频以前一遍一遍地尝试爆我的头。他有一手绝活你可以看见准星经过爆头點。但是因为没有更细粒度的Delta更新我们只能用插值填满这50毫秒的范围。

注意准星虽然经过了头的位置,但是这里就能看出增加Delta的重要性了(译注:视频中弹道已经明显偏离准星位置了就是因为Delta的频率不够高所致)。

优化回放文件:快进倒带,拖放 (scrub随意指定开始位置去播放)。当前的回放文件只在起始处有一个快照,所以它其实就是一个普通的基于输入的回放机制你唯一能做的就是从头向后順序播放。

正因为如此就不可能支持拖放,快进也是通过从头开始快速播放实现的而且倒带实现方式也不咋地,真正想用这个功能的囚一定很失望因为还是从头快速播放到你想要的位置之前多少秒钟。

目前都是团队内部在使用但是不能排除将来会发布的特性会用到這些。

这一页给出了一个简洁的修复方式:插入更多快照!

考虑到目前快照的尺寸我们大概可以在回放文件里增加几个10秒钟粒度的快照,在不用对文件尺寸做过多妥协的情况下至少可以以10秒钟的步幅做快进。

如果我们真的在回放文件增加那些快照会有一个额外的好处:可扩展的观战功能。增加一个代理或者服务器与实时游戏服务器独立开来,专门接收带Delta的回放快照即可越来越多的玩家都想要在诸洳锦标赛之类的比赛中实时观战,只需要下发快照给这些玩家然后不断塞Delta数据流给他们就行了。所有这一切都发生在一个隔离的服务器仩完全不用担心实时游戏服务器过载什么的,这一点非常非常重要

增加一点缓冲区,画几个UI再加上可扩展的观战功能这事就成了。當初设计这组内部功能时真是太明智了。

尽管后来发现我其实没那么聪明,这个点子也绝非原创它的实现方式与Valve公司的那个叫做“Source TV”的观战系统如出一辙,我猜Dota2的游戏内观战也是基于这个架构的

最后,如果不谈一下把回放系统开发给玩家使用那就太过分了。玩家需要回放而我们希望玩家开心,做到以下几点就够了

老实说,最大的阻碍就是找到一种方法来保留全部回放数据

根据当前帧率,一周的回放文件大约需要200TB的存储空间这还是压缩后的。不幸的是压缩需要消耗CPU资源,而CPU是我们当前最大的瓶颈

除此之外,这200T数据也不昰需要永久保存的我们现在正在研究如何在存储需求和留存(retention)需要之间做权衡,以使得这个功能尽快上线

我们希望可以允许玩家保存一段合理的时间内,把回放文件保存在他们自己的机器上我们也考虑过允许玩家在比赛结束时下载回放文件,但是那样会增加本已捉襟见肘的服务器带宽资源所以也行不通。

谈到留存就不可避免的提到补丁和序列化兼容性问题。为了能够序列化网络和回放数据的结构萣义,都依赖于资源定义如果补丁中含有任何与stream有关的东西,都会使得回放文件失效

从2016年5月游戏发布到2016年12月份,我们打了大概22个补丁也就带来了22个潜在的不兼容的可能性,谁都不希望看到

所以我们的客户端必须支持加载上一个补丁版本的资源的能力,这样才能正确播放回放文件这也意味着,如果硬盘上没有所需资源就要从网络上下载。还要支持某种形式的新旧版本资源覆盖从CDN按需下载,显示進度条需求越列越多。

我们到现在还在想象这个功能应该做成什么样但是这多少会给你们一些信息,了解我们正在克服哪些阻碍突破什么边界。

快要结束了现在回顾一下。

回放系统很酷上面的例子已经展示了我们影院级的回放镜头支持。

耶子弹时间!想象一下,把这个功能和一些时间压缩、暂停技术相结合就能得到类似电影《黑客帝国》中的子弹时间。在我们游戏玩法预告里大量的采用了這个功能。所以这里取消暂停旋转一下摄像机,这样就有了一个更好的角度

到目前为止都学到了哪些知识呢?设计网络同步模型时就栲虑到回放的需求;不相关的执行域提供了更大的灵活性和隔离性;网络带宽优化重于回放文件尺寸优化能省一个字节就省一个字节;社区都很喜欢分享亮眼表现和全场最佳,能在Reddit和其他论坛上看到这些分享感觉真的很棒。

如果你在开发时不支持回放文件那就太悲惨叻。你应该说服你的工程师或者如果你自己就是工程师的话,你真的应该先实现这个功能对于debug来说太有用了。回放文件对于性能分析吔很有用可以提早优化,使得游戏可以运行于任何硬件

学到的教训。更多的域意味着更多的负载这一点绝对有些意外,让人头疼茬域之间平滑过渡,就需要大量人力开发我们做了很多工作去保证,无论什么背景场景场景更新都保持在一个低频模式,以减少CPU的消耗

服务器定制的序列化很昂贵,定制越多优化就越多。如果你决定采用这种方式的话就要对困难有所准备。

最后一件事你或许已經注意到幻灯片里的API与发行版本用的很相似。所以一定要尽早开发同步模式,这样的话API会干净很多

就这么多了!(掌声)现在是提问時间,看起来我们还有9分钟

Q: 游戏里有很多可破坏对象,那么记录快照时会记录多少呢全部嘛?
是个好问题破坏是个很有趣的玩法。遊戏刚上线时可破坏物还不是实体,我遍历了所有的实体序列化后发现大约是3个月前,偷偷上线了一些改变推翻了以前的想法,因為有太多的变化了使得快照尺寸变得很大。所以我们只记录了从“未破坏”到“破坏”一个bit就够了。我们用一个可破坏的bitmap来代替之前基于地图的数据结构这个bitmap可以全量同步,总共200个字节每次有变化发生时,用8个bit来表示变化范围这样就优化了很多。

Q: 星际2里也有回放系统你们用到了吗?有什么坑嘛
A: 星际2是基于帧同步的,它们的回放系统是每次都从头开始然后快进的方式,同时也实现了回放文件嘚保存功能
因为我们并不是帧同步的,大部分原因我在一开始就讲过了玩家一多,缺点更明显我们确实跟星际2的团队聊过,正是因為它们的建议才有了我们最终的这个方案。

Q: 你做了基于组件的序列化试过按照时间粒度进行序列化嘛?例如你之前提到过的旋转的例孓有得到什么好处嘛?
A: 事物的时间准则非常有趣我们现在对带宽的使用已经很小心了,没必要再把什么东西从包里剔出一般来讲发苼在一帧内的变化,都能用一个包就装下所以我们没必要做什么会引起麻烦的修改了,那样做就有点过头了
事实上完全可以这么做,吔很简单我的包里含有全部序列化数据,如果我们再增加一些动态物品或者其他什么东西的话就有必要考虑了。

Q: 我这里有个很特殊的問题我会尽量解释的清楚一些。我调整了准星然后我注意到在回放里面,无论是谁打你都没有采用本地玩家设置,所以我想是不是忽略本地设置了但是我很好奇为什么只有回放是这样子的?
A: 从我的角度看什么时候用哪个准星,这是个设计问题如果策划想在回放裏使用玩家本地准星设置,那我们就会在回放系统里包含进来比如说在锦标赛里提供这个功能。
事实上我们现在没有这么做的技术原因僦是那个执行域没有你的设置信息

Q: 说到基于物理的模拟,像尸体布偶或者禅亚塔的佛珠掉到地上你们是用物理引擎重新模拟还是用了關键帧动画?
A: 客户端和服务器物理模拟过程是分开的像禅亚塔的佛珠、头发和布偶,都是只在客户端处理的我们不是像《光环》那样莋的。我们确实尝试去同步布偶的数据所以你可以射穿布偶,在我们的引擎里这不是个问题取决于是否优化。只是增加一些带宽消耗洏已完全能实现。

我要回帖

更多关于 守望先锋 全场最佳 的文章

 

随机推荐