9

これらのフレームで構成されたアトラス アニメーションがある「Hello World」スプライト キット テンプレートを使用して、小さなテスト プロジェクトを実行しました。 ここに画像の説明を入力

-

この騎士を見せたいのですが、それはアニメーションです。

DYNAMIC物理体を設定したい。

したがって、ツールを使用して単一のフレームを分離し、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
    }
}

出力は次のとおりです。

ここに画像の説明を入力

ご覧のとおり、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])

アニメーションの各フレームのボディを作成するために、これらのボディはシーン内にすべて一緒に残り、この写真のような醜い奇妙な状況を作り出します:

ここに画像の説明を入力


したがって、それを行う正しい方法は、アニメーション中にフレームをインターセプトし、その場で変更physicsBodyすることです。update()の方法も使えますSKSceneが、拡張を考えていました。

animation私の考えは、アクションを と組み合わせて、最初のアクションの実行をチェックする別のカスタム アクションを作成し、現在のフレームと配列にSKAction.group一致するフレームをインターセプトし、外部メソッドの起動を変更することです。この場合は.knight.texturetexturesphysicsBodysetPhysicsBody

それから、私はこれを書きました:

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])
    }
}

この拡張機能を追加して、コードのアニメーション部分を次のように変更します。

 //
        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
    }

これは最終的に機能し、出力は次のとおりです。

ここに画像の説明を入力

この結果を得るためのより良い方法、またはより洗練された方法を知っていますか?

4

4 に答える 4

3

コメントで述べたように、ボックス化された物理演算を行っているため、物理演算の接触部分を処理する子 SKSpriteNode を騎士に追加し、騎士のフレームに基づいてスケーリングします。

(注: これはデモのみを目的としています。複数のスプライトでこれを処理するためのよりエレガントな方法を考え出すことができると確信しています)

import SpriteKit
class GameScene: SKScene {
    var knight: SKSpriteNode!
    var textures : [SKTexture] = [SKTexture]()
    private var child = SKSpriteNode(color:.clear,size:CGSize(width:1,height:1))
    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.repeatForever(SKAction.animate(with: textures, timePerFrame: 0.15, resize: true, restore: false))
       knight.run(animation,withKey:"knight")
    }
    override func didEvaluateActions() {
        child.xScale = knight.frame.size.width
        child.yScale = knight.frame.size.height

    }
    func setPhysics() {
        child.physicsBody = SKPhysicsBody.init(rectangleOf: child.size)
        child.physicsBody?.isDynamic = false
        knight.addChild(child)
    }
}

テクスチャ ベースのボディを処理します。それを処理するカスタム アニメーションを作成します。

extension SKAction
{
    static func animate(withPhysicsTextures textures:[(texture:SKTexture,body:SKPhysicsBody)], timePerFrame:TimeInterval ,resize:Bool, restore:Bool) ->SKAction {

        var originalTexture : SKTexture!;
        let duration = timePerFrame * Double(textures.count);

        return SKAction.customAction(withDuration: duration)
        {
            node,elapsedTime in
            guard let sprNode = node as? SKSpriteNode
            else
            {
                    assert(false,"animatePhysicsWithTextures only works on members of SKSpriteNode");
                    return;
            }
            let index = Int((elapsedTime / CGFloat(duration)) * CGFloat(textures.count))
            //If we havent assigned this yet, lets assign it now
            if originalTexture == nil
            {
                originalTexture = sprNode.texture;
            }


            if(index < textures.count)
            {
                sprNode.texture = textures[index].texture
                sprNode.physicsBody = textures[index].body
            }
            else if(restore)
            {
                sprNode.texture = originalTexture;
            }

            if(resize)
            {
                sprNode.size = sprNode.texture!.size();
            }

        }
    }
}


import SpriteKit
class GameScene: SKScene {
    var knight: SKSpriteNode!
    var textures = [texture:SKTexture,body:SKPhysicsBody]()
    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))
            let texture = genericAtlas.textureNamed(textureName)
            let body = SKPhysicsBody(texture:texture)
            body.isDynamic = false
            textures.append((texture:texture,body:body))
        }
        if textures.count>0 {
            knight = SKSpriteNode(texture:textures.first.texture)
            knight.zPosition = 2
            addChild(knight)
            knight.position = CGPoint(x:self.frame.midX,y:self.frame.midY)
        }
        //
        let animation = SKAction.animate(withPhysicsTextures: textures, timePerFrame: 0.15, resize: true, restore: false)
        knight.run(animation, withKey:"knight")
    }
}
于 2016-12-22T23:13:46.897 に答える