I am making a game in Swift using spritekit and I want 2 objects on screen, one that I move with the touchesMoved function (player) and the other to stay still in its position in the screen (cell).
I wanted to make the objects physicsbody's so that I could work out collisions between them, but now whenever the scene loads the objects fall straight away. And the object moved through touch falls as soon as the user stops touching the screen.
Here's all of the code relating to the object that should stay stationary in the scene and I am unsure what makes it fall.
//Defining the cell
let cell = SKSpriteNode(imageNamed: "cell.png")
override func didMove(to view: SKView){
self.physicsWorld.contactDelegate = self
cell.physicsBody?.categoryBitMask = cellCategory
cell.physicsBody?.contactTestBitMask = playerCategory
cell.physicsBody?.isDynamic = true
cell.physicsBody = SKPhysicsBody(rectangleOf: cell.frame.size)
//Adding the cell to the scene
cell.position = CGPoint(x:size.width * 0.9, y:size.height * 0.9)
cell.size = CGSize(width: 50, height: 50)
addChild(cell)
Any help in what makes the object fall and how I can stop it from happening would be greatly appreciated.
Gravity is by default on. You can either turn it off entirely by setting the gravity property of the scene's physicsWorld:
// somewhere in the scene's initialization, or in didMove if you like
physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
See https://developer.apple.com/documentation/spritekit/skphysicsworld for info on that.
Or you can turn off whether individual objects are affected by gravity. E.g.,
let body = SKPhysicsBody(circleOfRadius: 10.0)
body.affectedByGravity = false
See https://developer.apple.com/documentation/spritekit/skphysicsbody, and in particular, https://developer.apple.com/documentation/spritekit/skphysicsbody/1519774-affectedbygravity
You have a different problem in that you're setting the properties of your cell's physics body before you're creating it. cell.physicsBody?... = will not assign anything until cell.physicsBody exists. So those assignments must be moved after you create cell.physicsBody = SKPhysicsBody(rectangleOf: ...)
Related
I am making a game with sprites that move and obstacles to learn SpriteKit. I want the sprites that move to collide with the obstacles and bounce off of them but I want the obstacles to stay fixed. How do I do this? I have tried the following with no success:
Setting obstacle.physicsBody?.isDynamic = true. This made the sprites go through the obstacle.
Fixing the movement and rotation of the object with SKConstraint. When I do this they just go through each other.
Setting the mass of the body to be really high as follows obstacle.physicsBody?.mass = CGPoint.maxFiniteMagnitude but this freezes the game. When I set it really high it doesn't seem to do anything.
Setting obstacle?.physicsBody.velocity = CGVector(dx: 0, dy: 0) when the objects collide. I know that the contact.bodyA and contact.bodyB are passed by value and not reference so I loop through an array with the obstacles and set the velocity this way. The obstacles are still pushed by the other sprites.
Update:
- Setting obstacle.physicsBody?.collisionBitMask = PhysicsCategory.none so the sprite collides with the obstacle but not the other way around.
The object is setup as follows, with fish being the other sprite:
obstacle.position = location
obstacle.physicsBody = SKPhysicsBody(rectangleOf: obstacle.size)
obstacle.physicsBody?.isDynamic = true
obstacle.physicsBody?.categoryBitMask = PhysicsCategory.obstacle
obstacle.physicsBody?.contactTestBitMask = PhysicsCategory.fish
obstacle.physicsBody?.collisionBitMask = PhysicsCategory.fish
obstacle.physicsBody?.usesPreciseCollisionDetection = true
self.obstacles.append(obstacle)
super.addChild(obstacle)
Please let me know if there is something I am doing wrong / misunderstanding. Thanks.
Found the answer here. One of the objects needs to have .physicsBody?.isDynamic = true. In this case I set the obstacle to false so it is stationary.
I know there are a couple other questions regarding this but none of them seem to work or they all are incomplete or half complete. I need to know how to keep an object within the visible screen! So I have a spaceship sprite that uses the device's tilt and core motion to fly across the screen. If I tilt my device, the spaceship will at one point leave my screen and continue going in that direction. How do I keep the spaceship stay inside my screen so that even if I tilt to the left or right it will never leave the visible screen? Here is some of my code to the spaceship.
class GameScene: SKScene, SKPhysicsContactDelegate {
var ship = SKSpriteNode(imageNamed:"Spaceship")
let manager = CMMotionManager()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
ship.xScale = 1/5
ship.yScale = 1/5
ship.position = CGPoint(x: self.frame.width/(2), y: self.frame.height/1.5)
self.physicsWorld.contactDelegate = self
manager.startAccelerometerUpdates()
manager.accelerometerUpdateInterval = 0.1
manager.startAccelerometerUpdatesToQueue(NSOperationQueue.mainQueue()){
(data, error) in
self.physicsWorld.gravity = CGVectorMake(CGFloat((data?.acceleration.x)! * 15), CGFloat((data?.acceleration.y)! * 15))
}
ship.physicsBody = SKPhysicsBody()
ship.physicsBody?.affectedByGravity = true
self.addChild(ship)
}
I already tried self.view?.bounds and making it into a physics body but my spaceship never comes into contact with it.
All I had to do was meddle around with the size of the screen in the .sks file. I specifically made mine 800x640 because I am testing on an I-Pod 5. Then I added this bit of code:
let edgeFrame = CGRect(origin: CGPoint(x: ((self.view?.frame.minX)! + 200) ,y: (self.view?.frame.minY)!), size: CGSize(width: (self.view?.frame.width)! + 90, height: (self.view?.frame.height)! + 90))
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: edgeFrame)
This made a proper boundary around my screen and did not allow my spaceship to leave the screen. Best of all this was a perfect fit so if you are testing on a different device then just adjust the width and height numbers as needed. Remember to also adjust your .sks screen size.
Credits to 0x141E for hinting me in the right direction.
I have a weird problem where my SKReferenceNode does not collide properly after being scaled up. It collides well in the center, but it ignores collisions and contacts on the edges.
Here is the first photo of the scene. The SKReferenceNode was scaled up significantly, and as seen in this photo, does not collide correctly on the edges. The PhysicsBody appears to be correct (with showPhysics on), yet the ball refuses to collide.
The SKReferenceNode is using an alpha collision mask, because I need to change it to a larger sprite in the future to do animations and such. Additionally, non-scaled objects work completely fine. Finally, after the ball collides with the center, which does work, and the block is reset, collisions start working as expected again. The code to fix it is probably in the reset function, but I reset everything before the level is loaded, so this wouldn't make sense. Here is a part of my reset code:
func reset() {
physicsWorld.gravity = CGVectorMake(0, -9.8)
for grav in gravityBlock {
grav.reset()
}
gravity = -9.8
//Resets blocks
for blocks in destroyedBlocks { //the blocks are stored in destroyedBlocks when collided with
blocks.reset()
}
destroyedBlocks = []
/*Irrelevant code removed*/
}
Here's my blocks.reset() function:
override func reset() {
super.reset()
self.physicsBody?.categoryBitMask = 1
removeAllActions()
self.texture = self.text
shadow.hidden = false
self.alpha = 0
shadow.alpha = 0
let appear = SKAction(named: "Appear")!
self.runAction(appear)
shadow.runAction(SKAction.fadeInWithDuration(0.25))
}
Here is super.reset()
func reset() {
self.hidden = false
state = .NotBroken
}
Thanks so much!
When you scale the sprite you are just changing it's visual representation. The Physics Body stays the same. That's why you are getting the "strange" during the collisions.
You can see that showing the physics uses in your scene, just open GameViewController and add this line
skView.showsPhysics = true
inside viewDidLoad, just after this line
skView.showsNodeCount = true
Then run again your game, the physics boundaries now will be highlighted.
I have a SKSpriteNode (Bird) with a SKPhysicsBody (Circle).
The bird node is affected by gravity and falls down, until it reaches a y position of 100px.
override func didSimulatePhysics() {
if (bird.position.y < 100) {
bird.position = CGPointMake(bird.position.x, 100)
}
}
The problem is, that the physicsBody is moving away from the Node.
The physicsBody should stay centered on the Bird.
As soon as I tap the screen the Bird gets an impulse and moves up. Immediately the physicsBody is back in place.
This is the physicsBody code:
bird.physicsBody = SKPhysicsBody(circleOfRadius: bird.frame.width * 0.5)
bird.physicsBody!.contactTestBitMask = PhysicsCategory.LeftBoard | PhysicsCategory.RightBoard | PhysicsCategory.Border
bird.physicsBody!.categoryBitMask = PhysicsCategory.Bird
bird.physicsBody!.collisionBitMask = PhysicsCategory.None
bird.physicsBody!.mass = 1
bird.physicsBody!.affectedByGravity = false
bird.physicsBody!.allowsRotation = false
You need to stop the velocity. You are forcing the position but the velocity continues after each step.
Also you should typically make such changes in the update method before the physics are simulated.
I simply have put a square shape node onto a scene, and made the physicsBody for the scene be the edge of the screen. I turned on the physics view so you can see all of the physics stuff. The square has a physics body around it, as does the border of the scene. However, when the square runs into the border, nothing happens. The square just passes straight through. Here is how I initialize the square:
let rect1 = CGRect(x: 100, y: 100, width: 50, height: 50)
let square = Square(rect: rect1)
square.fillColor = UIColor.blackColor()
square.zPosition = 200
square.physicsBody = SKPhysicsBody(edgeLoopFromRect: rect1)
square.physicsBody?.allowsRotation = false
square.physicsBody?.affectedByGravity = false
square.physicsBody?.restitution = 0.4
self.addChild(square)
Here is how I initialize the physics body for the scene:
let physicsBody = SKPhysicsBody(edgeLoopFromRect: view.bounds)
self.physicsBody = physicsBody
Very similar code has worked for me on other games, so I am not sure why no collisions happen. Any help is greatly appreciated thanks!
The movement of the balls must be caused by physics rather than just setting the position. The issue was fixed through using impulses to move the balls which then made them collide with the various objects.