使用数学图表而非图表进行游戏



在此屏幕截图中,向您展示了一个看似普通的带有像素图形的游戏。 但是,并非一切都那么简单。

地球上的浮雕类似于正弦曲线,子弹看起来像x根的两个对称图。

实际上,您以一种或另一种方式在屏幕上看到的所有内容都与数学,数学曲线和图形有关。

背景知识


一次,在观看“ Numberphile”频道的视频时,我遇到了非常有趣的视频资料,名为“万事通”

在此视频中,介绍了Tapper的自指代公式 ,该公式具有一定的k值,从而在图表上重新创建了其图像。 该公式如下所示:

 frac12< lfloormod lfloor fracy17 rfloor217 lfloorx rfloormod lfloory rfloor172 rfloor


这个公式使我很感兴趣,我有了一个主意:

“如果创建一个游戏,而不是使用存储在.png和.jpg格式的各种文件中的普通纹理,而是使用数学图形和曲线,该怎么办?”

在我看来,这个想法很有趣并且难以实现。

任务


我面临以下任务:

  • 提出游戏的意义,游戏玩法
  • 派生公式,其图形将是我需要的字符,项目符号,表面的轮廓
  • 在游戏中实现所有这些

游戏玩法和游戏意义


在游戏开发过程中,游戏玩法和游戏含义发生了数次变化。 这是因为存在一些困难,尤其是在导出某些轮廓的公式时。 但总的来说,该游戏是关于一个孤独的飞碟,它绕着行星飞行,探索行星并杀死阻碍她前进的敌人。

公式及其在游戏中的后续实现


我将以下两段合并为一个小标题,因为在一个公式及其实现之间“跳过”是不现实的。

为了创建游戏,选择了c ++编程语言和SFML库来创建窗口并在它们上绘制一些东西。

由于我在学校学习,直到最近才发现正弦波是什么以及它的外观,因此在推导各种公式时遇到了很大的问题。 通常,这以简单的选择结束。

在文章的结尾,将有一个指向GitHub的链接,所有代码都发布在这里。 在本文中,将只给出一小段代码,以免乱码。

行星表面


对于行星表面,我得出以下公式:

fx=|sinx|

一个相当简单的公式,但是在实现时,需要控制给定正弦曲线的高度和长度。 另外,为了避免凸凹的曲率,将x乘以π。 因此,最终代码如下所示:

int groundC = ceil(abs(sin(((i+1)*groundScale*M_PI)))*groundHeight); 

行星在太空中的纹理






行星的纹理由一个圆和一个图案组成。 该游戏有4个用于创建模式的公式以及12种具有不同模式的行星的纹理。 根据公式的“步骤”,创建各种模式。 同样,在生成行星时,会以伪随机方式将其设置为颜色,大小和空间位置。

项目符号




游戏中子弹的图像。 子弹被转动。

对于子弹,选择了一个非常简单的公式:

 sqrtx

该公式的图形反映在横坐标上。

主角


因此,我们得出了最复杂的公式。

我很难推断出主角的配方。 看起来像这样:

 sqrtx frac12.8+x10.9x9.3x0.3

是的,这是一个非常弯曲,非常丑陋的公式。 但是主要的不是公式,主要的结果。

为了获得结果,首先,我只想沿着x轴移动一定的步伐,写下y坐标,然后连接所有这些点,从而获得我们的平板。 但是后来,我不小心采取了一个很小的步骤,除了两个最终连接的端点之外,我的整个盘子都非常漂亮。 结果,该板看起来像这样:



接下来,我们需要太空中主角的质感。 看起来像这样:



它基于一个圆。 主客舱使用以下公式制成:

x7x frac0.8x

该公式的图形沿纵坐标轴镜像。

这是此公式在c ++中的外观:

 int x = round(pow(pow(i, 7 - i), 0.8 / i)); 

敌人及其产卵者



在图像的右边是一个蓝色的生成器,红色的对象是敌人。

Spauner是一个具有不同模式的普通行星。 此模式是公式的图形:

sinxx0.8


敌人纹理公式:

x3x frac1x



树木


我承认,我无法导出或选择用于创建树木轮廓的公式。 但是,为了不违反整个游戏的基本概念以及不使用任何.png和.jpg格式文件的规则,我使用了一个技巧。 我用分形创建树。


分形树的例子

您很可能会同意分形树本身看起来很无聊。 例如,如果添加一些随机性元素,则两个分支可能不一定会增长,但可能会增长3或1,或者根本不会增长。 同样,不可能到处都具有相同的倾斜角度。

当然,可以根据计算机滴答声制作一台普通的机器pseudorand ,但以下想法对我来说似乎更有趣:

“如果给每棵树一个特定的编号(种子),从中计算出影响树的参数的伪随机数怎么办?”

幸运的是,c ++有一个单独的伪随机数库。

结果,生成的树如下所示:



左侧是一棵种子13的树,右侧-22

生成这些树的代码是:

 Branch Branch::createNewBranch(Branch cur, Tree* parent, float angleMultiplier, int level) { Vector2f sp(cur.startPoint.x, cur.startPoint.y); float randomAngle = ((*parent).getRand() * 15) - 5; float t = cur.thickness * 0.75; float l = cur.length * 0.67; float a = cur.angle + 30*angleMultiplier + randomAngle; sp.y -= (cos((cur.angle-180)*3.1415926 / 180) * cur.length); sp.x += (sin((cur.angle-180)*3.1415926 / 180) * cur.length); Branch gen(sp, t, l, a, level); if (level > 0) { int count = 100 * (*parent).getRand(); if (count >= 25 && count < 80) { //     ,      && count < 80,        ,  . , -          20%  10%,  ,    count<90 (*parent).addBranch(gen.createNewBranch(gen, parent, 1, level - 1)); (*parent).addBranch(gen.createNewBranch(gen, parent, -1, level - 1)); } if (count >= 80) { //    ,    count >= 90 if (count % 2 == 0) { (*parent).addBranch(gen.createNewBranch(gen, parent, -1, level - 1)); } else { (*parent).addBranch(gen.createNewBranch(gen, parent, 1, level - 1)); } } } return gen; } 

注意事项 是的,我知道我“硬编码”了一些变量,但是请不要为此怪我。 我认为创建单独的常量变量没有任何意义,原则上,这只会影响创建新分支的机会。

一些代码


上面,我仅提供了用于生成纹理的代码。 在此字幕中,我将描述游戏本身的代码。 所有代码都在GitHub上,到项目的链接已结束。

播放器


播放器有两种不同的更新方法-spaceUpdate和planetUpdate。 因此,spaceUpdate在太空中时会更新播放器,planetUpdate –在行星上时。 在这个星球上,计算出玩家的加速度和速度。 取决于水平加速度,板的倾斜角度从30度变为-30度。 接近障碍物,玩家的速度下降。 对于x轴(0; mapSize.x)和y轴存在此类障碍。 对于y轴,情况要复杂一些。 有一个最小高度,其计算如下:取地球的最小高度,将其添加到正弦波的高度中,然后将树木的高度相加。 树的高度以非常简单的方式计算-分支的初始长度乘以在树的生成过程中执行的循环数。 没有上限-从上方飞出地图,玩家切换到spaceUpdate并绘制了空间。

SpaceUpdate的运行方式如下:计算玩家的加速度和速度。 接下来,计算玩家的旋转角度。 角度的计算方法如下:如果加速度为零,则相对于玩家的速度计算角度,如果不相对于加速度,则计算角度。 另外,在太空中,玩家可以射击。 射击发生如下-像子弹一样旋转创建子弹并将其添加到列表中。 更新太空玩家时,此列表中的每个项目符号也会更新。 渲染玩家时,也会绘制项目符号。 此外,在太空中,障碍变得更加复杂。 太空分为多个扇区,每个扇区共有4颗行星-1000000颗行星和25000个扇区。 每个扇区都有唯一的ID。 如果除以500时的余数等于0-则有一个左屏障,如果除以499的余数是对的,则如果除以500则结果为0-则存在较高的屏障,如果499是较高的屏障。 如果没有障碍,那么当玩家飞出框架时,将移动到相应的扇区。

空间篇


我已经概述了其中的大部分内容,但是仍然有些事情要做。 在每个空间区域中都有4颗行星。 当玩家按下E键时,如果他距离该星球半径一定距离,则玩家将移动到该星球。

敌人


敌人的AI非常愚蠢-如果在可见范围内有玩家,那么他们只会撞向敌人,并且有轻微的错误,因此他们的路径相当弯曲。 如果在可见范围内没有玩家,则将他们发送到其生成者。

扳手


在每个空间区域中都有1个生成器。 扳手的大小可以不同。 大小会影响播放器的可见性。 如果玩家在其可见区域内,则生成器每5秒创建一次敌人,但敌人数量不能超过10。

结论


花了大约一周的时间之后,我创建了一个不使用任何.png或.jpg文件的游戏。

链接到GitHub上的项目

对于那些懒得下载项目并运行游戏的人,请在游戏过程中播放一段简短的视频:

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


All Articles