Rotate object under gravity force - ios

I'm trying to create a scenario where there's a radial gravity field. In this scene, there's also an object built by two physics bodies with a different mass.
When I run this code, the radial gravity field is created correctly and the body goes to gravityCenter.
I'm expecting that the body rotates too because the head is heavier than the tail but this doesn't happend.
Why?
class GameScene: SKScene {
let object = SKSpriteNode(imageNamed: "myobj")
let myCategory : UInt32 = 0x1 << 0
override func didMove(to view: SKView) {
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
let gravityCenter = SKFieldNode.radialGravityField()
gravityCenter.isEnabled = true
gravityCenter.position = CGPoint(x: size.width, y: size.height * 0.5)
gravityCenter.strength = 0.5
addChild(gravityCenter)
object.position = CGPoint(x: size.width * 0.1, y: size.height * 0.9)
object.scale(to: CGSize(width: 100, height: 25))
let head = SKPhysicsBody(circleOfRadius: object.size.width/5, center: CGPoint(x: object.size.width/2, y: 0))
let tail = SKPhysicsBody(circleOfRadius: object.size.width/50, center: CGPoint(x: -object.size.width/2, y: 0))
head.mass = 500
head.categoryBitMask = myCategory
head.allowsRotation = true
head.isDynamic = true
head.angularDamping = 0
head.affectedByGravity = true
tail.mass = 2
tail.categoryBitMask = myCategory
tail.allowsRotation = true
tail.isDynamic = true
tail.angularDamping = 0
tail.affectedByGravity = true
object.physicsBody = SKPhysicsBody(bodies: [head, tail])
object.physicsBody?.categoryBitMask = myCategory
object.physicsBody?.allowsRotation = true
object.physicsBody?.isDynamic = true
object.physicsBody?.angularDamping = 0
object.physicsBody?.affectedByGravity = true
addChild(object)
}
}

Well, from a physics standpoint SpriteKit is behaving correctly. If you think about it, more mass does mean more gravitational force, but it also means more inertia, which exactly cancels out the increased force. Perhaps introduce a little bit of linearDamping into the tail? That would get the body to rotate by making the head drag the tail a little bit.

Related

Weird Spritekit Collision Bug depending on SKSpriteNode position

I'm having a weird Spritekit issue where my moving SKSpriteNode is passing through a fixed SKSpriteNode depending on the position of the fixed SKSpriteNode.
UPDATE: Code works in simulator but not on real device.
Example:
Placing my bin SKSpriteNode at position x: -500, y: 100 works fine and my moving SKSpriteNode collides as expected.
Placing my bin SKSpriteNode at position x: -600, y: 100 DOES NOT work and my moving SKSpriteNode DOES NOT collide with the bin.
Using view.showsPhysics = true shows that there is physics bodies in both cases.
x values between -500 and -508 work as expected. All other values I have tried did not work.
Collisions with my other fixed SKSpriteNodes work as expected.
enum CollisionTypes: UInt32 {
case Plane = 1
case FloorAndRoof = 2
case OtherObject = 4
case FinishPoint = 8
}
Code to create levels
func createLevel(level: Int) {
switch level {
case 1:
createFloor()
createRoof()
createTable(position: CGPoint(x: 750, y: 150))
createCeilingFan(position: CGPoint(x: 750, y: 560))
createCeilingFan(position: CGPoint(x: 2000, y: 560))
createWaterDispenser(position: CGPoint(x: 1500, y: 212))
createBin(position: CGPoint(x: -500, y: 100)) // THIS IS THE PROBLEM LOCATION
createFinishPoint(position: CGPoint(x: -500, y: 100))
break
case 2:
createFloor()
createRoof()
createTable(position: CGPoint(x: 250, y: 150))
createCeilingFan(position: CGPoint(x: 750, y: 560))
createCeilingFan(position: CGPoint(x: 2000, y: 560))
createWaterDispenser(position: CGPoint(x: 1500, y: 212))
createBin(position: CGPoint(x: -600, y: 100))
createFinishPoint(position: CGPoint(x: -300, y: 200))
break
default:
break
}
}
Moving SKSpriteNode
func createPlane() {
plane = SKSpriteNode(imageNamed: "plane1")
plane.name = "plane1"
//plane.position = CGPoint(x: -UIScreen.main.bounds.width + plane.size.width , y: 0)
plane.position = CGPoint(x: 0, y: 300)
plane.zPosition = 1
//plane.physicsBody = SKPhysicsBody(texture: plane.texture!, size: plane.texture!.size())
plane.physicsBody = SKPhysicsBody(texture: planeTexture, size: planeTexture.size())
plane.physicsBody?.allowsRotation = true
plane.physicsBody?.categoryBitMask = CollisionTypes.Plane.rawValue
plane.physicsBody?.collisionBitMask = CollisionTypes.FloorAndRoof.rawValue | CollisionTypes.OtherObject.rawValue // dont collide with finish point
plane.physicsBody?.contactTestBitMask = CollisionTypes.FloorAndRoof.rawValue | CollisionTypes.OtherObject.rawValue | CollisionTypes.FinishPoint.rawValue
plane.physicsBody?.usesPreciseCollisionDetection = true
plane.physicsBody?.isDynamic = true
plane.physicsBody?.friction = 1
plane.physicsBody?.restitution = 0
plane.physicsBody?.mass = 0.1 // customise for different planes
plane.physicsBody?.angularDamping = 1
plane.physicsBody?.linearDamping = 0.2 // customise for different planes
liftFactor = 0.1 // customise for different planes
addChild(plane)
flightMode = 4 // dead, should drop to floor and change to mode 0 when at rest
//print(flightMode)
}
Bin SKSpriteNode that moving Plane should collide with.
func createBin(position: CGPoint) {
binFront = SKSpriteNode(imageNamed: "binFront")
binFront.name = "binFront"
binFront.setScale(0.15)
binFront.position = position
binFront.zPosition = 2 // in front of plane
addChild(binFront)
binBack = SKSpriteNode(imageNamed: "binBack")
binBack.name = "binBack"
binBack.setScale(0.15)
binBack.position = position
binBack.zPosition = 0 // behind plane
addChild(binBack)
binPhysicsBody = SKSpriteNode(imageNamed: "binPhysicsBody")
binPhysicsBody.name = "binPhysicsBody"
binPhysicsBody.physicsBody = SKPhysicsBody(texture: binPhysicsBody.texture!, size: binPhysicsBody.texture!.size())
binPhysicsBody.setScale(0.15)
binPhysicsBody.physicsBody?.allowsRotation = false
binPhysicsBody.physicsBody?.categoryBitMask = CollisionTypes.OtherObject.rawValue
binPhysicsBody.physicsBody?.collisionBitMask = CollisionTypes.Plane.rawValue
binPhysicsBody.physicsBody?.contactTestBitMask = CollisionTypes.Plane.rawValue
binPhysicsBody.physicsBody?.usesPreciseCollisionDetection = false
binPhysicsBody.physicsBody?.isDynamic = false
binPhysicsBody.physicsBody?.friction = 1
binPhysicsBody.physicsBody?.restitution = 0
binPhysicsBody.physicsBody?.mass = 50
binPhysicsBody.position = position
binPhysicsBody.zPosition = 0
addChild(binPhysicsBody)
}
I still don't know the cause of my issue but I got around it by changing the image used to create my physics body. I used a slightly thicker 'wall thickness' on the bin.

SKShapeNode motionless when made the scene.frame a physicsBody

It's my very first post in coding forum - it means i'm REEAAAAALLLY stuck (I've searched everywhere and tested many options).
Creating a simple bouncing ball on my ipad screen, coding the app using SpriteKit.
The below code prevent the ball to move at all BUT when i remove the SKPhysicsBody created out the frame.edge : the ball falls.
I've got the feeling that the self.physicsBody is ultimately made as a volume despite I ask it to be made from an edge...
Can you please help ?
override func didMove(to view: SKView) {
self.physicsWorld.gravity = CGVector(dx: 0,dy: -6)
self.physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame) // ball's cage
self.physicsBody?.friction = 0;
let fbound = SKShapeNode(rect: self.frame)
fbound.lineWidth = 1
fbound.strokeColor = .red
addChild(fbound)
let path = CGMutablePath()
path.addArc(center: CGPoint(x: view.bounds.width / 2, y: view.bounds.height / 2),
radius: 15,
startAngle: 0,
endAngle: CGFloat.pi * 2,
clockwise: true)
let ball = SKShapeNode(path: path)
ball.lineWidth = 1
ball.fillColor = .red
ball.strokeColor = .black
ball.glowWidth = 0.5
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.frame.size.width/2)
ball.physicsBody?.friction = 0
ball.physicsBody?.restitution = 1
ball.physicsBody?.mass = 0.6
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.allowsRotation = false
ball.physicsBody?.isDynamic = true
addChild(ball)
}
Please let me know if you need more details
Thanks a mil in advance
Rgds

How do I make an object bounce off the edges in swift

I am trying to make my 'Enemy' bounce off all the boundaries. For example, when the Enemy collides with the left, right, top and bottom of the screen, it should change direction.
This is my current code:
import SpriteKit
import GameplayKit
struct Physics {
static let Enemy: UInt32 = 0x1 << 1
let BorderCategory : UInt32 = 0x1 << 2
let BottomCategory : UInt32 = 0x1 << 3
let BallCategory : UInt32 = 0x1 << 4
}
class GameScene: SKScene {
var Enemy = SKSpriteNode()
var gameStarted = Bool()
var gameState = "running"
var destX : CGFloat = 0.0
var destY : CGFloat = 0.0
var score = 0
override func didMove(to view: SKView) {
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
let screenSize: CGRect = UIScreen.main().bounds // get the screen size
let screenWidth = screenSize.width //get the width
let screenHeight = screenSize.height //get the height
Enemy = SKSpriteNode(imageNamed: "red2")
Enemy.size = CGSize(width: 60, height: 70)
Enemy.position = (CGPoint(x: self.frame.width / 6 - Enemy.frame.width, y: self.frame.height / 10))
Enemy.physicsBody = SKPhysicsBody(circleOfRadius: Enemy.frame.height / 2)
Enemy.physicsBody?.categoryBitMask = Physics.Enemy
//Enemy.physicsBody?.categoryBitMask = Physics.Ground | Physics.wall
//Enemy.physicsBody?.contactTestBitMask = Physics.Ground | Physics.wall
Enemy.physicsBody?.affectedByGravity = false
Enemy.physicsBody?.isDynamic = true
self.addChild(Enemy)
Enemy.physicsBody?.velocity = CGVector(dx: 50, dy: 50)
if (Enemy.position.x == screenWidth) {
Enemy.physicsBody?.velocity = CGVector(dx: -50, dy: 0) // change direction at edge DOESN'T WORK
}
if(Enemy.position.y == screenHeight){
Enemy.physicsBody?.velocity = CGVector(dx: 0, dy: -50) //change direction at edge DOESN'T WORK
}
}
OK, based on your code I've written a working bouncing sprite. It uses the physics-engine to achieve this, rather than manually changing the direction (which I guess is how you should go about doing this).
override func didMove(to view: SKView) {
let borderBody = SKPhysicsBody(edgeLoopFrom: frame)
borderBody.friction = 0
borderBody.categoryBitMask = Physics.wall
physicsBody = borderBody
let enemy = enemySprite(size: CGSize(width: 60, height: 70), position: CGPoint(x: frame.size.width/2, y: frame.size.height/2))
addChild(enemy)
let force = SKAction.applyForce(CGVector(dx: 300, dy: 300) , duration: 0.1)
enemy.run(force)
}
func enemySprite(size: CGSize, position: CGPoint) -> SKSpriteNode {
let enemy = SKSpriteNode(color: SKColor.red(), size: CGSize(width: 60, height: 80))
enemy.position = CGPoint(x: frame.width/2, y: frame.height/2)
enemy.physicsBody = SKPhysicsBody(circleOfRadius: enemy.frame.height / 2) // A bit silly with circle, but you seem to have an image for this use
enemy.physicsBody?.categoryBitMask = Physics.enemy
enemy.physicsBody?.restitution = 1
enemy.physicsBody?.friction = 0
enemy.physicsBody?.collisionBitMask = Physics.wall
enemy.physicsBody?.affectedByGravity = false
enemy.physicsBody?.angularDamping = 0
enemy.physicsBody?.linearDamping = 0
return enemy
}
A question to consider is also how you set up the scene? Is the scene-size the same as the view-size? Otherwise the scene's physicsBody will not make the ball bounce where you expect it to...
It won't (in most cases) because of your conditions - using equal to operators instead of greater/lower than.
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
// Create enemy with physics body attached and add it to the scene
let enemy = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width: 20, height: 20))
enemy.name = "enemy"
enemy.position = CGPoint(x: size.width / 2, y: size.height / 2)
enemy.physicsBody = SKPhysicsBody(rectangleOfSize: enemy.size)
enemy.physicsBody!.affectedByGravity = false
enemy.physicsBody!.linearDamping = 0
enemy.physicsBody!.mass = 0.1
enemy.physicsBody!.velocity = CGVector(dx: 150, dy: 0)
addChild(enemy)
}
override func didSimulatePhysics() {
let enemy = childNodeWithName("enemy")!
if (enemy.position.x > size.width && enemy.physicsBody!.velocity.dx > 0)
|| (enemy.position.x < 0 && enemy.physicsBody!.velocity.dx < 0) {
enemy.physicsBody!.velocity.dx *= -1
}
}
}

Swift SpriteKit Gravity Point

I need to make something like a Gravity Point in Swift.
The SpriteNode is in the middle of the screen, and by Left or Right Gestures it can move along the x-axis.
But after for e.g. a 'RightSwipe', it should come back to his starting point. You could say it should look like a jump along the x-axis.
The World Gravity cant be a specific point, so how can I 'Force' it to come back to his original point?
I tried it with SKFieldNode's RadialGravityField, but after the Nodes impulse is made, it goes back to his original point and disappears.
How can I avoid that?
My Code:
{
.......
Node = SKSpriteNode(texture: Node1)
Node.size = CGSize(width: 100, height: 140)
Node.position = CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height / 4)
Node.zPosition = 3
Node.runAction(RunAnimation)
Node.physicsBody = SKPhysicsBody(rectangleOfSize: Node.size)
Node.physicsBody?.dynamic = true
Node.physicsBody?.allowsRotation = false
Node.physicsBody?.usesPreciseCollisionDetection = true
Node.physicsBody?.restitution = 0
Node.physicsBody?.velocity = CGVectorMake(0, 0)
self.addChild(Node)
let swipeRight:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("SwipeRight:"))
swipeRight.direction = .Right
view.addGestureRecognizer(swipeRight)
let swipeLeft:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector("SwipeLeft:"))
swipeLeft.direction = .Left
view.addGestureRecognizer(swipeLeft)
}
func SwipeRight(recognizer:UISwipeGestureRecognizer) {
Node.physicsBody?.applyImpulse(CGVectorMake(10,0))
}
func SwipeLeft(recognizer:UISwipeGestureRecognizer) {
Node.physicsBody?.applyImpulse(CGVectorMake(-10,0))
}
override func update(currentTime: CFTimeInterval) {
if(Node.position.x > self.frame.size.width / 1.6)
{
fieldNode = SKFieldNode.radialGravityField()
fieldNode.enabled = true
fieldNode.position = CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height / 4)
fieldNode.strength = 0.4
addChild(fieldNode)
}
if(Node.position.x < self.frame.size.width / 2.6)
{
fieldNode = SKFieldNode.radialGravityField()
fieldNode.enabled = true
fieldNode.position = CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height / 4)
fieldNode.strength = 0.4
addChild(fieldNode)
}
Sounds like a job for a radial gravity SKFieldNode. link

How can I apply friction to a circular SKPhysicsBody

I am trying to understand friction, or some other aspect of physics, in SpriteKit. I created a circle that rotates. I placed a square on top of the rotating circle. I expected the square to rotate along with the circle and fall off, but the square does not move. I set physicsBody.friction = 1.0 in an attempt to make the surfaces sticky but it does not help. What changes to physics do I need to make so that the square is affected by the circle's rotation?
I created a self-contained example using a Playground. To test:
Download attached bluecircle.png and place it on your desktop (or wherever)
Copy code below into your Playground
Change NSImage path to wherever you saved bluecircle.png
Code:
import Cocoa
import SpriteKit
import XCPlayground
let screenWidth = 1400.0
let screenHeight = 1000.0
let xOffset = 300.0
let smallsquareSize = CGSize(width: 100, height: 100)
let bluesquareSize = CGSize(width: 400 , height: 400)
// the img is a blue circle w/ radius of 200 located on my desktop
let img = NSImage(byReferencingFile: "/Users/CHANGETHIS/Desktop/bluecircle.png")
class PlayScene: SKScene {
let redsquare = SKSpriteNode(color: SKColor.redColor(), size: smallsquareSize)
let greensquare = SKSpriteNode(color: SKColor.greenColor(), size: smallsquareSize)
let bluesquare = SKSpriteNode(color: SKColor.blueColor(), size: bluesquareSize)
let bluecircle = SKSpriteNode(texture: SKTexture(image: img))
var lastTime = NSTimeInterval(0)
init(size: CGSize) {
super.init(size: size)
}
override func didMoveToView(view: SKView!) {
backgroundColor = SKColor.blackColor()
// RED SQUARE
redsquare.position = CGPoint(x: screenWidth/2 - xOffset, y: screenHeight - 100)
redsquare.physicsBody = SKPhysicsBody(rectangleOfSize: smallsquareSize)
redsquare.physicsBody.friction = 1.0
addChild(redsquare)
// BLUE SQUARE
bluesquare.position = CGPoint(x: screenWidth / 2 - xOffset, y: screenHeight / 2)
bluesquare.physicsBody = SKPhysicsBody(rectangleOfSize: bluesquareSize)
bluesquare.physicsBody.dynamic = false
bluesquare.physicsBody.affectedByGravity = false
bluesquare.physicsBody.friction = 1.0
addChild(bluesquare)
// GREEN SQUARE
greensquare.position = CGPoint(x: screenWidth/2 + xOffset, y: screenHeight - 100)
greensquare.physicsBody = SKPhysicsBody(rectangleOfSize: smallsquareSize)
greensquare.physicsBody.friction = 1.0
addChild(greensquare)
// BLUE CIRCLE
bluecircle.position = CGPoint(x: screenWidth / 2 + xOffset, y: screenHeight / 2)
bluecircle.physicsBody = SKPhysicsBody(circleOfRadius: 200)
bluecircle.physicsBody.dynamic = false
bluecircle.physicsBody.affectedByGravity = false
bluecircle.physicsBody.friction = 1.0
addChild(bluecircle)
}
override func update(currentTime: NSTimeInterval) {
let delta = currentTime - lastTime
lastTime = currentTime
bluesquare.zRotation += delta
bluecircle.zRotation += delta
}
}
var view = SKView(frame:NSRect(x: 0.0, y: 0.0, width: screenWidth, height: screenHeight))
var playScene = PlayScene(size: CGSize(width: screenWidth, height: screenHeight))
view.presentScene(playScene)
view.showsFPS = true
view.showsDrawCount = true
view.showsNodeCount = true
XCPShowView("View", view)
`
I think the answer to your question are Joints . Creating a fixed joint between your nodes I believe will solve your problem .
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKPhysicsJoint_Ref/Reference/Reference.html

Resources