Sprite Kit - Creating node from didBeginContact() - ios

I am using a custom brick class:
import SpriteKit
class Brick: SKSpriteNode {
enum type { case Normal }
convenience init (type: Brick.type) {
self.init(color: .greenColor(), size: CGSizeMake(75, 25))
physicsBody.SKPhysicsBody(rectangleOfSize: size)
physicsBody!.mass = 9999
physicsBody!.affectedByGravity = false
position = CGPointMake(100, 50)
// Category and collision bitmasks ...
// Action to move the brick upwards ...
}
// Stuff
}
In my scene, I'm creating one initial brick, and everything works just fine. However, when I try to add a brick in the didBeginContact() function, I get unexpected results.
I have a SKNode at 1/5 height of the screen. So everytime a brick reaches that height, it will make contact with this invisible node and create a new brick:
// Inside SKScene
func didBeginContact(contact: SKPhysicsContact) {
// I set physics body A and B ...
if a.categoryBitMask == Category.brick && b.categoryBitMask == Category.brickSpawner {
addChild(Brick(type: .Normal))
}
}
So the problem is: when I create a new brick inside this function, the position is set to (0, 0) instead of (100, 50) as defined in the Brick class. Actually, if I go ahead and use println(brick.position) I get (100, 50), but in the screen it looks positioned at (0, 0).
If I create a brick anywhere else in the code, for example in the touchesBegan() or update() functions, the position is set correctly. This problem only happens when creating the node from didBeginContact().

This should help, it explains the steps in a single frame.
https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Introduction/Introduction.html
You are creating it at the wrong time, so one of the steps behind the scenes is resetting it due to a few steps being missing. Queue that process up for the didFinishUpdate command. ( I would just create the object in the did contact, then throw it into an array, then on didFinishUpdate, go through the array and add them to the scene, and clear the array)

Related

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

SpriteKit: how to smoothly animate SKCameraNode while tracking node but only after node moves Y pixels?

This question and others discuss how to track a node in SpriteKit using a SKCameraNode.
However, our needs vary.
Other solutions, such as updating the camera's position in update(_ currentTime: CFTimeInterval) of the SKScene, do not work because we only want to adjust the camera position after the node has moved Y pixels down the screen.
In other words, if the node moves 10 pixels up, the camera should remain still. If the node moves left or right, the camera should remain still.
We tried animating the camera's position over time instead of instantly, but running a SKAction against the camera inside of update(_ currentTime: CFTimeInterval) fails to do anything.
I just quickly made this. I believe this is what you are looking for?
(the actual animation is smooth, just i had to compress the GIF)
This is update Code:
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
SKShapeNode *ball = (SKShapeNode*)[self childNodeWithName:#"ball"];
if (ball.position.y>100) camera.position = ball.position;
if (fabs(ball.position.x-newLoc.x)>10) {
// move x
ball.position = CGPointMake(ball.position.x+stepX, ball.position.y);
}
if (fabs(ball.position.y-newLoc.y)>10) {
// move y
ball.position = CGPointMake(ball.position.x, ball.position.y+stepY);
}
}
I would not put this in the update code, try to keep your update section clutter free, remember you only have 16ms to work with.
Instead create a sub class for your character node, and override the position property. What we are basically saying is if your camera is 10 pixels away from your character, move towards your character. We use a key on our action so that we do not get multiple actions stacking up and a timing mode to allow for the camera to smoothly move to your point, instead of being instant.
class MyCharacter : SKSpriteNode
{
override var position : CGPoint
{
didSet
{
if let scene = self.scene, let camera = scene.camera,(abs(position.y - camera.position.y) > 10)
{
let move = SKAction.move(to: position, duration:0.1)
move.timingMode = .easeInEaseOut
camera.run(move,withKey:"moving")
}
}
}
}
Edit: #Epsilon reminded me that SKActions and SKPhysics access the variable directly instead of going through the stored property, so this will not work. In this case, do it at the didFinishUpdate method:
override func didFinishUpdate()
{
//character should be a known property to the class, calling find everytime is too slow
if let character = self.character, let camera = self.camera,(abs(character.position.y - camera.position.y) > 10)
{
let move = SKAction.move(to: character.position, duration:0.1)
move.timingMode = .easeInEaseOut
camera.run(move,withKey:"moving")
}
}

Detecting when SKSpriteNode is completely below another SKSpriteNode

For a game I'm creating, an SKSpriteNode gradually makes its way down the user's screen. There's another SKSpriteNode (position is static) near the bottom of the screen, leaving only a little bit of space for the original SKSpriteNode to fit in. I need to detect when the first SKSpriteNode is COMPLETELY below the second SKSpriteNode, which I'm having a bit of trouble with. Here's the code I'm currently using:
if (pos.y > barPosY) //pos.y = 1st SKSpriteNode, barPosY = 2nd SKSpriteNode
{
touchedTooEarly = true
}
For some reason, when the first SKSpriteNode goes just a little bit over the 2nd SKSpriteNode (not all the way), it still detects it as being completely over. Is there a coordinate space issue I'm missing?
The logic
A sprite a covers a sprite b if
b.frame is inside a.frame
b.zPosition is below a.zPosition
The extension
Now let's build an extension
extension SKSpriteNode {
func isCoveredBy(otherSprite: SKSpriteNode) -> Bool {
let otherFrame = CGRect(
origin: convertPoint(otherSprite.position, fromNode: otherSprite),
size: otherSprite.frame.size
)
return zPosition < otherSprite.zPosition && CGRectContainsRect(frame, otherFrame)
}
}
Problem #1: transparency
This mechanism totally ignores transparency.
Problem #2: same sprite
If you compare 2 sprites of the same type the function CGRectContainsRect will return true only when they are exactly aligned. Althoug you can solve this problem creating a smaller rectangle when you compare the sprites.

Track the position of SKSpriteNode while doing a SKAction moveTo

I'm trying to figure a way to track a postions for multiple nodes, that spwan randomly on the screen so i can make a changes to them while moving when the reach random postion.
the nodes just move along the x axis and i want to be able to generate random number from 0 to postion.x of the ball, and change the color when it reachs the postion
override func update(currentTime: CFTimeInterval)
i tried tacking changes in update method but as soon as new node appers i lost track of the previos one
i also tried
let changecolor = SKAction.runBlock{
let wait = SKAction.waitForDuration(2, withRange: 6)
let changecoloratpoint = SKAction.runBlock { self.changecolorfunc(self.ball)}
let sequence1 = SKAction.sequence([wait, changecoloratpoint])
self.runAction(SKAction.repeatAction(sequence1, count: 3))
}
but it doesn't give me any control over the random postion
You have already all you needed.
Suppose you have a reference for your node:
var sprite1: SKSpriteNode!
And you want to spawn it to a random position (an example method..):
self.spawnToRandomPos(sprite1)
And suppose you want to moveTo your sprite1:
self.sprite1.runAction( SKAction.moveToY(height, duration: 0))
Everytime you check his position you know where is it:
print(sprite1.position)
So to know always your sprite1 position you could do this code and you see all it's movements:
override func update(currentTime: CFTimeInterval) {
print(sprite1.position)
}
P.S.:
In case you dont have references about your object spawned you can also give a name to a generic object based for example by a word followed to a counter (this is just an example):
for i in 0..<10 {
var spriteTexture = SKTexture(imageNamed: "sprite.png")
let sprite = SKSpriteNode(texture: spriteTexture, size: spriteTexture.size)
sprite.name = "sprite\(i)"
self.addChild(sprite)
}
After this to retrieve/re-obtain your sprite do:
let sprite1 = self.childNodeWithName("sprite1")
There are probably a hundred different ways of doing this. Here is how I would do it.
in your update func
checkForCollisions()
this will just scroll through "obstacles" that you randomly generate, and place whoever you want the color trigger to happen. They can be anything you want just change the name to match. if they overlap your "ball" then you can color one or the other.
func checkForCollisions() {
self.enumerateChildNodesWithName("obstacles") { node, stop in
if let obstacle: Obstacle = node as? Obstacle {
if self.ball.intersectsNode(obstacle) {
//color node or ball whichever you want
}
}
}
}

Swift SKSpriteNode Position Issues - logging correct, placement wrong

the situation
I am working through a Lynda.com video series (iOS Game Dev. With Sprite Kit), the tutorial is last gen for Objective-C and as a training exercise I am converting it to Swift and playing with new functionality. The goal is an old school brick breaking game.
I have an updated full project repository on GitHub here
the issue
in the GameScene.swift file, I have written a method addBricks(size: CGSize) :
func addBricks (size: CGSize){
println(" the input parameter size: \(size)")
var maxRows = 1
var maxCols = 1
var xPos : CGFloat
var yPos : CGFloat
for (var rows = 0; rows < maxRows; rows++){
for (var i = 0; i < maxCols ; i++){
var brick: SKSpriteNode = SKSpriteNode(imageNamed: "brick")
brick.physicsBody = SKPhysicsBody(rectangleOfSize: brick.frame.size)
xPos = CGFloat(size.width) / CGFloat(maxCols+1) * CGFloat(i + 1)
yPos = CGFloat(size.height) - CGFloat(80 * rows) - 100.0
brick.physicsBody.dynamic = false
brick.physicsBody.categoryBitMask = brickCategory
brick.shadowCastBitMask = 1
brick.lightingBitMask = 1
//brick.position = CGPointMake(CGFloat(xPos), CGFloat(yPos))
brick.position = CGPointMake(320.0, 1036.0)
println("Brick position - xpos: \(xPos), ypos: \(yPos) || overall:\(brick.position)")
self.addChild(brick)
}
}
}
I first call it in the scene from didMoveToView() and everything is peachy, my brick nodes are placed in the appropriate locations. In my game logic I later call this method from didBeginContact(), when the ball strikes the paddle after a logic check to see there are no bricks left. In the "When you run out of bricks, add more" sense...
func didBeginContact(contact: SKPhysicsContact!){
var notTheBall : SKPhysicsBody
// check the contacts and find the one thats not the ball
if ( contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask ){
notTheBall = contact.bodyB
} else {
notTheBall = contact.bodyA
}
/* more logic here for other contacts */
if ( notTheBall.categoryBitMask == paddleCategory ){
self.runAction(playSFXBlip)
//println("\(self.children.count) and \(staticSize)")
if ( self.children.count <= 5 ){
addBricks(self.frame.size)
}
}
}
On any subsequent method calls to addBricks(), ALL of the bricks are placed in what appears to be point 0,0 at the bottom left of the view, even when console is showing they are placed at a different position:
the self object is the current instance of SKScene println(self) -> <SKScene> name:'(null)' frame:{{0, 0}, {640, 1136}}
i've tried:
casting all elements of my position calculation as Floats and CGFloats
debugging the addBricks method by setting static float values
making a property for frame size outside of the contact method and passing that size to addBricks.
google-ing and consulting the Swift Programming Language Guide and the docs for anything relevant
what's causing inappropriate positioning? thanks in advance sorry for the TL;DR
The problem is, that you're trying to set the position of a Node in your didBeginContact method. You cannot position a node in didBeginContact that already has a physicsbody.
The ideal solution would be to add the bricks without a physicsbody, then set it's position to where ever you like, and finally add the physicsbody.
Another solution is to create some code that is being called after you've run your didBeginContact. For example
func didBeginContact(contact: SKPhysicsContact!){
var notTheBall : SKPhysicsBody
if ( contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask ){
notTheBall = contact.bodyB
} else {
notTheBall = contact.bodyA
}
if ( notTheBall.categoryBitMask == paddleCategory ){
addBricksInUpdate = true
}
}
func update:(currentTime: CFTimeInterval) {
if (addBricksInUpdate) {
if ( self.children.count <= 5 ){
addBricks(self.frame.size)
}
}
}
I don't know enough about what "self" is when you are calling, or what self's properties and values are, but when child nodes appear in the bottom left corner it is usually for one of two reasons:
1-The objects have been placed at (0,0) - either because that's their position, or because some other constraint, like size or rect, returned zero values. So you have a parent with size (0,0), or converted coordinates, etc.
[OR]
2- The parent node to the objects is offset/scaled/positioned from the "screen" rect in such a way that the values you are inputting for position on the child have had the children end up looking like they are in the bottom left corner, even though they are actually being placed relative to the parent or the paren't coordinate space. This is easily checked by seeing if different coords still put the object in the same place.
So, check the anchor points for your parent, be it a scene or a node. And check the size and position of the parent. I've heard mention that there are strange default size settings for the sks file in swift with regards to sprite kit and scene size, but haven't opened a swift project yet.

Resources