Swift 游戏开发之「方块弹珠」(二)

前言

在上一篇文章中,咱们已经了解了经过 UIKit 能够模拟的物理场景,相对来讲比较有限,但在完成一些简单需求的时还能稍微应付一下。git

在这篇文章中,咱们将关注 SpriteKit 的初体验,如何从零开始搭建出符合 SpriteKit 开发哲学的物理世界。程序员

初体验

工程建立

经过 Xcode 建立新工程时,咱们不须要使用 Xcode 提供的默认 Game 模版,由于咱们的这个游戏本质上是基于 app 的架构去实现的,底层驱动也是 Cocoa Touch 框架,只不过咱们须要经过 SpriteKit 中几个特殊的场景类来承载具体的游戏逻辑实现,所以,选择 signle View 模版工程便可。github

(为了偷懒,我仍是选择的 Game 模版...编程

性能监控

在进行游戏开发时,咱们最须要关心的就是「性能」自己,不少人认为如今设备硬件条件已经很是好了,能够不用太关注性能,但从我我的的角度出发,若是在出发某个场景你写的逻辑渲染耗时 500ms,而我通过优化的逻辑只须要 100ms 便可完成渲染,这应该就是程序员的追求吧~swift

在咱们新建的项目中开启性能监控很是简单。任何基于 SpriteKit 的「物体」想要添加到其中的物理世界中,咱们须要一个「容器」去承载,而这个容器在 SpriteKit 中就是 SKView缓存

所以,咱们对游戏的性能监控也回落到了对某个 SKView 的性能监控上,删除 Game 模版中的多余代码后,整理以下:markdown

import UIKit
import SpriteKit

class GameViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        if let view = self.view as! SKView? {
            
            let scene = GameScene(size: view.frame.size)
            scene.scaleMode = .aspectFill
            view.presentScene(scene)
            view.ignoresSiblingOrder = true
            view.showsFPS = true
            view.showsNodeCount = true
        
        }
    }

    override var prefersStatusBarHidden: Bool {
        return true
    }
}
复制代码

此时个人 GameScene 里调整以下,经过 SKShapeNode 建立了一个小球:架构

import SpriteKit
import GameplayKit

class GameScene: SKScene {
    
    override func didMove(to view: SKView) {
        let ball = SKShapeNode(circleOfRadius: 10)
        ball.fillColor = .red
        addChild(ball)
        ball.position = CGPoint(x: size.width / 2, y: 40)
    }
}

复制代码

SKView or SKScene

大部分初学者会对 SpriteKit 中的这两个类感到困惑,SKView 继承自 UIView,是 UIView 的子类,而 SKScene 的最终父类是 SKNodeSKNodeUIView 是两个彻底不一样的类型。app

SKScene 可能会与咱们常规的思惟不太同样,由于它不是继承自 UIView,所以也就没有所谓的 viewWillxxx 等方法,取而代之的 didMove 方法。咱们能够在这个方法中做为初始化场景的入口:框架

import SpriteKit
import GameplayKit

class GameScene: SKScene {
    var contentCreated = false
    
    override func didMove(to view: SKView) {
        if !contentCreated {
            createContent()
            contentCreated = true
        }
    }
    
    private func createContent() {
        let ball = SKShapeNode(circleOfRadius: 10)
        ball.fillColor = .red
        addChild(ball)
        ball.position = CGPoint(x: size.width / 2, y: 40)
    }
}
复制代码

须要注意的是 SpriteKit 会自动划分在具有使用 Metal 渲染引擎的设备上开启 Metal 渲染,在不具有使用的设备上使用 OpenGL ES 进行渲染。SpriteKit 至关因而一个独立与底层硬件的 framework,只须要提供渲染接口便可工做,也就是说,咱们不须要手动管理让 SpriteKit 和哪个渲染引擎进行关联,这一切都是全自动的。

在上面的代码中,我使用了一个 contentCreated 变量在 didMove 方法中进行了标记,这是由于咱们的 BGPlayScene 有可能会屡次被重复添加到某个 SKView 上,底层的渲染引擎会自动协助咱们缓存已经被渲染过的内容,这样能够节省提升必定的性能。

就像刚才我所说的同样,SpriteKit 是一个独立的 framework,在 iOS 和 macOS 平台上也会自动抹掉平台差别性,好比我对 BGPlayView 设置的背景颜色,我不须要区分当前工程运行的环境究竟是哪一个平台,由于 SpriteKit 会帮助咱们自动将 .blue 根据工程运行的平台转换为对应的 UIColor 或者 NSColor

在上文的代码中,咱们已经经过 SKShapeNode 来建立出一个「精灵」,也就是咱们的后边会用到的小球。

加入重力

若是咱们想要给一个 SKSpriteNode 具备物理特性,须要建立一个 SKPhysicsBody 对象,而后赋给节点的 physicsBody 属性。

class GameScene: SKScene {
    
    // ...

    private func createContent() {
        let ball = SKShapeNode(circleOfRadius: 10)
        ball.fillColor = .red
        addChild(ball)
        ball.position = CGPoint(x: size.width / 2, y: 400)
        ball.physicsBody = SKPhysicsBody(rectangleOf: ball.frame.size)
    }
}
复制代码

得益于 SpriteKit 框架的便捷,当咱们将一个 SKPhysicsBody 关联到 SKShapeNode 上时,被关联的精灵的运动将自动符合物理学特征。这会致使精灵出现如下状况:

  • SpriteKit 的物理引擎开始跟踪施加在这个物体上的一切外力,好比重力;
  • 每一帧上都会根据这些外力对该精灵的应该处在的位置和角度进行计算;
  • 该精灵会和其它一样关联了 SKPhysicsBody 的经历发生碰撞。

此时,咱们运行代码,会发现红色的小球直接掉出屏幕,由于咱们还未给 GameScene 中添加地面。咱们须要建立一个固定的精灵,它是静止不动的,以便其它物体可以撞在它上面。

class GameScene: SKScene {
    
    // ...

    private func createContent() {
        let ball = SKShapeNode(circleOfRadius: 10)
        ball.fillColor = .red
        addChild(ball)
        ball.position = CGPoint(x: size.width / 2, y: 400)
        ball.physicsBody = SKPhysicsBody(rectangleOf: ball.frame.size)

        let ground = SKSpriteNode(color: .gray, size: CGSize(width: size.width, height: 200))
        ground.position = CGPoint(x: size.width / 2, y: ground.size.height / 2)
        addChild(ground)
        ground.physicsBody = SKPhysicsBody(rectangleOf: ground.size)
        ground.physicsBody?.isDynamic = false
    }
}
复制代码

1111.png

此时运行工程,咱们能够看见小球下落时停在了地面上。在 SpriteKit 中有两种物体。运动物体,可以受外力影响,可以在场景中运动。静止物体,不受外力影响,固定在一个地方,运动物体可以和它发生碰撞。

在上文的代码中,咱们将 groundphysicsBody 属性 isDynamic 设置为 false,这个物体将再也不受外力的影响,同时当即中止运动和旋转(若是以前是在运动的话),可是,咱们依然能够经过改变 ground 的位置和角度或使用动做 SKAction 来改变它的位置。

修改精灵刚体形状

如今,咱们小球具有了刚体属性,但小球是圆形的,而小球的刚体外形却不是圆形的,咱们能够经过打开 SKView 的 showsPhysics 属性来进行查看。

import UIKit
import SpriteKit

class GameViewController: UIViewController {

    override func viewDidLoad() {
            // ...
            view.showsPhysics = true   
        }
    }
}

复制代码

运行工程,经过一个 for 循环来增长掉落在地面上的小球数量。能够发现,此时众多小球的刚体外形是矩形,咱们应该调整其刚体外形为圆形。

import SpriteKit
import GameplayKit

class GameScene: SKScene {
    
    private func createContent() {
        for _ in 0..<10 {
            let ball = SKShapeNode(circleOfRadius: 10)
            ball.fillColor = .red
            addChild(ball)
            ball.physicsBody = SKPhysicsBody(circleOfRadius: 10)
            ball.position = CGPoint(x: size.width / 2, y: 400)
        }
        
        // ...
    }
}
复制代码

修改精灵的速度

若是咱们想要改变小球的速度,能够经过修改小球精灵 physicsBodyvelocity 的属性,这是修改物体速度的最简单方法,这是一个 CGVector 类型的属性,以像素/秒为单位表示移动速度。

ball.physicsBody?.velocity = CGVector(dx: 200, dy: 200)
复制代码

注意,直接修改小球速度确实能达到一个很好的效果,咱们能够用这种方式设置物体的初速度,这一点很是重要!!!对后续从发射台发射小球时的帮助很是大!

建立一个墙壁

建立墙壁最有效的方式是使用「边缘碰撞体」。

class GameScene: SKScene {
    
    private func createContent() {
        // ...
        
        let wall = SKNode()
        wall.position = CGPoint(x: 0, y: 0)
        wall.physicsBody = SKPhysicsBody(edgeLoopFrom: CGRect(x: 0, y: 0, width: size.width, height: size.height))
        addChild(wall)
    }
}
复制代码

「边缘碰撞体」也是一种碰撞体,可是它仅仅只是一个线条,或者多个链接在一块儿的线条,它没有体积、没有质量,它只是静态物体。

有两种不一样的边缘碰撞体:edgeLoopedgeChainedgeChain 由链接在一块儿的多条险段的集合;前者是由起点、终点以及两点之间的链接线组成。

总结

在这篇文章中,咱们把思惟转向来 SpriteKit,并经过 SpriteKit 的一些封装好的 API 完成游戏的开局设置,搭建好了一个初步的游戏框架,下一篇文章中,咱们将继续完善这个游戏框架,往其中填充内容。

咱们如今完成的内容有:

  • 游戏讲解;
  • 熟悉 2D 编程(ing);
  • 刚体碰撞与检测(检测未完成);
  • 小球的发射与方块的消除;
  • 游戏逻辑完善。

GitHub 地址: github.com/windstormey…

相关文章
相关标签/搜索