JS之战:我如何编写我的eval()

您可以在“魔法门英雄”的浏览器版本中记住亚历山大·科罗泰耶夫Alexander Korotayev)解密有关他的报告的报道,引起了人们对哈布雷的大量见解。 现在,他制作了一款针对程序员的游戏:您需要使用JS代码进行游戏。

这次开发耗时不是数周,而是数周,但没有任何有趣的挑战,这是不可能的。 即使以前没有接触过JavaScript的开发人员,如何使游戏变得方便? 如何通过简单的方法保护自己,使其胜过游戏?



结果,亚历山大再次发表了有关HolyJS的报告,我们(会议的组织者)再次为哈勃准备了文本版本。


我叫Alexander Korotaev,我在Tinkoff.ru工作,我从事前端工作。 此外,作为Spb前端社区的一部分,我帮助组织mitaps。 我制作了Drinkcast播客,我们邀请有趣的人并讨论各种主题。

玩具的本质是什么? 首先,您需要从建议的单位中选择一个单位,这就是一个RPG系统:每个单位都有其自身的优缺点。 您会看到敌人选择了哪些单位,并选择报仇。 然后,您需要用JavaScript编写脚本来反映您军队的行为-换句话说,脚本“每个部队在战场上应该做什么”。

这是在调试模式下完成的:实际上,您要借记代码,然后两个对手都推送他们的代码,然后双方之间的战斗就开始了。

因此,您可以看到两个脚本,两个逻辑,两个算法如何相互抗衡。 我一直想做这样的事情,现在终于实现了。

实际上,一切都看起来像这样:


对其进行的工作是什么样的? 文档中进行了大量工作。 当玩家坐在笔记本电脑旁时,他会看到文档,其中详细介绍了所有内容。

我花了很多时间进行布局,修订以及在人们中质疑它是否清晰。 结果,对于不了解JS的sishnikov,javists和其他开发人员来说,结果显而易见。 您甚至可以使用这个玩具来宣传JavaScript:“这并不可怕,即使有什么有趣的事情,也可以看看如何编写。”



我们公司举办了一场大型比赛,几乎所有我们参与的程序员都参加了比赛。

从技术上讲,我使用了JS世界中最受欢迎的游戏引擎-Phaser。 最大和最常用的Ace编辑器。 这是Web上的编辑器,与Sublime或VSCode非常相似,可以将其嵌入网页中。 我还使用RxJS处理来自不同用户的异步交互,并使用Preact呈现html。 在本地技术中,他尤其与工人和Websocket合作。





程序员游戏


一般来说,程序员应该玩什么游戏? 在我看来,这些游戏需要编写代码,然后才能得到一些有趣的结果,可以与某人进行比较,这是一场战斗。 在这些可用的在线游戏中,我知道“电梯传奇” -您可以根据某些参数为电梯编写脚本。 “爬行” -关于生物学,分子,为它们编写脚本。

有时在会议上也有玩具。 其中最受欢迎的是“黑暗中的代码”,我们今天也介绍了它。 顺便说一下,“黑暗中的代码”在某些方面启发了我。



为什么这样做? 我得到的任务是,您需要在会议上提出一个很棒的立场,这是不寻常的。 并不是说有附有调查问卷的茶char。 显然,每个人都希望引起关注并收集联系。 我们决定走的更远,为程序员带来了一些有趣而有趣的东西。 我意识到程序员想战斗,竞争,我们需要给他们这样的机会。 有必要创建一个支架,它们将在其上进行编码。

游戏化 我们不仅在程序员实践中,而且在学生中也这样做。 我们在职业日那天在学院举行了这样的比赛。 我们需要以某种方式看看有没有适合我们的人。 我们使用游戏化来吸引人们进入流程,了解他们的行为方式和行为。 他们玩耍并分心,但这给了我们信息。 有些人甚至在没有运行代码的情况下就推送了代码,这很明显对于开发而言还为时过早。



在第一个版本中的外观。 这是一个主屏幕和两台供玩家使用的笔记本电脑。 所有这些都联系了服务器,服务器存储了State并在所有连接的客户端之间进行了翻炒。 每个屏幕都是连接的客户端。 播放器的笔记本电脑是交互式屏幕,可以从中更改此状态。 屏幕牢固地连接到一台服务器。

缺乏时间的故事


我在此开发过程中遇到的第一个故事是我很少有时间的故事。 从字面上看,在五分钟内便提出了一个主意,在五秒钟内,当需要在GitHub上创建存储库时,就发明了一个名称。 我晚上只能花四个小时,就连我妻子都拿走了。 结果,我距离会议只有三个星期的时间,至少在某种程度上意识到了这一点。 一切都开始了,因此只需要在头脑风暴的框架内提出一个想法,就在五分钟之内诞生了这个想法:“让我们在JS中为RPG编写某种人工智能”。 它很酷,很有趣,我可以实现它。



在第一个实现中,该屏幕具有代码编辑器和战斗屏幕,战斗本身就在战斗屏幕上。 Phaser,Ace Editor和纯Node.js被用作没有任何框架的服务器。 不过,后来我后悔了,但是随后服务器不需要任何特殊的操作。



我设法实现了渲染战斗本身的渲染器Renderer。 最困难的部分是JS代码的沙箱,即一个沙箱,其中每个玩家都应隔离执行。 还有来自服务器的状态共享。 玩家以某种方式改变了状态,将其扔到服务器上,服务器将其余的发送到了网络套接字。 服务器是事实的来源,所有连接的客户端都信任来自服务器的内容。

沙盒


实施沙箱有什么困难? 事实是,沙盒是必须存在代码的代码的整个世界。 也就是说,您通过与之交互的API为他创建了与周围几乎相同的世界,但仅包含一些约定。 如何在JS上实现呢? JS似乎无能为力,它充满了漏洞和自由,以至于无法在不使用带有单独操作系统的单独虚拟机的情况下将用户代码完全封装在某种形式的盒子中。



沙箱应该做什么?

首先,正如我所说,隔离代码。 它必须是一个无法突破的世界。

另外,应该将单元管理API扔在那里。 玩家必须与战场互动,移动单位,指挥它们,为他们提供进攻方向。
而且单元的任何动作都是异步的,也就是说,它应该以某种方式与异步代码一起工作。



我想对异步说些什么? 事实是,在JS中,它基本上是使用promises实现的。 这里的每个人都清楚一切,诺言是一件伟大的事情,它们运作完美,我们几乎一直与我们在一起。 多年以来,每个人都知道如何与他们合作,但是这个玩具不仅用于javascript。 想象一下,如果我开始向Javists解释如何使用Promise编写战斗代码? 然后,然后,然后为什么为什么有时没有必要...对条件或循环怎么办?



当然,您可以采用最佳方法并采用异步/等待语法。 [幻灯片8:57]但是您还可以想象非JavaScript程序员如何解释您几乎需要在每行之前等待吗? 因此,处理异步的最佳方法是完全不使用它。



制作最同步的代码和最简单的API,类似于几乎所有编程语言。 我们不仅为使用JS编写代码的人制作玩具,而且还希望所有知道如何编写代码的人都可以使用它。



我们都需要以某种方式启动它。 用户编写代码,我们需要执行它,然后在地图上移动单位。 首先想到的是,我们需要eval()加上不建议使用with语句, 不建议在MDN上使用该语句。 这将起作用,但是存在问题。



例如,我们拥有的代码会完全破坏我们的整个想法,从而使我们无法做进一步的事情。 这是阻止执行的代码。 需要做一些事情,使用户无法阻止该应用程序。 例如,无限循环会破坏所有内容。 如果alert()和prompt()仍然可以重新定义,那么我们根本无法重新定义无限循环。

eval()是邪恶的


因此,我们到了eval()邪恶的地步。 他们称他为邪恶并不是徒劳的,因为它是一个隐蔽的功能,实际上包含了JS中所有最自由和开放的功能,使我们完全无法防御。 通过一个简单的功能,我们在应用程序中产生了巨大的漏洞。



但是,如果我(以史蒂夫·乔布斯的声音)告诉您我们重新发明了eval(),该怎么办?

我们在其他技术上做了eval(),它的工作原理几乎与我们已经拥有的eval()相同。 实际上,我的代码中有一个eval()函数,但是使用Workers,with语句和Proxy来实现。



为什么是工人? 事实是他们创建了一个单独的执行线程,即JS是单线程的,但是由于有工作人员,我们可以得到另一个线程。 这给了我们很多好处。 例如,在相同的无穷循环中,我们可以从主线程中断通过worker创建的线程,这也许就是我使用worker的主要原因。 如果工人的工作速度快于一秒钟,那么我们认为他成功了,我们就可以得到他的结果。 如果没有,那么我们就切断它。 实际上,由于某些原因,用户代码不起作用,发生了一些奇怪的错误,或者由于无限循环而使它变慢了。 今天许多人试图写(true)时,我警告说这行不通。



要编写工作程序,我们只需要将脚本提供给工作程序构造函数即可,该脚本将通过http下载。 在脚本内部,我们需要从主线程创建一个消息处理程序。 使用worker内部的postMessage()函数,我们可以将消息路由到主线程。 这样,我们在两个线程之间进行通信。 一个相当方便的简单API,但其中缺少一些东西,即我们必须在此工作程序中执行的用户代码。 我们不会每次都在服务器上生成脚本文件并将其提供给工作程序。



我找到了一种使用URL.createObjectURL()的方法。 我们制作一个块并将其提供给src worker。 因此,它直接从该行卸载我们的代码。 顺便说一句,这种方式适用于DOM中任何具有src的对象-例如,image的工作方式就是这样,即使在iframe中,您也可以仅通过从字符串生成html来加载html。 我认为非常酷且灵活。 我们还可以通过简单地从URL传递特别生成的对象来管理该工作程序。 我们也可以终止它,并且它已经可以根据需要运行,并且我们创建了第一个沙箱。



异步交互更进一步,因为与工人的任何工作都是异步的。 我们发送了一些消息,我们无法同步等待下一条消息,worker只将实例返回给我们,我们可以订阅消息。 我们使用RxJS捕获消息,我们创建了两个线程:一个线程用于接收来自工作程序的成功消息,第二个线程通过超时来完成。 然后我们通过合并控制两个线程。



RxJS具有允许我们使用线程的运算符。 实际上,同步操作就像lodash一样。 我们可以指出一些功能,而不必考虑它在内部如何实现,这使我们免于头痛。 我们需要开始在线程中思考,合并运算符合并我们的线程,响应任何消息。 它将响应超时和消息。 我们只需要第一条消息,在第一条消息终止后,我们就可以了。 如果有错误,请打印此错误;如果成功,我们会解决。



这里的一切都很简单。 我们的代码变为声明性的,异步的复杂性随处可见。 最主要的是要学习这些运算符。



这就是我们使用Unit API的方式。 我希望Unit API尽可能简单。 谈到JS,许多人认为这很困难,您必须爬上某个地方,学习一些东西。 我想使其尽可能地简单:全局区域中的所有内容,仅存在Unit API的范围,仅此而已。 一切都用于单位管理,甚至可以自动完成。



[幻灯片15:20]该解决方案本身表明,所有这些都可以放入非常禁止的声明中。 让我们了解为什么它被禁止。

事实是,这样做有问题。 例如,不幸的是,with泄漏在我们所涉及的范围之外,因为它试图比Unit API更深入地研究全局范围。


这里的最后一个示例特别酷,因为对于我们的代码,即使是四个也可能很危险,因为所有这些功能都可以由用户代码执行。 用户可以做任何事情。 这是一款适合程序员的游戏,他们喜欢探索问题和破解方法。



就像我说的那样,作用域非常泄漏,因此全局作用域始终可用。 无论我们在自定义代码上使用了多少个作用域,无论我们包装了多少个作用域,全局作用域仍然可见。 都是因为有了。

实际上,它没有隔离任何东西,只是给我们增加了新的抽象层,新的全局范围。 但是我们可以使用代理更改此行为。



事实是Proxy负责我们对通过新API代理的对象的所有调用,并且我们可以控制该对象中新数据请求的行为。



实际上,用作品很简单。 当我们给他提供某种变量时,他会在后台检查该变量是否在对象中(即,它执行in运算符),如果是,则在对象中执行它;如果不是,则在较高范围中执行,我们的案例是全球性的。 这很简单。 代理可以帮助我们的主要事情是我们可以覆盖此行为。



代理中有钩子之类的东西。 一件奇妙的事情,使我们可以代理对对象的任何请求。 我们可以更改属性请求的行为,更改属性作业的行为,最重要的是,我们可以在操作符中更改此行为。 有一个hook钩子,我们只能返回true。 因此,我们采用并完全欺骗了我们的with语句,使我们的API比以前更加安全。



如果我们尝试运行eval(),他将首先询问该eval()是否在unitApi中,他们将回答“是”并得到“未定义不是函数”。 这似乎是我第一次对此错误感到满意! 这个错误正是我们应该收到的。 我们接受了它,并告诉用户:“对不起,忘记了您所知道的关于窗口对象的一切,仅此而已。” 我们已经解决了一些问题,但这还不是全部。



事实是,with语句与JS中的相同,JS是动态的并且有点怪异。 奇怪的是,在不研究规范的情况下,并不是所有事情都能如我们所愿。 事实是,with也可以与原型属性一起使用。 也就是说,我们可以给他喂一个数组,执行这个晦涩的代码。 在此范围内,所有数组函数都可以全局使用,这看起来有些奇怪。



这对我们而言并不重要,对于用户而言,用户可以执行valueOf()并获取我们所有的沙箱对我们而言至关重要。 直接拿起,看看里面有什么。 我也不想要这个,因此在规范中引入了一个有趣的东西:Symbol.unscopables。 也就是说,在新的符号规范中,专门为with语句引入了Symbol.unscopables,这是禁止的。 因为他们相信其他人正在使用它。 例如我!



因此,我们将制作另一个拦截器,在其中专门检查该符号是否在所有unscopables属性的列表中。 如果不是,则退回,但如果是,那么抱歉,我们将不退回。 我们也不使用它。 因此,我们甚至无法获得沙箱的原型。



我们仍然有工人环境。 这是一个挂在全球范围内并且仍然可以访问的东西。 事实是,如果您简单地覆盖它,它将在原型中可用。 几乎所有内容都可以通过JS中的原型提取出来。 令人惊讶的是,所有这些方法仍然可以通过原型获得。



我只需要拿走并清理所有这些东西。 我们仔细检查所有按键并进行清洁。



然后,我们为用户保留了一些复活节彩蛋,他们仍然尝试称其为。 我们采用通常的功能,主要不是箭头功能,它有一个作用域,然后将其作用域更改为我们的对象,在该对象中,我们留了一个小的复活节彩蛋给特别好奇的用户,他们希望在控制台中显示这些内容或自我。 我认为复活节彩蛋很棒,应该留在代码中。



进一步证明,它们仅留给我们的Unit API。 我们完全封锁了所有内容-实际上,我们留下了白名单。 我们需要添加有用和需要的那些API。 例如,Math API,它具有许多人在编写单位代码时使用的有用的随机函数。

我们还需要没有任何破坏性功能的控制台和许多其他实用功能。 我们为我们的API创建一个while列表。 , blacklist, , .



whitelist, try-catch . , .



, Worker . , worker, , « , » . , JavaScript , .

, , - . , . , webpack .



patchMethod(), , postMessage(). postMessage() console log, error, warn, info. , . , <div>, , , , , .



, . : - - — , , - . , , promises. , actions, .



actions. , real-time ? , real-time workers , worker, . - , . , , . , , . .



workers, . workers , . , . , , ( , ), . : — .


Math.random()


, , , . Math.random().

, , , . , Math.random() - .



, , ( ), JS , . .



, , , . , - .

, . , random(), .



, random() — « » , . , - , random() , . - , . , , random() .



. , , random() . - seed ( , , ).


, . , random(). , random() -.

, worker, , JS . «» — . . , JS . random() unit API. , worker.



State sharing: RxJS,


因此,我们弄清楚了与客户的关系。 现在让我们讨论状态共享,为什么需要这种状态以及如何组织状态共享。 我们将状态存储在服务器上,服务器必须与连接的客户端混淆。 [幻灯片28:48]

我们具有可以连接到服务器的不同客户端的四个角色:“左用户”,“右用户”,查看主屏幕的查看者和可以执行任何操作的管理员。

左屏幕无法更改右播放器的状态,查看器无法更改任何内容,并且管理员可以执行所有操作。



为什么这是一个问题? 一切安排都非常简单。 任何连接的客户端都可以抛出会话,服务器接受该会话并在服务器内部将其状态合并为git,然后将其分发给所有客户端。 他为即将发生的任何变化而摸索。 有必要以某种方式对其进行过滤。



首先,我要说一下为什么服务器还具有RxJS。 与两个或多个连接的用户的所有交互都变为异步。 我们必须等待两个用户的结果。 例如,两个用户都单击“完成”按钮,则必须等待两个人都单击,然后才能执行操作。 通过这种方式,在RxJS上一切都非常简单:



我们再次使用线程进行操作,套接字产生了一个流,称为套接字。 为了使一个线程仅监视左玩家,我们只需由左玩家(和右玩家类似)从此套接字中获取并过滤消息。 然后,我们可以使用forkJoin()运算符将它们组合在一起,该运算符的工作方式类似于Promise.all(),并且是其类似物。 我们等待这两个动作,然后调用setState()方法,该方法将我们的状态设置为“就绪”。 事实证明,我们正在等待这两个玩家并更改服务器的状态。 在RxJS上,这尽可能做到声明式,这就是我使用它的原因。

玩家可以相互更改状态仍然存在问题。 必须禁止他们这样做。 尽管如此,他们还是程序员,还是有人尝试过的先例。 让我们为它们创建从Client继承的单独的类。



它们将具有玩家与服务器进行通信的基本逻辑,并且在每个特定的班级中,都会有他的自定义逻辑来过滤数据。

客户端实际上是一个连接池,即与客户端的连接。



他只是存储它们,并且拥有onUnsafeMessage流,这是完全不安全的:无法信任他,这些只是他从用户那里收到的原始消息。 我们将这些原始消息写入流中。

此外,在实现特定播放器时,我们将此onUnsafeMessage进行过滤。



我们只需要过滤从我们可以信任的播放器中获得的数据。 左玩家只能更改左玩家的状态,我们分别从他可以发送的所有数据中获取左玩家的状态。 如果您没有发送,好吧。 如果发送-我们接受。 因此,从完全不安全的消息中,我们获得了我们在会议室中工作时可以信任的安全消息。



我们拥有可将玩家聚集在一起的游戏室。 在房间内,我们只需编写我们已经可以信任的流程,就可以编写可以直接更改状态的功能。 我们从一堆支票中抽象出来。 我们根据角色进行检查,并将它们称为单独的类。 我们以这样一种方式对代码进行划分:在控制器内部,执行改变状态的重要功能的代码变得尽可能简单和声明。

RxJS也用于客户端,它连接到背面的套接字,发出事件并以各种方式重定向它们。

在这种情况下,我想举一个例子,说明我需要更换合适对手的军队。



要订阅它,我们从相同的套接字创建一个流并对其进行过滤。 我们确保这确实是正确的球员,并从他那里得到有关他的军队是什么的信息。 如果没有这样的消息,则该流将不返回任何内容,也不会有任何消息,它将保持沉默,直到玩家更换军队为止。 我们立即以声明性的方式解决了过滤事件的问题,我们没有老套。

当流中已经有东西时,我们调用setState()函数。 这非常简单,正是这种方法使我们可以透明和声明方式进行所有操作。 我从事RxJS项目的过程以及对我有很大帮助的事情。



我创建的流具有一个非常明确的名称,我知道该如何使用它,所有内容都是声明性的,调用了必要的函数,没有太多的if和filter事件,这一切都由RxJS完成。

历史:从单人游戏到多人游戏



因此,我写了玩具的第一个版本。 我们可以说这是一个玩家,因为只有两个玩家可以玩,所以连接的客户端数量是固定的。 我们有一个左播放器,一个右播放器,后面是屏幕,所有这些都直接连接到服务器。 一切都进行了硬涂,但是在三周内就完成了。

我收到了一个新的提议:为公司中的所有程序员扩展玩具,以便他们可以在计算机上打开并播放它。 这样我们就可以获得多人游戏的领导者名单,以便他们可以一起玩。 然后我意识到我有很多重构。



事实证明并不是那么困难。 我只是将所有实体合并到单独的房间中。 我得到了“房间”的精髓,它可以结合所有角色。 现在,不是玩家自己直接与服务器通信,而是房间。 房间已经直接将请求代理到服务器,替换状态,并且每个房间的状态都变得独立。



我拿走并重写了所有内容,添加了领导者列表,我们获得了最好的奖项。 只需要有大量的用户,就不可能跟随所有人,有必要写一些东西来收集所有数据。

JS Gamedev及其问题



因此,我变得更加认真地熟悉JS-gamedev。 我为最后一个项目花了大约三年时间,经常休息。 在这里我两次都呆了三个星期。 每天我都坐着,晚上做些事。

在JS上开发游戏有什么问题? 一切都与我们的业务应用程序不同,从头开始编写东西就不成问题。 此外,它甚至受到很多欢迎:我们将做我们自己的事情,用NPM记起故事并自己左撇子。



在JS Gamedev中不可能做到这一点,因为所有用于显示图形的技术都非常低级,以至于在这些图形上写一些东西在经济上是无利可图的。 如果我拿起这个玩具并开始在WebGL上从头开始编写它,我还会坐在它后面大约六个月,只是试图找出一些奇怪的错误。 最受欢迎的游戏引擎Phaser向我消除了这些问题...



...并向我添加了新的:捆绑包中的5 MB。 而且对此无能为力;他根本不知道什么是树上的树。 此外,只有最新版本的Phaser才能与webpack和bundle一起使用。 在此之前,Phaser仅在脚本的html标签中连接,这对我来说很奇怪。

我来自各种webpacks脚本,在JS游戏开发人员中,几乎什么也做不到。 所有模块的输入都非常差或者根本没有输入,或者根本不知道如何使用webpack,因此有必要寻找包装方法。 事实证明,即使是纯格式的Ace编辑器也根本无法与webpack一起使用。 要开始工作,您需要下载一个单独的已经打包好的软件包(括号)。

与Phaser大致相同,但在新版本中,它们或多或少正常地执行了此操作。 我继续写在Phaser上,发现如何使Webpack像以前一样工作:键入和测试都可以附加到这一切上。 我发现您可以单独使用PixiJS (这是一个Webpack渲染器),并为它找到许多可以使用的模块。



PixiJS是一个很棒的库,可以在WebGL或Canvas上呈现。 而且,您甚至可以像编写Canvas一样编写代码,它将在WebGL中呈现。 该库可以非常快速地渲染2D。 最主要的是要知道它如何与内存一起工作,以免在内存用完时掉入一个位置。

我另外推荐GitHub上的awesome-pixijs存储库,您可以在其中找到不同的模块。 我最喜欢的是React-pixi 。 当我们直接在控制器中编写命令式函数以绘制几何形状,子图形,动画等时,我们可以简单地忽略视图的解决问题。 我们都可以在JSX中进行标记。 我们来自JSX世界,并带有我们的业务应用程序,可以进一步使用它们。 这就是我喜欢抽象的目的。 React-pixi给了我们这种熟悉的抽象。

我还建议您使用tween.js-来自Phaser的著名动画引擎,该引擎可让您制作类似于CSS动画的声明性动画:我们在状态之间进行转换,而tween.js则为我们确定了如何移动对象。

玩家类型:他们是谁以及如何与他们成为朋友


我遇到了不同的玩家,我还想告诉您有关测试玩具的信息。 我把同事聚集在一间封闭的房间里,直到他们完成比赛后才让他们出去。 不幸的是,并不是每个人都能完成游戏,因为一开始我就有很多错误。 幸运的是,一旦至少有一些可行的原型出现,我就开始进行测试。 老实说,第一次测试失败了,因为几个玩家什么也没开始。 真可惜,但它给了我一脚,让我继续前进。

准备好玩具后,您会收到很好的礼物,或者拿着干草叉和火把也可以收到。 所有人都在等待游戏中的狂热者,等待着您将带给他们的幸福。 而且,您给他们提供了一些根本不起作用的东西,尽管它似乎对您有用。 当您拥有在线玩具时,还会有更多此类错误。

结果,我遇到的最愉快的人是“研究人员”,他们总是在您的玩具中发现比实际更多的东西。 他们可以愉快地补充各种小东西,提示您添加一些东西。 但是,不幸的是,与这些人的交流并没有带来重要的事情-玩具的稳定性。

有一些普通玩家只是为了粉丝而来。 他们有时甚至可能不会注意到错误,以某种方式在愉悦的过程中溜走了它们。

另一个类别是错误收集器,对于它而言,几乎所有内容都不起作用。 您需要与这些人成为朋友,尽管他们会谈论很多负面情绪。 我们需要与他们建立奇怪的关系:他们伤害了你,而你正试图从他们那里获取对自己有用的东西,“让我们坐在电脑前看看”。 您需要与他们合作,因为最终是这些人才能提高您的游戏质量。

您只需要对有生命的人进行测试。 您的眼睛模糊了,测试肯定会显示出隐藏的内容。 您正在开发玩具并进一步潜水,看到了一些功能,但可能甚至不需要。 您直接去找消费者,向他们展示并观察他们的演奏,他们按下了哪些键。 这使您有动力去做自己需要的事情。 您会看到有些人不断按Ctrl + S,因为他们习惯于保存代码-至少让代码在Ctrl + S上运行,播放器会让您感到更舒服。 您需要为玩家创造一个舒适的环境,为此您需要爱他并跟随他。

80/20规则有效:您可以在整个游戏开发过程中20%的时间进行演示,而对于玩家来说,这就像80%完成的游戏。 感知有效,因此基本机制已经准备就绪,一切都可以正常工作,这意味着游戏即将准备就绪,开发人员将很快完成。 但实际上,开发人员仍有80%的出路。 正如我所说,很长一段时间以来,我不得不处理文档,以便每个人都可以理解。 我把它展示给了许多发表意见的人,我过滤了它们,试图理解声明的本质。 而且花了很多时间来寻找错误。

因此,在游戏开发中,我只能建议您进行演示:它们使每个人都很满意,不需要太多时间,而且没有人真正期望演示提供任何东西。 完成游戏是一个无聊的过程,但是入门非常好。

最后,我给您留下链接:

针对JavaScript开发人员的会议HolyJS 2019 Piter将于5月24日至25日在圣彼得堡举行。 首批发言人已经出现在该网站上。
您也可以申请报告, 征集截止日期为3月11日。
门票价格将于2月1日上涨。

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


All Articles