Physics body not showing only for one sprite? - swift

The problem I have is when I initialize my sprite, I have another sprite defined the same way and that sprite shows that it has a physics body because I see the green outline on that, but this sprite below has no green outline on it, I think that there is a problem when I create the SKPhysicsBody or maybe because I create 13 duplicates of the same sprite at random positions as shown in the second snippet of code.
#objc func CreateNewAsteroid() {
var asteroid : SKSpriteNode?
let moveAsteroidDown = SKAction.repeatForever(SKAction.moveBy(x: 0, y: -1, duration: 0.01))
let rotateAsteroid = SKAction.repeatForever(SKAction.rotate(byAngle: 25, duration: 5))
let asteroidXpos = randomNum(high: self.frame.size.width/2, low: -1 * self.frame.size.width/2)
let asteroidYpos = randomNum(high: 2.5*self.frame.size.height, low: self.frame.size.height/2)
let asteroidOrigin : CGPoint = CGPoint(x: asteroidXpos, y: asteroidYpos)
asteroid = SKSpriteNode(imageNamed: possibleAsteroidImage[Int(arc4random_uniform(4))])
asteroid?.scale(to: CGSize(width: player.size.height, height: player.size.height))
asteroid?.position = asteroidOrigin
asteroid?.run(moveAsteroidDown)
asteroid?.run(rotateAsteroid)
let asteroidRadius : CGFloat = (asteroid?.size.width)!/2
asteroid?.physicsBody? = SKPhysicsBody(rectangleOf: (asteroid?.size)!)
asteroid?.physicsBody?.categoryBitMask = asteroidCategory
asteroid?.physicsBody?.affectedByGravity = false
asteroid?.physicsBody?.isDynamic = false
asteroid?.physicsBody?.allowsRotation = false
asteroid?.physicsBody?.categoryBitMask = asteroidCategory
asteroid?.physicsBody?.collisionBitMask = 0
asteroid?.physicsBody?.contactTestBitMask = shipCategory
asteroidArray.append(asteroid!)
self.addChild(asteroid!)
}
I create 13 of the sprites like this
for _ in 0...13 {
CreateNewAsteroid()
}

Instead of making the SKSpriteNode asteroid an optional, make it a non-optional so it does not become a null value by deleting the ? in var asteroid : SKSpriteNode?
So you want to put var asteroid : SKSpriteNode
After making this change, some errors will show up telling you to delete some !'s and ?'s (Make sure to delete these of course).
then go to your GameViewController.swift file and add the line view.showsPhysics = true under view.showsNodeCount = true
When you are done you should see a green outline around the sprite.

Related

SpriteKit Inelastic Collision Reducing Velocity

I'm building a pong/breaker game with a ball and non-static blocks. I'd like the ball to never stop moving, but whenever it hits a block it loses velocity.
I have the restitusion = 1 for all sprites involved, I've tried setting the mass equal to each other and the density and the friction = 0. But, the ball still loses velocity on a bounce.
When the ball hits a block I'm removing it in the didBegin(contact:) function. I've also tried delaying the removal and it didn't help.
I'd like for the ball to have a constant velocity, but still be able to interact with the blocks as later I'd like to add blocks that can be hit without immediately being broken. So, the blocks can't be static but the ball needs to have a constant velocity.
My code for creating the ball node:
func ballNode(_ position: CGPoint?) -> SKSpriteNode {
let node = SKSpriteNode()
node.position = position == nil ? CGPoint(x: size.width/2, y: 100) : position!
node.size = CGSize(width: 17, height: 17)
//background
let background = SKShapeNode(circleOfRadius: 8.5)
background.fillColor = UIColor.white
node.addChild(background)
//physics
node.physicsBody = SKPhysicsBody(circleOfRadius: 8.5)
node.physicsBody?.allowsRotation = true
node.physicsBody?.friction = 0
node.physicsBody?.restitution = 1
node.physicsBody?.linearDamping = 0
node.physicsBody?.angularDamping = 0
node.physicsBody?.categoryBitMask = BallCategory
node.physicsBody?.contactTestBitMask = AddBlockBorderCategory | PaddleCategory
node.physicsBody?.collisionBitMask = PaddleCategory | BlockCategory | BorderCategory
return node
}
My code for creating the block node:
func createBlockNode() -> BlockNode {
let width = (size.width-CGFloat(6*layout[0].count))/CGFloat(layout[0].count)
let height = width*0.5
let nodeSize = CGSize(width: width, height: height)
let node = BlockNode(texture: nil, size: nodeSize)
let background = SKShapeNode(rectOf: nodeSize)
background.fillColor = .darkGray
background.strokeColor = .lightGray
//physics
node.physicsBody = SKPhysicsBody(rectangleOf: nodeSize)
node.physicsBody?.restitution = 1
node.physicsBody?.allowsRotation = true
node.physicsBody?.friction = 0
node.physicsBody?.categoryBitMask = BlockCategory
node.physicsBody?.contactTestBitMask = BallCategory
node.addChild(background)
return node
}
And a screen recording:
screen recording of the ball losing velocity
I'm starting the ball using this:
ball!.physicsBody?.applyForce(CGVector(dx: 0, dy: 50))

How do I create a circular collision bound with Swift UIDynamics?

This post has been modified to include a screenshot and code...
It's been four days of trying to solve this, so can someone please clearly point out what's wrong with my implementation? It's my first time lol.
Have a look at my screenshot below.
-I'm using UIKit Dynamics and am trying to trap those 3 smaller balls inside the ROUND bounds of the larger ball, but they are only being kept inside the bounds of the larger ball's rectangle.
Here's the relative code:
The large bubble is created in IB and is declared in my class:
#IBOutlet weak var homeMainBubble: UIButton!
var gravity: UIGravityBehavior!
var animator: UIDynamicAnimator!
var collision: UICollisionBehavior!
var ballOne : UIImageView!
var ballTwo : UIImageView!
var ballThree : UIImageView!
var circlePath : UIBezierPath!
override func viewDidAppear(_ animated: Bool) {
circlePath = UIBezierPath(arcCenter: CGPoint(x: homeMainBubble.frame.midX,y: homeMainBubble.frame.midY), radius: homeMainBubble.frame.width/2, startAngle: CGFloat(0), endAngle:CGFloat(Double.pi * 2), clockwise: true)}
In viewDidAppear(), I create a UIBezierPath()
//Adds or removes fake balls inside 'mainBubble()' when swiped up or down---------------------
func addOrRemoveFakeBalls(fakeBalls: String) {
let ballOneIs = positionOneBubble()
let ballImages = ["playBubble", "statsBubble", "settingsBubble", "cheatsBubble"]
let ballOneImage = ballImages[ballOneIs.tag - 1] //Determines the image to used for 'fake ball 1' based on the bubble in position one
var ballTwoImage : String = ""
var ballThreeImage : String = ""
if ballOneImage == "playBubble" { //Derivitive from ballOneImage, this determines the ball to the left & ensures correct array looping.
ballTwoImage = ballImages[3]
} else {ballTwoImage = ballImages[ballOneIs.tag - 2]}
if ballOneImage == "cheatsBubble" { //Derivitive from ballOneImage, this determines the ball to the right & ensures correct array looping.
ballThreeImage = ballImages[0]
} else {ballThreeImage = ballImages[ballOneIs.tag]}
switch fakeBalls {
case "Add":
ballOne = Ellipse(frame: CGRect(x: 125, y: 50, width: largeBubbleWidth, height: largeBubbleWidth))
ballOne.image = UIImage(named: ballOneImage)
ballOne.layer.zPosition = 1
ballOne.alpha = 0.3
ballTwo = Ellipse(frame: CGRect(x: 100, y: 50, width: mediumBubbleWidth, height: mediumBubbleWidth))
ballTwo.image = UIImage(named: ballTwoImage)
ballTwo.layer.zPosition = 1
ballTwo.alpha = 0.3
ballThree = Ellipse(frame: CGRect(x: 150, y: 50, width: mediumBubbleWidth, height: mediumBubbleWidth))
ballThree.image = UIImage(named: ballThreeImage)
ballThree.layer.zPosition = 1
ballThree.alpha = 0.3
homeMainBubble.addSubview(ballOne)
homeMainBubble.addSubview(ballTwo)
homeMainBubble.addSubview(ballThree)
animator = UIDynamicAnimator(referenceView: homeMainBubble)
gravity = UIGravityBehavior(items: [ballOne, ballTwo, ballThree])
gravity.magnitude = 1
animator.addBehavior(gravity)
collision = UICollisionBehavior(items: [ballOne, ballTwo, ballThree])
collision.addBoundary(withIdentifier: "Circle" as NSCopying, for: circlePath)
collision.collisionDelegate = self
collision.translatesReferenceBoundsIntoBoundary = true
animator.addBehavior(collision)
case "Remove":
animator.removeAllBehaviors()
gravity.removeItem(ballOne)
gravity.removeItem(ballTwo)
gravity.removeItem(ballThree)
collision.removeAllBoundaries()
ballOne.removeFromSuperview()
ballTwo.removeFromSuperview()
ballThree.removeFromSuperview()
default: break
}
}
I've temporarily added collision.translatesReferenceBoundsIntoBoundary = true so that the balls don't drop off the screen, but with that commented out, why does the 'circlePath' UIBezierPath() not hold my smaller balls inside?
I have pretty much tried every approach and had every outcome EXCEPT for what I want :P.
What am I missing in order to keep the 3 smaller balls trapped inside the large ball's image (not rectangle) bounds?
THANK YOU for saving my week! :)
If you are using UIDynamics then you can follow the advice in this other question & answer
Namely, looks like you'll want to define collisionBoundingPath to be a UIBezierPath that is round.
override public var collisionBoundingPath: UIBezierPath {
let radius = min(bounds.size.width, bounds.size.height) / 2.0
return UIBezierPath(arcCenter: CGPointZero, radius: radius, startAngle: 0, endAngle: CGFloat(M_PI * 2.0), clockwise: true)
}
From there you can customize the behavior of the smaller balls hitting the outer edge through the UICollisionBehaviorDelegate (again, see linked question and answer).
Update
With more code posted, another issue I think you are having is the UICollisionBehavior isn't including the "main ball". You are only adding collision behavior between the 3 sub-balls, which is likely why they are not properly interacting with the big ball as you'd want. Hopefully that helps!

Touch Sprite, make it jump up then fall down again(repeat as many times as spritenode is tapped.)

I created a project where I have a ball and when the view loads, it falls down, which is good. I'm trying to get the ball to jump back up and fall down again when the Spritenode is tapped.
--This Question was edited--
Originally, I was able to get it to work when sprite.userInteractionEnabled = false. I had to turn this statement true in order to get the score to change.
Now I can't get the balls to fall and be tapped to jump. When I turn ball.physicsBody?.dynamic = true, the balls will fall due to gravity. How do I tap the sprite itself and make it jump.
GameScene.swift (For those who want to try the code for themselves.)
import SpriteKit
class GameScene: SKScene {
var ball: Ball!
private var score = 0 {
didSet { scoreLabel.text = "\(score)" }
}
override func didMoveToView(view: SKView) {
let ball = Ball()
scoreLabel = SKLabelNode(fontNamed:"Geared-Slab")
scoreLabel.fontColor = UIColor.blackColor()
scoreLabel.position = CGPoint( x: self.frame.midX, y: 3 * self.frame.size.height / 4 )
scoreLabel.fontSize = 100.0
scoreLabel.zPosition = 100
scoreLabel.text = String(score)
self.addChild(scoreLabel)
ball.position = CGPoint(x:self.size.width / 2.0, y: 440)
addChild(ball)
ball.physicsBody = SKPhysicsBody(circleOfRadius: 120)
ball.physicsBody?.dynamic = true
ball.physicsBody?.allowsRotation = false
ball.physicsBody?.restitution = 3
ball.physicsBody?.friction = 0
ball.physicsBody?.angularDamping = 0
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.usesPreciseCollisionDetection = true
}
class Ball: SKSpriteNode {
init() {
let texture = SKTexture(imageNamed: "Ball")
super.init(texture: texture, color: .clearColor(), size: texture.size())
userInteractionEnabled = true
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let scene = self.scene as! GameScene
scene.score += 1
}
Before, it was a SKNode being tapped using CGVectorMake(impulse, velocity) now, it's a SKSpriteNode, and I tried using SKAction, but it either does not work, or I'm putting it in the wrong place(touches begin).
I tested your code and it seems that using firstBall.userInteractionEnabled = true is the cause. Without it, it should work. I did some research (here for example), but can't figure out what's the reason of this behavior with userInteractionEnabled. Or for what reason do you use userInteractionEnabled?
Update due to update of question
First ball.physicsBody?.restitution = 3 defines the bounciness of the physics body. The default value is 0.2 and the property must be between 0.0 ans 1.0. So if you set it to 3.0 it will cause some unexpected effects. I just deleted it to use the default value of 0.2.
Second, to make the ball jump after tap and increase the score I put
physicsBody?.velocity = CGVectorMake(0, 600)
physicsBody?.applyImpulse(CGVectorMake(0, 1100))
in the touchesBegan method of the Ball class
Result

Randomize image in array in sprite kit swift.

I was wondering if someone could show me how to make it so that I can spawn random image missiles. Right now I am using one image called "meteor", I have a few more images I would like to show and randomize. I know I need to put them in an array and create an arc for random. I have done it for sound but I'm not sure how to do it for images. This is my code so far.
var lastMissileAdded : NSTimeInterval = 0.0
let missileVelocity : CGFloat = 4.0
func addMissile() {
// Initializing missile node
var missile = SKSpriteNode(imageNamed: "meteor")
missile.setScale(0.44)
// Adding SpriteKit physics body for collision detection
missile.physicsBody = SKPhysicsBody(rectangleOfSize: missile.size)
missile.physicsBody?.categoryBitMask = UInt32(obstacleCategory)
missile.physicsBody?.dynamic = true
missile.physicsBody?.contactTestBitMask = UInt32(shipCategory)
missile.physicsBody?.collisionBitMask = 0
missile.physicsBody?.usesPreciseCollisionDetection = true
missile.name = "missile"
// Selecting random y position for missile
var random : CGFloat = CGFloat(arc4random_uniform(300))
missile.position = CGPointMake(self.frame.size.width + 20, random - 20)
self.addChild(missile)
}
func moveObstacle() {
self.enumerateChildNodesWithName("missile", usingBlock: { (node, stop) -> Void in
if let obstacle = node as? SKSpriteNode {
obstacle.position = CGPoint(x: obstacle.position.x - self.missileVelocity, y: obstacle.position.y)
if obstacle.position.x < 0 {
obstacle.removeFromParent()
}
}
})
}
All you need to do is name them meteor0, meteor1 and meteor2 and use String Interpolation to create your node with your random image:
var missile = SKSpriteNode(imageNamed: "meteor\(arc4random_uniform(3))")

Swift SpriteKit SKPhysicsJointPin

I'm trying to implement a rope in Swift SpriteKit and to add physics to it, the position is good for all, but they won't attach, when I hit play they all fall except the first one which is the "holder". Here is my code:
// create rope holder
let chainHolder = SKSpriteNode(imageNamed: "chainHolder")
chainHolder.position.y = self.frame.maxY - chainHolder.size.height
chainHolder.physicsBody = SKPhysicsBody(circleOfRadius: chainHolder.size.width / 2)
chainHolder.physicsBody?.dynamic = false
//chainHolder.physicsBody?.allowsRotation = true
chains.append(chainHolder)
addChild(chainHolder)
// add each of the rope parts
for i in 0...5 {
let chainRing = SKSpriteNode(imageNamed: "chainRing")
let offset = chainRing.size.height * CGFloat(i + 1)
chainRing.position = CGPointMake(chainHolder.position.x, chainHolder.position.y - offset)
chainRing.name = String(i)
chainRing.physicsBody = SKPhysicsBody(rectangleOfSize: chainRing.size)
//chainRing.physicsBody?.allowsRotation = true
chains.append(chainRing)
addChild(chainRing)
}
// set up joints between rope parts
for i in 1...5 {
var nodeA = chains[i - 1]
var nodeB = chains[i]
var joint = SKPhysicsJointPin.jointWithBodyA(nodeA.physicsBody, bodyB: nodeB.physicsBody,
anchor: CGPointMake(CGRectGetMidX(nodeA.frame), CGRectGetMinY(nodeA.frame)))
physicsWorld.addJoint(joint)
}
I found out that the problem was because I was setting the anchor point for the scene in (0.5, 0.5). If I leave it in (0, 0) everything is ok.