参与Game Jam的历史。 雪箱

图片 在2017年底,我有机会测试自己作为众多世界游戏果酱之一的参与者的力量和热情。

由于这是我在这样的项目中的第一次经验,因此我学到了一些有用的课程和一些令人惊喜的惊喜。 嗯,我还有一个玩具,可以在星期五假期与同事一起玩。

在猫之下,描述了30天的开发密集度和20天的等待结果缓慢过程。

注意:本文本质上是叙述性的,几乎没有技术细节。

序言


我一直想尝试自己做一名游戏开发人员,在去年,这种愿望变得越来越具有侵略性和侵略性。 在这一年年底,我决定开始做一些事情-参加游戏开发的聚会,读书,甚至制作游戏的小型原型。

然后有一天,我的同事Selim提供参加众多Game Jam的机会之一,我认为这是学习游戏厨房的最佳机会,没有任何长期义务。 这不是原型,但还不是它的长期项目。 此外,紧迫的期限通常是最好的激励因素。 而且主要的优点是,无论如何,输出都是整体而完整的,这总是会激发新的成就。

因此,我们决定参加。 两位完全没有游戏制作经验的Java开发人员。 但是什么时候需要开始?

准备工作


选择的Game Jam是主题性的,组织者必须在开发开始之前严格宣布该主题。 参赛者需要1个月的时间来制作游戏。

我们在开始前一个星期进行了注册,由于没有话题,我们花了很多时间在懒的期待和不作为上。 花时间选择一种游戏类型和游戏机制可能更好。 然后将其包装成合适的样式。

在第X点,组织者宣布了比赛的主题-倒回(返回),倒计时开始了。

Selim对游戏唯一的希望是游戏服务器的存在和实时性。 他很想知道开发服务器端的乐趣和困难。 此外,游戏本来应该很简单,否则我们可能没有时间完成它。

关于这个话题,最初的想法要么是关于复古,要么是关于史前时代。 复古的主题对我来说并不有趣,因此我试图提出一些有关恐龙或古代人的东西。 但是,在这个主题上,我无法提出一些简单且多用户的建议。

然后我想到了制作一个字面意义上的沙盒游戏的想法-指的是海滩上的儿童娱乐活动。 我们建造城堡,向竞争对手奔波扔沙(我希望不是每个人都有如此艰难的童年),但是对此感到不满的父母会受到惩罚。 所有这些都是2D俯视图。

塞利姆(Selim)认为沙子在眼睛中太残酷了,建议用雪代替城堡,用冰堡垒代替城堡。 在此选项上,我们停止了。 根据我们的能力和时间,我们可以添加新的功能和多样性。

发展。 第一周


我们在项目中的角色是自己划分的。 如我所说,Selim只对服务器开发感兴趣。 我很想尝试开发UI,因为我认为它与业务应用程序的开发有很大不同。 游戏界面是计划中的网络。

所用技术的简要说明
客户端与服务器的交互完全建立在Web套接字上,因为我们需要将消息从服​​务器异步且定期地发送到客户端。 来自客户端的消息也通过Web套接字传输,只是出于“为什么不这样”的原因。 所有消息均为Json格式。

为了使用服务器上的Web套接字,使用了Spark微框架。 作为最受欢迎的物理引擎之一,box2d被用作物理引擎。 他负责计算服务器上的物理量。

客户端是用纯JS编写的,没有使用任何框架,因为我并不擅长使用它们,此外,这对于一个小型项目来说毫无意义。 作为游戏引擎,使用了Phaser 2-一个年轻而有前途的JS引擎。 与服务器上的物理引擎不同,它的主要目的是图形和简单的物理计算。 KnockoutJS也用于数据绑定

JetBrains产品用作IDE:Intellij IDEA和WebStorm的试用版(在Game Jam发行时仅为30天)。

开发从休息日的联席会议开始。 在最初的两个小时内,我捡起了“临时”精灵,并认识了一个会跑的男孩,他知道如何扔雪球。 可能有一张“曾经/已经成为”的图片,但是它没有任何意义-视觉上是什么,然后保留了。

具有有效的用户界面, 仅能加强服务器交互。 因此,剩下的时间专门用于集成:API的讨论,建立连接,实现交互。 到一天结束时,我们的小矮人可以通过按一个键向侧面移动1个像素。 这虽然适度,但仍然成功。

进一步的发展仅在下班的空闲时间继续进行,每周分别有6天供家庭使用,一起休息一天。 这要归功于简单的API和角色的严格分配。

为了重复第一天的成功(奔跑和扔雪球),但已经通过服务器,我们花了一个星期的时间。 这同样受到许多技术因素的影响,包括针对我们的客户-服务器交互模型设计不良的工具的问题。 我想更详细地介绍这个模型。

客户端-服务器交互模型


最初,决定由服务器负责任何移动,而客户端将仅发送命令(例如,按下按键)并绘制来自服务器的内容。 后来,对这一理念进行了稍微的修改,但是直到最后,服务器仍然是中心链接。

在第一个实现中,服务器每隔一个滴答声就向客户端发送更新。 即 例如,如果按住某个键,则玩家的位置每隔几毫秒就会发生变化,并且新位置会发送给客户端。

使用这种简单的算法,我们实现了玩家的移动。 但是,追求最好的努力促使我们将算法更改为一种事件:只有足以进行同步的重要事件才被发送到服务器以及客户端。

例如,要将玩家移动到地图上,您需要4个事件:

  1. 客户 :按下向上键;
  2. 服务器 :玩家开始从点[x, y1 ]以角度ν移动,速度为ν;
  3. 客户 :按下向上键;
  4. 服务器 :玩家已经完成在[x, y2 ]点的移动。

这种方法有利有弊。

缺点

  • 客户端的逻辑要多得多:客户端本身必须计算运动,碰撞等。
  • 客户端上的逻辑必须非常准确地重复服务器上的逻辑,否则将获得对象运动的跳跃。 在本文的进一步内容中,将有关于此主题的几个示例。
  • 在网络问题的情况下,以后接收事件可能会导致错误状态(越界,穿过对象等);
  • 通常,在服务器和客户端上使状态不同步要容易得多。

优点

  • I / O上的负载少得多;
  • 其他逻辑可以挂在事件上。 例如,在运动的开始/结束时,您可以开始/停止动画。
  • 网络问题较少影响运动的平稳性。 在滚雪球飞行时,即使延迟一秒钟也不会影响任何内容,因为服务器不应发送任何内容。
  • API和交互本身变得更加透明。

从这些列表中可以清楚地看到,如果您为实现客户端而努力,那么几乎有一个好处。 对于我们的项目,我们设法实现了这一目标,并且客户端与服务器之间的交互完美地进行了。 但是我们必须致敬,这花了很多精力和神经。

发展。 第2周和第3周


一周后,当我们至少成功地掌握了集成并为服务器和客户端之间的几种消息提供支持时,是时候为游戏添加更多功能,而不仅仅是运行中的男孩。

因此,我们决定增加一个跑步女孩! 一路上,对于这个女孩,我必须创建一个角色选择窗口。 而且由于仍然必须完成窗口操作,因此不添加名称是一个罪过。 另外,我必须拉伸图像,类似于9-patch方法

图片
看起来像登录窗口的第一个版本

正因为如此,添加一个女孩这一简单而重要的任务花费了两个晚上,但这显然是值得的。 然后出现了具有相同任务的灰色日常生活:新的简单功能,错误修复,较小的视觉改进。

随着我在客户端的进步,出现了服务器开发速度和UI不一致的问题。 并且由于服务器是所有交互的中心部分,而服务器上没有完成的工作功能,因此客户端开发已停止。

因此,我直接在客户端中实现了一个简单的模拟服务器。 其中的很多事情都非常笨拙地实现了,包括 通过使用游戏世界的全球状态。 这足以开发一个完全独立于服务器的UI,并节省了我很多时间。

模拟服务器还有另一个明确的优势。 其中有一些机器人不知道如何射击,因此至少有人有机会获胜。

同时,Selim在服务器方面苦苦挣扎。 在服务器上,他连接了box2d物理引擎以模拟游戏中的物理。 这个引擎有很多细微差别,他们的研究花费了很多时间。 开发中最大的困难是缺乏游戏世界的可视化。 我们的客户只提供所需的东西。 在客户端,“服务器世界”的某些元素是隐藏的。 此外,不能完全保证客户端正确呈现所有内容。

Selim必须解决的重要子任务之一是检查对象的碰撞。 在客户端上,仅针对固定元素检查冲突。 在服务器上,必须诚实做所有事情,以免侵犯移动物体的权利。

在碰撞的发展过程中,我记得一个有趣的错误,它可能伪装成特殊功能:当玩家扔雪球时,它会被“反冲”地扔回去。 发生这种情况是因为在默认情况下,在box2d中,每个人都可以与每个人发生碰撞,并且总是会发生排斥。

通过引入掩膜解决了这个问题,即通过指定不能相互碰撞的物体类别。 例如,对于一个球员和他的雪球,面具是一样的。

Selim决定不将时间浪费在彼此打雪仗的特殊处理上,并评论了这样的碰撞:

// for the unlikely event that we collide with a sibling snowball

如实践所示,此“不太可能”的事件的频率倾向于投掷雪球的频率,因为当彼此相对站立时,雪球的轨迹重合。 因此,外星人的雪球不断被自己击败。 我们对此行为的看法存在分歧,因此我们将其保持原样。

当Selim体验碰撞的乐趣时,我调试了服务器和客户端上对象的同步移动。 我们自己的实现中存在一些小错误,但是Phaser带来了最大的惊喜。 在他的物理引擎中,将物体的真实速度调整为FPS并停止运行。 这样做是为了增加平滑度。 不幸的是,这种巧妙的行为与针对服务器的同步操作不一致,因此我必须自己实现移动对象的实现。

在“移相器”或“有阴影的种族”中描述速度的细节
为了检查和调试速度,我在地图上添加了另一个玩家的对象,该对象以相同的速度移动但在附近。 该影子播放器和普通播放器使用了不同的移动算法。 因此,我组织了比赛并比较了速度的稳定性。

最初,我尝试使用引擎的设置和所使用的物理方法来解决速度不均匀的问题。 但是,据我了解,这种行为无法以任何方式改变。 可以切换到更复杂的物理实现,但是我不想仅仅为了速度而这样做。 另外,该实现也没有给出绝对稳定的速度。

下一步,我尝试自己实现对象的运动,但是从引擎开始,我控制了世界上每个周期之间的时间差。 Phaser中有几种时间模型,标准速度实现基于其中之一。 但是,由于某种未知的原因,这些时间中没有一个具有稳定性,因此无法提供恒定的速度。 这是一个已知问题,不打算在版本2中解决: github.com/photonstorm/phaser/issues/798

我花了2天时间在玩家和影子的“竞争”上,却没有找到可行的选择。 所以最后,我根据JS中的标准时间进行了所有速度处理。 如此重要的功能在引擎中得到如此奇特的支持,不得不实施自己的自行车,这是非常令人惊讶的。

有了这样的笑话,我们悄悄度过了第二周和第三周。 而且,每个星期都以“好,到下周,我们有义务准备一个可播放的版本”这句话开始-每次在我们看来我们即将准备就绪。 完全缺乏经验和过于乐观是评估时间框架的最糟糕方法。

在交付前一周,在开发人员会议上,我们甚至无法显示正常版本,而不得不显示模拟服务器。

当然,以如此高的开发速度,最初构想的功能列表就没什么可梦想的了,拥有东西的乐趣更是如此。 因此,我们不得不忘记“沙箱”,而停在最简单的2D射击游戏中。

发展。 第4周,最后


在开发的最后一周,我们专注于修复错误。 适度而有效的功能比多功能功能要好,但会崩溃。 存在许多问题,其中大多数与命运不佳的整合有关。 到处都是小瑕疵,大大恶化了游戏的印象。

除了修复错误外,我还为UI带来了最终的亮点:添加音乐和声音,使用字体播放以及改善小细节。

在所有功能中,本周仅增加了积分系统,并限制了雪球的存量及其恢复。

在Game Jam的最后一天,我们可以和同事一起玩一些游戏。 尽管获得了普遍的好评,但由于存在严重的错误,该游戏仍无法成功-雪球以不同的速度飞向服务器和客户端。 因此,进入其他玩家更有可能是一次意外。

原因描述和错误纠正
我们在最后一刻犯了这个错误,使服务器上的FPS数量从实验性的1000减少到了100。

应该注意的是,到目前为止,我们还无法在客户端和服务器上实现完全同步的移动-有时移动会发生跳跃。 通过更改FPS,我们试图提高服务器的响应速度。

当我开始研究此错误时,我发现了两种模式:

  • 玩家的动作正确,稳定地进行;
  • 客户端上的雪球运动总是比服务器上的雪球运动更快。

对象速度的值是从服务器传到客户端的,也就是说,这不可能是值的平常不匹配。 同样,FPS反向增加到1000可以改善这种情况。

我花了很多时间尝试修复此错误。 但是没有任何帮助。 最后,找到原因的原因是使用谷歌搜索-box2d通过在世界的一步中将对象移动不超过2个像素来间接限制最大速度。 即 在100 FPS时,最大速度为200像素/秒(p / s),在1000 FPS-2000 p / s时。 此值是在常量中指定的,不能动态更改。 这充分解释了我们雪球减速的原因,因为它们的速度本来应该为700 p / s,这需要高于350的稳定FPS。

为了解决此问题,我将FPS增加到500,但这是有原因的。 在box2d中,您需要向世界的step函数传递自上一步以来经过了多少时间。 在进行更改之前,我们总是在调用函数之前先计算出这种差异。 但是现在,知道了所需的频率后,始终可以指示2ms的恒定增量。 随着世界落后于真实世界,必须一步一步地重复步骤,直到世界赶上这个时滞。 然后一点点睡觉,一切都是新的。

如预期的那样,此修复解决了滚雪球速度问题。 此外,服务器和客户端上的不同步移动问题最终消失了。 那时,这种奇迹般的修复功能让我们感到完全惊讶,但现在我明白了原因:尽管最高可以达到1000 FPS,但没有人取消服务器的缓慢运行,尤其是垃圾收集。 即 在某些时间点,FPS可能会随意降至所需的350 FPS以下,从而导致速度任意跳跃。

因此,在截止日期前2小时,由于一个封闭的虫子和一个可以工作的玩具而高兴,我们准备投降。 只剩下将游戏发送到项目网站。

我希望将游戏发布顺利并且徒劳无功。 必须创建一个项目页面,进行描述,上传屏幕截图等等。 正如预期的那样,我们见面了。 尽管后来组织者仍然单独接受后期的项目。

图片
游戏最终版本的屏幕截图

投票


根据竞赛的条款,开发完成后,将立即进行20天的投票。 在此期间,任何人都可以看到已完成的项目,这些项目已累积了200多个,只有参与者可以投票。 每个游戏可以分为以下几类:常规,图形,声音,游戏玩法,创新性和与主题的相关性。

投票阶段已经为我们准备了一个与我们游戏的多人游戏性质有关的严重惊喜。 观看比赛的人数很少,与敌人见面的机会趋于零。 即 人们去找一张空牌,扔了几个雪球,跑了几米,失望地离开了。

我们试图通过项目论坛来组织游戏。 另外,我和塞利姆(Selim)定期参加比赛,希望娱乐一个无聊,孤独的流浪者。 所有这些几乎没有结果。

我很想看一些人测试游戏。 我特别记得当玩家同时输入多个角色并建立一个五角星男孩的情况。 我不知道作者想说什么,但是我仍然有其创建过程中的屏幕截图。

图片

另一个玩家“入侵”了我们的游戏。 我们进行了名称长度检查,但仅在客户端进行。 因此,他绕过了这种防御,并开始以这种方式与我通信,每次输入一个新角色,并以一个男人的名字输入他的短语。

随着投票的进行,我和我的同事再次参加比赛,为项目完成当天的失败比赛报仇。 这次一切都进行得很顺利,我们对新功能有了很多想法,但是现在添加为时已晚。

在我看来,游戏质量,尤其是与服务器交互的质量非常好。 我们玩过多少次,没有发现任何问题,也没有听到其他玩家的声音。 考虑到交货前几天的错误质量和数量,对我个人而言,这是一个惊喜。

评估比赛所用的20天时间很长,但最终他们结束了,期待已久的结果来了。

图片

因此,在200多名参与者中,我们排名第36位。 一方面,这对于第一个项目来说还不错,但另一方面,它对于骄傲却有些不愉快。 特别是考虑到前十名的游戏都不错,但并不是所有人都值得特别关注。

经验教训


为了在半温室条件下上课,一切都开始了。 我们试图自己发明很多东西,并限制在一套最简单的工具上,以感知问题并体验自己皮肤上的不良方法。 但是现在有了关于如何做以及为什么做的知识,学习理论将变得更加容易。

需要一个艺术家 。 正如实践所示(不仅是我们的实践),您可以在没有良好图形的情况下制作出出色的游戏。 但是,选择正确的图片,字体和用户界面元素会占用大多数时间。 最糟糕的是,最终它们并不一致。 由此,失去了热管气氛,游戏看起来也不是整体。

游戏测试非常重要 。 由于开发速度问题,我们大部分时间无法测试实现的可玩性。 当我们能够正常玩耍时,就没有时间来解决问题区域了。 而且我们很幸运没有这么多问题。
我认为,对于游戏来说,对真实用户的测试要比对商业应用程序更为重要,因为除了方便和解决用户问题外,还必须保持一定的情感和参与度。

并非所有库都同样有用 。 几乎没有人使用自己的引擎来开发游戏,并且市场上有适合各种场合的引擎。 在院子里是2017年,人们会期望它们的高品质。 我选择了Phaser作为最推荐和最年轻的JS引擎之一。 在遇到他的所有问题之后,我恐怕无法想象引擎的外观如何。 不,总的来说,与Phaser合作的印象是相当积极的,尤其是考虑到良好的文档和示例。 但是,在不了解大量细微差别的情况下与他合作非常困难。 在春季,将发布一个新版本,希望对此进行重大改进。 在我的计划中,还有一个用于比较其他引擎的小型项目。
而且我会在很长一段时间内都记得box2d中最大速度的问题。

在Game Jam的过程中,学习是很有可能的 。 开始这个项目时,我们几乎对游戏开发或所用的库一无所知。 而且大部分时间都花在研究这些东西上。 尽管如此,我们仍然设法将游戏带入完整状态。

不需要很多功能 。 许多人喜欢我们的游戏,我感到有些惊讶。 是的,他们不是每天晚上都坐在那里,而是享受一两个小节。 但是在我们的游戏中,既没有原始思想,也没有大量功能,也没有任何历史。 可以说Game Jam中大多数获得好评的游戏也是如此。

(游戏)果酱是尝试的好理由 。 没关系:一个想法,一个新库,您自己的长处。 当有一个明确的目标并且其他人应该看到您的结果时,这是非常激励人的,不要变得软弱无力,尽力而为。 即使结果比预期的要糟糕,也不会遗憾地丢掉它,自己为自己学习课程并继续前进!

资源链接:



谢谢大家的时间和心情愉快!

Source: https://habr.com/ru/post/zh-CN409679/


All Articles