I'm trying to convert SKShapeNode into a Sprite node by first converting the SKShapeNode into a texture. However the results is invisible sprite. The Physics body is there normally but no graphics are being displayed. Something wrong with the way I try to convert the SKShapeNode, I suppose but I can't think of what. Do you have sense on what's wrong? ALL HELP APPRECIATED!
let createdShape = SKShapeNode(path: path)
createdShape.physicsBody = SKPhysicsBody(edgeLoopFromPath: createdShape.path)
createdShape.physicsBody?.categoryBitMask = PhysicsCategory.Shape
createdShape.physicsBody?.contactTestBitMask = PhysicsCategory.None
createdShape.name = "paintedArea"
createdShape.strokeColor = shapeBorderColor
createdShape.lineWidth = 5.0
createdShape.fillColor = shapeFillColor
createdShape.alpha = 1.0
createdShape.zPosition = shapeZPosition
let texture = view?.textureFromNode(createdShape)
println(texture?.description)
let sprite = SKSpriteNode(texture: texture)
sprite.physicsBody = createdShape.physicsBody
sprite.position = createdShape.position
sprite.color = UIColor.blueColor()
self.addChild(sprite)
Ahh I got it! My z-position was wrong! The created shape was on top of my backgrond image but the newly created sprite wasn't. Stupid of me...
Related
I am trying to create nodes, that are put into an array, and then are added to the scene with their physics bodies.
Here is the code for creating the initial sprites:
let name = createTarget()
let targetNode = SKSpriteNode(imageNamed: name)
targetNode.name = name
chickenNodes.append(targetNode)
targetNode.position = generateRandomLocation()
let range = SKRange(lowerLimit: targetNode.position.y, upperLimit: targetNode.position.y)
let lockToCenter = SKConstraint.positionY(range)
targetNode.constraints = [lockToCenter]
if movingItems { animateTargets(targetNode) }
Once all of these nodes are in the array, I add them in didMove to a background node, fgNode, in the scene, like this:
for chicken in chickenNodes {
let texture = SKTexture(imageNamed: chicken.name!)
chicken.physicsBody = SKPhysicsBody(texture: texture, size: texture.size())
chicken.physicsBody?.isDynamic = true
chicken.physicsBody?.affectedByGravity = true
chicken.physicsBody?.allowsRotation = false
chicken.physicsBody?.linearDamping = 0.0
chicken.physicsBody?.restitution = 1.0
chicken.physicsBody?.friction = 0.0
fgNode.addChild(chicken)
}
When I view the physics through the scene, the physics bodies keep falling away from the sprite (as though they're responding to the gravity in the scene); and the sprite is just locked where it is. How do I ensure that the physicsBody sticks to the sprite?
SKEffectionNodes have a shouldRasterise "switch" that bakes them into a bitmap, and doesn't update them until such time as the underlying nodes that are impacted by the effect are changed.
However I can't find a way to create an SKTexture from this rasterised "image".
Is it possible to get a SKTexture from a SKEffectNode?
I think you could try a code like this (it's just an example):
if let effect = SKEffectNode.init(fileNamed: "myeffect") {
effect.shouldRasterize = true
self.addChild(effect)
...
let texture = SKView().texture(from: self)
}
Update:
After you answer, hope I understood better what do you want to achieve.
This is my point of view: if you want to make a shadow of a texture, you could simply create an SKSpriteNode with this texture:
let shadow = SKSpriteNode.init(texture: <yourTexture>)
shadow.blendMode = SKBlendMode.alpha
shadow.colorBlendFactor = 1
shadow.color = SKColor.black
shadow.alpha = 0.25
What I want to say is that you could proceed step by step:
get your texture
elaborate your texture (add filters, make some other effect..)
get shadow
This way of working produces a series of useful methods you could use in your project to build other kind of elements.
Maybe, by separating the tasks you don't need to use texture(from:)
I've figured this out, in a way that solves my problems, using a Factory.
Read more on how to make a factory, from BenMobile's patient and clear articulation, here: Factory creation and use for making Sprites and Shapes
There's an issue with blurring a SKTexture or SKSpriteNode in that it's going to run out of space. The blur/glow goes beyond the edges of the sprite. To solve this, in the below, you'll see I've created a "framer" object. This is simply an empty SKSpriteNode that's double the size of the texture to be blurred. The texture to be blurred is added as a child, to this "framer" object.
It works, regardless of how hacky this is ;)
Inside a static factory class file:
import SpriteKit
class Factory {
private static let view:SKView = SKView() // the magic. This is the rendering space
static func makeShadow(from source: SKTexture, rgb: SKColor, a: CGFloat) -> SKSpriteNode {
let shadowNode = SKSpriteNode(texture: source)
shadowNode.colorBlendFactor = 0.5 // near 1 makes following line more effective
shadowNode.color = SKColor.gray // makes for a darker shadow. White for "glow" shadow
let textureSize = source.size()
let doubleTextureSize = CGSize(width: textureSize.width * 2, height: textureSize.height * 2)
let framer = SKSpriteNode(color: UIColor.clear, size: doubleTextureSize)
framer.addChild(shadowNode)
let blurAmount = 10
let filter = CIFilter(name: "CIGaussianBlur")
filter?.setValue(blurAmount, forKey: kCIInputRadiusKey)
let fxNode = SKEffectNode()
fxNode.filter = filter
fxNode.blendMode = .alpha
fxNode.addChild(framer)
fxNode.shouldRasterize = true
let tex = view.texture(from: fxNode) // ‘view’ refers to the magic first line
let shadow = SKSpriteNode(texture: tex) //WHOOPEE!!! TEXTURE!!!
shadow.colorBlendFactor = 0.5
shadow.color = rgb
shadow.alpha = a
shadow.zPosition = -1
return shadow
}
}
Inside anywhere you can access the Sprite you want to make a shadow or glow texture for:
shadowSprite = Factory.makeShadow(from: button, rgb: myColor, a: 0.33)
shadowSprite.position = CGPoint(x: self.frame.midX, y: self.frame.midY - 5)
addChild(shadowSprite)
-
button is a texture of the button to be given a shadow. a: is an alpha setting (actually transparency level, 0.0 to 1.0, where 1.0 is fully opaque) the lower this is the lighter the shadow will be.
The positioning serves to drop the shadow slightly below the button so it looks like light is coming from the top, casting shadows down and onto the background.
In my original build, my sprites were static, so to make the collision physics as accurate as I could rather than use rectangleOfSize for SKPhysicsBody passing in the node, I used this tool to define a path SpriteKit's SKPhysicsBody with polygon helper tool.
However now I'm animating the sprites so that they move back and forth during gameplay (obviously my physics path remains static given the above) so my physicsbody no longer matches what the player sees on screen.
The helper tool seemed like a bit of hack that Apple would eventually fix in the API, has there been anything recent in SpriteKit that would help me out here so that I can pass in the node and define a precise physicsbody rather than the hard-coded approach? If not, any other alternatives?
Not sure how performant this is but you can pre-generate the physics bodies for each texture then animate the sprite along with its physicsBody using a custom action.
Here is an example:
func testAnimation() {
var frameTextures = [SKTexture]()
var physicsBodies = [SKPhysicsBody]()
for index in 1...8 {
// The animation has 8 frames
let texture = SKTexture(imageNamed: "guy\(index)")
frameTextures.append(texture)
let physicsBody = SKPhysicsBody(texture: texture, size: texture.size())
physicsBody.affectedByGravity = false
physicsBodies.append(physicsBody)
}
let sprite = SKSpriteNode(texture: frameTextures[0])
let framesPerSecond = 16.0
let animation = SKAction.customAction(withDuration: 1.0, actionBlock: { node, time in
let index = Int((framesPerSecond * Double(time))) % frameTextures.count
if let spriteNode = node as? SKSpriteNode {
spriteNode.texture = frameTextures[index]
spriteNode.physicsBody = physicsBodies[index]
}
})
sprite.run(SKAction.repeatForever(animation));
sprite.position = CGPoint(x: 0, y: 0)
addChild(sprite)
}
My SKEmitterNode in my game isnt changing size when I call its property particleSize.
let myParticle = SKEmitterNode(fileNamed: "MyParticle.sks")
myParticle?.particleSize = CGSize(width: 100, height: 100)
self.addChild(myParticle!)
My Particle still has its large rectangle size. Am I calling the wrong property or what? Any ideas or suggestions would be greatly appreciated! Thanks!
Try:
let myScale: CGFloat = 2 //2 = double dimension; 0.5 = half dimension
let myParticle = SKEmitterNode(fileNamed: "MyParticle.sks")
myParticle?.particleScale = myScale
self.addChild(myParticle!)
I'm trying to put a physics body to a line.
For some reason the physics body ends up materializing on the lower left corner.
How can I control the position of a physics body? I was under the impression that once I had attached the body into a shape, it would automatically take its shape.
Here's the code:
var distance = calculateDistance(wayPoints[wayPoints.count-1], point2: wayPoints[wayPoints.count-2])
var linePhysicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: distance, height: 1.0))
linePhysicsBody.categoryBitMask = PhysicsCategory.Line
linePhysicsBody.contactTestBitMask = PhysicsCategory.Ball
linePhysicsBody.collisionBitMask = PhysicsCategory.Ball
linePhysicsBody.dynamic = false
linePhysicsBody.usesPreciseCollisionDetection = true
lineNode.removeFromParent()
lineNode = SKShapeNode()
lineNode.physicsBody = linePhysicsBody
lineNode.name = "drawingLine"
lineNode.path = pathToDraw
lineNode.lineWidth = 5.0
lineNode.strokeColor = UIColor.redColor()
lineNode.glowWidth = 1.0
self.addChild(lineNode)
You have not positioned your line node. That means per default is't on point(0,0).
As LearnCocos2D mentioned in his comment. You are drawing then a path relative to your line node position. That does not influence your physicsBody. This is still positioned at (0,0)