I'm trying to create a simple car with SKSpriteNode. Physics and everything works great, except for one tiny thing: when my car lands on the ground from a greater distance the body of the car is pushed down and then goes back to its place.
See image:
I want to get the left image, but I get the one on the right.
When it lands on the ground I don't want the body to go into the wheels, I want it to be in a fixed place relative to the wheels or vice-versa.
So here is how I do it:
This is in the car class:
carWheelJoint1 = SKPhysicsJointPin.jointWithBodyA(self.physicsBody!, bodyB: leftWheel.physicsBody!, anchor: leftWheel.position)
carWheelJoint2 = SKPhysicsJointPin.jointWithBodyA(self.physicsBody!, bodyB: rightWheel.physicsBody!, anchor: rightWheel.position)
And this is in the GameScene class:
//Add vehicle
vehicle = DevCar(color: UIColor.blueColor(), size: CGSize(width: 250, height: 50), cameraThreshold: cameraThreshold)
vehicle.position = (CGPoint(x: 0, y: cameraThreshold))
addChild(vehicle)
self.physicsWorld.addJoint(vehicle.carWheelJoint1)
self.physicsWorld.addJoint(vehicle.carWheelJoint2)
I tried using SKPhysicsJointFixed but that did not work either.
Any ideas?
EDIT: this is how the physicsBody is defined:
Here is the car body in the car class.
The car class is an SKSpriteNode and is the body of the car itself, the wheels are its children.
let baseBody = SKPhysicsBody(rectangleOfSize: size)
baseBody.mass = CGFloat(1)
baseBody.dynamic = true
baseBody.affectedByGravity = true
baseBody.friction = 0.2
baseBody.allowsRotation = false
baseBody.density = CGFloat(10)
self.physicsBody = baseBody
And here is the wheel body in the same class:
leftWheel = SKShapeNode(circleOfRadius: wheelSize)
leftWheel.position = CGPoint(x: size.width / -2 + wheelSize, y: size.height / -2 - wheelSize)
rightWheel = SKShapeNode(circleOfRadius: wheelSize)
rightWheel.position = CGPoint(x: size.width / 2 - wheelSize, y: size.height / -2 - wheelSize)
leftWheel.physicsBody = SKPhysicsBody(circleOfRadius: wheelSize)
leftWheel.physicsBody!.mass = CGFloat(0.2)
leftWheel.physicsBody!.dynamic = true
leftWheel.physicsBody!.affectedByGravity = true
leftWheel.physicsBody!.friction = 0.3
rightWheel.physicsBody = SKPhysicsBody(circleOfRadius: wheelSize)
rightWheel.physicsBody!.mass = CGFloat(0.2)
rightWheel.physicsBody!.dynamic = true
rightWheel.physicsBody!.affectedByGravity = true
rightWheel.physicsBody!.friction = 0.3
addChild(leftWheel)
addChild(rightWheel)
EDIT2: Added video
https://www.youtube.com/watch?v=TTwa-t4u4pI
Related
I set up the sprite's physics body, zPosition, color, size, etc., but it won't appear visually. (It has the highest zPosition of my sprites, and YES I did import SpriteKit)
let wall = SKSpriteNode()
wall.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
//Takes screen size / width to make each piece one/(value set by "maze" at top) of the screen
let width: CGFloat = (UIScreen.main.bounds.width) / CGFloat(mazeSize)
let height: CGFloat = (UIScreen.main.bounds.height - 134) / CGFloat(mazeSize) //subtract so there's extra room on top+bottom of screen
wall.color = UIColor(ciColor: .black)
wall.isHidden = false
wall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: width, height: height))
wall.physicsBody?.restitution = 0
wall.physicsBody?.friction = 0
wall.physicsBody?.isDynamic = false
wall.zPosition = 4
//sets up x+y location based on row count and which row this is on
//locations (ONLY positive)
let xLoc: CGFloat = ((UIScreen.main.bounds.width) / CGFloat((mazeSize/2))) * CGFloat(i/mazeSize)
let yLoc: CGFloat = ((UIScreen.main.bounds.height - 134) / CGFloat(mazeSize/2)) * CGFloat(i/mazeSize)
if((i/mazeSize) > (mazeSize/2)){ //if positive on x
wall.position.x = xLoc
}
else{ //if negative on x
wall.position.x = -xLoc
}
if(i > ((mazeSize^2)/2)){ //if positive on y
wall.position.x = yLoc
}
else{ //if negative on y
wall.position.y = yLoc
}
I'm using this to set up walls in a maze (Yes I know the x+y positions are wrong, but they still "appear" clumped in the middle), but only the physics of these nodes show up. Is there a way I can make these black walls appear?
The geometry of the sprite node and the geometry of the physics body don't have to be related, and in this case you've made a rectangular physics body and basically an empty sprite node. Try something like
let wall = SKSpriteNode(color: .black, size: CGSize(width: 100, height: 100)
or using the SKSpriteNode(texture:) initializer.
I am creating a space shooter game, and I want to make one of my nodes only be dynamic when it interacts with ones specific node, as opposed to all nodes. Essentially, I only want the node to act as dynamic when it interacts with one specified node. How can I do this?
Thanks.
I know the question is a but confusing, but if anyone has any ideas it would be extremely helpful!!
let Bullet = SKSpriteNode(imageNamed: "BulletGalaga.png")
Bullet.zPosition = -5
Bullet.position = CGPoint(x: ship.position.x, y: ship.position.y)
Bullet.zRotation = ship.zRotation
//let action = SKAction.moveTo(CGPointMake(400 * cos(Bullet.zRotation),(400 * -sin(Bullet.zRotation))), duration: 0.8)
let action = SKAction.move(
to: CGPoint(
x: 1200 * -cos(Bullet.zRotation - 1.57079633) + Bullet.position.x,
y: 1200 * -sin(Bullet.zRotation - 1.57079633) + Bullet.position.y
),
duration: 2.4)
//let action = SKAction.moveToY(self.size.height + 30, duration: 0.8)
//let action = SKAction.moveTo(self.ship.size.height, duration: 0.8)
let actionDone = SKAction.removeFromParent()
Bullet.run(SKAction.sequence([action, actionDone]), withKey: "bulletAction")
Bullet.physicsBody = SKPhysicsBody(rectangleOf: Bullet.size)
Bullet.physicsBody?.affectedByGravity = false
Bullet.physicsBody?.isDynamic = false
self.addChild(Bullet)
I added this:
ship.physicsBody?.categoryBitMask = shipCategoryBitMask
Bullet.physicsBody?.categoryBitMask = bulletCategoryBitMask
enemy.physicsBody?.categoryBitMask = enemyCategoryBitMask
enemyBullet.physicsBody?.categoryBitMask = enemyBulletCategoryBitMask
ship.physicsBody?.collisionBitMask = enemyBulletCategoryBitMask
Bullet.physicsBody?.collisionBitMask = enemyCategoryBitMask
enemyBullet.physicsBody?.collisionBitMask = shipCategoryBitMask
enemy.physicsBody?.collisionBitMask = bulletCategoryBitMask
Once you've set a SpriteNode's physics body, you can use the collisionBitMask to specify which other SpriteNodes it will collide with. You'll need to first set each node's physics body's categoryBitMask.
let node1CategoryBitMask = 0x1 << 0
let node2CategoryBitMask = 0x1 << 1
let node3CategoryBitMask = 0x1 << 2
node1.physicsBody.categoryBitMask = node1CategoryBitMask
node2.physicsBody.categoryBitMask = node2CategoryBitMask
node3.physicsBody.categoryBitMask = node3CategoryBitMask
At this point, three of the sprites have their categoryBitMask set. This is what identifies the physics body. Now to set the collisionBitMasks.
node1.collisionBitMask = node2CategoryBitMask
node2.collisionBitMask = node1CategoryBitMask | node3CategoryBitMask
This tells node1 to by collide with node2, but not node3. Node2 will collide with node1 and node3.
Hope this helps.
After my game ends, I added a PNG file with custom font and tried adding score in front of it, but when the game is over, the PNG file gets added and instead of putting the score in front of it, it prints out the details of the PNG file like this, 'ScoreText' (150 x 50 ) position:(465.4356789)....
All of this is happening in my didBeginContact method:
let score = SKSpriteNode(imageNamed: "ScoreText")
score.position = CGPoint(x: size.width / 2.2, y: size.height / 1.9)
self.addChild(score)
let scoreTextNode = SKLabelNode(fontNamed: "Copperplate")
scoreTextNode.text = "\(score)"
scoreTextNode.fontSize = 12
scoreTextNode.fontColor = SKColor.whiteColor()
scoreTextNode.position = CGPoint(x: size.width / 1.1, y: size.height / 1.9)
addChild(scoreTextNode)
I have a player. He has to jump over some blocks.
However, it seems like the SKPhysicsBody(rectangleOfSize: ... ) isn't aligned properly. When the character hits the upper half of the block it just passes through and he can't pass below it because it collides with an invisible SKPhysicsBody.
Depending on how I put the anchor point it sometimes even happens that the physics body is to the left or right of it's SKSpriteNode.
My code for the blocks:
self.smallBlock.anchorPoint = CGPointMake(0.5, 0.5)
self.smallBlock.position = CGPointMake(CGRectGetMaxX(self.frame) + self.smallBlock.size.width, CGRectGetMinY(self.frame) + self.runningBar.size.height * 2)
self.smallBlock.physicsBody = SKPhysicsBody(rectangleOfSize: self.smallBlock.size)
self.smallBlock.physicsBody?.dynamic = false
self.smallBlock.physicsBody?.categoryBitMask = colliderType.Block.rawValue
self.smallBlock.physicsBody?.contactTestBitMask = colliderType.Character.rawValue
self.smallBlock.physicsBody?.collisionBitMask = colliderType.Character.rawValue
self.smallBlock.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(self.smallBlock)
self.character.anchorPoint = CGPointMake(0, 0.25)
self.character.position = CGPointMake(CGRectGetMinX(self.frame) - self.character.size.width, CGRectGetMinY(self.frame) + self.character.size.height)
self.initPosX = CGRectGetMinX(self.frame) + (self.character.size.width * 1.25)
Code for the character
self.character.anchorPoint = CGPointMake(0, 0.25)
self.character.position = CGPointMake(CGRectGetMinX(self.frame) - self.character.size.width, CGRectGetMinY(self.frame) + self.character.size.height)
self.initPosX = CGRectGetMinX(self.frame) + (self.character.size.width * 1.25)
self.character.physicsBody = SKPhysicsBody(rectangleOfSize: self.character.size)
self.character.physicsBody?.affectedByGravity = true
self.character.physicsBody?.categoryBitMask = colliderType.Character.rawValue
self.character.physicsBody?.contactTestBitMask = colliderType.Block.rawValue
self.character.physicsBody?.collisionBitMask = colliderType.Block.rawValue
self.character.physicsBody?.usesPreciseCollisionDetection = true
self.character.physicsBody?.affectedByGravity = true
self.character.physicsBody?.allowsRotation = false
self.addChild(self.character)
Solution:
I've added skView.showsPhysics = true to my View Controller. When you run the app in the simulator it will outline every object like shown here.
It turns out setting custom anchor points messes up the rectangleOfSize-positions.
The goal is to rotate a physics body around a point using angular velocity, not zRotation. Because of this, it seems necessary to add a second physics body (the one controlling the rotations), but changing the angular velocity on this parent doesn't move the child. In other words, the parent rotates, but the child remains in place.
How can you rotate both the parent and child?
parent = SKNode()
sprite = SKSpriteNode(color: SKColor.whiteColor(), size: spriteSize)
sprite.position = CGPoint(x: 0, y: 50)
parent.addChild(sprite)
parent.physicsBody = SKPhysicsBody(rectangleOfSize: parentSize)
parent.physicsBody?.affectedByGravity = false
parent.physicsBody?.friction = 0
parent.physicsBody?.linearDamping = 0
parent.physicsBody?.angularDamping = 0
parent.physicsBody?.collisionBitMask = 0
sprite.physicsBody = SKPhysicsBody(rectangleOfSize: spriteSize)
sprite.physicsBody?.affectedByGravity = false
sprite.physicsBody?.friction = 0
sprite.physicsBody?.linearDamping = 0
sprite.physicsBody?.angularDamping = 0
sprite.physicsBody?.categoryBitMask = bitMask
sprite.physicsBody?.collisionBitMask = 0
parent.physicsBody?.angularVelocity = 2.0
You can try to create joint between parent and child before setting angular velocity
let fixedJoint = SKPhysicsJointFixed()
fixedJoint.bodyA = parent.physicsBody
fixedJoint.bodyB = sprite.physicsBody
self.physicsWorld.addJoint(fixedJoint)