3

例如,如果您有一张蹦床的图像,并且有一个角色在上面跳跃。然后,您想要制作蹦床如何在中心向下弯曲的动画。

为此,我必须获取位图并将其应用于密集镶嵌的 OpenGL ES 网格。然后对其应用纹理。然后变形网格。

SpriteKit 是否支持这一点,还是只能按原样显示纹理?

4

2 回答 2

4

在 iOS 10 中,SKWarpGeometry添加了允许您对精灵进行变形的功能。扭曲是使用包含八个控制点的源和目标网格定义的,这些点定义了扭曲变换;Sprite Kit 将处理曲面细分和其他低级细节。

您可以直接为精灵设置包裹几何体,或者使用 SKActions 动画变形。

SKAction.animate(withWarps: [SKWarpGeometry], times: [NSNumber]) ->     SKAction?
SKAction.animate(withWarps: [SKWarpGeometry], times: [NSNumber], restore: Bool) -> SKAction?
SKAction.warp(to: SKWarpGeometry, duration: TimeInterval) -> SKAction?

2016 年 11 月更新

在 Xcode 8+ 上,可以在 Playground 中使用以下代码片段:拖动控制点并查看生成的 SKSpriteNode 相应变形。

例子

// SpriteKit warp geometry example
// Copy the following in an Xcode 8+ playground
// and make sure your Assistant View is open
// 


import UIKit
import PlaygroundSupport
import SpriteKit


let view = SKView(frame: CGRect(x: 0, y: 0, width: 480, height: 320))

let scene = SKScene(size: view.frame.size)


view.showsFPS = true
view.presentScene(scene)

PlaygroundSupport.PlaygroundPage.current.liveView = view


func drawCanvas1(frame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200)) -> UIImage {

    UIGraphicsBeginImageContext(frame.size)

    //// Star Drawing
    let starPath = UIBezierPath()
    starPath.move(to: CGPoint(x: frame.minX + 100.5, y: frame.minY + 24))
    starPath.addLine(to: CGPoint(x: frame.minX + 130.48, y: frame.minY + 67.74))
    starPath.addLine(to: CGPoint(x: frame.minX + 181.34, y: frame.minY + 82.73))
    starPath.addLine(to: CGPoint(x: frame.minX + 149, y: frame.minY + 124.76))
    starPath.addLine(to: CGPoint(x: frame.minX + 150.46, y: frame.minY + 177.77))
    starPath.addLine(to: CGPoint(x: frame.minX + 100.5, y: frame.minY + 160))
    starPath.addLine(to: CGPoint(x: frame.minX + 50.54, y: frame.minY + 177.77))
    starPath.addLine(to: CGPoint(x: frame.minX + 52, y: frame.minY + 124.76))
    starPath.addLine(to: CGPoint(x: frame.minX + 19.66, y: frame.minY + 82.73))
    starPath.addLine(to: CGPoint(x: frame.minX + 70.52, y: frame.minY + 67.74))
    starPath.close()
    UIColor.red.setFill()
    starPath.fill()
    UIColor.green.setStroke()
    starPath.lineWidth = 10
    starPath.lineCapStyle = .round
    starPath.lineJoinStyle = .round
    starPath.stroke()

    let image = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()
    return image
}

class ControlNode : SKShapeNode {

    override init() {
        super.init()
        self.path = CGPath(ellipseIn: CGRect.init(x: 0, y: 0, width: 10, height: 10), transform: nil)
        self.fillColor = UIColor.red
        self.isUserInteractionEnabled = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    typealias UpdateHandler = (ControlNode) -> ()
    var positionUpdated: UpdateHandler? = nil

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.fillColor = UIColor.yellow
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = touches.first, let parent = self.parent {
            let pos = touch.location(in: parent).applying(CGAffineTransform(translationX: -5, y: -5))
            self.position = pos
            positionUpdated?(self)
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.fillColor = UIColor.red
    }

    var warpPosition: vector_float2 {
        if let parent = self.parent {
            let size = parent.frame.size
            let pos = self.position.applying(CGAffineTransform(translationX: -size.width/2 + 5, y: -size.height/2 + 5))
            return vector_float2(
                x: Float(1 + pos.x / size.width),
                y: Float(1 + pos.y / size.height)
            )
        }
        return vector_float2(x: 0, y: 0)
    }
}

extension SKSpriteNode {
    func addControlNode(x: CGFloat, y: CGFloat) -> ControlNode {
        let node = ControlNode()
        node.position = CGPoint(
            x: x * frame.width - frame.width / 2 - 5,
            y: y * frame.height - frame.height / 2 - 5
        )
        self.addChild(node)
        return node
    }
}




let texture = SKTexture(image: drawCanvas1())
let image = SKSpriteNode(texture: texture)
image.position = CGPoint(x: 200, y: 160)
scene.addChild(image)

let controlPoints: [(x:CGFloat, y:CGFloat)] = [
    (0, 0),
    (0.5, 0),
    (1.0, 0),
    (0, 0.5),
    (0.5, 0.5),
    (1.0, 0.5),
    (0, 1.0),
    (0.5, 1.0),
    (1.0, 1.0),
]

let sourcePositions = controlPoints.map {
    vector_float2.init(Float($0.x), Float($0.y))
}

var controlNodes: [ControlNode] = []

controlPoints.forEach { controlNodes.append(image.addControlNode(x: $0.x, y: $0.y)) }


for node in controlNodes {
    node.positionUpdated = {
        [weak image]
        _ in
        let destinationPoints = controlNodes.map {
            $0.warpPosition
        }
        image?.warpGeometry = SKWarpGeometryGrid(columns: 2, rows: 2, sourcePositions: sourcePositions, destinationPositions: destinationPoints)
    }
}

PS:我参考了SKWarpGeometry文档。

于 2016-06-17T14:37:18.377 回答
1

编辑:使用iOS 10 / tvOS 10 / macOS 10.12 中可用的 API,查看@Benzi 的答案以获得不错的选择。SKWarpGeometry在这些版本之前,Sprite Kit 只能将纹理显示为广告牌——这个答案的其余部分解决了 2016 年之前的情况,如果您针对的是旧设备,这可能仍然相关。

SKEffectNode不过,您也许可以通过使用几何失真核心图像过滤器之一来获得您想要的失真效果。filter(例如 CIBumpDistortion。)在您的游戏逻辑代码中(在您的SKScene子类中或您放置它的任何其他位置)保留对效果节点或其属性的引用,您可以使用setValue:forKey:每个动画帧调整过滤器的参数。

于 2013-11-05T18:51:35.983 回答