SpriteKit Advanced —如何构建2,5D游戏(第三部分)

编程入门 行业动态 更新时间:2024-10-28 05:20:49

SpriteKit Advanced —如何构建2,5D<a href=https://www.elefans.com/category/jswz/34/1770081.html style=游戏(第三部分)"/>

SpriteKit Advanced —如何构建2,5D游戏(第三部分)

by Luke Konior

卢克·科尼尔(Luke Konior)

SpriteKit Advanced —如何构建2,5D游戏(第三部分) (SpriteKit Advanced — How to build a 2,5D game (Part III))

介绍 (Intro)

This article is about improving visuals of Raft Challenge by applying GPU’s shaders to the still scenery. It explains the algorithms and potential pitfalls when using GLSL in SpriteKit.

本文旨在通过将GPU的着色器应用于静态场景来改善Raft Challenge的视觉效果。 它说明了在SpriteKit中使用GLSL时的算法和潜在的陷阱。

The reader should have basic experience in writing fragment shaders. We discussed these in part 1 and part 2 of this series.

读者应该具有编写片段着色器的基本经验。 我们在本系列的第1部分和第2 部分中讨论了这些内容。

问题 (The problem)

After the game entered beta stage, we received feedback from various people. We frequently heard that the graphics were good, but also static, which in the long run led to boredom.

游戏进入测试阶段后,我们收到了许多人的反馈。 我们经常听到图形很好,但还是静态的,从长远来看,这导致了无聊。

My instant reaction was like: “They said it’s static? So we’ll add some wind to move the whole thing!” After that, we thought more about the problem.

我的即时React是:“他们说这是静态的吗? 因此,我们将施加一些风来移动整个东西!” 之后,我们考虑了更多问题。

Such huge objects as the trees cannot animate frame-by-frame, because it would lead to memory problems. We were considering adding small animated objects like animals. But it would complicate the scene graph even more. And it would have unknown performance impact.

树木之类的巨大对象无法逐帧进行动画处理,因为这会导致内存问题。 我们正在考虑添加像动物一样的小型动画对象。 但这会使场景图更加复杂。 它将带来未知的性能影响。

The solution I came up with was to animate the whole forest using the fragment shaders. I wanted to create the wind effect.

我想到的解决方案是使用片段着色器对整个森林进行动画处理。 我想创造风的效果。

The idea was to apply a horizontal distortion to the sprite’s texture with strength proportional to the distance from the trunks’ base. That strength has also been changing in time and influenced by the scene’s “depth”.

这个想法是要对子画面的纹理施加水平失真,其强度与到树干底部的距离成正比。 这种力量也随着时间变化,并受场景的“深度”影响。

Other pros of this solution:

该解决方案的其他优点:

  • easy integration

    易于整合

    It’s as simple as filling an existing object’s properties

    就像填充现有对象的属性一样简单

  • performance

    性能
  • huge flexibility

    巨大的灵活性

Here’s the source (GLSL):

来源(GLSL):

void main( void ){    float horizonAbsoluteOffset = 0.64; // 1    float distanceFromTrunksBase = abs(v_tex_coord[1] - horizonAbsoluteOffset); // 2    float maxDivergence = mix(0.0,1.0,distanceFromTrunksBase)*0.038; // 3    float factor = sin(u_time*2+(attrDepth * 1.3)); // 4    vec2 deltaUV = vec2(maxDivergence * factor, 0); // 5        gl_FragColor = texture2D(u_texture, v_tex_coord + deltaUV); //6}
  1. This float holds the vertical position of all trunks’ bases

    float保持所有行李箱底座的垂直位置

    — This value is specific to our texture

    -此值特定于我们的纹理

  2. We calculate the distance between the current sampling point and the above value

    我们计算当前采样点与上述值之间的距离

    — This value is less than 1.0 and can be negative

    —此值小于1.0,可以为负

  3. We calculate max divergence

    我们计算最大散度

    — The magic number at the end was tweaked through trial and error

    -通过反复试验对最后的魔术数字进行了调整

  4. We calculate the changing strength and the wind’s direction

    我们计算变化的强度和风向

    — The sin function is a good foundation since it returns predictable values (-1 to 1)

    -sin函数是一个很好的基础,因为它返回可预测的值(-1至1)

    — It’s also a continuous function

    —这也是一个连续的功能

    — The latter means that we can put any garbage as the argument and it will still work

    —后者意味着我们可以将任何垃圾作为参数,并且仍然可以使用

    — In this case “the garbage” is the current time plus the “depth” of the current sprite

    —在这种情况下,“垃圾”是当前时间加上当前子画面的“深度”

    — Magic numbers are added to shape the animation

    -添加魔术数字以塑造动画

  5. The delta vector is created

    创建增量矢量

    — Max divergence multiplied by the factor goes into the X position while Y is left with 0.

    —最大散度乘以系数,将X放到X位置,而将Y留给0。

  6. This line takes the color from a specific point in the texture and outputs it to the screen

    该行从纹理中的特定点获取颜色并将其输出到屏幕

    — By adding delta to our current position with

    —通过将delta添加到我们当前的位置

    vtexcoord, we alter the point from which the sampler is extracting color value

    vtexcoord ,我们更改采样器从中提取颜色值的点

Result:

结果:

Note that reflections on the water are also moving. That is because the trees and reflections are a part of the same sprite and texture. No sorcery here.

请注意,水面上的反射也在移动。 那是因为树木和反射是相同的精灵和纹理的一部分。 这里没有巫术。

改善雾 (Improving fog)

Is there anything else that we can do? Well, if we can’t invent anything new, we can always improve something that exists. Our designer said once ago that trees further away should have solid color to merge better with the fog.

我们还有什么可以做的吗? 好吧,如果我们不能发明任何新东西,我们总是可以改进已有的东西。 我们的设计师曾说过,较远的树木应具有纯色,以更好地与雾融合。

The above image is almost self-explanatory. Earlier, I’ve mentioned about the “depth”. Every layer of the forest has an attribute attrDepth. It represents the distance between the mountains (0.0) and the viewer (6.0).

上面的图像几乎是不言自明的。 之前,我提到过“深度”。 森林的每一层都有一个属性attrDepth 。 它表示山脉(0.0)和查看者(6.0)之间的距离。

Let’s tweak this fog!

让我们调整一下雾吧!

__constant vec3 colorLightMountains = vec3(0.847, 0.91, 0.8);__constant vec3 colorDarkMountains = vec3(0.729, 0.808, 0.643);
void main( void ){       //get color    vec4 color = texture2D(u_texture, v_tex_coord);    float alpha = color.a; // 1
//fog    vec3 outputColor = vec3(color.rgb);    if (attrDepth < 1.0) {					// 2        outputColor = colorLightMountains;        alpha = min(attrDepth,alpha);    } else if (attrDepth < 2.0) {			// 3        outputColor = mix(colorLightMountains, colorDarkMountains, attrDepth - 1.0);    } else if (attrDepth <= 3.0) {		// 4        outputColor = mix(colorDarkMountains, color.rgb, attrDepth - 2.0);    }        gl_FragColor = vec4(outputColor, 1.0) * alpha; // 5}

The code above is pretty straightforward so I’ll focus only on the most important things:

上面的代码非常简单,因此我仅关注最重要的事情:

  1. Extract alpha from the texture.

    从纹理中提取alpha

  2. The far stage

    远期

    When the forest is the furthest possible, it has the

    当森林最远时,

    Light Mountains color and 0 alpha

    Light Mountains0 alpha

    As it’s moving closer, it emerges by increasing

    随着它越来越近,它通过增加而出现

    alpha up to depth == 1.0

    直到depth == 1.0 alpha depth == 1.0

  3. The medium distance

    中距离

    The color shifts towards

    颜色向

    Dark Mountains as the sprite’s get closer to the viewer.

    Dark Mountains般的Dark Mountains离观众越来越近。

  4. The close distance

    接近距离

    The color is a mix between the

    颜色是介于

    Dark Mountains and the native texture color

    Dark Mountains和原生纹理颜色

    Naturally, the closer it is, the more normal it looks

    自然,距离越近,看起来越正常

  5. Pass the final color to the output using the alpha extracted at the beginning

    使用开始时提取的Alpha将最终颜色传递到输出

Again, the result:

再次,结果是:

结合两种效果 (Combining both effects)

The best thing that I like about shaders is their flexibility. It’s not only possible to merge both effects without sacrificing anything. It’s even recommended to do so.

我最喜欢着色器的是它们的灵活性。 不仅可以合并两种效果,而且不牺牲任何内容。 甚至建议这样做。

Merging shaders decrease the draw calls and that increases the frame rate.

合并着色器可减少绘图调用,并提高帧速率。

__constant vec3 colorLightMountains = vec3(0.847, 0.91, 0.8);__constant vec3 colorDarkMountains = vec3(0.729, 0.808, 0.643);
void main( void ){    //wind    float horizonAbsoluteOffset = 0.64;    float distanceFromTrunksBase = abs(v_tex_coord[1] - horizonAbsoluteOffset);    float maxDivergence = mix(0.0,1.0,distanceFromTrunksBase)*0.038;    float factor = sin(u_time*2+(attrDepth * 1.3));    vec2 deltaUV = vec2(maxDivergence * factor, 0);        //get color    vec4 color = texture2D(u_texture, v_tex_coord + deltaUV);    float alpha = color.a;
//fog    vec3 outputColor = vec3(color.rgb);    if (attrDepth < 1.0) {        outputColor = colorLightMountains;        alpha = min(attrDepth,alpha);    } else if (attrDepth < 2.0) {        outputColor = mix(colorLightMountains, colorDarkMountains, attrDepth - 1.0);    } else if (attrDepth <= 3.0) {        outputColor = mix(colorDarkMountains, color.rgb, attrDepth - 2.0);    }        //output    gl_FragColor = vec4(outputColor, 1.0) * alpha;}

The final result:

最终结果:

陷阱 (Pitfalls)

There’s no rose without a thorn.

没有刺就没有玫瑰。

  • Using shaders on multiple big sprites with alpha channel may cause visible frame rate drop.

    在具有alpha通道的多个大型精灵上使用着色器可能会导致可见帧速率下降。

  • Same GPU may give 60fps on the iPhone but only 20fps on iPad with more pixels

    同一GPU在iPhone上可能提供60fps,但在iPad上只能提供20fps,且像素更多

    Test your code frequently on different devices, especially the iPads with retina displays

    经常在不同的设备上测试您的代码,尤其是带有视网膜显示屏的iPad

  • There is no reliable way to estimate the performance of the device from the code

    没有可靠的方法可以根据代码估算设备的性能

    Run your game on multiple physical devices and white list those that are capable of running shaders with decent performance

    在多个物理设备上运行游戏,并在白名单上列出能够运行性能良好的着色器的设备

    To distinguish devices, you can use

    要区分设备,您可以使用

    UIDevice-Hardware.m

    UIDevice-Hardware.m

  • Your partially transparent texture loses color and becomes gray? Google premultiplied alpha!

    您的部分透明的纹理会变色并变成灰色? Google 预乘alpha

  • Beware of using SKTextureAtlases if you’re altering the coordinates like in the wind example

    如果要像风示例中那样更改坐标,请当心使用SKTextureAtlases

    During the atlas generation, XCode may rotate and move some textures.

    在生成图集期间,XCode可能会旋转并移动某些纹理。

    It’s impossible to detect such anomaly from the code, or at least I don’t know how

    无法从代码中检测到这种异常,或者至少我不知道如何

  • For some sprites, you may receive a texture with swapped X and Y coordinates!

    对于某些精灵,您可能会收到X和Y坐标互换的纹理!
  • You may accidentally warp to a completely different sub-texture!

    您可能会不小心扭曲到完全不同的子纹理!

摘要 (Summary)

We’ve learned how to use fragment shaders to create wind and fog. When writing Your own GLSL code, you’ll surely produce many display artifacts. Some of them are annoying, and some are hilarious. Keep in mind that some of them may have potential to become a feature!

我们已经学习了如何使用片段着色器创建风和雾。 在编写自己的GLSL代码时,您肯定会产生许多显示瑕疵。 其中有些令人讨厌,有些则很有趣。 请记住,其中一些可能具有成为功能的潜力!

About the author: Kamil Ziętek is an iOS Developer at www.allinmobile.co

关于作者:KamilZiętek是www.allinmobile.co上的iOS开发人员

翻译自: /

更多推荐

SpriteKit Advanced —如何构建2,5D游戏(第三部分)

本文发布于:2024-03-13 15:41:08,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1734306.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:游戏   SpriteKit   Advanced

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!