setting up physics body from code when using required init(nscoder) - ios

I'm attempting to take advantage of the init?(coder aDecoder: NSCoder) function so that I can subclass sprites in my sks file.
I can set the physics body in the sks file, but it doesn't work too well with my rectangular sprites I'm using that also have rounded corners. Because of the rounded corners, prior to using the init function, I was setting up the physicsbody in code, and setting the width slightly smaller than the squares width (that way when my hero slides off the square, there isn't a gap where the rounded corner is..
I know I can use the alpha mask physics body to get the same affect, except that with this, There seems to be a 1 px'ish gap when my hero is on the blocks, and it doesn't look good.
Wondering the reason why I can't set the physics body this, and also how I can do it or get around it?
Thanks for any help!!!
Update:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
if let p = userData?.value(forKey: "col") as? String {
setup(p)
print("working")
}
}
func setup(_ col: String) {
setupPhysics()
//allSprites.append(self)
In the setupPhysics I have:
override func setupPhysics() {
physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: frame.width - 4, height: frame.height - 1))
...more code...
}
This is my code for the sprite, and I have it correctly setup in the sks file...
I commented out the append to allSprites, so that I can demonstrate that it doesn't work. With the append uncommented, the sprites get added to allSprites, and then in my didMove function in the Scene file, it loops through allSprites and runs the setupPhysics():
func setup(_ col: String) {
//setupPhysics()
allSprites.append(self)
// scene file
func setupAllSpritesPhysics() {
for sprite in allSprites { sprite.setupPhysics() }
}
#Knight0fDragon :
You mention that this is an ugly fix. I disagree given the fact that it won't work the 'correct' way. And currently I don't have enough sprites to cause any kind of lag in the scene loading. I can look into that in the future if that is the case.
I'll admit I haven't tried the method you mentioned because I don't want to create a child node for each sprite I create in order to get around this even if it works. I appreciate the advice so far though.

To make this work with the SKS file, I would add a child SKNode with the dimensions of the body you want, and then in the Custom class code, do
required init(coder aDecoder:NSCoder){
super.init(coder:aDecoder)
if let child = childNode(withName:"child"), let physicsBody = child.physicsBody{
self.physicsBody = physicsBody
child.removeFromParent()
}
}
What we are doing here is transfering the physics body from the child to the parent, and then killing the child off. This allows our code to be a little more dynamic, and keeps design seperate from functionality.
Well, nice little bug from apple.
What is happening is physicsWorld doesn't exist during the time NSCoder happens, and instead of placing the physics body attached to the sprites body into the world when it does exist, it decides to cache the one located in the SKS file, and this is why it doesn't work.
So a very convenient way to get around this issue, is to implement a worldNode (It is good practice to do this anyway, especially if you want to create a pause screen) and just remove it then add it back in during sceneLoad:
class GameScene: SKScene,SKPhysicsContactDelegate {
lazy var worldNode : SKNode = { self.childNode(withName:"//worldNode")!}()
override func sceneDidLoad() {
worldNode.removeFromParent()
addChild(worldNode)
}
}

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.

swift scene.sks file objects not applying z rotation value

I'm making a simple game application for IOS devices, loosely based from the book 'swift game development'. I have created a protocol which I use as a template for creating a class for each type of in game object. A platform for the player to jump on has the following class based on the protocol
import SpriteKit
class GrassyPlatform: SKSpriteNode, GameSprite {
var textureAtlas:SKTextureAtlas = SKTextureAtlas(named: "Enviroment")
var initialSize: CGSize = CGSize(width:630, height:44)
init() {
super.init(texture: textureAtlas.textureNamed("GrassPlatform1"), color: .clear, size: initialSize)
self.anchorPoint = CGPoint(x: 0.5, y:0.5)
physicsBody = SKPhysicsBody(rectangleOf: initialSize)
physicsBody?.restitution = 0
self.physicsBody?.isDynamic = false
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
I'm using the scene editor to place these objects onto the scene, assigning each object the relevant custom class. Like the one above.
When I run the game the objects position (assigned only from the scene editor) is respected, but the zRotation value is ignored. For example setting the platform in the scene editor as so
But this results in the platform appearing at the correct position but with the default zRotation value and not the one assigned in the game scene.
I can adjust the rotation manually through self.zRotation but this defeats the whole point of using the scene.sks for level design.
Is there away to adjust the zRotation through game scene and if so how?
Thanks
Solved the problem was in a piece of code that I never added to the original post because I 'thought' it was irrelevant! boy was I wrong. Another class handles the different scenes. This code I took from the book and was not 100% on its operation when I added it, before getting caught up in something else
class EncounterManager {
let encounterNames:[String] = ["Level1A", "Level1B"] //A array of all the scenes in the level
var encounters:[SKNode] = [] //each scene is a node
var currentEncounterIndex:Int?
var previousEncounterIndex:Int?
init() {
for encounterFileName in encounterNames { //Loop all scenes in the scene array
let encounterNode = SKNode() //create a new node for the encounter/scene
if let encounterScene = SKScene(fileNamed: encounterFileName) { //load the encounter to the skscene
for child in encounterScene.children { //Loop through each child node of the skscene
let copyOfNode = type(of: child).init() //copy the node type and initilize to the encounterNode
copyOfNode.position = child.position //copy the position
copyOfNode.zPosition = child.zPosition //copy of zPosition
copyOfNode.name = child.name //copy the name
encounterNode.addChild(copyOfNode) //add child to encounter node
}
}
encounters.append(encounterNode)
//Save initial sprite positions for this encounter
saveSpritePositions(node: encounterNode)
}
}
This takes a copy of each object node and adds it to the game scene. The origonal author never needed to worry about the rotation of objects. So I added this and it worked
copyOfNode.zRotation = child.zRotation //copy of zRotation
Thanks anyway if you looked.

Unexpected physicsBody in SpriteKit scene

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

Can I have physics bodies collide without pushing each other?

I have a game made of little SKNodes moving around. They can not overlap, so I use physics bodies, so that they move around each other. However, in the case that one sknode is animated to follow another, it pushes the sknode ahead. Setting the collision bitmask to 0 makes them overlap, so that is not an option. But otherwise, they push each other way beyond my desired speed. I need a way to get rid of the 'pushing' without overlapping using skphysics bodies. Is there a property I can set to fix this?
Notes: I use skanimations to move my nodes around. If you need any pertinent code, tell me... I don't know where to start. I am using Swift 3.0, but I will accept answers with 2.2/2.3 syntax.
EDIT: The real solution was to change the node's velocity instead of animating movement with SK Actions.
Change the restitution on your physics body of the moving object to 0 when a contact happens, and set the weight of the object you do not want to move really high
Have you tried setting the dynamic property to false on the body being pushed?
After you have set contactTestBitMask you could set this:
myHero.physicsBody!.collisionBitMask = 0
to prevent collisions but permit contacts.
Updating position of dynamic physics body with SKAction doesn't work well. It's just like saying - Hey, no matter what physics world rules say just move my node like I want.
First solution - to make one node follow another with some particular distance use SKConstraint.
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
let nodeA = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width: 50, height: 50))
nodeA.name = "nodeA"
addChild(nodeA)
let nodeB = SKSpriteNode(color: SKColor.redColor(), size: nodeA.size)
nodeB.name = "nodeB"
addChild(nodeB)
let followConstraint = SKConstraint.distance(SKRange.init(lowerLimit: nodeB.size.width * 1.5, upperLimit: nodeB.size.width * 1.5), toNode: nodeA)
nodeB.constraints = [followConstraint]
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let touch = touches.first else {
return
}
childNodeWithName("nodeA")!.runAction(SKAction.moveTo(touch.locationInNode(self), duration: 2))
}
}
If you still want physics bodies and SKActions...
Set "dynamic" property of every physics body to false.
Check in didBeginContact (SKPhysicsContactDelegate protocol) if they are overlapping. If it's true than remove all the actions that are changing their positions. Tip - make the physics body little bigger than it's node.

Super weird spritekit bug

Okay I've reproduced this in a simple project and want to see if anyone can figure this out.
https://drive.google.com/file/d/0B4kzFbLmJr19QzFEa3NEZERLVnc/view?usp=sharing
tap to send yellow squares at the red square causing a collision. The red square SHOULD rotate but it doesnt. Go into didBeginContact, and uncomment the code noted, and you will see it rotate. super weird.
I've hit a bug that has me stumped. I really cant figure this one out. I've tried all the obvious things, and simplified it down pretty far.
When I try to rotate my sprite inside of didBeginContact I have to do so inside of an action for it to work. Look at the following example:
doesnt work
func didBeginContact(contact: SKPhysicsContact) {
println(self.hyperShip.zRotation) // prints 0 (next time STILL zero, NO PLACE IS THIS RESET)
self.hyperShip.zRotation = 1
println(self.hyperShip.zRotation) // prints 1
}
if i run the code in an action
works
func didBeginContact(contact: SKPhysicsContact) {
println(self.hyperShip.zRotation) // first time 0, thereafter 1
self.runAction(SKAction.runBlock({
self.hyperShip.zRotation = 1
}))
println(self.hyperShip.zRotation) // first time 0, thereafter 1
}
what is the difference? why would this behave any differently. it's so freaking weird. I can't see why they behave any differently.
if I trigger this code in my update or wherever else it works just fine the normal way
hypership is a subclass of another subclass of skspritenode (baseship).. not sure that why that'd make a difference, but i figured id include that detail
EDIT: how I assign hypership
I assign it at the top of hyperscene as a property
let hyperShip: HyperShip = HyperShip()
some setup in my scenes init
hyperShip.lives = self.lives
self.indicators.livesDisplay.text = "\(hyperShip.lives)"
hyperShip.addToParent(gameLayer)
addToParent function
override func addToParent(parentNode: SKNode){
super.addToParent(parentNode)
parentNode.addChild(self)
self.hyperScene = parentNode.scene as HyperScene
self.shipTrail.zPosition = -1
self.shipTrail.position = CGPoint(x: convertNum(-8), y: 0)
self.addChild(self.shipTrail)
self.hyperTrail.targetNode = self.hyperScene.starFieldFG.node
self.addChild(self.hyperTrail)
}
addToParent function in baseShip
func addToParent(parentNode: SKNode){
self.baseScene = parentNode.scene as BaseScene
}
hypership's init
override init(){
super.init()
}
baseship's init
override init() {
let texture = shipAtlas.textureNamed("ship")
super.init(texture: texture, color: nil, size: texture.size())
self.color = SKColor.grayColor()
self.zPosition = 1
}
I could copy paste more code, related to hyperships functionality, but I feel like its unrelated code. It's a fairly simple class.

Resources