美文网首页iOS DeveloperiOS-swiftSwift编程
SpriteKit之添加动作SKAction

SpriteKit之添加动作SKAction

作者: 狂奔的胖蜗牛 | 来源:发表于2017-02-07 13:59 被阅读1507次

    本文使用的是Xcode8.0,语言是Swift3.0。

    我们绘制好精灵后,精灵只是一张静态的图,作为游戏,我们需要精灵有动作。动作就是开发者相对场景所做的改变的对象,当创建好动作后,只需要告诉精灵运行动作,SpriteKit自动动态的改变精灵的位置等想要精灵执行的动作,直到动作完成。

    1 动作概要

    每一个动作是一个不透明的(opaque)对象,描述你相对场景、精灵等节点做的改变。动作由SKAction类表示,各种类型的动作都使用类方法来实例化,动作可以做的常见事情有:

    • 改变节点的位置和方向
    • 改变节点的尺寸或缩放属性
    • 改变节点的可视性和透明度
    • 改变精灵节点的内容,以便它可以通过一系列的纹理动起来
    • 给精灵节点着色
    • 播放简单的声音
    • 从节点树种移除一个节点
    • 自定义动作调用一个块或调用对象上的选择器

    一旦动作被创建,动作的类型就不能被改变,动作具有不可变的性质。所以,当游戏中要反复使用相同的动作时,可以创建出一个动作实例,当有节点需要执行动作时直接使用。
    动作分为瞬时和非瞬时,瞬时动作表示在一帧动画内开始并完成,非瞬时则会有一个动画效果的持续时间,动画会一帧一帧执行。

    2 单个动作使用

    2.1 创建动作

    最简单的动作创建方式:

    //飞船添加一个往下飞行的动作
        func addAction(ship: SKSpriteNode) {
            let move = SKAction.moveTo(y: 0, duration: 1)
            ship.run(move)
        }
    
    2017-02-07 10_27_23.gif

    动画有一个持续时间,duration表示持续的时间。当动作完成后,动作就会从节点中移除,无需手动移除。

    可以在任何时候运行动作,但是如果运行动作时,场景正在处理动作,新的动作可能不会立即执行。

    一个节点可以同时运行多个动作,即使那些动作在不同时间执行,场景会自动跟踪每个动作要多久完成,并且计算出动作对节点产生的影响。所以,如果设置一个大小相等,方向相反的移动动作,则节点会保持不变。

    由于动作是和场景绑定的,节点只有呈现时,节点的动作才会被处理。所以,我可以创建一个节点,并且设置好动作,等到需要的时候,再添加到场景,一旦添加到场景,则会自动执行动作。

    如下:

    //添加一个新的飞船,自带放大动作,当点击屏幕后,将该飞船添加到场景,添加后,飞船会自动执行放大动作。
    func createScaleShip() {
            scaleShip = createShip()
            let scale = SKAction.scale(to: 2, duration: 1)
            scaleShip.run(scale)
        }
    
    1.gif

    如果一个节点正在运行任何动作hasActions=true

    2.2 动作取消

    可以取消节点正在运行的动作,调用节点的removeAllActions()方法即可。如果节点正在运行动作,则动作会立马取消,已经做出的改变保持不变,但是不会执行后续的改变。

    2.3 动作完成回调

    节点的该方法run(action: SKAction, completion: () -> Void)会在动作执行结束后,执行回调,如果动作被移除,则回调不会执行。

    2.4 动作命名

    通常情况下,我们不会移除全部动作,可能只是移除掉某一个动作,这时,我们就应该给动作命名,然后通过该名称来识别动作,然后对动作执行启动、移除、更换等操作。

    run(action: SKAction, withKey: String)
    该方法运行动作,会给动作添加上名称标识,如果已经存在相同名称,则该已经存在的动作会被先移除。
    action(forKey: String)
    该方法用于确定,是否已经有一个动作正在使用该名称。
    removeAction(forKey: String)
    该方法用于移除动作。

    //我们在点击方法内,给移动飞船添加一个带名称Move的动作,每次点击后会移动到点击的位置,同时,如果飞船正在移动,则会移除上一次正在移动的动作,执行新动作。
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            
            let touch = touches.first
            let move = SKAction.move(to: touch!.location(in: self), duration: 1)
            moveShip.run(move, withKey: "Move")
    }
    
    2.gif

    3 复合动作使用

    上面是单个动作的用法,SpriteKit提供了很多的单个动作类型,但很多复杂的动作,就需要将一个一个单个动作组合到一起使用。复合动作有三种类型:

    • 序列动作:多个子动作组成一个序列,依次执行。
    • 组动作:多个子动作组成一个组,在同一时间执行。
    • 重复动作:一个子动作,重复不停的执行。

    3.1 序列动作

    //添加一个序列动作的飞船
        func addListAction() {
            let listShip = createShip()
            listShip.position = CGPoint(x: 0, y: 0)
            addChild(listShip)
            
            let move = SKAction.move(to: CGPoint(x: view!.frame.width/2, y: view!.frame.height/2), duration: 1)
            let zoom = SKAction.scale(to: 4, duration: 1)
            let wait = SKAction.wait(forDuration: 1)
            let fade = SKAction.fadeOut(withDuration: 1)
            let remove = SKAction.removeFromParent()
            let sequence = SKAction.sequence([move,zoom,wait,fade,remove])
            listShip.run(sequence)
        }
    
    • wait:延时,控制序列的定时。
    • removeNode:瞬时动作,不会花费时间来执行。
    3.gif

    3.2 组动作

    组动作表示一组同时执行的动作集合。

    //添加组动作的飞船
        func addGroupAction() {
            let groupShip = createShip()
            groupShip.position = CGPoint(x: 0, y: 0)
            addChild(groupShip)
            
            let move = SKAction.move(to: CGPoint(x: view!.frame.width, y: view!.frame.height), duration: 1)
            let rotate = SKAction.rotate(toAngle: CGFloat(M_PI*2), duration: 1)
            let group = SKAction.group([move,rotate])
            groupShip.run(group)
        }
    
    • 在组动作执行时,开始是同时开始,但是结束要等到最后一个动作执行后才会结束。
    4.gif

    3.3 重复动作

    重复动作允许循环另一个动作,所以可以被重复执行多次或者无限次。

    //添加重复动作
        func repeatAction() {
            let repeatShip = createShip()
            repeatShip.position = CGPoint(x: view!.frame.width/2, y: view!.frame.height/2+100)
            addChild(repeatShip)
            
            let fadIn = SKAction.fadeIn(withDuration: 0.5)
            let fadOut = SKAction.fadeOut(withDuration: 0.5)
            let list = SKAction.sequence([fadIn, fadOut])
            let repeatA = SKAction.repeatForever(list)
            repeatShip.run(repeatA)
        }
    
    5.gif

    重复动作,组动作,序列动作之间是可以组合使用的,就是说组动作内可以包含组动作、序列动作、重复动作的组合。

    4 动作配置

    4.1 动画计时

    默认情况下,动作的持续时间内,动画是线性变化的,但是可以调整:

    • timingMode属性可以选择动作模式,比如开始快速动作,后续减速。
    • speed属性可以改变动作执行速率,默认为1.0,如果设置成2.0,则动作执行时,速度快一倍。设置0则暂停动作。如果动作内包含别的动作组合,则会全部应用到设置的值。
    • 节点的speed属性与动作的speed属性具有相同效果。

    4.2 动作存储

    当一个动作会被多次执行时,我们只需要创建一次,然后保存该动作,动作我们可以保存到如下位置:

    /**
         An optional dictionary that can be used to store your own data in a node. Defaults to nil.
         */
        open var userData: NSMutableDictionary?
    
    • 节点userData属性
    • 父节点userData属性
    • 场景的userData属性
    • 子类的userData属性

    4.3 多个节点组合

    多个节点的动作可以同时执行,他们是互不干扰的。

    2017-02-07 13_24_07.gif

    5 结语

    通过上面的讲解,我们可以随心所欲的设置自己想要的动作,达到想要的效果,但是有一点需要注意,创建并执行动作是有成本的,如果打算在动画的每一帧都改变节点的属性,而这些变化在每一帧都需要重新计算,那么最好的办法是直接改变节点,而不使用动作。

    本文代码(game03):https://github.com/flywo/SwiftGame

    相关文章

      网友评论

        本文标题:SpriteKit之添加动作SKAction

        本文链接:https://www.haomeiwen.com/subject/azlpittx.html