advanced east_SpriteKit Advanced —如何构建2,5D游戏(第二部分)
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中添加的SKAttribute
和SKAttributeValue
类的信息。
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
variablegl_FragColor
变量vec2
,vec3
andvec4
are the types similar to C's:float array[2]
,float array[3]
andfloat array[4]
vec2
,vec3
和vec4
与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 avec2
which contains our current position in texturev_tex_coord
是一个vec2
,其中包含我们当前的纹理位置texture2D(tex , p)
is a function that returns color from texturetex
inpoint p
asvec4
texture2D(tex , p)
是一个函数,将point p
处纹理tex
颜色返回为vec4
which contains rgba
其中包含rgba
gl_FragColor
is an output colorgl_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进行了重要更新,即SKAttribute
和SKAttributeValue
类。
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引入了SKAttribute
和SKAttributeValue
类。 这两个允许(最终!)将数据传递到着色器程序,而无需重新编译。 用法算法很简单:
- The shader part:着色器部分:
Create a shader program
创建一个着色器程序
Create a shader program
SKShader
创建一个着色器程序
SKShader
Create an array of
SKAttributes
创建一个
SKAttributes
数组- Assign array of attributes to the shader program将属性数组分配给着色器程序
The
sprite
part:sprite
部分:
- Assign the shader program to a sprite将着色器程序分配给精灵
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 plainNSString
着色器的源代码通常从
.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) toconst
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
andY
axisX
和Y
轴X
andY
axisColor modification is safe, geometry is notX
和Y
轴颜色修改是安全的,几何图形不是
摘要 (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游戏(第二部分)相关推荐
- SpriteKit Advanced —如何构建2,5D游戏(第一部分)
by Luke Konior 卢克·科尼尔(Luke Konior) SpriteKit Advanced -如何构建2,5D游戏(第一部分) (SpriteKit Advanced - How to ...
- 【独立开发一个2.5D游戏 】—— 2.5D视角构建
一.写在前面 本文提供常见2.5D游戏创建地图的方式,实现思路来源于Google以及个人的分析.如有解释不妥当之处,还请各位及时指出. 下面会留一个本文配套工程的运行截图,过后将开始我们的正文. 二. ...
- 从零开始的2.5D游戏开发
游戏按照镜头视角来分,可以分为2D游戏.3D游戏,除此之外还有一类游戏被称为2.5D游戏.这是一个比较有争议的分类,这个分类有着不同的解释.有的人认为这只是厂商的噱头,它本身就是2D游戏(我曾经也这么 ...
- Unity 中渲染顺序的理解以及一些坑点 以及2.5D游戏中的渲染排序解决方案
1.ZTest & ZWrite ZTest:深度测试,开启后测试结果决定片元是否被舍弃,可配置 ZWrite:深度写入,开启后决定片元的深度值是否写入深度缓冲,可配置 ZTest可设置的测 ...
- Unity3D 游戏引擎之构建简单的游戏世界(三)
Unity3D 游戏引擎之构建简单的游戏世界 雨松MOMO原创文章如转载,请注明:转载至我的独立域名博客雨松MOMO程序研究院,原文地址:http://www.xuanyusong.com/archi ...
- 画布式编程_构建HTML5画布游戏如何帮助我学习编程
画布式编程 by Surbhi Oberoi 由Surbhi Oberoi 构建HTML5画布游戏如何帮助我学习编程 (How building HTML5 canvas games helped m ...
- Java中2.5D游戏的设计与实现(3)—八方走法实现原理及相关代码
Java中2.5D游戏的设计与实现(3)-八方走法实现原理及相关代码 2.5D游戏,虽然在外观上近似于3D游戏,却又不是严格意义上讲的3D游戏,故此2.5D游戏又常被称为[伪3D游戏]. 在笔者的观念 ...
- 青瓷引擎之纯JavaScript打造HTML5游戏第二弹——《跳跃的方块》Part 3
继上一次介绍了<神奇的六边形>的完整游戏开发流程后(可点击这里查看),这次将为大家介绍另外一款魔性游戏<跳跃的方块>的完整开发流程. (点击图片可进入游戏体验) 因内容太多,为 ...
- 视频教程-微信小游戏第二季:飞扬的肖恩/弹球打砖块-Cocos
微信小游戏第二季:飞扬的肖恩/弹球打砖块 知识传播美丽,分享传递快乐.作者主要从事Java服务器端技术.前端与移动开发技术的研究和授课,已有10多年从业经验. IT老兵 ¥39.00 立即订阅 扫码下 ...
最新文章
- F5第一个10000台
- 【C++】explicit 关键字
- 自动控制原理第二版王建辉_王建辉自动控制原理配套题库名校考研真题课后答案资料课后习题章节题库模拟试题...
- python 数组维度_python – 非常基本的Numpy数组维度可视化
- linux arm下硬件驱动程序放哪里,Arm-Linux摄像头驱动程序的移植
- 【2016年第4期】突发大数据在存储辅助光电路交换网络中的传输
- 超过千字的文章,才统计勤写标兵
- Precision、Recall、F计算:举例
- 关于IE上embed自动播放
- JVM年轻代和老年代垃圾回收
- 聊聊几个阿里 P8、P9 程序员的故事
- 猿创征文|Android 11.0 12.0Launcher3中app列表页的app名称分两行显示
- spark使用之ALS版本对比
- 策略模式——多种发票上传实现案例
- css 动画 呼吸 呼吸灯 效果
- 【电力负荷预测】基于matlab BP神经网络电力负荷预测【含Matlab源码 278期】
- 对整行tr除最后一列外的每一列设置点击事件
- 复数系下常量乘向量的范数
- node.js安装后输入“node -v”提示'node' 不是内部或外部命令,也不是可运行的程序的解决方法
- 当你全力以赴世界也会为你让路,23级人大女王金融硕士准备中