2018-01-15 CHAPTER3 - SKAction

昨天一天把SKAction常见的用法都实操过了一遍,基本都实现了sample code中要实现的需求。先总结如下:

  1. 最基本的SKAction.move(to:)和 SKAction.move(by:)用法

学过英语的都从字面上能理解到move to和move by的区别,
move to代表具体移动到哪个位置CGPoint,
而move by的话是指sprite移动的矢量offset(大概可以这样理解)。
这个太简单了,就不举例了。

  1. Sequence action

其实sequence action就是将一个SKAction数组里面的action依次执行。首先依次定义好单独的SKAction,然后通过以下方法将之前定义好单独的SKAction append进来

let actionSequence:[SKAction] = []

actionSequence.append(action)

然后通过以下方法依次执行各个action:

run.SKAction(SKAction.sequence(actionAction))
  1. Wait-for-duration action
    在执行sequence的SKAction时,有时需要在两个动作之间有一些间歇时间,我们可以通过以下方法实现:
let action1 = SKAction()
        
let action2 = SKAction()
        
let actionWait = SKAction.wait(forDuration: 2.0)
   
zombie.run(SKAction.sequence([action1,actionWait,action2]))

然后action1和action2在执行时,中间就会停顿2秒

  1. Run-block action

如果想在sequence里面加一些打log的动作,可通过以下方法实现:

let logMessage = SKAction.run() {
  print("Reached bottom!")
}
  1. Reversing actions
    所谓的reverse,就是把可逆的action进行反向操作,比如move(by:),如下图所示


    2018-01-15 CHAPTER3 - SKAction_第1张图片
    image.png

    用法如下:

let reverseMid = actionMidMove.reversed()
  1. Repeat & RepeatForever actions
    前者是指定重复动作的次数,用法如下:
let repeatAction = SKAction.repeat(SKAction, count: Int)

后者是永远重复,用法如下:

let repeatAction = SKAction.repeatForever(SKAction)
  1. 在教程中间,插了一个取最小值与最大值之间随机数的算法,如下:
extension CGFloat {
  static func random() -> CGFloat {
//arc4random()是返回UInt32以内的一个随机数,UInt32.max是UInt32内的最大值,前者除以后者得到了一个0-1之间的随机数。
    return CGFloat(Float(arc4random()) / Float(UInt32.max))
  }
  static func random(min: CGFloat, max: CGFloat) -> CGFloat {
//首先断言最小值应该小于最大值,然后下面这个公式应该就不用赘述了
    assert(min < max)
    return CGFloat.random() * (max - min) + min
  }
}
  1. 在该section中,sample创建了很多元素,比如僵尸啊,敌人啊,猫猫狗狗之类的,他们都是用了一个spawnXXX()的方法将sprite素材加载出来、初始化位置(可能是固定的,也可能位置是随机的)、然后匹配一些初始化的动作。如果场景初始化的时候需要他们出现,则直接将spawnXXX()的方法在didMove()中进行调用即可,比如如下所示的方法:
func spawnEnemy() {
  let enemy = SKSpriteNode(imageNamed: "enemy")
  enemy.position = CGPoint(
    x: size.width + enemy.size.width/2,
    y: CGFloat.random(
      min: playableRect.minY + enemy.size.height/2,
      max: playableRect.maxY - enemy.size.height/2))
  addChild(enemy)
  let actionMove =
    SKAction.moveTo(x: -enemy.size.width/2, duration: 2.0)
  enemy.run(actionMove)
}

9.在创建敌人的时候使用了弱引用weak reference,原文的解释如下:

Note: You are using a weak reference to self here. Otherwise the closure passed to run(_ block:) will create a strong reference cycle and result in a memory leak.

暂时对内存泄漏的了解不是太多,先暂时知其然吧,后面再来看weak/strong reference的专题文章。

run(SKAction.repeatForever(
  SKAction.sequence([SKAction.run() { [weak self] in
                      self?.spawnEnemy()
                    },
                    SKAction.wait(forDuration: 2.0)])))

10.Remove-from-parent action
这个方法就是不用sprite的时候无情干掉,免得占用内存,浪费资源,用法如下:

 let actionRemove = SKAction.removeFromParent()
enemy.run(SKAction.sequence([actionMove, actionRemove]))

这个方法removeFromParent()顾名思义就是从他的父节点中移除他,不仅适用于SKAction也适用于node节点的移除

  1. Animation action
    这个就是序列帧素材的动画action,用法如下:
// 1
var textures:[SKTexture] = []
// 2
for i in 1...4 {
  textures.append(SKTexture(imageNamed: "zombie\(i)"))
}
// 3
textures.append(textures[2])
textures.append(textures[1])
// 4
zombieAnimation = SKAction.animate(with: textures,
  timePerFrame: 0.1)
//创建好动画后,就直接在sprite节点run这个动画即可
zombie.run(SKAction.repeatForever(zombieAnimation))
  1. Stop animation
    用过一个key介质来控制动画的开始和停止
func startZombieAnimation() {
  if zombie.action(forKey: "animation") == nil {
    zombie.run(
      SKAction.repeatForever(zombieAnimation),
      withKey: "animation")
} }
func stopZombieAnimation() {
  zombie.removeAction(forKey: "animation")
}
  1. Scale actions
    初始化scale
    cat.setScale(0)
    然后就是scale(to:) scale(by:)

  2. Rotate actions
    rotate(byAngle: )
    rotate(toAngle: )
    注:swift中π的用法为Double.pi

  3. Group actions
    之前学到的actions都是串行的,如果我们想让多个动作并行,应该怎么做呢?比如sample项目中的小猫,我们要他在rotate的同时,也要scale,那么group action的用法就派上用场了,方法如下:

//fullScale为scale的action, fullWiggle为rotate的action
let group = SKAction.group([fullScale, fullWiggle])

15.Collision detection
因为SK中有强大的物理引擎碰撞检测,所以我就打算跳过这个不学,所以此处略(不过还是把sample code贴进来做note),看了一下下面的sample code,大概的意思就是通过frame.intersects看不同sprite之间的交集,如果交集上了,就算是碰撞了

func zombieHit(cat: SKSpriteNode) {
  cat.removeFromParent()
}
func zombieHit(enemy: SKSpriteNode) {
  enemy.removeFromParent()
}
func checkCollisions() {
  var hitCats: [SKSpriteNode] = []
  enumerateChildNodes(withName: "cat") { node, _ in
    let cat = node as! SKSpriteNode
    if cat.frame.intersects(self.zombie.frame) {
      hitCats.append(cat)
    }
  }
  for cat in hitCats {
    zombieHit(cat: cat)
  }
  var hitEnemies: [SKSpriteNode] = []
  enumerateChildNodes(withName: "enemy") { node, _ in
    let enemy = node as! SKSpriteNode
    if node.frame.insetBy(dx: 20, dy: 20).intersects(
      self.zombie.frame) {
      hitEnemies.append(enemy)
    }
  }
  for enemy in hitEnemies {
    zombieHit(enemy: enemy)
  }

16.Sound actions
播放声音,用法如下:

  run(SKAction.playSoundFileNamed("hitCat.wav",
  waitForCompletion: false))

好了,SKAction的笔记大概就记到这了,本Section后还有三个challenges,现在就去完成它们。

PS:中英文夹杂做笔记切换好累啊,从下一篇起,尝试使用英文来记了,顺便也可以练习丢了很久的英文写作。

你可能感兴趣的:(2018-01-15 CHAPTER3 - SKAction)