昨天一天把SKAction常见的用法都实操过了一遍,基本都实现了sample code中要实现的需求。先总结如下:
- 最基本的SKAction.move(to:)和 SKAction.move(by:)用法
学过英语的都从字面上能理解到move to和move by的区别,
move to代表具体移动到哪个位置CGPoint,
而move by的话是指sprite移动的矢量offset(大概可以这样理解)。
这个太简单了,就不举例了。
- Sequence action
其实sequence action就是将一个SKAction数组里面的action依次执行。首先依次定义好单独的SKAction,然后通过以下方法将之前定义好单独的SKAction append进来
let actionSequence:[SKAction] = []
actionSequence.append(action)
然后通过以下方法依次执行各个action:
run.SKAction(SKAction.sequence(actionAction))
- 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秒
- Run-block action
如果想在sequence里面加一些打log的动作,可通过以下方法实现:
let logMessage = SKAction.run() {
print("Reached bottom!")
}
-
Reversing actions
所谓的reverse,就是把可逆的action进行反向操作,比如move(by:),如下图所示
用法如下:
let reverseMid = actionMidMove.reversed()
- Repeat & RepeatForever actions
前者是指定重复动作的次数,用法如下:
let repeatAction = SKAction.repeat(SKAction, count: Int)
后者是永远重复,用法如下:
let repeatAction = SKAction.repeatForever(SKAction)
- 在教程中间,插了一个取最小值与最大值之间随机数的算法,如下:
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
}
}
- 在该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节点的移除
- 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))
- Stop animation
用过一个key介质来控制动画的开始和停止
func startZombieAnimation() {
if zombie.action(forKey: "animation") == nil {
zombie.run(
SKAction.repeatForever(zombieAnimation),
withKey: "animation")
} }
func stopZombieAnimation() {
zombie.removeAction(forKey: "animation")
}
Scale actions
初始化scale
cat.setScale(0)
然后就是scale(to:) scale(by:)Rotate actions
rotate(byAngle: )
rotate(toAngle: )
注:swift中π的用法为Double.piGroup 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:中英文夹杂做笔记切换好累啊,从下一篇起,尝试使用英文来记了,顺便也可以练习丢了很久的英文写作。