advanced east

by Luke Konior

卢克·科尼尔(Luke Konior)

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

介绍 (Intro)

This article shows how to write basic shaders in the SpriteKit. It’s split into two parts: first we play, then we learn.

本文介绍如何在SpriteKit中编写基本着色器。 它分为两个部分:首先我们玩,然后学习。

It also contains information how to use SKAttribute and SKAttributeValue classes that were added in iOS SDK 10.0.

它还包含有关如何使用iOS SDK 10.0中添加的SKAttributeSKAttributeValue类的信息。

If you haven’t already read it, here’s part 1 of this article series.

如果您还没有阅读它,请参阅本系列文章的第1部分 。

准备项目 (Prepare the project)

Let’s get quick and dirty.

让我们变得快速又肮脏。

  • Open XCode 8 and create a new project from template: iOS > Game.打开XCode 8,然后从以下模板创建一个新项目:iOS>游戏。
  • Open the GameScene.sks and remove the label in the center of the screen.

    打开GameScene.sks并删除屏幕中心的标签。

  • Download this and put it inside Assets.xcassets

    下载此文件并将其放入Assets.xcassets

  • Name it “Trees”命名为“树木”
  • Open the GameScene.m

    打开GameScene.m

  • remove all instance variables删除所有实例变量
  • remove all methods删除所有方法

片段着色器 (The Fragment Shader)

Now we create an empty fragment shader In XCode:

现在,我们在XCode中创建一个空的片段shader

  • In the Project Navigator select Supporting Files在项目浏览器中,选择“支持文件”
  • Choose: File > New > File…选择:文件>新建>文件…
  • Select: Other > Empty选择:其他>空
  • Name it “myShader.fsh” and press Create.

    将其命名为“ myShader.fsh ”,然后按创建。

  • Put this inside:放在里面:
// currently a boring pass-thru shader void main( void ) { vec4 color = texture2D(utexture, vtexcoord); // here will emerge something worthy glFragColor = color;}

Above fragment shader does nothing perceptible. Quick explanation:

片段shader上方没有任何可察觉的内容。 快速说明:

  • void main()

    void main()

    this function gets called for each pixel of the sprite and outputs color for that pixel

    为精灵的每个像素调用此函数,并输出该像素的颜色

    Gets input data from surrounding globals and must set the

    从周围的全局变量获取输入数据,并且必须设置

    gl_FragColor variable

    gl_FragColor变量

  • vec2, vec3and vec4 are the types similar to C's: float array[2], float array[3] and float array[4]

    vec2vec3vec4与C的类型相似: float array[2]float array[3]float array[4]

  • u_texture is a texture ID

    u_texture是纹理ID

    Leave it alone :-)

    不要管它 :-)

  • v_tex_coord is a vec2 which contains our current position in texture

    v_tex_coord是一个vec2 ,其中包含我们当前的纹理位置

  • texture2D(tex , p) is a function that returns color from texture tex in point p as vec4

    texture2D(tex , p)是一个函数,将point p处纹理tex颜色返回为vec4

    which contains rgba

    其中包含rgba

  • gl_FragColor is an output color

    gl_FragColor是输出颜色

    We must assign it a

    我们必须给它分配一个

    vec4

    vec4

正在加载代码 (Loading code)

What’s left is the loading code.

剩下的就是加载代码。

  • Open the GameScene.m

    打开GameScene.m

  • add method -didMoveToView:

    添加方法-didMoveToView:

- (void)didMoveToView:(SKView *)view {   // 1. load the shader's source from myShaderFile.fsh   NSString *file = [[NSBundle mainBundle] pathForResource:@"myShader" ofType:@"fsh"];  NSString *sourceString = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];         // 2. create the shader SKShader *shader = [SKShader shaderWithSource:sourceString];       // 3. assign the shader to a newly created sprite node  SKSpriteNode *spriteNode = [SKSpriteNode spriteNodeWithImageNamed:@"Trees"];    spriteNode.shader = shader;        // 4. finally add the sprite to the scene   [self addChild:spriteNode];}

Ensure that myShader.fsh figures in ProjectFile > Target > Build Phases > Copy Bundle Resources!

确保myShader.fsh数字位于ProjectFile>目标>构建阶段>复制捆绑资源中!

You may now run the project on the iOS device. There shall be no errors in the XCode’s console and you should see a screen similar to this below:

您现在可以在iOS设备上运行该项目。 XCode的控制台中应该没有错误,并且您应该看到类似于以下的屏幕:

让我们玩吧! (Let’s play a bit!)

Now is the fun part. We’ll replace the shader’s main function.

现在是有趣的部分。 我们将替换着色器的主要功能。

颜色为红色,保留alpha (Color with red with alpha preservation)

void main( void ){    vec4 color = texture2D(u_texture, v_tex_coord);    float alpha = color.a;    gl_FragColor = vec4(1,0,0, 1.0) * alpha; //google "premultiplied alpha"}

缩小2倍 (Scale down by 2x)

void main( void ){    vec4 color = texture2D(u_texture, v_tex_coord * 2.0);    gl_FragColor = color;}

1秒后交换颜色 (Swap colors after 1 second)

void main( void ){    vec4 color = texture2D(u_texture, v_tex_coord);    float alpha = color.a;    float phase = mod(u_time, 3);    vec3 outputColor = color.rgb;    if (phase < 1.0) {        outputColor = color.bgr;    } else if (phase < 2.0) {        outputColor = color.brg;    }    gl_FragColor = vec4(outputColor, 1.0) * alpha;}

随时间着色 (Colorize over time)

void main( void ){    vec4 color = texture2D(u_texture, v_tex_coord);    float alpha = color.a;    float r = (sin(u_time+ 3.14 * 0.00)+1.0)*0.5;    float g = (sin(u_time+ 3.14 * 0.33)+1.0)*0.5;    float b = (sin(u_time+ 3.14 * 0.66)+1.0)*0.5;    gl_FragColor = vec4(r,g,b, 1.0) * alpha;}

波浪 (Waves)

void main( void ){    float deltaX = sin(v_tex_coord.y*3.14*10 + u_time * 4)*0.01;    vec2 coord = v_tex_coord;    coord.x = coord.x + deltaX;    vec4 color = texture2D(u_texture, coord);    gl_FragColor = color;}

新属性 (New Attributes)

At WWDC 2016 Apple introduced an important update to SpriteKit — the SKAttribute and SKAttributeValue classes.

苹果在WWDC 2016上对SpriteKit进行了重要更新,即SKAttributeSKAttributeValue类。

Before this SDK update, if we wanted to pass custom parameters into the shader program, we had to pass the data through a uniform value.

在此SDK更新之前,如果我们想将自定义参数传递给shader程序,则必须通过统一值传递数据。

This had two serious drawbacks:

这有两个严重的缺点:

  • every uniform change caused shader recompilation每次统一更改都会导致着色器重新编译
  • shader program handled every sprite in the exact same way着色器程序以完全相同的方式处理每个精灵

For example: if we wanted to dye a group of sprites red, and one of them blue, we had two ways. First we create two separate SKShader instances and change our custom myColor uniform.

例如:如果我们想将一组精灵染成红色,而其中一个染成蓝色,则有两种方法。 首先,我们创建两个单独的SKShader实例,并更改我们的自定义myColor制服。

Second we make one shader instance and change its uniform which causes a recompilation.

其次,我们制作一个shader实例并更改其统一性,从而导致重新编译。

Both ways cannot be drawn on same pass. And the second one requires complex management code.

两种方法不能同时绘制。 第二个要求复杂的管理代码。

SDK 10.0 introduced the SKAttribute and SKAttributeValue classes. These two allow (finally!) passing data to the shader programs without recompilation. The usage algorithm is simple:

SDK 10.0引入了SKAttributeSKAttributeValue类。 这两个允许(最终!)将数据传递到着色器程序,而无需重新编译。 用法算法很简单:

  • The shader part:着色器部分:
  1. Create a shader program

    创建一个着色器程序

    Create a shader programSKShader

    创建一个着色器程序SKShader

  2. Create an array of SKAttributes

    创建一个SKAttributes数组

  3. Assign array of attributes to the shader program将属性数组分配给着色器程序
  • The sprite part:

    sprite部分:

  1. Assign the shader program to a sprite将着色器程序分配给精灵
  2. Assign a dictionary of SKAttributeValues

    分配SKAttributeValues字典

属性示例 (Example with attributes)

In the last example, we’ll add two more sprites. Every one of them will have the same shader program and will differ only in attributes. Let’s modify the -didMoveToView: inGameScene.m:

在最后一个示例中,我们将再添加两个精灵。 它们中的每一个将具有相同的着色器程序,并且仅在属性上有所不同。 让我们修改- didMoveToView: inGameScene.m:

- (void)didMoveToView:(SKView *)view {    NSString *file = [[NSBundle mainBundle] pathForResource:@"myShader" ofType:@"fsh"];    NSString *sourceString = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];    SKShader *shader = [SKShader shaderWithSource:sourceString];        // 1. Add a custom attribute to shader    SKAttribute *attrProgress = [SKAttribute attributeWithName:@"THE_MIGHTY_DARK_FACTOR" type:SKAttributeTypeFloat];    shader.attributes = @[attrProgress];    // 2. Create tree sprites    NSArray *trees = @[                       [self createTreeWithShader:shader mightyFactor:0.3f zPosition:1],                       [self createTreeWithShader:shader mightyFactor:0.6f zPosition:2],                       [self createTreeWithShader:shader mightyFactor:0.9f zPosition:3],                       ];    for (SKSpriteNode *tree in trees) {        [self addChild:tree];    }}- (SKSpriteNode*)createTreeWithShader:(SKShader*)shader mightyFactor:(CGFloat)mightyFactor zPosition:(CGFloat)zPosition {    SKSpriteNode *treeNode = [SKSpriteNode spriteNodeWithImageNamed:@"Trees"];    treeNode.shader = shader;    // 3. Fill the custom attribute on the sprite    treeNode.attributeValues = @{@"THE_MIGHTY_DARK_FACTOR": [SKAttributeValue valueWithFloat:mightyFactor]};    treeNode.zPosition = zPosition;return treeNode;}

… and the shader program:

…和着色器程序:

void main( void ){    vec4 color = texture2D(u_texture, v_tex_coord * (2.5 * THE_MIGHTY_DARK_FACTOR));    float alpha = color.a;    vec3 baseColor = color.rgb * THE_MIGHTY_DARK_FACTOR;    gl_FragColor = vec4(baseColor, 1.0) * alpha;}

... and see the parameterized result!

...并查看参数化结果!

注意事项 (Caveats)

  • The shader’s source code is typically loaded from a .fsh file to a plain NSString

    着色器的源代码通常从.fsh文件加载到纯NSString

    This code must compile on the target device during the runtime

    此代码必须在运行时在目标设备上编译

    no buildtime checks!

    没有构建时间检查!

  • Older devices may use different version of OpenGL ES so beware GLSL syntax differences!

    较旧的设备可能使用不同版本的OpenGL ES,因此请注意GLSL语法的不同!

    In Raft Challenge’s case there was the need to replace

    在Raft Challenge的情况下,需要更换

    __constant (valid in OpenGL ES 3.0) to const for OpenGL ES 2.0.

    __constant (在OpenGL ES 3.0中有效)以const表示OpenGL ES 2.0。

  • It’s a good idea to keep a reference to SKShader object somewhere and reuse it as frequently as needed to avoid visible frame rate drop

    最好在某个地方保留对SKShader对象的引用,并根据需要重复使用它,以避免可见的帧速率下降

    While allocation and shader compilation takes less than 1/60 sec, it may become a huge burden in render loop

    虽然分配和着色器编译花费的时间少于1/60秒,但它可能会成为渲染循环中的巨大负担

  • When using SpriteKit’s Texture Atlases be cautious of vtexcoord

    使用SpriteKit的Texture vtexcoord谨慎使用vtexcoord

    XCode may rotate some textures which swap

    XCode可能会旋转一些交换的纹理

    X and Y axis

    XY

    X and Y axisColor modification is safe, geometry is not

    XY颜色修改是安全的,几何图形不是

摘要 (Summary)

We learned by examples how to use fragment shaders in the Sprite Kit. We added parameters to sprites so our shader program can render every instance in a different way without any performance loss.

我们通过示例学习了如何在Sprite Kit中使用片段着色器。 我们向精灵添加了参数,以便我们的着色器程序可以以不同的方式呈现每个实例而不会造成性能损失。

The complete project is available for a download.

完整的项目可下载。

You can read part 3 of this series here.

您可以在这里阅读本系列的第3部分 。

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

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

翻译自: https://www.freecodecamp.org/news/spritekit-advanced-how-to-build-a-2-5d-game-part-ii-30ddb613b568/

advanced east

advanced east_SpriteKit Advanced —如何构建2,5D游戏(第二部分)相关推荐

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

    by Luke Konior 卢克·科尼尔(Luke Konior) SpriteKit Advanced -如何构建2,5D游戏(第一部分) (SpriteKit Advanced - How to ...

  2. 【独立开发一个2.5D游戏 】—— 2.5D视角构建

    一.写在前面 本文提供常见2.5D游戏创建地图的方式,实现思路来源于Google以及个人的分析.如有解释不妥当之处,还请各位及时指出. 下面会留一个本文配套工程的运行截图,过后将开始我们的正文. 二. ...

  3. 从零开始的2.5D游戏开发

    游戏按照镜头视角来分,可以分为2D游戏.3D游戏,除此之外还有一类游戏被称为2.5D游戏.这是一个比较有争议的分类,这个分类有着不同的解释.有的人认为这只是厂商的噱头,它本身就是2D游戏(我曾经也这么 ...

  4. Unity 中渲染顺序的理解以及一些坑点 以及2.5D游戏中的渲染排序解决方案

    1.ZTest & ZWrite ZTest:深度测试,开启后测试结果决定片元是否被舍弃,可配置  ZWrite:深度写入,开启后决定片元的深度值是否写入深度缓冲,可配置 ZTest可设置的测 ...

  5. Unity3D 游戏引擎之构建简单的游戏世界(三)

    Unity3D 游戏引擎之构建简单的游戏世界 雨松MOMO原创文章如转载,请注明:转载至我的独立域名博客雨松MOMO程序研究院,原文地址:http://www.xuanyusong.com/archi ...

  6. 画布式编程_构建HTML5画布游戏如何帮助我学习编程

    画布式编程 by Surbhi Oberoi 由Surbhi Oberoi 构建HTML5画布游戏如何帮助我学习编程 (How building HTML5 canvas games helped m ...

  7. Java中2.5D游戏的设计与实现(3)—八方走法实现原理及相关代码

    Java中2.5D游戏的设计与实现(3)-八方走法实现原理及相关代码 2.5D游戏,虽然在外观上近似于3D游戏,却又不是严格意义上讲的3D游戏,故此2.5D游戏又常被称为[伪3D游戏]. 在笔者的观念 ...

  8. 青瓷引擎之纯JavaScript打造HTML5游戏第二弹——《跳跃的方块》Part 3

    继上一次介绍了<神奇的六边形>的完整游戏开发流程后(可点击这里查看),这次将为大家介绍另外一款魔性游戏<跳跃的方块>的完整开发流程. (点击图片可进入游戏体验) 因内容太多,为 ...

  9. 视频教程-微信小游戏第二季:飞扬的肖恩/弹球打砖块-Cocos

    微信小游戏第二季:飞扬的肖恩/弹球打砖块 知识传播美丽,分享传递快乐.作者主要从事Java服务器端技术.前端与移动开发技术的研究和授课,已有10多年从业经验. IT老兵 ¥39.00 立即订阅 扫码下 ...

最新文章

  1. F5第一个10000台
  2. 【C++】explicit 关键字
  3. 自动控制原理第二版王建辉_王建辉自动控制原理配套题库名校考研真题课后答案资料课后习题章节题库模拟试题...
  4. python 数组维度_python – 非常基本的Numpy数组维度可视化
  5. linux arm下硬件驱动程序放哪里,Arm-Linux摄像头驱动程序的移植
  6. 【2016年第4期】突发大数据在存储辅助光电路交换网络中的传输
  7. 超过千字的文章,才统计勤写标兵
  8. Precision、Recall、F计算:举例
  9. 关于IE上embed自动播放
  10. JVM年轻代和老年代垃圾回收
  11. 聊聊几个阿里 P8、P9 程序员的故事
  12. 猿创征文|Android 11.0 12.0Launcher3中app列表页的app名称分两行显示
  13. spark使用之ALS版本对比
  14. 策略模式——多种发票上传实现案例
  15. css 动画 呼吸 呼吸灯 效果
  16. 【电力负荷预测】基于matlab BP神经网络电力负荷预测【含Matlab源码 278期】
  17. 对整行tr除最后一列外的每一列设置点击事件
  18. 复数系下常量乘向量的范数
  19. node.js安装后输入“node -v”提示'node' 不是内部或外部命令,也不是可运行的程序的解决方法
  20. 当你全力以赴世界也会为你让路,23级人大女王金融硕士准备中

热门文章

  1. 腾讯,字节,阿里,小米,京东大厂Offer拿到手软!绝对干货
  2. JavaWeb--JavaEE
  3. 【数据库】Oracle用户、授权、角色管理
  4. 线性代数-矩阵-转置 C和C++的实现
  5. Day 3 网络基础
  6. 迷宫游戏 堆栈实现
  7. 什么原因成就了一位优秀的程序员?(转)
  8. iOS sqlite
  9. Struts+Spring+Hibernate练习(完整)
  10. deeplearning4j