終於有時間寫部落格了,隔了好長一段時間沒研究SpriteKit了,今天裝了個Xcode5的DP2版本,驚奇地發現,在建立project的時候居然看到了SpriteKit Game的選項,選了它之後直接建立一個SpriteKit項目,蘋果想得還真是周到,省去之前很多瑣碎的步驟,如果你是對SpriteKit很熟悉的開發人員的話那大可直接使用Sprite Game來建立項目,如果是初學者的話,建議還是建一個空項目,跟著官方的文檔,一步一步去做,自然會讓你更好地瞭解SpriteKit。其實,直接用SpriteKit Game建立項目,它只是幫你自動產生了一小部分代碼,自己打一遍也不是很難,起碼知道它是怎樣啟動和運作的。由於現在iOS7還是beta版本,Xcode5也是DP版本的,API文檔沒什麼變化,不過估計接下來關於SpriteKit還會有更多好玩的東西出來,拭目以待咯。 隔了這麼久,不打一下代碼還真有點生疏了,今天重新看了一遍SpriteKit的Simulate Physics,也就是物理系統,突然間發現了好多新的東西,之前看都是粗略地帶過,今天仔細一看,還真有很多東西,SpriteKit確實很強大,在2d方面,算是做到儘可能好的了。廢話不多說,下面就從頭來學習SpriteKit的物理系統。 完成遊戲的物理系統設定,你需要一下完成以下幾點:1、把SKPhysicsBody關聯到擁有物理屬性的遊戲對象上,每一個遊戲對象node節點都有physicsbody屬性2、設定physicsbody的各個屬性,讓遊戲對象在遊戲裡面呈現更好的遊戲效果3、設定整個遊戲世界的物理系統,比如重力,注意,scene也有物理屬性4、可以在physicsbody上施加力5、定義物體之間的碰撞處理6、最佳化整個物理系統 其實個人覺得比較有趣的事碰撞那一塊,有新的東西在裡面,至少我在其他遊戲引擎裡面沒見過,也可能是我見識得比較少吧,不過個人覺得,SpriteKit的碰撞檢測做得真心贊。 physics body(後面簡稱pb)有三種,一種是運動的,一種是靜止的,還有一種叫做edge的,運動的pb可以接受外界所有的關於物理的影響,靜止的pb沒有速度,不受力和碰撞的影響,區分這兩種pb是有好處的,普通的遊戲對象一般都是運動的pb,而牆壁啊,地面,遊戲情境的邊界等就可以靜止的pb edge的話,其實就是邊啦,可以這樣理解,自由無規則的pb,一般邊界可以用它來限定,它頻繁地被用來當作遊戲情境的邊界,還有一個用處是,遊戲對象都是有形狀的,SpriteKit提供了幾種形狀給我們,但其實在現實真正的遊戲當中,往往我們的遊戲對象都是不規則圖形,這個時候,如果你想要為你的遊戲對象加上一個具有相似形狀的pb的話,就可以考慮使用edge了,不過這不是經常採取的方法,使用edge來定形要很大量的計算,消耗資源,這會讓你的遊戲變得不順暢,所以不是很提倡這種做法。 下面貼段用edge作為scene邊界的代碼
代碼不難理解,最主要的函數就是bodyWithEdgeLoopFromRect:,就不多說了吧。 根據指定的形狀建立pb也不是很難,具體的代碼就不貼出來了,建議還是去看看SKPhysicsBody的api,看完會更有利於學習整個物理系統。 這裡說幾個pb的屬性吧,mass屬性,即品質,這個學過物理或者沒學過的都知道的拉,有mass當然就有density密度啦,這兩個屬性是會隨著對方的變化而變化的,唯一不變的是物體的形狀,整個真得學過物理才知道,mass等於density乘於area體積。 friction屬性,摩擦力,就是物體表面的粗糙程度,在其上面運動的物體會受到其friction的影響,力的話還有linearDamping阻力和angularDamping角力,就是旋轉的力,這個跟你的遊戲世界裡的環境有空,一般是空氣阻力啦,或者是在水中的阻力啦,由你的環境決定。 restitution屬性,可以理解為勢能吧,在碰撞的時候它決定碰撞的威力,即作用效果的強弱。 dynamic屬性,一般動態pb的預設值為yes,將其設定為no之後,就是靜態pb了。 affectedByGravity屬性,yes即接受重力影響,no則不受重力影響。 allowsRotation屬性,它決定物體是否能選擇,設定為no即凍結旋轉。 大概這些個屬性吧,需要注意的是,mass和density,在我們的pb建立後,area就已經被設定為固定的,不可改變的,所以mass和density之間的變化會相互影響,根據你的需要你可以選擇設定mass來確定density,也可以設定density來確定mass,根據實際情況而定,兩者會有不同的效果,這涉及物理方面的知識。 另外很多屬性都是會變化的,結合實際中的物理性質可知,SpriteKit在每一幀都會重新計算每個屬性值,你可以在更新的時候通過一些函數來修改一些屬性,另外,當切換到不同的情境不同的環境中時,根據你的需要,在pross-post scene的時候修改一些屬性,以到期望的效果。 前面說過,scene也有物理性質,它還有個physicsWorld的屬性,即整個的遊戲世界裡的物理系統,這裡說一下速度,正常速度為1,舉個例子吧,當為0.5時,pb的執行速度會減慢一半,為2時即加快一倍接下來,你可能需要為你的pb施加一些力,好讓其運動起來,有兩個力force和impulse,force是一個持續的力,每一幀都要設定,好讓力延續下去,impulse是一個即時的力,即發力又一個立即的效果,但不會持續下去,鑒於兩者區別不大,官方的文檔也只著重說了下force。 哦,加力其實就是加個向量,我們只需要提供一個點,讓物體朝那個點運動,加力可根據作用點分為三種,一種是讓物體直線運動的力,一種只讓物體轉動的力,還有就是作用在物體的一點上,這個時候,SpriteKit會根據物體的形狀去決定加在這個點上的力將會產生怎樣的效果。 終於說到碰撞了,之前的文章提到過,cocos2d做碰撞檢測用的是兩個物體的rect是否交叉,其他有關碰撞檢測的一些東西實現起來比較麻煩,而unity則提供了強大的物理系統,裡面的碰撞做得真的是沒話說,就是2d遊戲也依然能使用它的物理系統,SpriteKit的碰撞有點向unity,其實整個的物理系統都有點像,個人覺得,physics body即rigibody,當然,unity畢竟是3d引擎,關於物理系統的東西豐富很多。其實,在node節點關聯上physics body之後,SpriteKit在兩個物體碰撞時就做了些計算的,這個是我覺得很強大的地方之一,也就是說,你可以真的把它們當作是兩個真實世界中物體的碰撞,在碰撞之後,物體會怎樣都在精確的計算之中。不過單單靠這些,是不能做一些碰撞檢測的,如果你想兩個物體在碰撞時按你的想法去反應的話,你就需要設定一些屬性了。 還是pb的屬性,這三個屬性很重要:categoryBitMask,它標記了物體屬於那一類物體,預設值為0xffffffffcollisionBitMask,它標記了哪些物體可以跟其發生碰撞,預設值為0xffffffffcontactTestBitMask,它標記了哪些物體會和其發生碰撞後產生一些影響,預設值為0x00000000 這三個數值均由32位的十六進位數來表示,即你的遊戲可以有32中不同類型的物體,其實用不到那麼多,為什麼用這樣的值來表示,這也是我覺得新穎的地方,在做碰撞檢測時,這些值是拿來做邏輯與運算,計算碰撞用的,感覺很不粗吧。判斷其他物體是否可以跟自己發生碰撞,要根據自己的collisionBitMask的值與對方物體的categoryBitMask的值進行邏輯與運算,得到的結果不為0,即可以發生碰撞。 判斷碰撞的兩個物體是否發生影響時,假設為A和B,首先是A的categoryBitMask與B的contactTestBitMask進行邏輯與運算,B也做同樣的運算,當兩者的結果都不為0時,兩者即可在碰撞的時候互相影響這要仔細想一想,好好消化一下才行,這是約定好的東西,直接用就行了,不過如果想深入研究的話,再去看官方的api吧。 設定好了這些值以後呢,我們需要一些協議來為我們完成我們想要做的事情,這時又得用到scene啦。 SpriteKit提供給我們一個叫做SKPhysicsContactDelegate,這也是它唯一給的一個協議,在初始化scene的時候設定scene.physicsWorld.contactDelegate = self;在.h標頭檔加上該協議,它提供了好兩個檢測碰撞的函數,-didBeginContact:和-didEndContact:參數都是一個SKPhysicsContact,裡麵包含了兩個碰撞體的資訊,和碰撞的資訊,建議大家去看看SKPhysicsContact的api,開發起來才更加得心應手,我們就可以在這兩個函數裡做我們在碰撞時要做的事情了。 好一個碰撞檢測啊。 usePreciseCollisionDetection 提高碰撞精度的屬性,前面說過,就不多說了。 說完碰撞,接著是將多個pb物體串連在一起的joint,可以理解為骨骼吧,如果你想你的遊戲展現一些更複雜的物理特效的話,你可能會需要它,這裡也不多說了。 在遊戲中,有時我們需要操控我們的遊戲對象,整個時候就需要選中該對象,當對象有了pb之後,我們可以通過很多方法來找到它。最常用的方法,應該不陌生了,就是通過ray射線來尋找物體,首先我們需要確定一條射線,舉個例子吧,在平面中,我們可以以(0, 0)為起點,手指點擊螢幕的點為終點,一般在3d情境,是通過攝像機的位置點作為起點,然後點擊的地方為終點來確定一條射線,然後通過函數bodyAlongRayStart:end:來擷取射線穿過的物體,注意整個函數只能擷取一個,如果你的射線穿過很多個物體,這時可以使用函數來擷取射線上所有的物體,在你的block裡面,你可以拿到每個物體的資訊,根據這些資訊你就可以判斷那個是你想要的得到的。還有就是可以根據名字擷取物體,整個在前面的文章有提到過。 另外還可以根據點來尋找物體,bodyAtPoint:函數會根據你提供的點,判斷點是否屬於物體,是的話就返回該物體。還有bodyAtRect:函數,在情境中設定一個rect,當有物體進入rect時,bodyAtRect就會返回該物體。 接下來是一些注意事項,虛擬整個物理系統是很消耗資源的,所以盡量讓你的遊戲的物理方面的計算減少,讓你的遊戲運行得更順暢,記住你為你的pb設定的一切東西,根據這些設定帶來的性質去實現你的遊戲邏輯或者是遊戲效果,只有知道它們到底是怎麼一回事才能把它們的作用發揮出來,關於最佳化的,得花謝時間出來研究研究,每個遊戲引擎都有一套或者幾套最佳化方案,SpriteKit肯定也會有,知道你的機器是怎麼運作的,然後再知道你的引擎是怎麼在機器上運作的,這樣會讓對最佳化你的程式有更大的協助。 也差不多這些了吧,關於物理系統的東西,很強大,有些可能需要自己用過才知道,另外還有粒子系統,也找個時間好好研究研究,寫一篇專門的關於粒子系統的筆記。 最後奉上自己的代碼,關於Simulate Physics的一個Demo,Demo只實現了部分物理特性,重力摩擦力的設定,碰撞處理,添加force等,點擊螢幕就會產生一個燕尾俠Sprite,然後可以改變重力和摩擦力觀察其在情境中的表現,觸碰邊界會做出反應,大概就這樣子,想查看其他屬性可以直接在Demo上修改,哦,我的Xcode版本是DP2的,估計沒什麼大的影響,就這樣啦