SpriteKit是否支持精灵/纹理/形状的密集镶嵌,以便它可以自由变形?

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

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

SpriteKit是否支持此function,还是只能按原样显示纹理?

在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+上,可以在操场中使用以下代码片段:拖动控制点并查看生成的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, with event: UIEvent?) { self.fillColor = UIColor.yellow } override func touchesMoved(_ touches: Set, 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, 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文档。

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

但是,您可以通过使用SKEffectNode和其中一个几何失真Core Image滤镜来获得您想要的失真效果。 (例如,CIBumpDistortion。)在游戏逻辑代码中保存对效果节点或其filter属性的引用(在您的SKScene子类中或您放置的任何其他位置),并且可以使用setValue:forKey: for each调整filter的参数动画的框架。