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
Related
So I am using 3 SKSpriteNode's in my application. floorSprite, leftWall and rightWall.
This is the code I am currently using:
let floorSprite = SKSpriteNode()//(imageNamed: "floor")
floorSprite.alpha = 0.0
floorSprite.anchorPoint = CGPoint(x: 0.5, y: CGPoint.zero.y)
floorSprite.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.frame.size.width, height: floorSprite.size.height + 12))
floorSprite.physicsBody?.isDynamic = false
floorSprite.physicsBody?.restitution = 0.4
floorSprite.position = CGPoint(x: self.frame.minX, y: self.frame.minY + (floorSprite.size.height / 2))
floorSprite.physicsBody?.contactTestBitMask = 0x1 << 1
floorSprite.physicsBody?.collisionBitMask = 0x1 << 1
floorSprite.zPosition = 4
floorSprite.aspectFillToSize(self.frame.size)
self.addChild(floorSprite)
self.floorSprite = floorSprite
let leftwall = SKSpriteNode()
leftwall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 0.1, height: self.frame.size.height*2))
leftwall.physicsBody?.collisionBitMask = 0x1 << 1
leftwall.physicsBody?.isDynamic = false
leftwall.position = CGPoint(x: 0 , y: self.frame.size.height / 2.0)
leftwall.zPosition = 4
leftwall.physicsBody?.restitution = 1.0
self.addChild(leftwall)
let rightwall = SKSpriteNode()
rightwall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 0.1, height: self.frame.size.height*2))
rightwall.physicsBody?.collisionBitMask = 0x1 << 1
rightwall.physicsBody?.isDynamic = false
rightwall.physicsBody?.restitution = 1.0
rightwall.position = CGPoint(x: self.frame.size.width , y: self.frame.size.height / 2.0)
rightwall.zPosition = 4
self.addChild(rightwall)
Here is a screenshot of the issue I have. You can see the light blue lines that shows. If I remove the self.addChild line, the Sprite is not there, and the light blue color gone.
CLICK HERE FOR IMAGE.
Any ideas what might be wrong here? I have tried adding leftWall.isHidden = true and leftWall.alpha = 0.0 but that has no effect...
This looks like you have SKScene.showPhysics on.. turn this off in your appdelegate (or didmovetoview) or wherever you turned it on, and the lines should go away.
These are caused by your physics bodies :)
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.
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.
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))
I have a scrolling background, but the background image appears to be "zoomed in" compared to the original image. The background scrolls just fine, but I'm not sure why the image is "zoomed in". Any help would be greatly appreciated.
class GameScene: SKScene, SKPhysicsContactDelegate {
var blueBall:SKSpriteNode!
var backgroundImage:SKSpriteNode!
var backgroundImage2:SKSpriteNode!
override func didMoveToView(view: SKView) {
self.view!.backgroundColor = UIColor(patternImage: UIImage(imageLiteral: "backgroundImage.png"))
self.physicsWorld.gravity = CGVectorMake(0.0, -5.0)
self.physicsWorld.contactDelegate = self
blueBall = SKSpriteNode( imageNamed: "ball1111.png")
blueBall.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
blueBall.physicsBody = SKPhysicsBody(circleOfRadius: blueBall.size.width / 0.85)
blueBall.physicsBody!.dynamic = true
blueBall.physicsBody!.allowsRotation = true
self.addChild(blueBall)
blueBall.zPosition = 2
backgroundImage = SKSpriteNode(imageNamed: "backgroundImage.png")
self.addChild(backgroundImage)
backgroundImage.zPosition = 0
backgroundImage.anchorPoint = CGPoint(x: 0.5, y: 0.5)
backgroundImage.size.height = self.size.height
backgroundImage.size.width = self.size.width
backgroundImage.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
let backgroundTexture = SKTexture(imageNamed: "backgroundImage.png")
let shiftBackground = SKAction.moveByX(-backgroundTexture.size().width, y: 0, duration: 9)
let replaceBackground = SKAction.moveByX(backgroundTexture.size().width, y:0, duration: 0)
let movingAndReplacingBackground = SKAction.repeatActionForever(SKAction.sequence([shiftBackground,replaceBackground]))
for var i:CGFloat = 0; i<3; i++ {
let background = SKSpriteNode(texture: backgroundTexture)
background.position = CGPoint(x: backgroundTexture.size().width/2 + (backgroundTexture.size().width * i), y: CGRectGetMidY(self.frame))
background.size.height = self.frame.height
background.runAction(movingAndReplacingBackground)
self.addChild(background)
}
}
You have an issue with the image not being loaded correctly, so your numbers are off. Basically by loading SKSpriteNode(imageNamed: "backgroundImage.png") with the png extension, you are loading the actual file, not conforming to any retina rules. The width and height then would not be adjusted to handle this case. If you use an atlas inside the xcassets folder, it will allow you to specify the graphics for all display sizes, and will pick the correct one that is supported by the device.