在上一篇文章中,咱們已經基本實現了「方塊彈珠」小遊戲的核心邏輯。在這篇文章中,咱們主要關注在調整遊戲 UI,讓遊戲具有基本可玩的階段。node
以前咱們只是經過對小球的 physicsBody
經過調用 applyForce
設置一個初始力,讓小球在這個「初始力」的做用下進行運動,咱們已經寫死了對小球發射方向的設置,如今,咱們要對其改形成根據用戶手指在屏幕上滑動的方向進行運動。咱們先對小球進行初始化歸位,使其當用戶觸摸事件結束後再進行發射。git
class GameScene: SKScene {
private var balls = [Ball]()
// ...
private func createContent() {
// ...
for _ in 0..<5 {
// ...
balls.append(ball)
ball.position = CGPoint(x: size.width / 2, y: ground.frame.size.height + ball.frame.size.height / 2)
// ...
}
// ...
}
extension GameScene {
private func shot() {
for (index, ball) in balls.enumerated() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1 * Double(index)) {
ball.physicsBody?.applyForce(CGVector(dx: 400 + CGFloat(index) * 0.1, dy: 800))
}
}
}
}
extension GameScene {
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
shot()
}
}
複製代碼
此時,運行工程,小球將出如今地面的中心位置上,而且只有當用戶的觸摸事件結束後纔會進行「發射」。須要注意的是,咱們對每個小球都加上了時延,第二個小球發射出去的前提是第一個小球已經發射出去了,第三個小球發射出去的前提是第二個小球已經發射出去了,以此類推,咱們只須要在以前統一發射的 for
循環中增長一個時延方法來控制每個小球發射出去的時機便可。github
接着,咱們來完成當小球和地面進行接觸時,把撞擊地面的小球都進行歸位。不過這裏須要注意的是,SpriteKit 爲了提升渲染速度,推薦咱們把一段時間內不須要進行繪製的節點進行移除,注意是移除不是刪除,若是咱們不這麼作,那麼這些已經被「隱藏」起來的節點一樣會被歸入 SpriteKit 的物理計算中,從而拖慢咱們的遊戲系統的響應速度。編程
所以,咱們從新修改下底部地面的與碰撞相關的三個枚舉值。swift
struct BitMask {
// ...
static let Ground = UInt32(0x00004)
}
// ...
private func createContent() {
// ...
ground.physicsBody?.collisionBitMask = BitMask.Ball
ground.physicsBody?.categoryBitMask = BitMask.Ground
ground.physicsBody?.contactTestBitMask = BitMask.Ball
}
// ...
複製代碼
並新增小球對地面的碰撞檢測方法。markdown
extension GameScene: SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
switch contact.bodyA.categoryBitMask {
// ...
case BitMask.Ground:
checkNodeIsGround(contact.bodyB.node)
default:
break
}
switch contact.bodyB.categoryBitMask {
// ...
case BitMask.Ground:
checkNodeIsGround(contact.bodyA.node)
default:
break
}
}
}
extension GameScene {
// ...
private func checkNodeIsGround(_ node: SKNode?) {
guard let ball = node as? Ball else { return }
if (ball.physicsBody?.categoryBitMask == BitMask.Ball) {
ball.removeFromParent();
}
}
}
複製代碼
此時運行工程,發現當全部小球到了底部後都被自動從當前 Scene
中移除了,爲了提升可玩度,咱們給第一個下落的小球加上標記,告訴用戶這是你第一個觸底小球的位置,下一次再發射小球時,將會從這個位置從新出發。app
咱們須要一個定位小球變量,用於標記第一個小球到底地面的位置。async
private func checkNodeIsGround(_ node: SKNode?) {
guard let ball = node as? Ball else { return }
// NOTE: 小球 & 發射出去
if (ball.physicsBody?.categoryBitMask == BitMask.Ball && ball.isShot) {
ball.removeFromParent();
if (firstDownBall == nil || !children.contains(firstDownBall!)) {
firstDownBall = Ball(circleOfRadius: 10)
firstDownBall!.position = CGPoint(x: ball.position.x, y: ground.frame.size.height + ball.frame.size.height / 2 - 2)
addChild(firstDownBall!)
firstDownBall!.physicsBody?.isDynamic = false
}
ball.position = CGPoint(x: firstDownBall!.position.x, y: ground.frame.size.height + ball.frame.size.height / 2)
}
}
複製代碼
這裏須要注意的是,當定位小球已經建立出來時,咱們須要把後續觸底的小球二次發射的初始位置均設置爲一致值,不然二次發射時,各個小球都會從當前觸底位置直接發射,並不理會定位小球所定位的位置。ide
其中,爲了縮減每次建立 Ball
的代碼量,能夠重寫 init
方法。oop
class Ball: SKShapeNode {
var isShot = false
override init() {
super.init()
initObject()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func initObject() {
self.fillColor = .red
self.physicsBody = SKPhysicsBody(circleOfRadius: 10)
self.physicsBody?.categoryBitMask = BitMask.Ball
self.physicsBody?.contactTestBitMask = BitMask.Box | BitMask.Ground
self.physicsBody?.collisionBitMask = BitMask.Box
self.physicsBody?.usesPreciseCollisionDetection = true;
self.physicsBody?.linearDamping = 0
self.physicsBody?.restitution = 1.0
}
}
複製代碼
至此!咱們已經完成了「方塊彈珠」的所有核心邏輯。固然這是核心邏輯,換句話說,你也能夠認爲這就是一般所說的 demo,能夠拿去融資的 demo,固然,咱們的這個小遊戲是確定不行的了,只是按照同等實現的功能映射到其餘的產品層面。
因此,從下篇文章開始,咱們將結合該系列的第一篇文章裏說闡述的遊戲背景,去完善並拓展咱們的「方塊彈珠」。
咱們如今完成的內容有:
GitHub 地址: github.com/windstormey…