2

Is it possible to create an SKAction for SKSpriteNode in SpriteKit that generates the same effect as "Photoshop" with the Edit->Transform->Distort option?

Example:

Distort image

Maetschl
  • 1,330
  • 14
  • 23

3 Answers3

4

I solve with this implementation:

Swift 5

extension SKSpriteNode {

    func addSkew(value: CGFloat = -1){

        var effectNode = SKEffectNode()
        effectNode.shouldRasterize = true
        effectNode.shouldEnableEffects = true
        effectNode.addChild(SKSpriteNode(texture: texture))
        effectNode.zPosition = 1
        let transform = CGAffineTransform(a:  1    , b:  0,
                                          c:  value, d:  1,
                                          tx: 0    , ty: 0)
        let transformFilter = CIFilter(name: "CIAffineTransform")!
        transformFilter.setValue(transform, forKey: "inputTransform")
        effectNode.filter = transformFilter
        addChild(effectNode)
        texture = nil

    }

}
Maetschl
  • 1,330
  • 14
  • 23
3

You can create a skew using a 1x1 warp mesh. This is supported in iOS10.0+.

This extension receives the skew angle in degrees, and distorts around the anchor point of the given sprite.

Swift 4.2

extension SKWarpGeometryGrid {

    public static var skewPosGridZero:[float2] {
        get {
            return [float2(0.0, 0.0), float2(1.0, 0.0),
                    float2(0.0, 1.0), float2(1.0, 1.0)]
        }
    }

    public static func skewXPosGrid(_ skewX: CGFloat, node:SKSpriteNode? = nil) -> [float2] {

        let anchorY:Float = Float(node?.anchorPoint.y ?? 0.5)  
        var skewPosGrid = skewPosGridZero
        let offsetX = Float(tan(skewX.degToRad()) * (node == nil ? 1.0 : (node!.size.height/node!.size.width)) )
        skewPosGrid[2][0] += offsetX * (1.0 - anchorY)
        skewPosGrid[3][0] += offsetX * (1.0 - anchorY)
        skewPosGrid[0][0] -= offsetX * anchorY
        skewPosGrid[1][0] -= offsetX * anchorY

        return skewPosGrid

    }

    public static func skewYPosGrid(_ skewY: CGFloat, node:SKSpriteNode? = nil) -> [float2] {

        let anchorX:Float = Float(node?.anchorPoint.x ?? 0.5)  
        var skewPosGrid = skewPosGridZero
        let offsetY = Float(tan(skewY.degToRad()) * (node == nil ? 1.0 : (node!.size.width/node!.size.height)) )
        skewPosGrid[1][1] += offsetY * (1.0 - anchorX)
        skewPosGrid[3][1] += offsetY * (1.0 - anchorX)
        skewPosGrid[0][1] -= offsetY * anchorX
        skewPosGrid[2][1] -= offsetY * anchorX

        return skewPosGrid

    }

    public static func skewX(_ angle: CGFloat, node:SKSpriteNode? = nil) -> SKWarpGeometryGrid {

        return SKWarpGeometryGrid(columns: 1, rows: 1, sourcePositions: skewPosGridZero, destinationPositions: skewXPosGrid(angle, node:node))

    }
    public static func skewY(_ angle: CGFloat, node:SKSpriteNode? = nil) -> SKWarpGeometryGrid {

        return SKWarpGeometryGrid(columns: 1, rows: 1, sourcePositions: skewPosGridZero, destinationPositions: skewYPosGrid(angle, node:node))

    }

    public static func skewZero() -> SKWarpGeometryGrid {
        return SKWarpGeometryGrid(columns: 1, rows: 1)
    }

}

Example animation:

    let spriteNode = SKSpriteNode(imageNamed: "tex")
    spriteNode.anchorPoint = CGPoint(x:0.25, y:1.0)
    let skewA = SKWarpGeometryGrid.skewX(-45.0, node: spriteNode)
    let skewB = SKWarpGeometryGrid.skewX(45.0, node: spriteNode)
    spriteNode.warpGeometry = skewB
    if let skewActionA = SKAction.warp(to: skewA, duration: 3.0),
        let skewActionB = SKAction.warp(to: skewB, duration: 3.0){
        // Individual easing
        skewActionA.timingMode = .easeInEaseOut
        skewActionB.timingMode = .easeInEaseOut
        spriteNode.run(SKAction.repeatForever(SKAction.sequence([skewActionA,skewActionB])))
    }
Loks
  • 224
  • 2
  • 7
2

The list of available SKAction's is here: https://developer.apple.com/reference/spritekit/skaction

There is none to do exactly what you describe. Instead, you can export multiple sprite images from a photo editing tool like Photoshop, and use an animation action like class func animate(with: [SKTexture], timePerFrame: TimeInterval).

This is a little more work, but should achieve the desired effect.

nathangitter
  • 9,607
  • 3
  • 33
  • 42
  • Thanks, but i need transform image, not only scale/moving/alpha, im looking CGAffineTransform for solution. – Maetschl Apr 17 '17 at 14:37
  • 1
    If you add a series of custom textures as my answer describes, you can achieve a distortion effect. It also looks like you can use `CGAffineTransform` on an `SKEffectNode`: http://stackoverflow.com/a/32952011/6658553 – nathangitter Apr 17 '17 at 14:53