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.
Related
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.
Near as I can tell, there isn’t a way to add physics joints in the scene editor. Is that right?
Consider a simple person object with a body, child legs and arms w/ pin joints. If I want to design this person in the scene editor and then programmatically add him to a scene, I’m not getting very far. I’m able to find the nodes in the scene, remove them from their parent and add them as a child at a new position in my scene, but I still have to specify all their joints manually.
Thoughts?
Here is my solution. I'm still hoping there is a way to create physics joints with the scene editor but I haven't found it so...
Step 1) Add all the child nodes to the scene, ensuring that objects are grouped by parent.
Step 2) Define a swift class for your complex node.
class MyNode : SKSpriteNode {
func spawn(parentNode: SKNode, position: CGPoint) {
parentNode.addChild(self) // before physics joints
let arm = self.childNode(withName:"arm")
// note if you didn't add physics bodies in scene file
// do that first
let shoulders = SKPhysicsJointPin.joint(withBodyA:self.physicsBody!, bodyB: arm.physicsBody!, anchor: CGPoint(x:position.x,y:position.y-1))
scene!.physicsWorld.add(shoulders)
// feature of pulling a child from a scene, it's always paused by default.
self.isPaused = false
}
}
Set the class for your body node in your scene.
Step 3) Transfer your node to your game scene at init time.
let tmpScene = SKScene.init(fileNamed: "MyNode.sks")
var myNode = tmpScene.childNamed(withName:"myNode") as! MyNode
myNode.removeFromParent()
myNode.spawn(world, position) // or your own parent and position as needed for your scene
I am curious if it is possible to fire images from the middle of the screen, to anywhere on the screen with a random speed.
It is important that it will be done without files containing .sks, since Xcode is crashing when I try opening that kind of files. It has to be done in a UIViewController.
I already tried subclassing a view to a SKView but without success. Looking at existing projects always uses SKViews and files containing .sks which I can not open. I hope somebody can help me out!
Each of the images you need to "fire" should be an SKSpriteNode. They should be children of a SKScene. If you don't know how to create a SKScene or how to make the view controller present it, try creating a sample game project in Xcode - File > New Project > Game. It would create a nice sample project that would display some nice rectangles whenever you touch the screen.
Once you have your scene up and running, as well as your sprite nodes ready to fire, simply run these few lines for every node you need to fire from the middle of the screen:
let viewSize = UIScreen.main.bounds.size
let randomX = RandomInt(min: 0, max: viewSize.width)
let randomY = RandomInt(min: 0, max: viewSize.height)
let randomPosition = CGPoint(x: randomX, y: randomY)
let minDuration = 1 // Put here whatever value you feel fit for the minimum duration of flying
let maxDuration = 5 // Put here whatever value you feel fit for the maximum duration of flying
let randomDuration = TimeInterval(RandomInt(min: minDuration, max: maxDuration))
let fireAtWill = SKAction.move(to: randomPosition, duration: randomSpeed)
yourSpriteNode.runAction(fireAtWill)
Don't forget to replace "yourSpriteNode" with the name of the sprite node you need to fire.
Here is the RandomInt helper function:
func RandomInt(min: Int, max: Int) -> Int {
return Int(arc4random_uniform(UInt32(max-(min-1)))) + min
}
Your problem with sks files is an odd one - you may want to ask a separate question on that with more details.
You can use SpriteKit without sks files though - they are just a shortcut but everything can be done in code if you need/want to. See this SO answer for a discussion, an example and some useful links.
I'm implementing a mass-spring system (many small physics bodies joined together with SKPhysicsJointSpring instances) with SpriteKit. Some of the particles would get snagged while traversing the center of the scene.
There seems to be a small, static body in the middle of the scene and I don't know why it's there.
Here's an easy way to see what I'm talking about:
In XCode 8, create a brand new project with the "Game" template.
In GameViewController.viewDidLoad(), add view.showsPhysics = true
If you run the project, you should see a little dot in the middle, which is the errant body:
Anyone know how to get rid of it?
Edit: I tried to manually create the scene object:
In GameViewController.viewDidLoad(), I replaced this:
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
view.presentScene(scene)
}
with this:
let scene = GameScene(size: view.frame.size)
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
view.presentScene(scene)
but that didn't fix it.
Anyways, I decided to make an answer because comments are not suitable due to lot of info I want to share. Also my answer, sadly, doesn't answer the question but it gives some useful info about this unidentified, obviously capable of flying (physics body) object :)
So this is the code how to grab it (and modify it???):
//you can use self.frame here...I just copied Alessandro's code
self.physicsWorld.enumerateBodies(in:(label?.frame)!) { body, stop in
if let node = body.node {
print("Type of this node: \(type(of:node))")
print("Frame of this node: \(node.frame))")
}else{
print("This body's node property is nil")
body.affectedByGravity = true
body.isDynamic = true
body.applyImpulse(CGVector(dx: 0.003, dy: 0.0003))
}
print("Area covered by this node physicsBody: \(body.area)")
}
So if you put a break point inside of that else statement, you can scan this physics body completely and get all the info about it, like that its node property is set to nil or that its isDynamic property is set to false. But you can change that, and like in my code, set for example isDynamics to true. This makes it moveable. So if you apply some forces to it, it will move.
Still, like I said in comments, I don't have an idea why it is there and what it represents or what is its purpose.
Also, for those who are wondering how it is possible that one physics body doesn't have a node associated with it ( body.node equals nil) but is still visible on screen when showsPhysics is set to true, there is a reasonable explanation. Physics world is separated from the node tree. So we can remove a sprite from a node tree, but that doesn't mean that its physics body will be removed instantly. It may happen that physics engine haven't finished simulation... So you probably wonder, how this might happen?
Let say you have three SKSpriteNode objects intersecting at the same time (say A contacts B and A contacts C at the same time). SpriteKit can process only one contact at time. And say that you are removing A from a parent when it is contacting with B. Then, there is a contact between A and C also, so didBegin:(_ contact) will be called twice. And if you remove A from its parent in first didBegin(_ contact) call, in the next didBegin(_ contact) call, bodyA.node will be nil (bodyA is a physics body of sprite A), but its physics body will remain visible until engine finishes what needed. This is because node tree and a physics world are separated.
About the "hello world" xCode game template , it seems a little physicsBody associated to the GameScene node.
With some code I've found this:
class GameScene: SKScene {
private var label : SKLabelNode?
private var spinnyNode : SKShapeNode?
override func didMove(to view: SKView) {
...
// End part of this function:
if let b = physicsWorld.body(in: (label?.frame)!) {
if let node = b.node {
print("Type of this node: \(type(of:node))")
print("Frame of this node: \(node.frame))")
}
print("Area covered by this node physicsBody: \(b.area)")
}
}
}
With a breakpoint to the last brace, you can see two bodies (maybe an array of bodies), one of them is the physicsBody to the left debugArea (array with index 1) with the same hex address as my body b in my code : 0x79794f90, a little rectangle body with area 4.444
Printing description of ((PKPhysicsBody *)0x79794f90):
<SKPhysicsBody> type:<Rectangle> representedObject:[<SKScene> name:'(null)' frame:{{-375, -667}, {750, 1334}} anchor:{0.5, 0.5}]
(lldb)
I had a similar problem. I have a game with two sprite nodes joined together (SKPhysicsJointFixed.joint) moving around an SKEditor created scene.
As per my design, this node-pair would impact a third sprite node and be propelled smoothly away from the third sprite node, EXCEPT when the impact was in the center of the scene. For the center of the scene impact, the node-pair would compress together while be propelled away from the third sprite node, presenting a poor graphical image.
After significant time debugging my code, I found this post. Kudos for the explanations and code. I can’t answer the “why” question but for the “particles would get snagged while traversing the center of the scene” question my suggested solution is to clear the collisionBitMask instead of moving the body.
BTW categoryBitMask is 0 when loaded.
//moves the problem offscreen unless it hits another node
//body.affectedByGravity = true
//body.isDynamic = true
//body.applyImpulse(CGVector(dx: 0.003, dy: 0.0003))
//
// collisionBitMask loads with 4294967295 = default value to collide with all categories
//
body.collisionBitMask = 0
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