Sprite Kit physicsBody.resting behavior - swift

I am using Swift and Sprite Kit to develop a game on XCode Beta 6.
In order to detect if all nodes are sleeping, i check their physicsBody.resting property.
In update method i print out the result.
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var hero:SKSpriteNode!
override func didMoveToView(view: SKView) {
self.physicsWorld.gravity = CGVectorMake(0, 0)
self.physicsWorld.contactDelegate = self
self.physicsBody = SKPhysicsBody(edgeLoopFromRect:self.frame)
hero = SKSpriteNode(imageNamed: "Spaceship")
hero.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
hero.zPosition = 10.0
hero.physicsBody = SKPhysicsBody(circleOfRadius: hero.size.width/2)
hero.physicsBody.allowsRotation = false
hero.physicsBody.linearDamping = 0.5
self.addChild(hero)
}
override func update(currentTime: CFTimeInterval) {
if hero.physicsBody.resting {
println("resting")
} else {
println("moving")
}
}
}
To my surprise, the results are:
moving
resting
moving
(n times the same)
moving
resting
So why the hero is moving, although i didn't do anything. The node moves N times and takes a break(resting), after that goes on moving.
Can anyone explain that behaviour? Is that a bug or do i miss something? Thanks in advance.

If you examine the velocity of a physics body, you'll see that it is indeed moving but at a rate that is not perceivable. That's why the resting property is not set. A more reliable way to check if a SKPhysicsBody is at rest is to test if its linear and angular speeds are nearly zero. Here's an example of how to do that:
func speed(velocity:CGVector) -> Float {
let dx = Float(velocity.dx);
let dy = Float(velocity.dy);
return sqrtf(dx*dx+dy*dy)
}
func angularSpeed(velocity:CGFloat) -> Float {
return abs(Float(velocity))
}
// This is a more reliable test for a physicsBody at "rest"
func nearlyAtRest(node:SKNode) -> Bool {
return (self.speed(node.physicsBody.velocity)<self.verySmallValue
&& self.angularSpeed(node.physicsBody.angularVelocity) < self.verySmallValue)
}
override func update(_ currentTime: TimeInterval) {
/* Enumerate over child nodes with names starting with "circle" */
enumerateChildNodesWithName("circle*") {
node, stop in
if (node.physicsBody.resting) {
println("\(node.name) is resting")
}
if (self.nearlyAtRest(node)) {
println("\(node.name) is nearly resting")
}
}
}

Related

Increasing the size of a spriteNode using spriteKit in update function (Swift)

I'm having troubles with animations, physics and stuff like these.
Anyway, what I'm trying to do is creating a pulsing effect for a sphere where the pulse itself is able to collide with elements on the screen, so I created another spriteNode (child of the sphere) that I want to scale continuously, without depending on touches.
I read that in these cases is better not to use SKActions.
My question is: how do I scale or increase the size of a spriteNode via update function?
The code represents how I worked so far for movement for example.
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var entities = [GKEntity]()
var graphs = [String: GKGraph]()
var movingSquareLv1 = SKSpriteNode()
var pulsingSphereLv2 = SKSpriteNode()
var lv2SpherePulse = SKSpriteNode()
var xVeloicty: CGFloat = 100
override func sceneDidLoad() {
movingSquareLv1 = self.childNode(withName: "movingSquare") as!SKSpriteNode
movingSquareLv1.physicsBody = SKPhysicsBody(rectangleOf: movingSquareLv1.size)
movingSquareLv1.physicsBody ? .affectedByGravity = false
pulsingSphereLv2 = self.childNode(withName: "pulsingSphere") as!SKSpriteNode
pulsingSphereLv2.physicsBody ? .affectedByGravity = false
lv2SpherePulse = pulsingSphereLv2.childNode(withName: "lv2SpherePulse") as!SKSpriteNode
lv2SpherePulse.physicsBody ? .affectedByGravity = false
}
func touchDown(atPoint pos: CGPoint) {
}
func touchMoved(toPoint pos: CGPoint) {
}
func touchUp(atPoint pos: CGPoint) {
}
override func touchesBegan(_ touches: Set < UITouch > , with event: UIEvent ? ) {
for t in touches {
self.touchDown(atPoint: t.location( in: self))
}
}
override func touchesMoved(_ touches: Set < UITouch > , with event: UIEvent ? ) {
for t in touches {
self.touchMoved(toPoint: t.location( in: self))
}
}
override func touchesEnded(_ touches: Set < UITouch > , with event: UIEvent ? ) {
for t in touches {
self.touchUp(atPoint: t.location( in: self))
}
}
override func touchesCancelled(_ touches: Set < UITouch > , with event: UIEvent ? ) {
for t in touches {
self.touchUp(atPoint: t.location( in: self))
}
}
override func update(_ currentTime: TimeInterval) {
rectMovement()
}
func rectMovement() {
if movingSquareLv1.frame.maxX >= self.size.width / 2 {
xVeloicty = -100
} else if movingSquareLv1.frame.minX <= -self.size.width / 2 {
xVeloicty = 100
}
let rate: CGFloat = 0.5
let relativeVelocity: CGVector = CGVector(dx: xVeloicty - movingSquareLv1.physicsBody!.velocity.dx, dy: 0)
movingSquareLv1.physicsBody!.velocity = CGVector(dx: movingSquareLv1.physicsBody!.velocity.dx + relativeVelocity.dx * rate, dy: 0)
}
}
This is the object composed of two sprites, the one I want to work on is the largest one.
Whoever told you to not use SKActions is wrong. This is exactly when you want to use an SKAction, and if you have multiple circles pulsing, you use the same action on all circles.
You really want to keep your update function as small as possible. Trust in appleā€™s optimizations because they have better access to the API than you do.
I don't know why whatever you read said that actions aren't appropriate, but if you want to change a node's size from update explicitly, then you can set its scaling.
myNode.setScale(2.5) // 2.5x as big as normal
myNode.setScale(0.5) // half size
myNode.xScale = 1.5
myNode.yScale = myNode.xScale * 2 // Set x and y separately for non-uniform scaling
See the documentation in the "Scaling and Rotation" section of SKNode:
https://developer.apple.com/documentation/spritekit/sknode

Get node last position

It's the third time I'm looking for help in this question.
I have a class name BallNode of kind SKShapeNode. Inside my code I also have a function the spawn ball every 3 second from the top side of the screen. Now, I want to set a function that locate the ball position every 1 second, and so, if the ball.position.y > 200 to print a message to the console.
The purpose of this is that if any ball will be at this position (not while it's falling down) the I will call another function. I tried to do it via SKAction, update(_ currentTime: CFTimeInterval), Timer but I didn't succeed and I really have no idea what to do...
update - my current code:
var timeType1: CFTimeInterval = 0.0
var timeType2: CFTimeInterval = 2.0
override func update(_ currentTime: CFTimeInterval) {
if (currentTime - timeType1 > timeType2){
print("time")
self.checkBallsPosition()
self.timeType1 = currentTime
}
self.enumerateChildNodes(withName: "color.BallNode") { node, _ in
self.checkBallsPosition()
}
}
func checkBallsPosition() {
self.enumerateChildNodes(withName: "BALL") { (node: SKNode, nil) in
let x = self.createTopBorder()
x.isHidden = true
let wait2 = SKAction.wait(forDuration: 1)
let action2 = SKAction.run {
let position = Double(node.position.y)
if position < 200 {
}
else if position > 200 {
print("bolbo")
node.removeFromParent()
}
}
self.run(SKAction.sequence([wait2,action2]))
}
}
thats what I try do to so far, as I said the problem is that I want to get the ball last position. because the ball fall down the screen the last position should be when it touch the bottom border of the screen or when it touches another ball. if I set it at update I get the ball position every frame or (as I did) every second - but not the last. another problem is that the ball position can always change depends on another balls (when collision occurs).
update #2 - another functions:
func spawnBalls() {
let wait = SKAction.wait(forDuration: 3)
let action = SKAction.run {
self.createBall()
}
run(SKAction.repeatForever((SKAction.sequence([wait, action]))))
}
func createBall(){
let ball = BallNode(radius: 65)
print(ball.Name)
print(ball._subName!)
ball.position.y = ((frame.size.height) - 200)
let ballXPosition = arc4random_uniform(UInt32(frame.size.width))
ball.position.x = CGFloat(ballXPosition)
ball.physicsBody?.categoryBitMask = PhysicsCategory.ball
ball.physicsBody?.collisionBitMask = PhysicsCategory.ball
ball.physicsBody?.contactTestBitMask = PhysicsCategory.topBorder
ball.delegate = self
addChild(ball)
}
You did not post the entire code you are using. Not knowing how you are spawning your balls, how is the hierarchy and how you are moving them, I can't reproduce it here.
I am not sure if I understand it right, but I believe it might be simpler than what you are doing. See if the code below helps you, I am making the balls fall with SKAction (you can do it with physics also), and checking when they go below zero, when I remove them.
private let ballSpeed : CGFloat = 400
private let ballRadius : CGFloat = 10
private func spawnBall(atPoint pos : CGPoint){
let b = SKShapeNode(circleOfRadius: ballRadius)
b.name = "ball"
b.position = pos
addChild(b)
b.run(SKAction.moveTo(y: -size.height, duration: TimeInterval((pos.y+size.height)/400)))
}
override func update(_ currentTime: TimeInterval) {
super.update(currentTime)
enumerateChildNodes(withName: "ball") { (node, _) in
if node.position.y < 0 {
node.removeFromParent()
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let pos = touch.location(in: self)
spawnBall(atPoint: pos)
}
}

Detect object proximity to walls

I am trying to write something really simple - and use SpriteKit on this instance.
The way I did it on other platforms, is by having an invisible child "stick" sticking out a bit. By detecting collision between the invisible "sticks" I can tell wherether the object is close to the wall or not.
I am trying to replicate the same thing using SpriteKit. Of course, I'd prefer to have an invisible "beam" coming out of the object and giving me distance - but that's probably too much hassle.
I'd appriciate any ways to improve on my silly project I got so far.
My Project so far
Thanks.
Here is what I came up with that doesn't involve physics...
Drag the mouse to move the car, and the label in the center updates telling you the distance to the closest wall. Release mouse to reset car.
Very simple example, can be updated to give more accurate measurements.
class GameScene: SKScene {
let car = SKSpriteNode(color: .blue, size: CGSize(width: 50, height: 100))
let label = SKLabelNode(text: "")
func findNearestWall() -> CGFloat {
// you can make this more advanced by using the CGPoint of the frame borders of the car, or even 6+ points for more accuracy
let closestX: CGFloat = {
if car.position.x < 0 { // left wall
return abs(frame.minX - car.position.x)
} else { // right wall
return abs(frame.maxX - car.position.x)
}
}()
let closestY: CGFloat = {
if car.position.y < 0 { // bottom wall
return abs(frame.minY - car.position.y)
} else { // top wall
return abs(frame.maxY - car.position.y)
}
}()
if closestX < closestY {
return closestX.rounded() // as closest wall distance
} else {
return closestY.rounded() // as closest wall distance
}
}
override func didMove(to view: SKView) {
removeAllChildren()
label.fontSize *= 2
addChild(car)
addChild(label)
}
override func mouseDown(with event: NSEvent) {
}
override func mouseDragged(with event: NSEvent) {
let location = event.location(in: self)
car.position = location
}
override func mouseUp(with event: NSEvent) {
car.position = CGPoint.zero
}
override func didEvaluateActions() {
label.text = String(describing: findNearestWall())
}
}

Add SpriteNodes and Remove Based on Time or click using SpriteKit

I am new to swift and looking to build my first basic game. The game I have in mind involves sprites generating at random and then disappearing based on time or a click if the click is within the time allocated. So far I have created the basic framework and am still messing around with design. My problem comes in where I can't seem to remove the sprite based on time (its generating fine). Any help is appreciated and thanks in advancešŸ˜Š
Below is the framework I've built up so far.
import SpriteKit
var one = SKSpriteNode()
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
let myFunction = SKAction.runBlock({()in self.addOne()})
let wait = SKAction.waitForDuration(5)
let remove = SKAction.runBlock({() in self.removeOne()})
self.runAction(SKAction.sequence([myFunction, wait, remove]))
}
func addOne() {
let oneTexture = SKTexture(imageNamed: "blue button 10.png")
let one = SKSpriteNode(texture: oneTexture)
one.position = CGPoint(x: CGRectGetMidX(self.frame) - 100, y: CGRectGetMidY(self.frame) + 250)
one.zPosition = 1
self.addChild(one)
}
func removeOne() {
one.removeFromParent()
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
It doesn't disappear because your create a new SpiteNode, but try to remove the old one, do it like this:
var one : SKSpriteNode! //instead of creating it without data, just define the type(not necessary, but I would do it)
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
let myFunction = SKAction.runBlock({()in self.addOne()})
let wait = SKAction.waitForDuration(5)
let remove = SKAction.runBlock({() in self.removeOne()})
self.runAction(SKAction.sequence([myFunction, wait, remove]))
}
func addOne() {
let oneTexture = SKTexture(imageNamed: "blue button 10.png")
one = SKSpriteNode(texture: oneTexture) //removed the let, so you dont create a new "one"
one.position = CGPoint(x: CGRectGetMidX(self.frame) - 100, y: CGRectGetMidY(self.frame) + 250)
one.zPosition = 1
self.addChild(one)
}
func removeOne() {
one.removeFromParent()
}
}

How to pause an SKSpriteNode, Swift

I created this game using sprite kit. During the game sprite nodes are moving. When the "Game Over" Label pops up, I would like the monster sprite node to stop moving or pause, but the rest of the scene to still move on. I know where to put the code, I just don't know how to write it. This is my monster code.
func addMonster() {
// Create sprite
let monster = SKSpriteNode(imageNamed: "box")
monster.setScale(0.6)
monster.physicsBody = SKPhysicsBody(rectangleOfSize: monster.size)
monster.physicsBody?.dynamic = true
monster.physicsBody?.categoryBitMask = UInt32(monsterCategory)
monster.physicsBody?.contactTestBitMask = UInt32(laserCategory)
monster.physicsBody?.collisionBitMask = 0
monster.name = "box"
var random : CGFloat = CGFloat(arc4random_uniform(320))
monster.position = CGPointMake(random, self.frame.size.height + 20)
self.addChild(monster)
}
EDIT
override func update(currentTime: CFTimeInterval) {
if isStarted == true {
if currentTime - self.lastMonsterAdded > 1 {
self.lastMonsterAdded = currentTime + 3.0
self.addMonster()
}
self.moveObstacle()
} else {
}
self.moveBackground()
if isGameOver == true {
}
}
If I understand your question correctly, you want to pause a single (or small number of) nodes. In that case...
monster.paused = true
Is probably what you want.