SpriteKit simple shapes not drawing - ios

I'm doing some experimentation with SpriteKit and am roadblocked on a concept that to me seems very simple. I'm trying to draw simple shape (ie: square, circle) to the screen.
var testNode = SKSpriteNode()
testNode = SKSpriteNode(color: UIColor.orangeColor(), size: CGSizeMake(20, 20))
testNode.position = goalPoint
testNode.color = UIColor.orangeColor()
self.addChild(testNode)
According to the developer documentation for SKSpriteNode
An SKSpriteNode is a node that draws a textured image, a colored square, or a textured image blended with a color. You can also provide a custom shader to create your own rendering effects.
Apparently I'm missing how to do the colored square. I was able to create an SKTexture and get a sprite to appear, but without one I've been unable to get geometric shapes to appear.
I also attempted to use the SKShapeNode class but was unable to get that to appear either. I assume I'm missing something simple. Any suggestions?

The code in itself is correct, the only two things I can think of to be the problem is:
goalPoint - Is somewhere out of the screen bounds.
The location of the code. Where is your code located in your project?
In my didMoveToView I have the following :
var testNode = SKSpriteNode()
testNode = SKSpriteNode(color: UIColor.orangeColor(), size: CGSizeMake(20, 20))
testNode.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
testNode.color = UIColor.orangeColor()
self.addChild(testNode)
And it works like a charm.
So relocate your code to somewhere you are sure it's executed. And make sure goalPoint is within the screen.
Good luck :)

Related

SKEmitterNode does not appear when running on device

I've been learning to use Spritekit in Swift these past few days using the Hacking With Swift Tutorials (Specifically Project 23). I was learning how to use SKEmitterNodes however they don't ever seem to appear. On runtime, the screen is a blank grey color that runs at 1 fps with 1 node on screen (supposedly the SKEmitterNode). I've tried changing the zPosition of the node but it doesn't seem to have any effect. Oddly, SKSpriteNodes and SKLabelNodes appear just fine. I cleaned the project and got rid of the "Hello World" in the GameScene.sks so I know its not anything from the default project. I've googled around extensively for an answer to this question but I've come up blank. Below is the offending code.
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMove(to view: SKView) {
//backgroundColor = UIColor.black
if let starfield = SKEmitterNode(fileNamed: "Starfield.sks") {
starfield.position = CGPoint(x: 1024, y: 384)
starfield.advanceSimulationTime(10)
addChild(starfield)
starfield.zPosition = 0
}
edit: Alright guys I've solved it. It turns out Xcode doesn't like it when you try and add an SKEmitterNode exclusively programmatically (as opposed to SKSpriteNodes which it seems to handle fine). What I had to go and do was go into the GameScene.sks and manually add in my Starfield.sks file and then rename it to Starfield. Then I had to go inside the GameScene.swift file and hook the SKEmitterNode I had placed in the GameScene.sks editor by using this code inside the didMove function:
starfield = childNode(withName: "Starfield") as?
SKEmitterNode
Now the new code looks like this:
var starfield: SKEmitterNode!
override func didMove(to view: SKView) {
backgroundColor = UIColor.black
starfield = childNode(withName: "Starfield") as?
SKEmitterNode
starfield?.position = CGPoint(x: 1024, y: 384)
starfield?.advanceSimulationTime(10)
starfield?.zPosition = -1
}
I found this solution with the help of this tutorial
Looking at the code and trying it inside of Xcode, it seems it might be fine.... It runs fine on mine, but my particle emitter has a wide spread and barely visible...
Try changing the x position to the middle of the screen : 512.
It's a positioning of the emitter to a part of the screen that isn't to visible that's causing the problem.
You could also adjust the spread to make it visible from that position, but you said you are new and the most likely cause is the x position placement.
EDIT:
Ok it seems that you simply forgot to create the particles.
Just click File < New < File then click on SpriteKit Particle Emmitter, select a template you want, and finally make sure the name you gave the particle is the same as you named it in the code or vice versa.

Performance difference between SKShapeNode and SKSpriteNode

If I wanted to draw a simple red rectangle, should I use an SKShapeNode or an SKSpriteNode? What is the performance (speed to render) difference?
Are there any exceptions (like applying transparency, or maybe creating a physicsBody)?
I know what the primary purpose of both are, but I'm not sure which is better on performance for drawing a simple red square.
Code:
// Shape
let myShape = SKShapeNode(rectOf: CGSize(width: 100, height: 100))
myShape.fillColor = .red
myShape.strokeColor = .clear
// Sprite
let mySprite = SKSpriteNode(color: .red, size: CGSize(width: 100, height: 100))
SKSpriteNode offers higher performance than SKShapeNode class. See documentation here
If you want to add physicsbody to SKShapeNode, you should be aligned with shapenode points. For more sophisticated performance use SKSpriteNode. SKShapeNodes are useful when it is difficult to use a texture(may be a custom shape) and for building or displaying debugging information.
Furthermore, drawing/displaying a red rectangle is OK with SKShapeNode.

How to merge SKSpriteNode into a SKTexture to form new SKSpriteNode in Swift 3?

I'm developing an ios app. I have a SKSpriteNode on screen and I'm getting a drawing from the user that I'm storing as SKTexture. I would like to combine these two and introduce them as a new SKSpriteNode.
THE PROBLEM: I cannot find documentation about how to merge SKSpriteNode into SKTexture. I'm sure there's a way to do this. Would someone have example code for this?
You can use a container that will hold two sprites, and create a texture from it, using SKView's texture(from:) method, like this:
override func didMove(to view: SKView) {
backgroundColor = .black
let container = SKNode()
let left = SKSpriteNode(color: .purple, size: CGSize(width: 100, height: 100))
let right = SKSpriteNode(color: .yellow, size: CGSize(width: 100, height: 100))
container.addChild(left)
container.addChild(right)
left.position.x = 50
right.position.x = -50
addChild(container)
if let texture = view.texture(from: container) {
let sprite = SKSpriteNode(texture:texture)
addChild(sprite)
sprite.run(SKAction.moveTo(y: 300, duration: 5))
}
}
If you want create a texture from a portion of node's content, then you can do some cropping using SKView's texture(from:crop:) method.
You will probably have to use CoreGraphics to do this. Convert both SKTextures to CGImage using SKTexture.cgImage() method, combine them using CoreGraphics and then create a new texture from the result. SKTexture.init(cgImage:) method let you use a CGImage object to construct the texture.
I haven't used Core Graphics much, but here are a few links to related reads. I think you will want to use the CGLayer class to tell CoreGraphics how to stack the textures and reuse the sprite texture over time so CoreGraphics can cache it to make things faster.
Core Graphics uses Quartz 2D to do fast 2D graphics operations. I haven't played much with it, but I think it should be what you are looking for.
References :
SKTexture related:
cgImage() Method
init(cgImage:) Method
Core Graphics related:
Core Graphics framework
CGLayer class
Extra reference:
A series of tutorials on CG, part 2 is about offscreen rendering
Keep me posted, I think it's very interesting!
EDIT 1:
You will also have to use an SKMutableTexture on the sprite. That class allows for the texture data to be changed dynamically.
Not sure what you mean by "merge". If you want to create a new SKSpriteNode using an existing SKTexture then this may be the code you're looking for:
let newSprite = SKSpriteNode(texture: yourSKTexture)
Problem: You have two or more SKSpriteNodes and want them combined into a single new SKSpriteNode.
Why?: It is used to reduce the amount of Nodes to increase performance.
If you're using massive amounts of static sprites, you can benefit from this method.
Solution: Instance an SKView object and use it to render a container SKSpriteNode with multiple child SKSpriteNodes.
let mergedTexture = SKView().texture(from: containerNode)
let newNode = SKSpriteNode(texture: mergedTexture)
Whereby containerNode is the SKNode container with multiple SKSpriteNode children. This is what user Whirlwind suggested, but he used an existing SKView instance, that may not be available in your context.
I successfully use this method in my project (macOS) to achieve OPs goal "to merge SKSpriteNode into SKTexture".
Using Xcode 13.4.1 on macOS 12.4

SKEffectNode to an SKTexture?

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.

Put a ball inside a rectangle trapped inside, with some velocity and restitution

In this scene:
let rectangle = SKShapeNode(rectangleOfSize: CGSize(300, 400))
rectangle.fillColor = UIColor.clearColor()
rectangle.strokeColor = UIColor.grayColor()
rectangle.strokeWidth = 10
self.addChild(rectangle)
I want to put a ball inside this rectangle, trapped inside, with some velocity and restitution, that will be bouncing and changing the vector direction when collides with one of the walls.
I am not 100% sure if you can do that with SKShapeNodes and physics bodies because the physics body will apply to the whole shape node (with center).
Since you want to make the rectangle visible you can could either make a SKSpriteNode subclass with 4 SKSpriteNodes sides and than shape them as a rectangle.
Alternatively you could just draw a rectangle border in your program of choice, just make sure the centre is nothing basically, and export as PNG. Than just create a SKSpriteNode with the image.
let rectangle = SKSpriteNode(imageNamed: "Rectangle")
let texture = SKTexture(imageNamed: "Rectangle")
rectangle.physicsBody = SKPhysicsBody(texture: texture, size: texture.size())
rectangle.physicsBody?.collisionBitMask = 1
Now if you have drawn the rectangle correctly it will only have a physics body on the 4 sides since you did not draw a centre.
Than you simply create your ball and give it a physics body like above, with the same collision bitMask. Position it in the centre of the rectangle and give it an impulse or something. It should bounce around as you wish without leaving the rectangle.
If you are unsure about physics bodies than you need to read a bit about it, e.g
http://www.techotopia.com/index.php/A_Swift_iOS_8_Sprite_Kit_Collision_Handling_Tutorial

Resources