系列:用Swift作個游戲
作者:pmst(1345614869)
微博:PPPPPPMST
友情提示:為了方便大家快速上手項目沪编,我上傳了課時的教程至github行楞,請找到Code文件夾中->L05文件夾->FlappyBird-Start下載岭接。
倘若你覺得文章還不錯,請關(guān)注我并點擊喜歡,這是對我寫文章最大的鼓勵。
游戲的雛形已經(jīng)基本實現(xiàn)得哆,呈現(xiàn)了背景,地面持續(xù)滾動哟旗,Player上下跳竄以及源源不斷的仙人掌贩据。不過細心的你也應當發(fā)現(xiàn)有以下幾個不足:
- Player可以通過不斷點擊升高到屏幕外栋操。
- 仙人掌表示不服:你丫想穿越我就穿越,當我是透明嗎饱亮?
因此本節(jié)的任務是設(shè)置場景精靈的物理體矾芙,當課時完畢,Player一旦觸碰到仙人掌就會下落近上,不能繼續(xù)游戲剔宪。
01.設(shè)置場景內(nèi)精靈的物體形狀
暫且對游戲內(nèi)容按下不表,先談談咱們真實的世界壹无,重力加速度9.8g,非透明的物體之間碰撞會發(fā)生形變葱绒。而在Sprite Kit中的物理世界,首先我需要引出Physics Shapes —— 物體形狀斗锭,就拿人來說哈街,倘若我粗略地來形容一個人的物理體,我就會給出一個x*y*z
(長寬高計算體積)的長方體拒迅,一旦外物觸碰輪廓表面骚秦,我就說兩者發(fā)生了接觸;不過若已精確角度來說璧微,形容人的物理體以其皮膚表面為輪廓勾勒出一個體積作箍,顯然這比先前的立方體來的精確太多了;當然有時候嫌麻煩前硫,指定頭部(姑且就當成一個球體吧)作為人的物理體胞得,因此除頭部外的身體都相當于是透明的,外物接觸了手屹电、腿等都不算發(fā)生接觸阶剑,只有與頭部接觸才算。
講了那么多危号,現(xiàn)在回到游戲牧愁,開始塑造真實的物理世界,首先找到didMoveToView()
方法,在最上方添加一行代碼設(shè)置場景物理世界的重力為(0,0)外莲,原因是我們打算使用自定義設(shè)置的參數(shù):
override func didMoveToView(view: SKView) {
physicsWorld.gravity = CGVector(dx: 0, dy: 0)
//... 以下為早前內(nèi)容
}
接著咱們要說physicsBody猪半,譯為物理體。我們可以設(shè)置每個節(jié)點的物理體偷线,那樣它就可以和其他同樣設(shè)置了物理體的節(jié)點發(fā)生碰撞磨确、檢測接觸等,它有三個屬性声邦,值均為UInt32類型:
- categoryBitMask: 表明當前body屬于哪個類別乏奥。
- collisionBitMask: 當前物體可以與哪些類別發(fā)生碰撞。
- contactTestBitMask:用于告知當前物體與哪些類別物理發(fā)生接觸時亥曹。
游戲中類似這種邓了,我們往往用二進制數(shù)來表示物體盏檐,譬如0b1表明是Player,0b10表明障礙物,0b100表明地面驶悟。想必編程男都不陌生吧胡野。OK,請在enum Layer:CGFLoat{}
下方新增一個結(jié)構(gòu)體用于表明分類痕鳍,注意里面均為類型屬性:
struct PhysicsCategory {
static let None: UInt32 = 0
static let Player: UInt32 = 0b1 // 1
static let Obstacle: UInt32 = 0b10 // 2
static let Ground: UInt32 = 0b100 // 4
}
對于類型屬性硫豆,調(diào)用方法形如:PhysicsCategory.None
,更多關(guān)于類型屬性笼呆,請參看官方文檔Type properties一節(jié)熊响。
接下來我們主要添加以下物理體到場景中:
- Player,這里我們將借助一個勾勒工具來繪制其物理體诗赌。
- 障礙物,同上汗茄。
- 地面,其實就是一條水平線铭若。
為啥要設(shè)置以上三個物理體呢洪碳?因為設(shè)置完物理體后,我們才能知道誰和誰發(fā)生了接觸contact叼屠,如此進行下一步計算瞳腌。至于collision
咱們是不關(guān)心的,不需要設(shè)置镜雨。
設(shè)置地面的物理體
找到setupBackground()
方法 在方法最下方添加如下內(nèi)容:
func setupBackground(){
//...
//===以上為早前內(nèi)容===
//===以下為新增內(nèi)容===
let lowerLeft = CGPoint(x: 0, y: playableStart)//地板表面的最左側(cè)一點
let lowerRight = CGPoint(x: size.width, y: playableStart) //地板表面的最右側(cè)一點
// 1
self.physicsBody = SKPhysicsBody(edgeFromPoint: lowerLeft, toPoint: lowerRight)
self.physicsBody?.categoryBitMask = PhysicsCategory.Ground
self.physicsBody?.collisionBitMask = 0
self.physicsBody?.contactTestBitMask = PhysicsCategory.Player
}
對于1中嫂侍,我們用一條平行線來實例化物理體,然后是三部曲荚坞,分別設(shè)置了其分類為Ground挑宠;不予其他任何物理發(fā)生碰撞(因為設(shè)置了0);設(shè)置了能與其發(fā)生接觸的物體有Player颓影。
設(shè)置Player的物理體
找到setupPlayer()
方法 同樣新增以下內(nèi)容到方法最后:
func setupPlayer(){
player.position = CGPointMake(size.width * 0.2, playableHeight * 0.4 + playableStart)
player.zPosition = Layer.Player.rawValue
// 注意我們將worldNode.addChild(player)移到了最下方各淀。
//=========以下為新增內(nèi)容===========
let offsetX = player.size.width * player.anchorPoint.x
let offsetY = player.size.height * player.anchorPoint.y
let path = CGPathCreateMutable()
CGPathMoveToPoint(path, nil, 17 - offsetX, 23 - offsetY)
CGPathAddLineToPoint(path, nil, 39 - offsetX, 22 - offsetY)
CGPathAddLineToPoint(path, nil, 38 - offsetX, 10 - offsetY)
CGPathAddLineToPoint(path, nil, 21 - offsetX, 0 - offsetY)
CGPathAddLineToPoint(path, nil, 4 - offsetX, 1 - offsetY)
CGPathAddLineToPoint(path, nil, 3 - offsetX, 15 - offsetY)
CGPathCloseSubpath(path)
player.physicsBody = SKPhysicsBody(polygonFromPath: path)
player.physicsBody?.categoryBitMask = PhysicsCategory.Player
player.physicsBody?.collisionBitMask = 0
player.physicsBody?.contactTestBitMask = PhysicsCategory.Obstacle | PhysicsCategory.Ground
worldNode.addChild(player)// hey 我現(xiàn)在在這里!2t空。揪阿×莆遥?}
我們通過繪制路徑來勾勒出Player的自定義物理體咆畏,別吃驚,我只不過借助了某些工具吴裤,地址在這里,ps:可能需要翻墻旧找。
設(shè)置仙人掌的物理體
同理我們只需要在產(chǎn)生仙人掌的實例方法中添加其物理體即可,請定位到createObstacle()->SKSpriteNode
方法:
func createObstacle() -> SKSpriteNode {
let sprite = SKSpriteNode(imageNamed: "Cactus")
sprite.zPosition = Layer.Obstacle.rawValue
//========以下為新增內(nèi)容=========
let offsetX = sprite.size.width * sprite.anchorPoint.x
let offsetY = sprite.size.height * sprite.anchorPoint.y
let path = CGPathCreateMutable()
CGPathMoveToPoint(path, nil, 3 - offsetX, 0 - offsetY)
CGPathAddLineToPoint(path, nil, 5 - offsetX, 309 - offsetY)
CGPathAddLineToPoint(path, nil, 16 - offsetX, 315 - offsetY)
CGPathAddLineToPoint(path, nil, 39 - offsetX, 315 - offsetY)
CGPathAddLineToPoint(path, nil, 51 - offsetX, 306 - offsetY)
CGPathAddLineToPoint(path, nil, 49 - offsetX, 1 - offsetY)
CGPathCloseSubpath(path)
sprite.physicsBody = SKPhysicsBody(polygonFromPath: path)
sprite.physicsBody?.categoryBitMask = PhysicsCategory.Obstacle
sprite.physicsBody?.collisionBitMask = 0
sprite.physicsBody?.contactTestBitMask = PhysicsCategory.Player
return sprite
}
注意到不管是哪種方式設(shè)置物理體麦牺,我們都需要設(shè)置其分類钮蛛,碰撞掩碼以及測試接觸掩碼鞭缭,不過這里我們并不需要碰撞,所以全部設(shè)為0魏颓,即None岭辣。
最后請點擊運行,你會發(fā)現(xiàn)場景中的Player甸饱、仙人掌以及地面表層都有一層輪廓沦童。沒錯!這就是其各自的物理體叹话。我們在GameViewCOntroller中通過設(shè)置了skView.showsPhysics = true
來顯示的偷遗。

下文我將更新如何處理物體與物體之間的接觸事件。 倘若覺得文章不錯驼壶,點擊喜歡或者關(guān)注我吧氏豌。.