Sprite Kit 基础

Sprite Kit 是iOS 7开始添加的一个新内置框架。主要用于开发2D游戏。支持内容包括 精灵、各种特效(视频、滤镜遮罩),继承了物理引擎等许多内容。

timg-2.jpeg
SpriteKit常用的类:

SKSpriteNode ----- 用于绘制精灵的纹理
SKVideoNod ----- 用于播放视频
SKLabelNode ----- 用于渲染文本
SKShapeNode ----- 用于渲染基于Core Graphics 路径的形状
SKEmitterNode ----- 用于创建和渲染粒子系统
SKCropNode ----- 用于使用遮罩来裁剪子节点
SKEffectNode ----- 用于使用遮罩来裁剪子节点

简单介绍一下SpriteKit,下面介绍开始spriteKit 中基础类的使用。

SKScene 场景

使用SKScene之前先说一下SKView,SKView 类是专门用来呈现Sprite Kit 的View,在这个类中可以渲染和管理一个SKScene,每个Scene中可以加载多个精灵,Scene可以管理精灵的行为。

导入SpriteKit 创建SKScene:

func createScene() {
        let skView = SKView.init(frame: self.view.bounds)
        if(skView.scene == nil){
            skView.showsFPS = true
            skView.showsNodeCount = true
            let scene = GameScene(size: skView.bounds.size)
            skView.presentScene(scene)
        }
        self.view.addSubview(skView)
    }

在使用时,一般会将Scene子类化,在子类中设置它的属性。在游戏中都会有多个场景,就会有场景切换,单纯的场景切换会显得非常生硬,所以SpriteKit中提供了过度动画。

crossFadeWithDuration 交叉淡入淡出过渡动画
doorsCloseHorizontalWithDuration 从左右两边水平关闭
doorsCloseVerticalWithDuration 从上下两边垂直关闭
doorsOpenHorizontalWithDuration 从中间向左右两边水平打开
doorsOpenVerticalWithDuration 从中间向左右两边垂直打开
doorwayWithDuration 从中间向两边打开,新场景从后方向屏幕靠近
fadeWithColor 淡入淡出的过渡动画,先变成指定颜色,再变成目标场景
fadeWithDuration 淡入淡出的过渡动画,先变成黑色,再变成指定颜色
flipHorizontalWithDuration 以水平中轴线垂直翻转
flipVerticalWithDuration 以垂直中中轴线水平翻转
movenInWithDirection 新场景从指定方向移入
pushWithDirection 新场景从指定方向推入
revealWithDirection 旧场景向指定方向移除,新场景在旧场景下面
使用:skView.presentScene(scene, transition:SKTransition)

SKSpriteNode 精灵

精灵分为有纹理精灵和无纹理精灵,纹理精灵是常用的,是我们把插图放到场景中的方法,像游戏中的任务角色和背景等。

使用方法,创建精灵:
let KScreenWidth = UIScreen.main.bounds.width
let KScreenHeight = UIScreen.main.bounds.height
func addNode() {
        let texture = SKTexture.init(imageNamed: "ao.jpg")  // 纹理
        let splash = SKSpriteNode.init(texture: texture)
//        let splash = SKSpriteNode.init(imageNamed: "ao.jpg")
        splash.size = CGSize.init(width: KScreenWidth, height: KScreenHeight)
        splash.position = CGPoint.init(x: KScreenWidth/2, y: KScreenHeight/2)
        // 设置锚点
//        splash.anchorPoint = CGPoint.init(x: 0.0, y: 0.0)
        // 精灵着色
        splash.color = SKColor.green
        splash.colorBlendFactor = 0.3   // 颜色混合因子0~1
        splash.setScale(2)  // 缩放 放大两倍
//        splash.xScale = 2   // 单独缩放x
        splash.zRotation = CGFloat(Double.pi)/2  // 旋转
        splash.alpha = 0.5
        splash.isHidden = false
        self.addChild(splash)
        
//        splash.removeFromParent()
//        removeAllChildren()
    }

其中的锚点可以根据下面的图理解

B2588466-9F89-4209-9EB8-BC732A1F1935.png

可以理解为将图片订在墙上,锚点相当于钉子钉的位置,默认是(0.5, 0.5)。

删除精灵
splash.removeFromParent()
removeAllChildren()
SKLightNode

为了让精灵更具有真实感,SpriteKit提供了SKLightNode作为光源节点。

SKLightNode 可以定义光源的颜色、阴影和衰减程度。继承于SKNode。为了在SKSpriteNode的效果更加逼真,SKSpriteNode 提供了normalTexture 属性,用来储存原贴图的发现贴图NormalMap。

SKLightNode常用属性:

enabled 光源的开关
ambientColor 环境颜色,默认黑色(无)
lightColor 光的颜色,默认白色
shadowColor 被精灵物体遮挡产生的阴影颜色
falloff 光源强度的衰减比率
categoryBitMask 光的种类,32位整数。SKSpriteNode的 lightingBItMask(被何种光照亮)、shadowedBitMask(被何种光产生阴影)、shadowCastBitMask(遮挡何种光线并产生阴影) 存储着光的种类。

SKLightNode使用:
func addLightNode() {
        backgroundColor = SKColor.black
        // 创建精灵
        let spriNode = SKSpriteNode.init(color: UIColor.white, size: CGSize.init(width: 300, height: 300))
        spriNode.position = CGPoint.init(x: KScreenWidth/2, y: KScreenHeight/2)
        self.addChild(spriNode)
        // 添加光源
        let nodeNormMap = spriNode.texture?.generatingNormalMap()
        spriNode.normalTexture = nodeNormMap
        spriNode.lightingBitMask = 1    // 被何种光照
        spriNode.name = "SprNOde"
        let lightSprite = SKLightNode() //实例化光源
        lightSprite.position = CGPoint.init(x: KScreenWidth/2 + 60, y: KScreenHeight/2) // 关照位置
        lightSprite.name = "LightSptite"
        lightSprite.categoryBitMask = 1 // 光的种类
        self.addChild(lightSprite)
    }

运行结果截图:

C9C46872B5421D98C7940C17C21B4D3C.png

SKAction 动作

SKAction是游戏中很重要的一部分,内容很多啊,会写的比较长。首先预览下内容,动作的分类:动作属性、移动动作、序列动作、重复动作、延迟动作、缩放动作、旋转动作、调整尺寸的动作、组合动作、改变透明度的动作、改变颜色的动作、以动画的形式改变纹理的动作、路径动作、反向运动、速度动作、显示或隐藏动作、块动作、自定义动作、删除动作。

创建动作方法
let spriteMoveUp = SKAction.moveBy(x: 300, y: 0, duration: 1.0) // X方向向右移100
1、移动动作

包括以点的方式移动和 以偏移量的方式移动

func nodeAction() {
        let texture = SKTexture.init(imageNamed: "field")  // 纹理
        let background = SKSpriteNode.init(texture: texture)
        background.position = CGPoint.init(x: KScreenWidth/2, y: KScreenHeight/2)
        background.size = CGSize.init(width: KScreenWidth, height: KScreenHeight)
        self.addChild(background)

        let ball = SKSpriteNode.init(imageNamed: "soccer")
        ball.position = CGPoint.init(x: KScreenWidth/2, y: KScreenHeight/2)
        ball.setScale(1.05) // 缩放
        ball.size = CGSize.init(width: 50, height: 50)
        self.addChild(ball)

        // 动作   以点的方式移动
        let spriteMoveUp = SKAction.moveBy(x: 300, y: 0, duration: 1.0) // X方向向右移100
        // ball.run(spriteMoveUp)
        // 动作  以偏移量的方式移动
        let negDelta = CGVector.init(dx: -300, dy: -100)
        let actionMove = SKAction.move(by: negDelta, duration: 2)
        ball.run(actionMove)
    }
2、动作属性

speed 速度
duration 时间
timingMode 曲线方式

曲线方式包括四种:

SKActionTimingLinear 平均分布
SKActionTimingEaseIn 开始较慢,再加快
SKActionTimingEaseOut 开始快,再变慢
SKActionTimingEaseInEaseOut 开始慢,再加快至中间,再变慢
let spriteMoveUp = SKAction.moveBy(x: 300, y: 0, duration: 1.0)

spriteMoveUp.speed = 1.3  // 速度
spriteMoveUp.timingMode = SKActionTimingMode.easeInEaseOut  // 曲线方式
print(ball.speed)  // 获取并输出动作所需时间
3、 序列动作

使用sequence() 方法实现。以上面移动动作为例,将spriteMoveUp、actionMove作为一个序列动作然后执行。

let sequence = SKAction.sequence([spriteMoveUp, actionMove])
ball.run(sequence)
4、重复动作

重复动作分为无限重复和有次数的重复,使用方法:

let sequence = SKAction.sequence([spriteMoveUp, actionMove])
//        let runForever = SKAction.repeatForever(sequence)  // 无限重复
let runRepeat = SKAction.repeat(sequence, count: 2)  // 重复两次(执行两次)
ball.run(runRepeat)
5、延迟动作

使用waitForDuration()方法实现。以序列动作为例,在序列中插入一个延迟动作。

let wait = SKAction.wait(forDuration: 1.0)   // 延迟动作(延迟1s)
let sequence = SKAction.sequence([spriteMoveUp, wait, actionMove])
ball.run(sequence)
6、缩放动作

缩放动作分为:以缩放倍数缩放(scaleTo()、scaleXTo())、以增量值缩放(scaleBy()、scaleXBy())

func scaleAction() {
        let ball = SKSpriteNode.init(imageNamed: "soccer")
        ball.position = CGPoint.init(x: KScreenWidth/2, y: KScreenHeight/2)
        ball.size = CGSize.init(width: 50, height: 50)  // 设置初始大)
        self.addChild(ball)
        
        // 以缩放倍数缩放
        let shrink = SKAction.scale(to: 0.1, duration: 0.5) // 缩小
        let zoom = SKAction.scale(to: 10, duration: 2) // 放大
        // 对宽高进行缩放
//        let shrink = SKAction.scaleX(to: 0.1, duration: 3) // 缩小
//        let zoom = SKAction.scale(to: 1, duration: 3) // 放大
        let sequence = SKAction.sequence([shrink, zoom])
        let runForever = SKAction.repeatForever(sequence)
//        ball.run(runForever)
        
        // 以增量值缩放
        let scaleZoom = SKAction.scale(by: 1, duration: 0.2)    // 放大
        let scaleShrink = SKAction.scale(by: -1, duration: 0.2) // 缩小
//        let scaleZoom = SKAction.scaleX(by: 1, duration: 0.2)    // 放大
//        let scaleShrink = SKAction.scaleX(by: -1, duration: 0.2) // 缩小
        let scaleSequence = SKAction.sequence([scaleZoom, scaleShrink])
        let scaleForever = SKAction.repeatForever(scaleSequence)
        ball.run(scaleForever)
    }
7、旋转动作

使用rotateByAngle()方法实现。

   let rotate = SKAction.rotate(byAngle: CGFloat(Double.pi)*2, duration: 3)
   ball.run(rotate)
8、调整尺寸的动作

调整尺寸的动作包括:以目标值调整尺寸、以增量调整尺寸

let resizeWidth = SKAction.resize(toWidth: 100, height: 100, duration: 2)  // 以目标值调整尺寸
ball.run(resizeWidth)
let resize = SKAction.resize(byWidth: 200, height: 200, duration: 2)  // 以增量调整尺寸
ball.run(resize)
9、组合动作

就是同时对两个及以上的动作进行执行。使用group()方法实现。

func groupAction() {
        backgroundColor = SKColor.black
        
        let star = SKSpriteNode.init(imageNamed: "star.jpg")
        star.position = CGPoint.init(x: KScreenWidth/2, y: KScreenHeight/2)
        star.size = CGSize.init(width: 300, height: 300)
        self.addChild(star)
        
        let zoom = SKAction.scale(to: 1, duration: 0.2)
        let shrink = SKAction.scale(to: 0.1, duration: 0.2)
        let sequence = SKAction.sequence([zoom, shrink])
        let rotate = SKAction.rotate(byAngle: CGFloat(Double.pi)*2, duration: 1.5)
        
        let group = SKAction.group([sequence, rotate])  // 组合动画
        let runForever = SKAction.repeatForever(group)
        star.run(runForever)
    }
10、改变透明度的动作

有两种,分别是不需要指定alpha值和指定alpha值(以目标值指定、以增量值指定)。

// 不需要指定alpha值
let fadeOut = SKAction.fadeOut(withDuration: 0.25)
let fadeIn = SKAction.fadeIn(withDuration: 0.25)
// 指定alpha值
let fadeOutAlpha = SKAction.fadeAlpha(to: 0, duration: 0.2) // 以目标值指定
let fadeInAlpha = SKAction.fadeAlpha(to: 1, duration: 0.2)
let hide = SKAction.fadeAlpha(by: -1, duration: 0.2)    // 以增量值指定
let show = SKAction.fadeAlpha(by: 1, duration: 0.2)
11、改变颜色的动作

分为两种:改变混合因子、改变颜色和混合因子。

// 改变混合因子
let color1 = SKAction.colorize(withColorBlendFactor: 0.8, duration: 0.5)
let color2 = SKAction.colorize(withColorBlendFactor: 0.0, duration: 0.5)
// 改变颜色和混合因子
let color = SKAction.colorize(with: SKColor.green, colorBlendFactor: 0.7, duration: 2)
12、以动画的形式改变纹理的动作

使用animateWithTextures()方法实现

func animateTexturesAction() {
        // 加载动画纹理
        let textures = NSMutableArray.init()
        for index in 4...13 {
            let texture = SKTexture.init(imageNamed: "frame-\(index)")
            textures.add(texture)
        }
        
        let playerSprite = SKSpriteNode.init(texture: textures.firstObject as? SKTexture)
        playerSprite.position = CGPoint.init(x: KScreenWidth/2, y: KScreenHeight/2)
        self.addChild(playerSprite)
        
        let runRightAction = SKAction.animate(with: textures as! [SKTexture], timePerFrame: 0.1)
        let runForever = SKAction.repeatForever(runRightAction)
        playerSprite.run(runForever)
    }
13、路径动作

使用followPath()方法实现。方法中的属性:

path 用来指定一个CGPathRef路径
offset 用来指定路径中的点是否为相对坐标,true(相对坐标),false(绝对坐标)
orient 指定Z轴在旋转时属性是否改变
sec 动作时间

func pathAction() {
        let background = SKSpriteNode.init(imageNamed: "field")
        background.position = CGPoint.init(x: KScreenWidth/2, y: KScreenHeight/2)
        background.size = CGSize.init(width: KScreenWidth, height: KScreenHeight)
        self.addChild(background)
        
        let ball = SKSpriteNode.init(imageNamed: "soccer")
        ball.position = CGPoint.init(x: KScreenWidth/2, y: KScreenHeight/2)
        ball.size = CGSize.init(width: 50, height: 50)
        self.addChild(ball)
        
        let circle = CGPath.init(roundedRect: CGRect.init(x: 200, y:100 , width: 200, height: 200), cornerWidth: 100, cornerHeight: 100, transform: nil)
        let follow = SKAction.follow(circle, asOffset: false, orientToPath: false, duration: 5)
        let runRepeat = SKAction.repeatForever(follow)
        // 反向运动
        let reverse = runRepeat.reversed()
        // 速度动作
        let speedaction = SKAction.speed(to: 15, duration: 1.0)
        let group = SKAction.group([speedaction, reverse])
        ball.run(group)
        
    }
14、反向运动
let spriteMoveUp = SKAction.moveBy(x: 300, y: 0, duration: 1.0)
let reverse = spriteMoveUp.reversed()  // 反向运动
15、速度动作

使用 speedTo() 或者 speedBy() 实现。

let speedaction = SKAction.speed(to: 5, duration: 1.0)
16、显示或隐藏动作

使用 hide() 和unhide()方法实现

let hide = SKAction.hide()      // 显示动作
let show = SKAction.unhide()    // 隐藏动作
17、块动作

使用runBlock()方法实现,异步调用动作的方法,可指定执行的线程。

func blockAction() {
        let ball = SKSpriteNode.init(imageNamed: "soccer")
        ball.position = CGPoint.init(x: KScreenWidth/2, y: KScreenHeight/2)
        ball.size = CGSize.init(width: 80, height: 80)
        self.addChild(ball)

        // 创建块动作
        let blockAction = SKAction.run { 
            let rotate = SKAction.rotate(byAngle: CGFloat(Double.pi*2), duration: 3)
            let runRepeat = SKAction.repeatForever(rotate)
            ball.run(runRepeat)
        }
        ball.run(blockAction)
    }

设置动作执行的线程:

SKAction.run({
}, queue: DispatchQueue)
18、自定义动作

使用customActionWithDuration()实现

func customAction() {
        let background = SKSpriteNode.init(imageNamed: "field")
        background.position = CGPoint.init(x: KScreenWidth/2, y: KScreenHeight/2)
        background.size = CGSize.init(width: KScreenWidth, height: KScreenHeight)
        self.addChild(background)
        
        let ball = SKSpriteNode.init(imageNamed: "soccer")
        ball.position = CGPoint.init(x: KScreenWidth/2, y: KScreenHeight/2)
        ball.size = CGSize.init(width: 50, height: 50)
        self.addChild(ball)
        
        let customAction = SKAction.customAction(withDuration: 2) { (node: SKNode, elapsedTime: CGFloat) in
            let fraction = CGFloat(elapsedTime) / 2.0
            let yOff = 100 * 4 * fraction * (1 - fraction)
            node.position = CGPoint.init(x: node.position.x, y: KScreenHeight/2 + CGFloat(yOff))
        }
        let runRepeat = SKAction.repeatForever(customAction)
        ball.run(runRepeat)
        
        // MARK: 删除动作
//        let remove = SKAction.removeFromParent()
    }
19、删除动作

使用removeFromParent()方法实现。

let remove = SKAction.removeFromParent()


用户交互

1、触摸

触摸是最常见的用户交互之一。分为单拍、多拍触摸(touchesBegan),移动触摸(touchesMoved),结束和取消触摸(touchesEnded)。

在触摸中常用的事件处理:单拍和多拍触摸在一个方法中监听,所以需要进行区分

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let mytouches = touches as NSSet
        let touch: AnyObject? = mytouches.anyObject() as AnyObject
        if(touch?.tapCount == 1){
            // 单拍
        }
        if(touch?.tapCount == 2) {
            // 多拍
        }
    }

在移动触摸中获取当前点击的坐标位置

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        let mytouchs = touches as NSSet
        let touch = mytouchs.anyObject() as! UITouch
        let touchLocation = touch.location(in: self)
        print(touchLocation)
    }
2、手势

常见的手势识别器:

名称 功能
UITapGestureRecognizer 轻拍
UIPinchGestureRecognizer
UISwipeGestureRecognizer 滑动
UIRotationGestureRecognizer 旋转
UIPanGestureRecognizer 移动
UILongPressGestureRecognizer 长按
  • 轻拍
    分两步,添加手势、实现手势方法。
    func addGestureRecognizer() {
        let tapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(self.handleTap))
        self.view?.addGestureRecognizer(tapGestureRecognizer)
    }

    func handleTap() {
        backgroundColor = UIColor.yellow
    }

  • 捏 其实就是通过两个手指的张合,实现图片的放大和缩小。
    func addGestureRecognizer() {
        let pinchGestureRecognizer = UIPinchGestureRecognizer.init(target: self, action: #selector(handlepin(recognizer:)))
        self.view?.addGestureRecognizer(pinchGestureRecognizer)
    }

    var lastScale: CGFloat = 0.0
    func handlepin(recognizer: UIPinchGestureRecognizer) {
        self.view?.bringSubview(toFront: view!)
        // 复原
        if(recognizer.state == UIGestureRecognizerState.ended){
            lastScale = 1.0
            return
        }
        
        let scale: CGFloat = 1.0 - (lastScale - recognizer.scale)
        let current: CGAffineTransform = recognizer.view!.transform
        let newaa = current.scaledBy(x: scale, y: scale)
        recognizer.view?.transform = newaa
        lastScale = recognizer.scale
    }
  • 旋转
    通过旋转手势,两个手指旋转控制图片旋转。
    func addGestureRecognizer() {
        let rotationGestureRecognizer = UIRotationGestureRecognizer.init(target: self, action: #selector(handRotation(recognizer:)))
        self.view?.addGestureRecognizer(rotationGestureRecognizer)
    }
    // 旋转
    func handRotation(recognizer: UIRotationGestureRecognizer) {
        let rataion:CGFloat = recognizer.rotation
        sprite.zRotation = -rataion
    }
  • 移动
    和触摸中的touchesMoved类似效果,通过移动手势,可以使图片跟着手指的移动手势移动。
    func addGestureRecognizer() {
        let panGestureRecognizer = UIPanGestureRecognizer.init(target: self, action: #selector(handPan(recognizer:)))
        self.view?.addGestureRecognizer(panGestureRecognizer)
    }
    // 移动
    func handPan(recognizer: UIPanGestureRecognizer) {
        let point = recognizer.location(in: self.view)
        let pointY = (self.view?.frame.size.height)! - point.y
        sprite.position = CGPoint.init(x: point.x, y: pointY)
    }
  • 滑动
    滑动手势:UISwipeGestureRecognizer。在使用时可以使用direction属性设置滑动手势滑动的方向。
    func addGestureRecognizer() {
        let swipeLeftGestureRecognizer = UISwipeGestureRecognizer.init(target: self, action: #selector(handSwipeLeft))     // 向左滑动
        swipeLeftGestureRecognizer.direction = UISwipeGestureRecognizerDirection.left;
        self.view?.addGestureRecognizer(swipeLeftGestureRecognizer)
        
        let swipeRightGestureRecognizer = UISwipeGestureRecognizer.init(target: self, action: #selector(handSwipeRight))     // 向右滑动
        swipeRightGestureRecognizer.direction = UISwipeGestureRecognizerDirection.right;
        self.view?.addGestureRecognizer(swipeRightGestureRecognizer)
    }
    // 滑动
    func handSwipeLeft() {
        let actionMove = SKAction.move(to: CGPoint.init(x: sprite.size.width/2, y: KScreenHeight/2), duration: 1)
        sprite.run(actionMove)
    }
    func handSwipeRight() {
        let actionMove = SKAction.move(to: CGPoint.init(x: KScreenWidth - sprite.size.width/2, y: KScreenHeight/2), duration: 1)
        sprite.run(actionMove)
    }
  • 长按
    func addGestureRecognizer() {
        let longPressGestureRecognizer = UILongPressGestureRecognizer.init(target: self, action: #selector(handLongPress))
        self.view?.addGestureRecognizer(longPressGestureRecognizer)
    }
    // 长按
    func handLongPress() {
        backgroundColor = UIColor.gray
    }
3、重力感应

重力感应事件又被称为加速计事件,属于运动事件的一种。运动时间产生可以通过:倾斜、摇动设备等。这些运动事件的检测基于设设备的加速计或陀螺仪。需要访问这些数据,需要通过coreMotion这个框架,coreMotion有提供访问加速计和陀螺仪数据的接口。

导入coerMotion框架,实例化CMMotionManager()对象。
在使用重力感应前,需要判断设备是否支持重力感应:

import CoreMotion
var mManger:CMMotionManager = CMMotionManager()
override func didMove(to view: SKView) {
    if(!mManger.isAccelerometerAvailable){
        // 重力感应不可用
    }
}

在判断设备支持重力感应后。我们来做一个小功能,使用重力感应来控制精灵对象的移动。

步骤:

  • 1、创建场景。
  • 2、判断重力感应是否可用。
  • 3、添加物理引擎,添加物理体。
  • 4、获取重力感应accelerometer的数据。
  • 5、根据数据移动精灵。

代码:

import Foundation
import UIKit
import SpriteKit
import CoreMotion

var mManger:CMMotionManager = CMMotionManager()
class GravityScene: SKScene {
    
    var gravityBall = SKSpriteNode.init(imageNamed: "soccer")
    
    override func didMove(to view: SKView) {
        // 创建两个精灵对象,场景和移动体
        let background = SKSpriteNode.init(imageNamed: "field")
        background.position = CGPoint.init(x: KScreenWidth/2, y: KScreenHeight/2)
        background.size = CGSize.init(width: KScreenHeight, height: KScreenWidth)
        self.addChild(background)
        
        gravityBall.position = CGPoint.init(x: KScreenWidth/2, y: KScreenHeight/2)
        gravityBall.size = CGSize.init(width: 50, height: 50)
        self.addChild(gravityBall)
        
        // 判断重力感应是否可用
        if(!mManger.isAccelerometerAvailable){
            // 重力感应不可用
            let alert:UIAlertController = UIAlertController.init(title: "提示", message: "重力感应不可用", preferredStyle: UIAlertControllerStyle.alert)
            let cancel:UIAlertAction = UIAlertAction.init(title: "取消", style: UIAlertActionStyle.cancel, handler: nil)
            alert.addAction(cancel)
            
            self.getCurrentVC()?.present(alert, animated: true, completion: {
            })
            
        }else {
            mManger.startAccelerometerUpdates()
        }
        
        
        // 添加物理引擎,添加物理体
        self.physicsBody = SKPhysicsBody.init(edgeLoopFrom: self.frame)
        //创建矩形物理体
        self.gravityBall.physicsBody = SKPhysicsBody.init(rectangleOf: self.gravityBall.frame.size)
        self.gravityBall.physicsBody?.isDynamic = true  // 能承受碰撞和其他外力作用
        self.gravityBall.physicsBody?.affectedByGravity = false // 不承受重力影响
        self.gravityBall.physicsBody?.mass = 0.2    // 给物体任意质量,使移动就会显得自然
        
    }
    
    // 飞创移动的方法
    func ballUpdate() {
        let data:CMAccelerometerData? = mManger.accelerometerData
        // 获取accelerometer数据
        var value:Double? = data?.acceleration.x
        if(value == nil){
            value = 0
        }
        
        // 判断设备倾斜方向
        if(fabs(value!) > 0.2){
            let fvector = CGVector.init(dx: 40*CGFloat(value!), dy: 0)
            self.gravityBall.physicsBody?.applyForce(fvector)
        }
    }
    
    override func update(_ currentTime: TimeInterval) {
        ballUpdate()
    }
    
    // 当前控制器响应者
    func getCurrentVC()->UIViewController?{
        var result:UIViewController?
        var window = UIApplication.shared.keyWindow
        if window?.windowLevel != UIWindowLevelNormal{
            let windows = UIApplication.shared.windows
            for tmpWin in windows{
                if tmpWin.windowLevel == UIWindowLevelNormal{
                    window = tmpWin
                    break
                }
            }
        }
        
        let fromView = window?.subviews[0]
        if let nextRespnder = fromView?.next{
            if nextRespnder.isKind(of: UIViewController.classForCoder()){
                result = nextRespnder as? UIViewController
            }else{
                result = window?.rootViewController
            }
        }
        return result
    }
}

将此场景添加到控制器的视图上显示即可。

    // 创建重力感应场景
    func createGravityScene() {
        let skView = SKView.init(frame: self.view.bounds)
        if(skView.scene == nil){
            skView.showsFPS = true
            skView.showsNodeCount = true
            let scene = GravityScene(size: skView.bounds.size)
            skView.presentScene(scene)
        }
        self.view.addSubview(skView)
    }

最后完成的效果是,场景中的ball精灵会根据手机的左右倾斜方向来移动。

最后奉上前面所有内容的demo Sprite Kit 基础

参考:《iOS游戏框架 Sprite Kit技术详解》

基础篇就这些了。接下来就是进阶部分了:Sprite Kit 进阶(音频、视频、粒子系统)。 还有最后的高级:Sprite Kit 高级(物理引擎、瓦片地图)。会不定期更新,有兴趣的可以关注下哦。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • Translate form http://www.raywenderlich.com/84434/sprite-...
    CenturyGuo阅读 2,186评论 1 11
  • 〔风聊000〕~老司机约会:大猫,神一样存在朋友圈的兄弟,因他在业余全力打造Suncat全系列品牌,喜欢叫自己大猫...
    风上阅读 300评论 0 1
  • 破天荒 今天居然6:30 就醒了 自然醒真舒服 天天这样就好了 早晨傻练了一套 当活雷锋帮人家发东西过瘾 尝试蒸了...
    了了妈2017阅读 149评论 5 4
  • 爸爸,小时候,你从没把我当宝,我也从来不敢跟你撒娇。你那张威严的脸,总是让我夺路而逃。 在战战兢兢中,我熬过了青春...
    言行合一小号阅读 348评论 0 6
  • 做好任何一件事,都蕴藏着极大的学问,开车也不例外。 在我的认识里,坐一辆车舒服与否和车的配置有关,和开车的人也有关...
    如我所意阅读 298评论 0 1