Swift physicsbody edge recognition issue - ios

The following code recognizes the bottom and top edge of the scene and the ball bounces off as expected. However, the left and right edges of the scene are breached all the time. The ball goes off screen and then eventually returns back if enough force is applied. It is as if the edges of the scene are beyond the edges of the iphone simulator window. Can someone help? Thanks.
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView){
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
backgroundColor = UIColor.cyanColor()
var ball = SKSpriteNode(imageNamed: "ball")
ball.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
self.addChild(ball)
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.frame.size.height/2)
let push = CGVectorMake(10, 10)
ball.physicsBody.applyImpulse(push)
}
}

You can try to change self.physicsBody size for example;
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: CGRectMake(self.frame.origin.x, self.frame.origin.y, self.size.width, self.size.height))
and change the parameter self.size.width as you wish

Related

SKSpriteNode losing velocity upon collision

I am making an iOS app and running into some problems with physics. As you can tell by the .GIF below, when I rotate the hexagon and the ball hits the rectangle at an angle, it loses some of its velocity and doesn't bounce as high. This is because of the reason shared here (basically because I am constraining the balls horizontal position, it's only using the vertical velocity when hitting an angle, thus losing speed).
I cannot for the life of me figure out a solution to fix this problem. Does anybody have any ideas??
Code for Ball node:
func createBallNode(ballColor: String) -> SKSpriteNode {
let ball = SKSpriteNode(imageNamed: ballColor)
ball.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame)+30)
ball.zPosition = 1
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.width/2)
ball.physicsBody?.affectedByGravity = true
ball.physicsBody?.restitution = 1
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.friction = 0
ball.physicsBody?.categoryBitMask = ColliderType.Ball.rawValue
ball.physicsBody?.contactTestBitMask = ColliderType.Rect.rawValue
ball.physicsBody?.collisionBitMask = ColliderType.Rect.rawValue
let centerX = ball.position.x
let range = SKRange(lowerLimit: centerX, upperLimit: centerX)
let constraint = SKConstraint.positionX(range)
ball.constraints = [constraint]
return ball
}
Yes, the problem is probably causes by the ball hitting the hexagon when it is not perfectly "aligned". In this case the ball loses vertical speed in favour of the horizontal axis.
Since you want a "discrete logic" I believe in this scenario your should avoid physics (at least for the bouncing part). It would be much easier repeating an SKAction that moves the ball vertically.
Example
I prepared a simple example
class GameScene: SKScene {
override func didMove(to view: SKView) {
super.didMove(to: view)
let ball = createBallNode()
self.addChild(ball)
}
func createBallNode() -> SKSpriteNode {
let ball = SKSpriteNode(imageNamed: "ball")
ball.position = CGPoint(x: frame.midX, y: frame.minY + ball.frame.height / 2)
let goUp = SKAction.move(by: CGVector(dx: 0, dy: 600), duration: 1)
goUp.timingMode = .easeOut
let goDown = SKAction.move(by: CGVector(dx: 0, dy: -600), duration: 1)
goDown.timingMode = .easeIn
let goUpAndDown = SKAction.sequence([goUp, goDown])
let forever = SKAction.repeatForever(goUpAndDown)
ball.run(forever)
return ball
}
}
Update
If you need to perform a check every time the ball touches the base of the Hexagon you can use this code
class GameScene: SKScene {
override func didMove(to view: SKView) {
super.didMove(to: view)
let ball = createBallNode()
self.addChild(ball)
}
func createBallNode() -> SKSpriteNode {
let ball = SKSpriteNode(imageNamed: "ball")
ball.position = CGPoint(x: frame.midX, y: frame.minY + ball.frame.height / 2)
let goUp = SKAction.move(by: CGVector(dx: 0, dy: 600), duration: 1)
goUp.timingMode = .easeOut
let goDown = SKAction.move(by: CGVector(dx: 0, dy: -600), duration: 1)
goDown.timingMode = .easeIn
let check = SKAction.customAction(withDuration: 0) { (node, elapsedTime) in
self.ballTouchesBase()
}
let goUpAndDown = SKAction.sequence([goUp, goDown, check])
let forever = SKAction.repeatForever(goUpAndDown)
ball.run(forever)
return ball
}
private func ballTouchesBase() {
print("The ball touched the base")
}
}
As you can see now the method ballTouchesBase is called every time the ball is a te lower y coordinate. This is the right place to add a check for the color of your hexagon.

Ball dropping animation not working (Swift)

This code is supposed to drop a ball from the top of the screen to the bottom. And once it touches the bottom of the screen, it should appear back to the top of the screen. It doesn't relocate to the top and it stops moving. I want it to be a continuous loop that resets the ball.y position every time it touches the bottom.
import SpriteKit
class GameScene: SKScene {
let ball = SKShapeNode(circleOfRadius: 20)
let label = SKLabelNode(fontNamed: "Futura")
let movingObjects = SKSpriteNode()
override func didMoveToView(view: SKView) {
let sceneBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody = sceneBody
//Ball Transition
let ballTransition = SKAction.sequence([SKAction.fadeInWithDuration(1)])
ball.runAction(ballTransition)
//Ball function
ball.fillColor = SKColor.redColor()
ball.physicsBody = SKPhysicsBody(circleOfRadius: 25)
ball.physicsBody?.affectedByGravity = false
//Ball Movement
ball.position = CGPoint(x: self.frame.size.width/2, y: CGFloat(self.frame.size.height*1))
ballMovement()
movingObjects.addChild(ball)
self.addChild(label)
}
func ballMovement() {
let moveBall = SKAction.moveToY(self.frame.size.height*0, duration: 3)
let removeBall = SKAction.removeFromParent()
let moveAndRemove = SKAction.sequence([moveBall, removeBall])
ball.runAction(moveAndRemove)
//Label Sprite
label.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
label.fontColor = SKColor.redColor()
label.fontSize = 30
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
label.text = "\(ball.position.y)"
if ball.position.y < 26 {
ball.position = CGPoint(x: self.frame.size.width/2, y: CGFloat(self.frame.size.height*1))
}
}
}
removing the ball from its parent within the action and still acting on it elsewhere is going to become very fragile as your program gets more complex. Why not just do it all with the actions and let sprite kit worry about it ?
let moveBall = SKAction.moveToY(0, duration: 3)
let goBackUp = SKAction.moveToY(self.frame.size.height, duration:0)
let keepFalling = SKAction.sequence([moveBall, goBackUp])
ball.runAction(SKAction.repeatActionForever(keepFalling))

Programmatically create constrained region of physics, SpriteKit

I would like two regions, as shown in the image below, where the yellow region is to contain sprites. For example I'd like to have balls in the yellow region bouncing and reflecting off the boundaries of the yellow region. How can I programmatically do this without using an sks file?
You create an edge based physics body using the +bodyWithEdgeLoopFromRect:
override func didMoveToView(view: SKView) {
//Setup scene's physics body (setup the walls)
physicsBody = SKPhysicsBody(edgeLoopFromRect: frame)
let yellowSprite = SKSpriteNode(color: .yellowColor(), size: CGSize(width: 300, height: 300))
yellowSprite.position = CGPoint(x: frame.midX, y: frame.midY)
//Create the rectangle which will represent physics body.
let rect = CGRect(origin: CGPoint(x: -yellowSprite.size.width/2, y: -yellowSprite.size.height/2), size: yellowSprite.size)
yellowSprite.physicsBody = SKPhysicsBody(edgeLoopFromRect: rect)
addChild(yellowSprite)
//Add Red ball "inside" the yellow sprite
let red = SKShapeNode(circleOfRadius: 20)
red.fillColor = .redColor()
red.strokeColor = .clearColor()
red.position = yellowSprite.position
red.physicsBody = SKPhysicsBody(circleOfRadius: 20)
red.physicsBody?.restitution = 1
red.physicsBody?.friction = 0
red.physicsBody?.affectedByGravity = false
addChild(red)
red.physicsBody?.applyImpulse(CGVector(dx: 20, dy: 15))
}
About rect parameter:
The rectangle that defines the edges. The rectangle is specified
relative to the owning node’s origin.
Hope this helps!

Why is my camera not centered?

I've noticed that the camera property my SKScene is nil. I remedied that by creating a SKCameraNode setting that equal to the camera property, setting its position and adding it to the scene. However, when I do this all of a sudden my objects are no longer in the center of the scene. They are all shifted in some way that does not make sense to me. What am I doing wrong?
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
/*
// This is the problem code
let cam = SKCameraNode()
cam.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
self.camera = cam
self.addChild(cam)
*/
let sprite = SKSpriteNode(imageNamed:"Spaceship")
sprite.xScale = 0.5
sprite.yScale = 0.5
sprite.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
self.addChild(sprite)
}
}
Object appears in middle of scene, as expected.
Now the object is shifted to the left. Why?
UPDATE
The following code is giving me the behavior I want. The key is to reposition the camera every time the size of the SKScene changes. Thanks for the suggestions, they got me on the right track.
class GameScene: SKScene {
override func didChangeSize(oldSize: CGSize) {
if let view = self.view{
camera?.position = convertPointFromView(view.center)
}
}
override func didMoveToView(view: SKView) {
let cam = SKCameraNode()
cam.position = CGPoint(x: frame.midX, y:frame.midY)
self.camera = cam
self.addChild(cam)
let sprite = SKSpriteNode(imageNamed:"Spaceship")
sprite.xScale = 0.5
sprite.yScale = 0.5
sprite.position = CGPoint(x: frame.midX, y: frame.midY)
self.addChild(sprite)
}
}

Is it possible to position physicsbody to only one part of the Sprite?

Is it possible to position a physics body on a sprite? I only want a certain part of my sprite node to have collision detection, not the whole image.
Heres my physics body
physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: CGFloat(54.0), height: CGFloat(100.0)))
But i want to position the physics body at the top of the node, where it usually gets placed in the middle of the node.
You can try creating a smaller SKSpriteNode of the same size as the SKPhysicsBody and adding the larger SKSpriteNode as a child to the smaller one. Changing the position of the larger one as you want. For example
override func didMoveToView(view: SKView) {
let smallerSprite = SKSpriteNode(color: UIColor.redColor(), size: CGSizeMake(30, 30))
smallerSprite.physicsBody = SKPhysicsBody(rectangleOfSize: smallerSprite.size)
smallerSprite.position = CGPointMake(100, 400)
self.addChild(smallerSprite)
let largerSprite = SKSpriteNode(color: UIColor(white: 0.5, alpha: 0.5), size: CGSizeMake(100, 100))
largerSprite.position = CGPointMake(-10, -10)
smallerSprite.addChild(largerSprite)
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
}
As an addition to rakesh's answer...A different approach to get the same result would be to use
+ bodyWithRectangleOfSize:center: method. Like this:
override func didMoveToView(view: SKView) {
let sprite = SKSpriteNode(color: SKColor.whiteColor(), size: CGSize(width: 100.0, height: 100.0))
//I assume that you have initialized view and scene properly. If so, this will position a sprite in the middle of the screen.
sprite.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame))
var physicsBodySize:CGSize = CGSize(width: sprite.size.width, height: 30.0) //Create a size here. You can play with height parameter.
sprite.physicsBody =
SKPhysicsBody(rectangleOfSize: physicsBodySize, center: CGPoint(x: 0.0, y: sprite.size.height / 2.0 - physicsBodySize.height / 2.0))
//Not needed, just set to restrict that sprite can't off screen
sprite.physicsBody?.affectedByGravity = false
self .addChild(sprite)
}
The result:
If you try to change the height of physics body to 10.0, you will get something like this:

Resources