系列:用Swift作個(gè)游戲
作者:pmst(1345614869)
微博:PPPPPPMST
Flappy Bird整個(gè)項(xiàng)目臨近尾聲谓谦,要做的只是對游戲體驗(yàn)的優(yōu)化,本文先解決兩個(gè)贪婉,分別是:
- 實(shí)現(xiàn)Player 靜態(tài)時(shí)的動(dòng)畫反粥,修改早前掉落時(shí)直上直下的問題。
- Player撞擊障礙物時(shí)疲迂,給出一個(gè)shake搖晃動(dòng)畫才顿。
游戲最后實(shí)現(xiàn)的效果是這樣的:
Player動(dòng)畫實(shí)現(xiàn)
當(dāng)游戲狀態(tài)為.Tutorial的時(shí)候,Player是靜態(tài)呈現(xiàn)在教程界面上的尤蒿,為此我們想要實(shí)現(xiàn)一個(gè)動(dòng)畫郑气,讓其揮動(dòng)翅膀。而實(shí)現(xiàn)方法也很簡單腰池,動(dòng)畫由多張圖片組成尾组,指定一定時(shí)間播放完畢,具體用SKTexture實(shí)例化每一個(gè)圖片巩螃,然后放到數(shù)組中演怎;緊接著調(diào)用animateWithTextures(_:timePerFrame:)播放動(dòng)畫。
找到setupTutorial()方法避乏,再其下方新增一個(gè)方法:
func setupPlayerAnimation() {
var textures: Array<SKTexture> = []
// 我們有4張圖片
for i in 0..<4 {
textures.append(SKTexture(imageNamed: "Bird\(i)"))
}
// 4=3-1
for i in 3.stride(through: 0, by: -1) {
textures.append(SKTexture(imageNamed: "Bird\(i)"))
}
let playerAnimation = SKAction.animateWithTextures(textures, timePerFrame: 0.07)
player.runAction(SKAction.repeatActionForever(playerAnimation))
}
正如前面所說爷耀,我們采用for-in
循環(huán)實(shí)例化了4個(gè)SKTexture
實(shí)例存儲于數(shù)組中,接著調(diào)用方法播放動(dòng)畫∨钠ぃ現(xiàn)在請將該方法添加到switchToMainMenu()
以及switchToTutorial()
方法中的最后歹叮,點(diǎn)擊運(yùn)行,看看Player是否揮動(dòng)翅膀了。
在玩游戲的時(shí)候我們會(huì)注意到Player掉落時(shí)是直上直下铆帽,有些呆板咆耿,這里需要替換掉,動(dòng)畫效果如圖:
在開始實(shí)現(xiàn)Player旋轉(zhuǎn)機(jī)制前爹橱,先定義幾個(gè)常量以及變量,請?jiān)?code>GameScene()類中添加如下屬性
// 新增常量
let kMinDegrees: CGFloat = -90 // 定義Player最小角度為-90
let kMaxDegrees: CGFloat = 25 // 定義Player最大角度為25
let kAngularVelocity: CGFloat = 1000.0 // 定義角速度
// 新增變量
var playerAngularVelocity: CGFloat = 0.0 // 實(shí)時(shí)更新player的角度
var lastTouchTime: NSTimeInterval = 0 // 用戶最后一次點(diǎn)擊時(shí)間
var lastTouchY: CGFloat = 0.0 // 用戶最后一次點(diǎn)擊坐標(biāo)
請找到flapPlayer
方法萨螺,這個(gè)方法是在游戲狀態(tài)下,用戶點(diǎn)擊一次屏幕需要調(diào)用的方法(具體請?zhí)?code>touchesBegan方法)愧驱,為此我們將在這里進(jìn)行lastTouchTime
與lastTouchY
變量的更新,替換后的內(nèi)容如下:
func flapPlayer(){
// 發(fā)出一次煽動(dòng)翅膀的聲音
runAction(flapAction)
// 重新設(shè)定player的速度N考肌!
playerVelocity = CGPointMake(0, kImpulse)
//===========新增內(nèi)容============
playerAngularVelocity = kAngularVelocity.degreesToRadians()
lastTouchTime = lastUpdateTime
lastTouchY = player.position.y
//==============================
// 使得帽子下上跳動(dòng)
let moveUp = SKAction.moveByX(0, y: 12, duration: 0.15)
moveUp.timingMode = .EaseInEaseOut
let moveDown = moveUp.reversedAction()
sombrero.runAction(SKAction.sequence([moveUp,moveDown]))
}
如此每次用戶點(diǎn)擊一次屏幕组砚,就會(huì)重新計(jì)算Player應(yīng)該旋轉(zhuǎn)多少吻商。那么什么時(shí)候去真正更新Player的狀態(tài)呢?答案是update()
方法糟红。這里我們要更新的是Player的信息艾帐,請找到updatePlayer()
方法乌叶,新增如下內(nèi)容到最后:
if player.position.y < lastTouchY {
playerAngularVelocity = -kAngularVelocity.degreesToRadians()
}
// Rotate player
let angularStep = playerAngularVelocity * CGFloat(dt)
player.zRotation += angularStep
player.zRotation = min(max(player.zRotation, kMinDegrees.degreesToRadians()), kMaxDegrees.degreesToRadians())
點(diǎn)擊運(yùn)行!不出意味應(yīng)該和預(yù)期效果一樣柒爸。
Shake動(dòng)畫
先前說到Player撞擊障礙物后要有一個(gè)搖晃的動(dòng)畫以及閃爍的小鍋准浴,那樣顯得更有真實(shí)感不是嗎,這里需要調(diào)用screenShakeWithNode來實(shí)現(xiàn)揍鸟,搖晃對象是誰兄裂?自然是worldNode嘍。
由于內(nèi)容簡單阳藻,請直接定位到switchToFalling()
方法晰奖,替換早前內(nèi)容:
enum Layer: CGFloat {
case Background
case Obstacle
case Foreground
case Player
case UI
case Flash //新增一個(gè)層
}
func switchToFalling() {
gameState = .Falling
// Screen shake
let shake = SKAction.screenShakeWithNode(worldNode, amount: CGPoint(x: 0, y: 7.0), oscillations: 10, duration: 1.0)
worldNode.runAction(shake)
// Flash
let whiteNode = SKSpriteNode(color: SKColor.whiteColor(), size: size)
whiteNode.position = CGPoint(x: size.width/2, y: size.height/2)
whiteNode.zPosition = Layer.Flash.rawValue
worldNode.addChild(whiteNode)
whiteNode.runAction(SKAction.removeFromParentAfterDelay(0.01))
runAction(SKAction.sequence([
whackAction,
SKAction.waitForDuration(0.1),
fallingAction
]))
player.removeAllActions()
stopSpawning()
}
哦對了,請注釋掉GameViewController.swift中的幾行代碼腥泥,去掉所有調(diào)試信息匾南,這樣才是一個(gè)完整的游戲;
// 4.設(shè)置一些調(diào)試參數(shù)
//skView.showsFPS = true // 顯示幀數(shù)
//skView.showsNodeCount = true // 顯示當(dāng)前場景下節(jié)點(diǎn)個(gè)數(shù)
//skView.showsPhysics = true // 顯示物理體
//skView.ignoresSiblingOrder = true // 忽略節(jié)點(diǎn)添加順序
點(diǎn)擊運(yùn)行蛔外,享受你的勞動(dòng)果實(shí)吧蛆楞!
結(jié)尾
這個(gè)游戲系列文章終于連載完成,當(dāng)時(shí)可能是一時(shí)興起夹厌,最后還是堅(jiān)持下來了豹爹。文章更多是在敘述整個(gè)游戲是如何開發(fā)出來,并未在一些基礎(chǔ)知識以及實(shí)現(xiàn)原理上細(xì)說矛纹,這是之后我要補(bǔ)充的臂聋,最后謝謝大家的支持。如果覺得不錯(cuò)或南,請點(diǎn)擊喜歡并關(guān)注我孩等,同時(shí)將我的文章推薦給你的朋友。8~