SpriteKit在iOS8和OSX10.10中的新特性(强悍来袭)
在iOS8和OSX10.10中SpriteKit迎來了重大升級(jí)。在物理表現(xiàn)方面增加了著色器,光照和陰影;在物理模擬方面增加了像素物理體、力場(chǎng)和宇宙動(dòng)力學(xué)和約束等;在Xcode中集成了場(chǎng)景編輯器,你不需要寫代碼就能完成一些復(fù)雜的工作;此外它還集成了SceneKit以及其他的改進(jìn)。
Shader
有時(shí)候?yàn)榱吮憩F(xiàn)一些形變和模糊效果,比如透過熱氣和火焰看一些物體,或者是飛船被攻擊而產(chǎn)生彎曲。SpriteKit新加入了?SKShader?類來幫助我們更簡(jiǎn)單的實(shí)現(xiàn)這個(gè)效果。它通過使用自定義的OpenGL ES碎片著色來完成繪制一些?SKNode的自定義行為。現(xiàn)在支持以下幾種類型的繪制:
- SKSpriteNode
- SKShapeNode
- SKEmitterNode
- SKEffectNode
- SKScene
放一張官方演示的效果圖:
SKShader?內(nèi)建了一些?uniforms?,用?SKUniform?來描述,當(dāng)你創(chuàng)建一個(gè)?SKShader?時(shí),需要通過一個(gè)?fsh?文件或者一個(gè)字符串代碼來創(chuàng)建,也可選擇性的傳入?uniforms?來定義游戲中一些額外的參數(shù)。下面是創(chuàng)建Shader方法的集合:
(下面的只是偽代碼,不是OC也不是Swift語法,只是為了更簡(jiǎn)潔的標(biāo)記方法名,后面類似情形都會(huì)標(biāo)記為“偽代碼”)
| ? | + shaderWithFileNamed: + shaderWithSource:uniforms: + shaderWithSource: + shader - initWithSource:uniforms: - initWithSource: |
這些方法很相似,都是傳入?source?或?source?與?uniforms?來創(chuàng)建shader。官方建議我們用?fsh?文件作為?source?而不是字符串代碼;并且避免更改?source?或添加刪除?uniforms?(修改?uniforms?是可以的),因?yàn)檫@樣會(huì)導(dǎo)致后臺(tái)花時(shí)間重新編譯shader;盡量在加載時(shí)初始化shader。這些注意事項(xiàng)都可以總結(jié)為盡量使用內(nèi)建的?uniforms?和共享shader對(duì)象。
我們可以將創(chuàng)建好的?SKShader?對(duì)象賦值給支持Shader渲染對(duì)象的?shader屬性。
下面列舉一下Shader中的預(yù)定義符號(hào):
Lighting and Shadows
燈光和陰影效果可以給游戲增加真實(shí)感,SpriteKit這次增加了?SKLightNode來作為光源節(jié)點(diǎn)。我們可以定義光源的顏色,陰影和衰弱程度。
SKLightNode?繼承于?SKNode?,也就是說你可以把它加在其他?SKNode上,并能夠動(dòng)起來,它是一個(gè)會(huì)發(fā)光的?SKNode?,cool!NOTE:它的光照以及陰影效果只對(duì)?SKSpriteNode?起作用。
下面我們看看?SKLightNode?的一些屬性:
- enabled?光源的開關(guān)
- ambientColor?環(huán)境色,默認(rèn)是黑色,也就是沒有環(huán)境色。它無視自身的alpha以及?SKLightNode?的?falloff?屬性和?SKSpriteNode?的normalTexture?屬性,分分鐘照亮全場(chǎng)。
- lightColor?顧名思義就是光的顏色,默認(rèn)是白色。
- shadowColor?被精靈物體遮擋產(chǎn)生的陰影顏色。
- falloff?光源強(qiáng)度的衰減比率
- categoryBitMask?光的種類,32位整型數(shù)。?SKSpriteNode?的lightingBitMask?、?shadowedBitMask?和?shadowCastBitMask存儲(chǔ)著光的種類,分別意味著:被何種光照亮、被何種光產(chǎn)生的陰影覆蓋和遮擋何種光線并產(chǎn)生陰影。
為了在?SKSpriteNode?上實(shí)現(xiàn)更加逼真的光照效果(如陰影和反射光),SKSpriteNode?新增了?normalTexture?屬性來儲(chǔ)存原帖圖的法線貼圖(Normal Map):
上圖中左側(cè)的是原貼圖,加上中間的法線貼圖就合成出最右側(cè)帶有質(zhì)感的光照紋理。當(dāng)然提供這樣一張發(fā)現(xiàn)紋理圖片會(huì)增加開發(fā)者的工作量,蘋果還提供了另一種更加簡(jiǎn)單的方案-“automatic normal map”:
SpriteKit能夠根據(jù)給出的紋理圖片,用一系列算法分析原貼圖,然后生成一個(gè)最佳的法線貼圖,又是蘋果的黑魔法!
畢竟眾口難調(diào),所以SpriteKit讓你也可以在生成法線貼圖的時(shí)候給出平滑度(smoothness)和對(duì)比度(contrast)來調(diào)節(jié)你想要的效果:(偽代碼)
| ? | - textureByGeneratingNormalMapWithSmoothness:contrast: |
PS:法線貼圖將具有高細(xì)節(jié)的模型通過映射烘焙出法線貼圖,貼在低端模型的法線貼圖通道上,使之擁有法線貼圖的渲染效果。可以大大降低渲染時(shí)需要的面數(shù)和計(jì)算內(nèi)容,從而達(dá)到優(yōu)化動(dòng)畫渲染和游戲渲染的效果。
在一個(gè)場(chǎng)景中可以添加多個(gè)光源,程序會(huì)運(yùn)行的很快;但是如果用兩個(gè)或以上的光源照亮同一個(gè)精靈,在某些iOS設(shè)備上可能保證不了60幀的刷新頻率。
New Physics
Per-Pixel Physics
在定義一些復(fù)雜輪廓的物理體時(shí),我們經(jīng)常用簡(jiǎn)單圖形代替,否則就用CGPath?一點(diǎn)點(diǎn)描繪多邊形或者把多個(gè)物理體組合在一起(這也是新加入的API,后面會(huì)提到),比如下面這把斧頭,大多數(shù)程序員直接用矩形當(dāng)做它的物理體:
而Per-Pixel Physics根據(jù)紋理圖片的alpha通道遮罩來生成一個(gè)粗略的形狀,然后再用粗略的形狀生成精確的形狀,它讓以前復(fù)雜的?CGPath?創(chuàng)建工作轉(zhuǎn)變成一行代碼:
畢竟眾口難調(diào),所以SpriteKit給出了一個(gè)可以自由調(diào)節(jié)alpha閾值的物理體生成方法,所有alpha值大于?alphaThreshold?的像素點(diǎn)都將被認(rèn)為是不透明的,并納入物理體范圍內(nèi):(偽代碼)
| ? | + bodyWithTexture:alphaThreshold:size: |
因?yàn)镾priteKit是逐個(gè)像素計(jì)算才得出精確的物理體輪廓,所以我們應(yīng)該盡量給出合適大小的圖片,不要將分辨率過高的圖片用在很小的?SKSpriteNode?上。
Constrains
試想如果你要做一款塔防游戲,你需要讓你的大炮一直瞄準(zhǔn)某個(gè)怪物,大炮會(huì)隨著怪物的行走來轉(zhuǎn)動(dòng)炮臺(tái)。我們需要不停地根據(jù)怪物和大炮的位置來計(jì)算需要旋轉(zhuǎn)的角度,甚至當(dāng)怪物跑的快的時(shí)候還要考慮怪物的速度來調(diào)整大炮旋轉(zhuǎn)的速度,這是一個(gè)很麻煩的事情。現(xiàn)在SpriteKit幫你把這些都做好了,你只需要建立一個(gè)SKConstrains?對(duì)象,并約束大炮的角度跟怪物一致就行。
約束的計(jì)算工作發(fā)生在模擬物理之后,SpriteKit提供了一個(gè)回調(diào)函數(shù)didApplyConstraints?,我們可以在約束完成后在里面做一些善后工作:
在SpriteKit中我們可以向SKNode添加三種約束:(工廠方法的偽代碼)
- 位置約束:
| ? | //約束節(jié)點(diǎn)的X坐標(biāo)范圍 + positionX: //約束節(jié)點(diǎn)的Y坐標(biāo)范圍 + positionY: //約束節(jié)點(diǎn)的X和Y坐標(biāo)范圍 + positionX:Y: |
- 方向約束:
| ? | //約束節(jié)點(diǎn)基于另一個(gè)SKNode旋轉(zhuǎn) + orientToNode:offset: //約束節(jié)點(diǎn)基于一個(gè)固定點(diǎn)旋轉(zhuǎn) + orientToPoint:offset: //約束節(jié)點(diǎn)基于另一個(gè)SKNode坐標(biāo)系中的一個(gè)固定點(diǎn)旋轉(zhuǎn) + orientToPoint:inNode:offset: //約束節(jié)點(diǎn)的方向范圍 + zRotation: |
- 距離約束:
| ? | //約束節(jié)點(diǎn)與另一節(jié)點(diǎn)保持一定距離 + distance:toNode: //約束節(jié)點(diǎn)與一個(gè)固定點(diǎn)保持一定距離 + distance:toPoint: //約束節(jié)點(diǎn)與另一個(gè)SKNode坐標(biāo)系中的一個(gè)固定點(diǎn)保持一定距離 + distance:toPoint:inNode: |
向?SKNode?添加約束很簡(jiǎn)單,只需要將一個(gè)?SKConstrains?數(shù)組賦值給SKNode.constraints?屬性即可。約束執(zhí)行的順序取決于它們?cè)跀?shù)組中的順序。
Inverse Kinematics
反向運(yùn)動(dòng)學(xué),沒有機(jī)械工程學(xué)位或沒寫過動(dòng)畫引擎的人干脆不知道這是個(gè)啥。它其實(shí)是解決連接體運(yùn)動(dòng)的,比如現(xiàn)在有一個(gè)機(jī)器人的手臂,我們想讓它動(dòng)起來去用手抓某個(gè)東西。我們會(huì)想到每個(gè)關(guān)節(jié)轉(zhuǎn)多少度才能準(zhǔn)確讓機(jī)器手抓到物體,計(jì)算的時(shí)候還應(yīng)該考慮連接體的層級(jí)關(guān)系:肩膀連接上臂,上臂連接小臂,小臂連接手。哦天啊這真蛋疼,不過SpriteKit的反向動(dòng)力學(xué)解決了這一點(diǎn),我們只需要指定每個(gè)SKNode?的活動(dòng)約束還有需要抓取物體的位置,那么這一切只需要幾行代碼就能搞定。
機(jī)器人手臂轉(zhuǎn)動(dòng)約束是靠?SKReachConstraints?類來定義的,它只有一個(gè)初始化方法:(偽代碼)
| ? | - initWithLowerAngleLimit:upperAngleLimit: |
這個(gè)方法給?lowerAngleLimit?和?upperAngleLimit?屬性賦值,約束了reach事件使其發(fā)生旋轉(zhuǎn)角度的下限和上限。
當(dāng)一個(gè)?SKReachConstraints?創(chuàng)建好后,將其賦值給?SKNode?的reachConstraints?屬性,然后用?SKPhysicsJoint?將這些?SKNode?連接起來。使用?SKAction?的一套工廠方法創(chuàng)建來讓連接體reach到活動(dòng)目標(biāo)或固定點(diǎn)的動(dòng)作:(偽代碼)
| ? | + reachTo:rootNode:duration: + reachTo:rootNode:velocity: + reachToNode:rootNode:duration: + reachToNode:rootNode:velocity: |
讓機(jī)器人的手部節(jié)點(diǎn)運(yùn)行創(chuàng)建好的?SKAction?對(duì)象即可達(dá)到最初描述的動(dòng)畫效果。如果機(jī)器人的手觸碰不到指定的位置或節(jié)點(diǎn)(gif中就是這樣),?SKAction會(huì)執(zhí)行動(dòng)畫讓其盡可能接近目的地。
PS:IK(Inverse Kinematics)也能在SceneKit上運(yùn)行。
Physics Fields
在一個(gè)模擬宇宙空間的游戲中,星球?qū)ζ渌矬w的引力是不可忽視的,這就涉及到物理場(chǎng)。SpriteKit為我們提供了一個(gè)專門描述物理場(chǎng)的類?SKFieldNode?,它繼承于?SKNode?,也就是說它可以被添加到其他節(jié)點(diǎn)中。它能夠描述多種場(chǎng):電場(chǎng)、磁場(chǎng)、矢量重力場(chǎng)、輻射重力場(chǎng)、噪聲場(chǎng)等十余種場(chǎng)。?SKFieldNode?的strength?和?falloff?屬性決定了場(chǎng)的強(qiáng)度和衰減比率。
SKFieldNode?還有一些屬性決定了游戲中哪些物理體會(huì)被場(chǎng)影響:
- region?描述了場(chǎng)的影響區(qū)域,類型是?SKRegion?。?SKRegion?可以是無限區(qū)域,矩形、圓形、?CGPath?多邊形區(qū)域,還可以用兩個(gè)?SKRegion做邏輯運(yùn)算得出新的?SKRegion?,比如交集,并集,差集,還可對(duì)一個(gè)SKRegion?取反得到剩下的區(qū)域。
- minimumRadius?一些場(chǎng)對(duì)物體的影響程度跟距離有關(guān),當(dāng)物體與場(chǎng)的距離小于?minimumRadius?屬性的值時(shí),仍被當(dāng)做?minimumRadius?的值進(jìn)行處理計(jì)算。?minimumRadius?的默認(rèn)值很小很小,但不是0
- categoryBitMask?場(chǎng)的類別。當(dāng)一個(gè)節(jié)點(diǎn)進(jìn)入了場(chǎng)的影響區(qū)域,會(huì)根據(jù)節(jié)點(diǎn)的物理體屬性的?fieldBitMask?屬性來判斷此節(jié)點(diǎn)是否收到場(chǎng)的影響。此外?SKPhysicsBody?的?charge?屬性還會(huì)標(biāo)記物理體所帶的電荷量,這在跟電磁有關(guān)的場(chǎng)中會(huì)影響到物理體的運(yùn)動(dòng)。默認(rèn)為 0xFFFFFFFF
- enabled?場(chǎng)的“開關(guān)”
- exclusive?唯我獨(dú)尊,這個(gè)場(chǎng)的地盤內(nèi),別的場(chǎng)的影響被無視。別讓兩個(gè)唯我獨(dú)尊的場(chǎng)影響區(qū)域重疊,那樣會(huì)出亂子的。默認(rèn)為?NO?。
我用SpriteKit的力場(chǎng)中的噪聲場(chǎng)和輻射重力場(chǎng)做了一個(gè)小游戲?ColorAtom?,有興趣的可以去看看。應(yīng)用了很多粒子系統(tǒng)和碰撞檢測(cè)知識(shí),把SpriteKit之前的內(nèi)容能加的都加進(jìn)去了,歡迎指正。
Integration with SceneKit
在SpriteKit這樣的2D游戲引擎中也可以引入3D的內(nèi)容,可以將SceneKit中的3D物體當(dāng)做SpriteKit中的?SKNode?來操作,為了達(dá)到這一目的SpriteKit這次增加了SK3DNode?類作為3D物體到SpriteKit中的橋接。
通過?SK3DNode?的?scnScene?屬性可以獲取到?SCNScene?,因?yàn)镾K3DNode?把3D場(chǎng)景渲染成2D貼圖,所以創(chuàng)建?SK3DNode?對(duì)象的時(shí)候需要傳入一個(gè)渲染后貼圖的尺寸?viewportSize?。?pointOfView?屬性存儲(chǔ)了游戲視角的位置,你可以嘗試實(shí)現(xiàn)個(gè)上帝視角。
由于我也不太了解新出的SceneKit,所以這部分不過多介紹了。
Tools
Xcode6增加了SpriteKit編輯器,一行代碼都不用寫就能創(chuàng)建出個(gè)游戲場(chǎng)景。這樣你就將游戲內(nèi)容從游戲邏輯沖分離出來。我們只需要將控件拖拽到游戲場(chǎng)景你想要的位置上,而不必每次調(diào)整一個(gè)飛船的位置,編譯運(yùn)行看看結(jié)果,然后再改代碼微調(diào)位置再次編譯運(yùn)行。。。
SpriteKit編輯器不僅有編輯功能,也可用來debug。你可以再運(yùn)行過程中將當(dāng)前狀態(tài)存儲(chǔ)在一個(gè)sks文件中。
SpriteKit編輯器強(qiáng)大到你可以直接拖拽出一個(gè)SKSpriteNode并定義它的物理體,甚至使用Per-Pixel Physics。還可以編輯光照和陰影,編輯Shader并調(diào)整Uniforms,建立一個(gè)Inverse Kinematics并預(yù)覽其效果,強(qiáng)大極了。
其實(shí)說白了SpriteKit編輯器就像IB一樣可以可視化編輯屬性,并課即時(shí)預(yù)覽效果,整個(gè)過程不需一行代碼。
Xcode能編輯fsh文件,與SpriteKit場(chǎng)景編輯器對(duì)照編輯,并做語法檢查和編譯。
Improvements
SKScene
SpriteKit每一幀場(chǎng)景的執(zhí)行過程:
除了之前提到新加的Constrains,SpriteKit這次還加入了一個(gè)回調(diào)函數(shù)didFinishUpdate?。這絕對(duì)是SpriteKit將每幀所有東西打包好交給GPU渲染之前調(diào)用的最后一個(gè)函數(shù)。
SKTexture
- SKMutableTexture?是?SKTexture?新加的子類,它的內(nèi)容可以通過modifyPixelDataWithBlock:?方法動(dòng)態(tài)地修改。
- SKTexture?現(xiàn)在可以生成“noise textures”,參見textureVectorNoiseWithSmoothness:size:?和textureNoiseWithSmoothness:size:grayscale:?
SKShapeNode
- 增加了一些常見圖形便捷的構(gòu)造方法,比如矩形,圓形,橢圓和曲線。
- 可以用貼圖和Shader來美化形狀的描邊和填充
Physics Updates
- SKPhysicsBody?新加了?pinned?屬性來標(biāo)志此物理體對(duì)應(yīng)的節(jié)點(diǎn)是否被釘在它的父節(jié)點(diǎn)上。如果父節(jié)點(diǎn)也有物理體,那么這兩個(gè)物理體被認(rèn)為被一個(gè)pin連接。如果將?allowsRotation?設(shè)為NO并且?pinned?設(shè)為YES,那么它相當(dāng)于被焊在父節(jié)點(diǎn)上了,因?yàn)樗荒苻D(zhuǎn)動(dòng)了。
- SKPhysicsBody?允許用?bodyWithBodies:?把多個(gè)物理體組合在一起來創(chuàng)建一個(gè)新的物理體,還記得前面提到過的斧頭么:?
SKTexture Atlas
- 同時(shí)支持SpriteKit和SceneKit
- 同時(shí)支持Retina和非Retina
- 同時(shí)支持16位和32位格式
- 支持4k x 4k 分辨率
- 支持運(yùn)行時(shí)紋理圖集的生成。比如從網(wǎng)上下載資源,SpriteKit會(huì)自動(dòng)將透明的像素裁剪下去
總結(jié)
以上是生活随笔為你收集整理的SpriteKit在iOS8和OSX10.10中的新特性(强悍来袭)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【毫米波雷达】人体目标探测理论
- 下一篇: word实现多级自动编号