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.
Related
I have found a strange issue starting with iOS 11 where my app will not update sprites on my SKReferenceNode. Basically, I have an "overlay" node (an SKS file that I use for multiple scenes) that has a header and footer. In the header, there is a score bar that expands when the player scores points. I am animating the score bar with an SKAction. Since iOS 11, the score bar does not appear. It happens both on the simulator and device. What's interesting is if I pause/resume the app through the debugger, the score bar immediately appears. I was able to strip down the issue to the following code.
import SpriteKit
var overlayNode:SKNode?
var header = SKSpriteNode()
var scoreBar:SKSpriteNode?
var scaleX:CGFloat = 0.3
class GameScene: SKScene {
override func didMove(to view: SKView) {
let path = Bundle.main.path(forResource: "SceneOverlay", ofType: "sks")
let overlay = SKReferenceNode(url: URL(fileURLWithPath: path!))
self.addChild(overlay)
overlayNode = overlay.children.first
header = overlayNode!.childNode(withName: "header") as! SKSpriteNode
scoreBar = SKSpriteNode(imageNamed: "redBar")
scoreBar?.position = CGPoint(x: 0, y: 0)
scoreBar?.zPosition = 10
scoreBar?.anchorPoint = CGPoint(x: 0, y: 0.5)
scoreBar?.xScale = 0
header.addChild(scoreBar!)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
scaleX += 0.1
let scoreBarExpand = SKAction.scaleX(to: scaleX, duration: 0.8)
scoreBar!.run(scoreBarExpand)
print ("Expanding score bar to \(scaleX)")
}
}
The overlay SKS file is simply this:
SKS Overlay Image
If you run this code on an iOS 11 simulator or device, and tap on the screen, the score bar should appear and scale up by 10% each time you tap. However, you won't see anything. You can then pause and resume the app within the Xcode debugger, and the bar will immediately appear and expand. Again, this issue does not appear on an iOS 10 device/simulator.
Any thoughts on what's going on here?
Thanks in advance.
As I've pointed in some other scenarios, but they all point to the same issue. I've discovered a serious issue with iOS11 and SpriteKit. The Game Scene during the transition is getting paused automatically (but only in iOS11).
Knight0fDragon has also pointed out that it looks like the objects are getting paused on creation not during transition. Same result though.
After transitioning to the scene run
myScene.isPaused = false
and it should fix the issue, and should have no adverse affects when the game is run on iOS10
I was having the same problem in my ObjC project.
Knight0fDragons answer didn't work directly for me, but it inspired me.
Strangely first pausing and then unpausing worked for me.
myScene.paused = YES;
myScene.paused = NO;
Hope this helps someone.
I’m trying to animate specific parts of a SCNScene object in SceneKit (in my case I want to animate fingers of a hand). I import the .dae (COLLADA) file easily from Blender with the respective bones to generate articulation on the model.
override func viewDidLoad() {
super.viewDidLoad()
var scene = SCNScene(named: "hand.dae")!
sceneView.scene = scene
sceneView.allowsCameraControl = true
sceneView.autoenablesDefaultLighting = true
sceneView.backgroundColor = UIColor.lightGrayColor()
}
My goal is to animate those bones on iOS with user generated values between 0 and 1. Imagine a UISlider where you scroll back and forth and see the specific finger move depending on the value of the slider.
This is needed animation screenshot
Image with the animation pretended
I’ve tried animate the model by calling an animation file like the Apple’s Fox example:
private var indexFingerAnimation: CAAnimation!
indexFingerAnimation = CAAnimation.animationWithSceneNamed(“move_index_finger.dae”)
indexFingerAnimation = false
indexFingerAnimation = 0.3
indexFingerAnimation = 0.3
indexFingerAnimation = Float.infinity
The problem is that’s a Global animation instead of just the index finger. Besides it’s always a ‘pre-defined’ animation instead of an animation controlled by user input. Ultimately I want to mix animations (e.g. move index finger and thumb at the same time revealing gestures)
Is this possible? I’m struggling because I can’t figure out how to manipulate specific parts of the mesh. I’m starting to study MetalKit but it’s not clear to me that’s the solution.
Any help would be really appreciated.
I have never tried two animations at the same time
but I can rotate SCNNode in dae file with two or more animate
You must set pivot point and group them together
I'm teaching myself how to do SpriteKit programming by coding up a simple game that requires that I lay out a square "game field" on the left side of a landscape-oriented scene. I'm just using the stock 1024x768 view you get when creating a new SpriteKit "Game" project in XCode - nothing fancy. When I set up the game field in didMoveToView(), however, I'm finding the coordinate system to be a little weird. First of all, I expected I would have to place the board at (0, 0) for it to appear in the lower-left. Not so -- it turns out the game board has to be bumped up about 96 pixels in the y direction to work. So I end up with this weird code:
let gameFieldOrigin = CGPoint(x:0, y:96) // ???
let gameFieldSize = CGSize(width:560, height: 560)
let gameField = CGRect(origin: gameFieldOrigin, size: gameFieldSize)
gameBorder = SKShapeNode(rect: gameField)
gameBorder.strokeColor = UIColor.redColor()
gameBorder.lineWidth = 0.1
self.addChild(gameBorder) // "self" is the SKScene subclass GameScene
Furthermore, when I add a child to it (a ball that bounces inside the field), I assumed I would just use relative coordinates to place it in the center. However, I ended up having to use "absolute" coordinate and I had to offset the y-coordinate by 96 again.
Another thing I noticed is when I called touch.locationInNode(gameBorder), the coordinates were again not relative to the border, and start at (0, 96) at the bottom of the border instead of (0, 0) as I would have guessed.
So what am I missing here? Am I misunderstanding something fundamental about how coordinates work?
[PS: I wanted to add the tag "SpriteKit" to this question, but I don't have enough rep. :/]
You want to reference the whole screen as a coordinate system, but you're actually setting all the things on a scene loading from GameScene.sks. The right way to do is modify one line in your GameViewController.swift in order to set your scene size same as the screen size. Initialize scene size like this instead of unarchiving from .sks file:
let scene = GameScene(size: view.bounds.size)
Don't forget to remove the if-statement as well because we don't need it any more. In this way, the (0, 0) is at the lower-left corner.
To put something, e.g. aNode, in the center of the scene, you can set its position like:
aNode.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame));
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 :)
I'm trying to teach myself Sprite Kit and Swift. What I'm trying to do is access a child node from an ArcheryScene.sks file and affect that child from the ArcheryScene.swift file. For Example: In my ArcheryScene.swift file I have added this line of code: let scene = SKScene(fileNamed: "ArcheryScene").
This compiles fine and when I say println(scene), it correctly prints the scene I want it to print, this is how I know that ArcheryScene.sks is truly in my scene variable. After this, I access a child from that scene by adding this line of code: let ballChild = scene.childNodeWithName("Ball"). When I use println(ballChild), it prints the correct child, letting me know that the variable truly contains Ball child that is in ArcheryScene.sks. And now to my problem...
Why can't I say things in my ArcheryScene.swift file like:
ballChild?.physicsBody?.affectedByGravity = false
or
ballChild?.position.x = self.frame.size.width / 2
or
let move = SKAction.moveByX(40, y: 0, duration: 5.0)
ballChild?.runAction(move)
All of this code will compile without errors but when I run the game, the Ball is not affected at all. Also, if I run ballChild?.position.x = self.frame.size.width / 2 and then print the ballChild position, it will show up as x: 512, which is what it should be, but still when I run the game, the Ball is not affected. This is really confusing to me and I'd just like to figure out what is going on. Any suggestions would be appreciated, thank you.
If you look at the definition of Class ArcheryScene you will see it is a subclass of SKScene - so the class you are coding in is already your scene. The UIViewController subclass in your project has loaded ArcheryScene.sks and associated it with an instance of ArcheryScene.
When you subsequently say let scene=SKScene(fileName:"ArcheryScene.sks") you are actually creating a new instance of the scene that isn't presented into your SKView. You then modify the ball in that scene, but nothing happens as this is not the ball that is visible.
Instead you should say
let ballChild=self.childNodeWithName("Ball")
if (ballChild? != nil) {
let move=SKAction.moveByX(50 y:10 duration:10.0)
ballChild!.runAction(move)
}
When you write your following line :
let scene = SKScene(fileNamed: "ArcherySceneSKS")
You are creating a new scene. So the ballChild you are accessing is another one (not the one inside your self (ArcheryScene class)).
If you ArcheryScene class is properly instantiated, can't you access the ball by doing like so ?
let ballChild = self.childNodeWithName("Ball")
Let me know if it helped.