Ok. I am having a very odd problem with SKTextures and animating through - I have created a sprite node like this and set the normal texture to the last image in the array of sktextures I will eventually animate through. The texture (not normal texture) is set to the first image in the array.
theSprite = SKSpriteNode()
theSprite.texture = atlas[0]
theSprite.normalTexture = atlas[atlas.count-1]
theSprite.size = CGSize(width: 500, height: 1491)
theSprite.position = CGPoint(x: basePos.x+1, y: basePos.y+2)
superScene.addChild(theSprite)
when clicked it runs this func and as I read here https://developer.apple.com/reference/spritekit/skaction/1417810-animate I have restore set to TRUE so it SHOULD end on the last frame of the array:
let anim = SKAction.animate(with: atlas, timePerFrame: animSpeed, resize: false, restore: true)
theSprite.run(anim, completion: {() -> Void in
})
It should go back to normal texture (which is a blank image), but it doesn't. The only way it goes back to this is if I don't set the texture to begin with, which I need to do. How can I make it restore?
Suggested Solutions
Try setting your sprite to the last texture in the atlas like this: theSprite.texture = atlas[atlas.count-1]. Then run your code again. You should see that it restores to back to atlas[atlas.count-1].
If your animation stops at a texture other than your last atlas texture, then you could try this. Set theSprite.texture = atlas[atlas.count-1] within your animation's completion block. You would also set restore to false in this case. This will animate your sprite then set the final texture to whatever you want, in this case it will be set to the last atlas texture.
Explanation
Restore will return your sprite back to the texture that it had before the animation began. So in your case with your current code, it should end the animation with the texture atlas[0]. It is not meant to end on the last frame of the array as you stated.
According to the Apple dev reference, if restore is true:
When the action completes, the sprite’s texture is restored to the
texture it had before the action completed.
https://developer.apple.com/reference/spritekit/skaction/1417810-animate
Related
My goal is to render an SCNScene off screen with a transparent background, as a PNG. Full reproducing project here.
It works, but when I enable jittering, the resulting render is semitransparent. In this example I pasted in the resulting PNG on top of an image with black squares, and you will notice that the black squares are in fact visible:
As you can see, the black boxes are visible through the 3D objects.
But, when I disable jittering, I get this:
As you can see, the black boxes are not visible.
I'm on Monterrey 12.1 (21C52). I'm testing the images in Preview and in Figma.
I'm using standard SDK features only. Here's what I do:
scene.background.contents = NSColor.clear
let snapshotRenderer = SCNRenderer(device: MTLCreateSystemDefaultDevice())
snapshotRenderer.pointOfView = sceneView.pointOfView
snapshotRenderer.scene = scene
snapshotRenderer.scene!.background.contents = NSColor.clear
snapshotRenderer.autoenablesDefaultLighting = true
// setting this to false does not make the image semi-transparent
snapshotRenderer.isJitteringEnabled = true
let size = CGSize(width: 1000, height: 1000)
let image = snapshotRenderer.snapshot(atTime: .zero, with: size, antialiasingMode: .multisampling16X)
let imageRep = NSBitmapImageRep(data: image.tiffRepresentation!)
let pngData = imageRep?.representation(using: .png, properties: [:])
try! pngData!.write(to: destination)
The docs for jittering says
Jittering is a process that SceneKit uses to improve the visual quality of a rendered scene. While the scene’s content is still, SceneKit moves the pointOfView location very slightly (by less than a pixel in projected screen space). It then composites images rendered after several such moves to create the final rendered scene, creating an antialiasing effect that smooths the edges of rendered geometry.
To me, that doesn't sound like something that is expected to produce semi-transparency?
I want to move the camera every frame automatic in the z-axis in Scenekit. I have write this code in Swift:
func renderer(_ renderer: SCNSceneRenderer,
updateAtTime time: TimeInterval) {
cameraNode.position.z = cameraNode.position.z + 2
}
This works only for 3 frames, after that the camera doesn't move anymore. Can someone give me the correct code so the camera moves automatic every frame?
According to SceneKit documentation:
SceneKit calls this method exactly once per frame, so long as the SCNView object (or other SCNSceneRenderer object) displaying the scene is not paused.
If nothing else in your view is moving, the scene is ‘paused’ and does not render. According to this question, in order to force the render every frame, the workaround should be:
sceneView.loops = true
However, updating the position every frame is not the best practice because FPS can change, which would affect the camera speed. But you can keep a consistent camera speed using SCNAction without using the render function:
let move = SCNAction.moveBy(x: 0, y: 0, z: 120, duration: 1.0)
let moveForever = SCNAction.repeatActionForever(move)
cameraNode.runAction(moveForever)
This will also make controlling your camera easier if it needs to stop, and it does not force the render if it is unnecessary.
I have a SKSpriteNode with a lot of child nodes.
I use that node with all the children very frequently in my game.
A better approach would be to build this node once with all the children, create an image (SKTexture) and use the created texture, as opposed to recreating the SKSpriteNode and all its children.
Is there any way to do this?
There is actually a very easy way to do this.. I do this all the time to convert ShapeNodes to SpriteNodes (because shapes need 1 draw call per shape, and sprites dont)
let texture = SKView().texture(from: desiredNode)
let newSprite = SKSpriteNode(texture: texture)
or, in your gamescene for brevity:
let newSprite = SKSpriteNode(texture: view!.texture(from: desiredNode))
SKEffectNode & shouldRasterize
You can us SKEffectNode and set shouldRasterize to true.
let effectNode = SKEffectNode()
effectNode.shouldRasterize = true
scene.addChild(effectNode)
The SKEffectNode's output is rasterized and cached internally. This cache is reused when rendering.
Now just add your sprites to effectNode
effectNode.addChild(sprite0)
effectNode.addChild(sprite1)
effectNode.addChild(sprite2)
The first time the effectNode is rendered, the produced image will be automatically cached.
And the cached version will be used for the next frames.
I'm trying to make a game where the main user is animated and i converted the gif to png and took each frame and put them all into a folder. However, right now I'm simply trying to get the first frame to appear, but nothing appears. Sorry if i worded this weird my English isn't very good.
Right now I think just the first frame, "trumpyhappy1.png" should show when I try to run it, but when I run it nothing shows up.
For anyone that stumbles upon this and is still wondering why their animations are blank. I found that programmatically adding the actions would cause this before viewDidAppear got called. However, setting the animation in the scene file directly I was able to get around the blank animations
So basically in spritekit to animate a spritenode you should be doing this
override func didMove() {
let mainGuy = SKSpriteNode()
self.addChild(mainGuy)
// Give your size and position and everything
// Then to animate that spriteNode with frames
let textureAtlas = SKTextureAtlas(named: "trump")
let frames: [SKTexture] = []
for i in 0...textureAtlas.textureNames.count {
var name = "trumphappy\(i).png"
frames.append(SKTexture(named: name))
}
// Then to create the animation add this code
let animation = SKAction.animate(with: frames, timePerFrame: ) // Whatever time you want your animation to spend on each frame
}
That should work, hope it works!
I am trying to blur my entire GameScene when my pause button is pressed. I have a method called blurSceen() but it doesn't seem to be adding the effect to the scene. Is there a way I can accomplish this or am I doing something wrong? I have viewed other posts about this topic but haven't been able to accomplish the effect.
func blurScreen() {
let effectsNode = SKEffectNode()
let filter = CIFilter(name: "CIGaussianBlur")
let blurAmount = 10.0
filter!.setValue(blurAmount, forKey: kCIInputRadiusKey)
effectsNode.filter = filter
effectsNode.position = self.view!.center
effectsNode.blendMode = .Alpha
// Add the effects node to the scene
self.addChild(effectsNode)
}
From the SKEffectNode docs:
An SKEffectNode object renders its children into a buffer and optionally applies a Core Image filter to this rendered output.
The effect node applies a filter only to its child nodes. Your effect node has no children, so there's nothing to apply a filter to.
Probably what you want is to add an effect node to your scene early on--but don't set the filter on it yet--and put all the nodes that you'll later want to blur in as its children. When it comes time to apply a blur, set the filter on the (already existing, already with children) effect node.
I had the same issue trying to blur the whole SKScene and it just wasn't working. The missing piece of the puzzle was this line:
shouldEnableEffects = true
Swift 4:
from gameScene:
let blur = CIFilter(name:"CIGaussianBlur",withInputParameters: ["inputRadius": 10.0])
self.filter = blur
self.shouldRasterize = true
self.shouldEnableEffects = true