Ale*_*ano 8 sprite-kit texture-atlas skphysicsbody swift
我使用"Hello World"Sprite-kit模板做了一个小测试项目,其中有一个由这些框架组成的地图集动画:

-
所以我用一个工具来分隔单帧,我做了一个atlasc文件夹,所以代码应该是:
import SpriteKit
class GameScene: SKScene {
var knight: SKSpriteNode!
var textures : [SKTexture] = [SKTexture]()
override func didMove(to view: SKView) {
self.physicsWorld.gravity = CGVector(dx:0, dy:-2)
let plist = "knight.plist"
let genericAtlas = SKTextureAtlas(named:plist)
let filename : String! = NSURL(fileURLWithPath: plist).deletingPathExtension!.lastPathComponent
for i in 0 ..< genericAtlas.textureNames.count
{
let textureName = (String(format:"%@%02d",filename,i))
textures.append(genericAtlas.textureNamed(textureName))
}
if textures.count>0 {
knight = SKSpriteNode(texture:textures.first)
knight.zPosition = 2
addChild(knight)
knight.position = CGPoint(x:self.frame.midX,y:self.frame.midY)
}
//
self.setPhysics()
let animation = SKAction.animate(with: textures, timePerFrame: 0.15, resize: true, restore: false)
knight.run(animation, withKey:"knight")
}
func setPhysics() {
knight.physicsBody = SKPhysicsBody.init(rectangleOf: knight.size)
knight.physicsBody?.isDynamic = false
}
}
Run Code Online (Sandbox Code Playgroud)
该输出是:
正如你所看到的,physicsBody是STATIC,不尊重动画:这是正常的,因为在动画期间纹理改变尺寸/尺寸,我们不会改变physicsBody动作期间保持不变的尺寸.
根据消息来源,没有方法,在期间SKAction.animate,允许更改physicsBody.
虽然我们使用:
/**
Creates an compound body that is the union of the bodies used to create it.
*/
public /*not inherited*/ init(bodies: [SKPhysicsBody])
Run Code Online (Sandbox Code Playgroud)
为我们动画的每一帧创建实体,这些实体在场景中保持一致,创造了一个丑陋奇怪的情况,如下图:
因此,正确的方法应该是在动画期间拦截帧并physicsBody在运行中进行更改.我们也可以使用update()方法SKScene,但我正在考虑扩展.
我的思想是将结合animation有动作SKAction.group,使该检查第一动作的执行匹配的电流,截距帧另一自定义动作knight.texture与textures阵列和改变physicsBody启动外部的方法,在这种情况下setPhysicsBody.
然后,我写了这个:
extension SKAction {
class func animateWithDynamicPhysicsBody(animate:SKAction, key:String, textures:[SKTexture], duration: TimeInterval, launchMethod: @escaping ()->()) ->SKAction {
let interceptor = SKAction.customAction(withDuration: duration) { node, _ in
if node is SKSpriteNode {
let n = node as! SKSpriteNode
guard n.action(forKey: key) != nil else { return }
if textures.contains(n.texture!) {
let frameNum = textures.index(of: n.texture!)
print("frame number: \(frameNum)")
// Launch a method to change physicBody or do other things with frameNum
launchMethod()
}
}
}
return SKAction.group([animate,interceptor])
}
}
Run Code Online (Sandbox Code Playgroud)
添加此扩展,我们更改代码的动画部分:
//
self.setPhysics()
let animation = SKAction.animate(with: textures, timePerFrame: 0.15, resize: true, restore: false)
let interceptor = SKAction.animateWithDynamicPhysicsBody(animate: animation, key: "knight", textures: textures, duration: 60.0, launchMethod: self.setPhysics)
knight.run(interceptor,withKey:"knight")
}
func setPhysics() {
knight.physicsBody = SKPhysicsBody.init(rectangleOf: knight.size)
knight.physicsBody?.isDynamic = false
}
Run Code Online (Sandbox Code Playgroud)
这最终起作用,输出是:
你知道更好的方法,或者更优雅的方法来获得这个结果吗?
isDynamic = false。*以下更新至 2017 年
经过几天的测试,我的答案、Knight0fDragon 答案和其他一些想法来自其他 SO 答案(困惑和旋风建议..)我发现存在一个新问题:physicsBody无法将它们的属性充分传播到其他机构,并且正确。换句话说,将一个主体的所有属性复制到另一个主体是不够的。这是因为 Apple 限制了对原始类的某些方法和属性的访问physicsBody。physicsBody.applyImpulse当您充分传播 时,可能会发生以下情况velocity:尚未正确考虑重力。这真的很可怕……而且显然这是错误的。
所以主要目标是:不要改变physicBody重新创建它。换句话说,不要重新创建它!
我认为,您可以创建一个幽灵精灵来代替主精灵来完成这项工作,而不是创建子精灵,并且主精灵利用幽灵的变化,但只有主精灵有物理主体。
这似乎有效!
import SpriteKit
class GameScene: SKScene {
var knight: SKSpriteNode!
private var ghostKnight:SKSpriteNode!
var textures : [SKTexture] = [SKTexture]()
var lastKnightTexture : SKTexture!
override func didMove(to view: SKView) {
self.physicsWorld.gravity = CGVector.zero
let plist = "knight.plist"
let genericAtlas = SKTextureAtlas(named:plist)
let filename : String! = NSURL(fileURLWithPath: plist).deletingPathExtension!.lastPathComponent
for i in 0 ..< genericAtlas.textureNames.count
{
let textureName = (String(format:"%@%02d",filename,i))
textures.append(genericAtlas.textureNamed(textureName))
}
if textures.count>0 {
// Prepare the ghost
ghostKnight = SKSpriteNode(texture:textures.first)
addChild(ghostKnight)
ghostKnight.alpha = 0.2
ghostKnight.position = CGPoint(x:self.frame.midX,y:100)
lastKnightTexture = ghostKnight.texture
// Prepare my sprite
knight = SKSpriteNode(texture:textures.first,size:CGSize(width:1,height:1))
knight.zPosition = 2
addChild(knight)
knight.position = CGPoint(x:self.frame.midX,y:self.frame.midY)
}
let ghostAnimation = SKAction.repeatForever(SKAction.animate(with: textures, timePerFrame: 0.15, resize: true, restore: false))
ghostKnight.run(ghostAnimation,withKey:"ghostAnimation")
let animation = SKAction.repeatForever(SKAction.animate(with: textures, timePerFrame: 0.15, resize: false, restore: false))
knight.run(animation,withKey:"knight")
}
override func didEvaluateActions() {
if ghostKnight.action(forKey: "ghostAnimation") != nil {
if ghostKnight.texture != lastKnightTexture {
setPhysics()
lastKnightTexture = ghostKnight.texture
}
}
}
func setPhysics() {
if let _ = knight.physicsBody{
knight.xScale = ghostKnight.frame.size.width
knight.yScale = ghostKnight.frame.size.height
} else {
knight.physicsBody = SKPhysicsBody.init(rectangleOf: knight.frame.size)
knight.physicsBody?.isDynamic = true
knight.physicsBody?.allowsRotation = false
knight.physicsBody?.affectedByGravity = true
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
显然,您可以将其设置为 0.0 来隐藏alpha,并根据需要重新定位幽灵,使其消失。
2017年更新:
经过几个小时的测试,我尝试改进代码,最后我成功地删除了幽灵精灵,但是,为了正常工作,有一个条件非常重要:您不应该在 true 中使用SKAction.animatewith 。resize这是因为这种方法调整了精灵的大小并且不尊重比例(我真的不明白为什么,希望苹果未来能做出一些改进..)。这是我目前获得的最好的:
代码:
import SpriteKit
class GameScene: SKScene {
var knight: SKSpriteNode!
var textures : [SKTexture] = [SKTexture]()
var lastKnightSize: CGSize!
override func didMove(to view: SKView) {
self.physicsWorld.gravity = CGVector.zero
let plist = "knight.plist"
let genericAtlas = SKTextureAtlas(named:plist)
let filename : String! = NSURL(fileURLWithPath: plist).deletingPathExtension!.lastPathComponent
for i in 0 ..< genericAtlas.textureNames.count
{
let textureName = (String(format:"%@%02d",filename,i))
textures.append(genericAtlas.textureNamed(textureName))
}
if textures.count>0 {
// Prepare my sprite
knight = SKSpriteNode(texture:textures.first,size:CGSize(width:1,height:1))
knight.zPosition = 2
addChild(knight)
knight.position = CGPoint(x:self.frame.midX,y:self.frame.midY)
lastKnightSize = knight.texture?.size()
setPhysics()
}
let animation = SKAction.repeatForever(SKAction.animate(with: textures, timePerFrame: 0.15, resize: false, restore: false))
knight.run(animation,withKey:"knight")
}
override func didEvaluateActions() {
lastKnightSize = knight.texture?.size()
knight.xScale = lastKnightSize.width
knight.yScale = lastKnightSize.height
}
func setPhysics() {
knight.physicsBody = SKPhysicsBody.init(rectangleOf: knight.frame.size)
knight.physicsBody?.isDynamic = true
knight.physicsBody?.allowsRotation = false
knight.physicsBody?.affectedByGravity = true
}
}
Run Code Online (Sandbox Code Playgroud)
重要细节:
这isDynamic = true是不可能的,因为,在频繁改变大小的过程中,苹果也经常重置骑士的物理主体,但没有将最新physicsBody属性的继承应用到新的重置中physicsBody,这真是一个耻辱,你可以在更新打印中测试它(knight.physicsBody?.velocity始终为零,但应该由于重力而改变......)。这可能就是苹果建议不要在物理过程中缩放精灵的原因。在我看来,这是Sprite-kit 的限制。
| 归档时间: |
|
| 查看次数: |
1189 次 |
| 最近记录: |