Расширяющий круговой эффект перехода в Spritekit
Я пытаюсь получить эффект перехода, похожий на тот, что есть в игре Super Mario Run от Nintendo, в наборе спрайтов.
Я просто хочу, чтобы вырез круга расширялся, показывая следующую сцену или просто раскрывая текущую сцену. Это не доступно как один из стандартных переходов набора спрайтов iOS.
Я сделал это с полностью черным слоем, покрывающим всю сцену и использующим SKCropNode, чтобы оживить расширяющийся круг, чтобы показать сцену.
Ниже приведен код, который находится в didMove(to view: SKView)
let fullScreen = SKSpriteNode(color: .black, size: self.size)
let mask = SKSpriteNode(color: .black, size: self.size)
let circle = SKShapeNode(circleOfRadius: self.size.height / 2)
circle.fillColor = .white
circle.blendMode = .subtract
circle.setScale(0.001)
circle.isHidden = true
circle.name = "circleShape"
mask.addChild(circle)
let crop = SKCropNode()
crop.position = CGPoint(x: self.size.width / 2, y: self.size.height / 2)
crop.maskNode = mask
crop.name = "cropNode"
crop.addChild(fullScreen)
crop.zPosition = 100
self.addChild(crop)
let waitAction = SKAction.wait(forDuration: 2.0)
let callAction = SKAction.run({
let cropNode = self.childNode(withName: "cropNode") as! SKCropNode
let maskNode = cropNode.maskNode as! SKSpriteNode
let circleNode = maskNode.childNode(withName: "circleShape") as! SKShapeNode
circleNode.isHidden = false
let scaleAction = SKAction.scale(to: 2.0, duration: 2.0)
scaleAction.timingFunction = scaleAction.easeInQuartic
circleNode.run(scaleAction, completion: {
cropNode.removeFromParent()
})
})
let seqAction = SKAction.sequence([waitAction,callAction])
self.run(seqAction)
Это работает в симуляторе, но не во время работы на устройстве (iPad Pro 2016 с последней версией iOS 10). На устройстве не появляется расширяющийся круг, а черный наложенный слой просто исчезает после завершения действия масштабирования.
Мои вопросы:
1) почему это не работает на устройстве и только на симуляторе?
2) есть ли лучший и более эффективный способ добиться этого перехода в спрайте?
Заранее спасибо.
2 ответа
В итоге я использовал SKSpriteNode с фрагментным шейдером. Ниже приведен код в главной сцене:
let fullScreen = SKSpriteNode(color: UIColor.clear, size: CGSize(width: self.size.width, height: self.size.height))
fullScreen.position = CGPoint(x: self.size.width / 2.0, y: self.size.height / 2.0)
fullScreen.zPosition = 200
self.addChild(fullScreen)
let shader = SKShader(fileNamed: "transitionShader.fsh")
shader.attributes = [SKAttribute(name: "a_sprite_size", type: .vectorFloat2),
SKAttribute(name:"a_duration", type: .float)]
fullScreen.shader = shader
let spriteSize = vector_float2(Float(fullScreen.frame.size.width),Float(fullScreen.frame.size.height))
fullScreen.setValue(SKAttributeValue(vectorFloat2: spriteSize),forAttribute: "a_sprite_size")
fullScreen.setValue(SKAttributeValue(float: Float(mainGameTransitionDuration)), forAttribute: "a_duration")
И следующий код шейдера, который находится в файле с именем transitionShader.fsh
#define M 1000.0 // Multiplier
void main()
{
float aspect = a_sprite_size.y / a_sprite_size.x;
float u = v_tex_coord.x;
float v = v_tex_coord.y * aspect;
vec2 uv = vec2(u,v) * M;
vec2 center = vec2(0.60,0.55 * aspect) * M;
float t = u_time / a_duration;
if ( t < 1.0 )
{
float easeIn = pow(t,5.0);
float radius = easeIn * 2.0 * M;
float d = length(center - uv) - radius;
float a = clamp(d, 0.0, 1.0);
gl_FragColor = vec4(0.0,0.0,0.0, a);
}
else
{
gl_FragColor = vec4(0.0,0.0,0.0,0.0);
}
}
Кажется, что он работает без проблем и, по-видимому, не стоит дорого на процессоре или памяти.
Есть, вероятно, 100 различных способов достичь этого. Мой путь лучше твоего? вероятно нет, но это работает для меня как на симуляторе, так и на моем iPad.
просто перейдите в свою сцену с продолжительностью 0.0, а затем запустите эту забаву из didMove(для просмотра:)
Причина, по которой вам нужно отдельное изображение для круга, заключается в том, что он сохраняет свой четкий четкий край по мере увеличения
bgOverlay только для того, чтобы он начинал с полностью черного экрана, а затем применяет переход seudo
func circleTransition() {
let bgMask = SKSpriteNode(texture: SKTexture(imageNamed: "bg_mask"), color: .clear, size: CGSize(width: self.size.width, height: self.size.height))
bgMask.position = CGPoint(x: self.size.width / 2, y: self.size.height / 2)
bgMask.zPosition = 5000
self.addChild(bgMask)
let transitionCircle = SKSpriteNode(texture: SKTexture(imageNamed: "transition_circle"), color: .clear, size: CGSize(width: 13, height: 13))
transitionCircle.position = CGPoint.zero
transitionCircle.zPosition = 1
bgMask.addChild(transitionCircle)
let bgOverlay = SKSpriteNode(texture: SKTexture(imageNamed: "bg_overlay"), color: .clear, size: CGSize(width: self.size.width, height: self.size.height))
bgOverlay.position = CGPoint(x: self.size.width / 2, y: self.size.height / 2)
bgOverlay.zPosition = 5001
self.addChild(bgOverlay)
bgMask.run(SKAction.sequence([SKAction.scale(to: 100.0, duration: 2.0), SKAction.removeFromParent()]))
bgOverlay.run(SKAction.sequence([SKAction.fadeOut(withDuration: 0.1), SKAction.removeFromParent()]))
}