Second node instance SKPhysicsBody not responding to collision - swift

I've been trying to create a simple game in SK where the ball would bounce off the tops of trampolines that spawn gradually. Collisions work fine with the first trampoline but I'm having problems registering it when ball hits second trampoline spawned in run-time. Anyone might have a clue what I've been doing wrong?
Here is the code.
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var ball: SKShapeNode?
var trampoline: SKShapeNode?
var cam: SKCameraNode?
var hasHit: Bool?
var hasCollided: Bool?
var hasMoved: Bool?
func createBall() {
ball = SKShapeNode(circleOfRadius: 50)
ball?.position = CGPoint(x: 0, y: -600)
ball?.fillColor = .red
ball?.strokeColor = .red
let ballPhysics = SKPhysicsBody(circleOfRadius: 50)
ball?.physicsBody = ballPhysics
ballPhysics.categoryBitMask = 1
ballPhysics.collisionBitMask = 0
ballPhysics.contactTestBitMask = 0
ballPhysics.affectedByGravity = false //NO GRAVITY!
ballPhysics.isDynamic = true
addChild(ball!)
}
func createTrampoline(x: CGFloat, y: CGFloat) {
let rect = CGRect(x: x, y: y, width: 100, height: 20)
trampoline = SKShapeNode(rect: rect)
trampoline?.fillColor = .blue
trampoline?.strokeColor = .blue
let trampolinePhysics = SKPhysicsBody(rectangleOf: CGSize(width: 100, height: 20))
trampoline?.physicsBody = trampolinePhysics
trampolinePhysics.categoryBitMask = 2
trampolinePhysics.collisionBitMask = 0
trampolinePhysics.contactTestBitMask = 1
trampolinePhysics.affectedByGravity = false //NO GRAVITY!
trampolinePhysics.isDynamic = true
addChild(trampoline!)
}
override func didMove(to view: SKView) {
physicsWorld.contactDelegate = self
cam = SKCameraNode()
self.camera = cam
self.backgroundColor = .darkText
hasHit = false
hasCollided = false
hasMoved = false
createBall()
createTrampoline(x: -50, y: -10)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touches: AnyObject in touches {
let location = touches.location(in: self)
let move = SKAction.moveTo(x: location.x, duration: 0.1)
ball?.run(move)
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if (!hasMoved!) {
let vect = CGVector(dx: 0, dy: 600)
ball?.physicsBody?.applyImpulse(vect)
ball?.physicsBody?.affectedByGravity = true
hasMoved = true
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if (hasHit == true) {
cam?.position.y = (ball?.position.y)!
}
}
func didBegin(_ contact: SKPhysicsContact) {
let vect = CGVector(dx: 0, dy: 1500)
if (((contact.bodyB.node?.position.y)! - (contact.bodyA.node?.position.y)!) > 0) {
if (!hasCollided!){
hasHit = true
hasCollided = true
print("collision")
contact.bodyB.applyImpulse(vect)
createTrampoline(x: -50, y: ((ball?.position.y)! + CGFloat(3000)))
}
} else {
print("bad coll")
hasCollided = false
}
}
}
Thanks in advance.

Related

Child nodes inside SKNode disappears with fast shaking device with CMMotionManager applied to move balls

I've create a SKNode with physicsBody edgeFromLoop circular inside which i've added 8 small circular nodes. and 8 circular nodes are moving with CMMotionManager inside Parent circular node. with fast motion shake some balls disappears from screen. SKScene Class given below
Balls only disappears when anyone shake mobile hard randomly.
There are 8 balls initially but after some hard shake reduced.
class GameScene: SKScene {
let motionManager = CMMotionManager()
var Circle = SKShapeNode(circleOfRadius: 108)
var sound = SKAction.playSoundFileNamed(getRandomSound(), waitForCompletion: false)
var collisionBitmasks: [UInt32] = [UInt32]()
override func didMove(to view: SKView) {
Circle.fillTexture = SKTexture.init(image: UIImage.init(named: "img_Ball") ?? UIImage())
Circle.position = CGPoint.init(x: 0, y: 0)
Circle.name = "defaultCircle"
Circle.lineWidth = 0.0
Circle.fillColor = SKColor.lightGray.withAlphaComponent(0.8)
Circle.physicsBody = SKPhysicsBody.init(edgeLoopFrom: UIBezierPath.init(ovalIn: CGRect.init(x: Circle.frame.minX + 12,
y: Circle.frame.minY + 12,
width: Circle.frame.width - 24,
height: Circle.frame.height - 24)).cgPath)
Circle.physicsBody?.isDynamic = true
Circle.physicsBody?.affectedByGravity = false
Circle.physicsBody?.allowsRotation = false
addChild(Circle)
self.physicsWorld.contactDelegate = self
startAcceleroMeter()
for index in 1...8 {
addBalls(index)
}
}
func stop() {
motionManager.stopAccelerometerUpdates()
Circle.children.forEach { (ball) in
ball.physicsBody?.isDynamic = false
ball.physicsBody?.affectedByGravity = false
}
}
func startAcceleroMeter() {
Circle.children.forEach { (ball) in
ball.physicsBody?.isDynamic = true
ball.physicsBody?.affectedByGravity = true
}
motionManager.startAccelerometerUpdates()
motionManager.accelerometerUpdateInterval = 0.1
motionManager.startAccelerometerUpdates(to: .main) { (motionData, error) in
let gravity = CGVector.init(dx: (motionData?.acceleration.x ?? 0.0) * 20, dy: (motionData?.acceleration.y ?? 0.0) * 20)
print(gravity)
self.physicsWorld.gravity = gravity
}
}
func stopPlaying() -> [String] {
var dictionary = [String]()
for (_, ball) in (Circle.children).enumerated() {
dictionary.append("\(ball.position.x), \(ball.position.y), 0.0")
}
return dictionary
}
func addBalls(_ ballNo: Int) {
let ball = SKShapeNode.init(circleOfRadius: 12)
ball.name = "ball\(ballNo)"
ball.fillTexture = SKTexture.init(linearGradientWithAngle: CGFloat.pi, colors: [BallColors(rawValue: ballNo)?.toUIColor(false) ?? UIColor(), BallColors(rawValue: ballNo)?.toUIColor(true) ?? UIColor()], locations: [0, 1], size: ball.frame.size)
ball.fillColor = UIColor.white
ball.physicsBody = SKPhysicsBody.init(circleOfRadius: 12)
ball.physicsBody?.isDynamic = true
ball.physicsBody?.affectedByGravity = true
ball.physicsBody?.allowsRotation = true
ball.physicsBody?.friction = 0.2
ball.physicsBody?.restitution = 0.9
ball.physicsBody?.linearDamping = 0.1
ball.physicsBody?.angularDamping = 0.1
ball.physicsBody?.mass = 0.349065870046616
ball.physicsBody?.usesPreciseCollisionDetection = true
ball.position = CGPoint(x: Circle.frame.midX - CGFloat(ballNo), y: Circle.frame.midY - CGFloat(ballNo))
ball.physicsBody?.fieldBitMask = 1
ball.physicsBody?.categoryBitMask = UInt32.init(ballNo + 100)
let collisionBitMask = UInt32.init(ballNo + 20) | UInt32.init(ballNo + 100)
collisionBitmasks.append(collisionBitMask)
ball.physicsBody?.collisionBitMask = collisionBitMask //To be different for each
ball.physicsBody?.contactTestBitMask = UInt32.init(ballNo + 20)
Circle.addChild(ball)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
extension GameScene: SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
if collisionBitmasks.contains(contact.bodyA.collisionBitMask) && collisionBitmasks.contains(contact.bodyB.collisionBitMask) {
run(sound)
}
}
}
You need a hidden magic here. Each small ball adds a constraint that can prevent any accident from high speeds:
ball.constraints = [SKConstraint.distance(SKRange(upperLimit: 108 - 12), to: Circle)]

Moving and rotating sprite with touches using SKSpriteKit and Swift 4

I am wanting my sprite "Character" to move and rotate "follow" my finger as I move it around. I had it working fine but somehow have managed to screw it up. I'm not sure what I've done but can't for the life of me figure it out.
Any help would be greatly appreciated. Im sure its something stupid that I'm just overlooking. Thanks in advance.
Mark
GameScene.swift
import SpriteKit
import GameplayKit
//MARK: ---------------Global Variables Section
var nodeType: String = String()
var characterNode = Character()
let characterImage: String = String("default_pose")
var characterTexture:SKTexture = SKTexture()
let characterSize: CGSize = CGSize(width: 64, height: 64)
let characterPosition: CGPoint = CGPoint(x: 0, y: 0)
var enemyNode = Enemy()
let enemyImage: String = String("blob1")
var enemyTexture = SKTexture()
let enemySize: CGSize = CGSize(width: 50, height: 50)
let enemyPosition: CGPoint = CGPoint(x: 150, y: 150)
class GameScene: SKScene, SKPhysicsContactDelegate {
//MARK: --------------------Class Variables Section
var affectedByGravity: Bool = false
var dynamic: Bool = true
var rotation: Bool = false
var lastUpdateTime: CFTimeInterval = 0
//MARK: --------------------DidMove to View Section
override func didMove(to view: SKView) {
var node = SKSpriteNode()
node = addNode(NodeType: "Character")
applyPhysics(physicsNode: node, nodeImage: characterImage, nodeSize: characterSize, nodeName: node.name!)
node.zPosition = 10
addChild(node)
node = addNode(NodeType: "Enemy")
applyPhysics(physicsNode: node, nodeImage: enemyImage, nodeSize: enemySize, nodeName: node.name!)
node.zPosition = 9
addChild(node)
}
//MARK: -----------------------Add Node Section
func addNode(NodeType: String) -> SKSpriteNode {
var node = SKSpriteNode()
switch NodeType {
case "Enemy":
node = enemyNode.setupNode(nodeName: "Enemy", nodeImage: "blob1", nodeSize: enemySize, nodePosition: enemyPosition)
default:
node = characterNode.setupNode(nodeName: "Player", nodeImage: "default_pose", nodeSize: characterSize, nodePosition: characterPosition)
}
return node
}
//MARK: ------------------------Physics Section
func applyPhysics(physicsNode: SKSpriteNode, nodeImage: String, nodeSize: CGSize, nodeName: String) {
let nodeTexture: SKTexture = SKTexture(imageNamed: nodeImage)
physicsNode.physicsBody = SKPhysicsBody(texture: nodeTexture, size: nodeSize)
physicsNode.physicsBody?.isDynamic = dynamic
physicsNode.physicsBody?.affectedByGravity = affectedByGravity
physicsNode.physicsBody?.allowsRotation = rotation
physicsNode.physicsBody?.categoryBitMask = 1
physicsNode.physicsBody?.contactTestBitMask = 1
physicsNode.physicsBody?.collisionBitMask = 1
}
//MARK: ------------------------Movement and Rotation Section
func moveAndRotate(sprite: Character, toPosition position: CGPoint) {
let angle = atan2(position.y - sprite.position.y, position.x - sprite.position.x)
let rotateAction = SKAction.rotate(toAngle: angle - .pi / 2, duration: 0.05, shortestUnitArc:true)
sprite.run(rotateAction)
let offsetX = position.x - (sprite.position.x)
let offsetY = position.y - (sprite.position.y)
let normal = simd_normalize(simd_double2(x: Double(offsetX), y:Double(offsetY)))
characterNode.velocity = CGVector(dx: CGFloat(normal.x) * sprite.movePointsPerSecond, dy: CGFloat(normal.y) * sprite.movePointsPerSecond)
}
//MARK: ----------------------Touches Section
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let touchLocation = touch.location(in: self)
characterNode.isMoving = true
moveAndRotate(sprite: characterNode, toPosition: touchLocation)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let touchLocation = touch.location(in: self)
moveAndRotate(sprite: characterNode, toPosition: touchLocation)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
characterNode.isMoving = false
}
//MARK: ------------------Update Section
override func update(_ currentTime: TimeInterval) {
let deltaTime = max(1.0/30, currentTime - lastUpdateTime)
lastUpdateTime = currentTime
update(character: characterNode, dt: deltaTime)
}
func update(character: Character, dt: CFTimeInterval) {
if characterNode.isMoving == true {
let newX = character.position.x + character.velocity.dx * CGFloat(dt)
let newY = character.position.y + character.velocity.dy * CGFloat(dt)
character.position = CGPoint(x: newX, y: newY)
}
}
}
Character.swift
import SpriteKit
import GameplayKit
class Character: SKSpriteNode {
let movePointsPerSecond: CGFloat = 150.0
var velocity = CGVector(dx: 0.0, dy: 0.0)
var isMoving = false
var node: SKSpriteNode = SKSpriteNode()
func setupNode(nodeName: String, nodeImage: String, nodeSize: CGSize, nodePosition: CGPoint) -> SKSpriteNode {
node = SKSpriteNode(imageNamed: nodeImage)
node.name = nodeName
node.position = nodePosition
return node
}
}
Your node does not move because you never assign characterNode to the node on your screen
override func didMove(to view: SKView) {
var node = SKSpriteNode()
node = addNode(NodeType: "Character")
applyPhysics(physicsNode: node, nodeImage: characterImage, nodeSize: characterSize, nodeName: node.name!)
node.zPosition = 10
addChild(node)
characterNode = node //ADD THIS LINE
node = addNode(NodeType: "Enemy")
applyPhysics(physicsNode: node, nodeImage: enemyImage, nodeSize: enemySize, nodeName: node.name!)
node.zPosition = 9
addChild(node)
}

How to have a collision reset an image to a different location and update score

I would like to know what im doing wrong with my collision detection. I'm trying to make a game where the user controls a character using a virtual joystick and tries to catch donuts that fall from the top of the screen. The joystick works and having the donut fall works but when the two object collide the score isn't updated and the donut location isn't reset to its default location like I want it to. I have looked at several tutorials but can't seem to find my mistake.
import SpriteKit
import GameplayKit
struct BodyType {
static let Character: UInt32 = 1
static let Knife: UInt32 = 2
static let Donut: UInt32 = 4
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var score: Int = 0
var level: Int = 0
var scoreLabel: SKLabelNode = SKLabelNode(text: "Score: 0")
var BoostButton: SKSpriteNode = SKSpriteNode(imageNamed: "boost")
var Knife: SKSpriteNode = SKSpriteNode(imageNamed: "knife")
var Donut: SKSpriteNode = SKSpriteNode(imageNamed: "donut")
var velocityMultiplier: CGFloat = 0.15
var BoostActive: Bool = false
var KnifeVel: CGFloat = 3
var KnifeX: CGFloat = 0
var KnifeY: CGFloat = 0
var FoodX: CGFloat = 0
var FoodY: CGFloat = 0
var FoodVel: CGFloat = 3
enum NodesZPosition: CGFloat {
case Character, joystick
}
var Character: SKSpriteNode = SKSpriteNode(imageNamed: "character")
var analogJoystick: AnalogJoystick = {
let js = AnalogJoystick(diameter: 90, images: (UIImage(named: "substrate"), UIImage(named: "stick")))
js.position = CGPoint(x: 480, y: 75)
js.zPosition = NodesZPosition.joystick.rawValue
return js
}()
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
backgroundColor = UIColor.blue
self.anchorPoint = CGPoint(x: 0, y: 0)
Character.position = CGPoint(x: 100,y: 200)
Character.size = Donut.size
Character.physicsBody = SKPhysicsBody(texture: Character.texture!, size: Character.size)
Character.physicsBody?.affectedByGravity = false
Character.name = "Character"
Character.physicsBody?.isDynamic = true
Character.physicsBody?.categoryBitMask = BodyType.Character
Character.physicsBody?.collisionBitMask = BodyType.Knife | BodyType.Donut
Character.physicsBody?.contactTestBitMask = BodyType.Knife | BodyType.Donut
self.addChild(Character)
self.addChild(BoostButton)
BoostButton.position = CGPoint(x: 75, y: 75)
Knife.position = CGPoint(x: self.frame.width + 100, y: 200)
Knife.name = "Knife"
self.addChild(Knife)
Donut.position = CGPoint(x: 200, y: self.frame.height + 150)
Donut.name = "Donut"
self.addChild(Donut)
scoreLabel.position = CGPoint(x: 65, y: self.frame.height - 40)
scoreLabel.fontSize = 30
scoreLabel.fontColor = UIColor.black
self.addChild(scoreLabel)
setupJoystick()
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.friction = 0
self.physicsBody = border
}
func setupJoystick (){
addChild(analogJoystick)
analogJoystick.trackingHandler = {[unowned self] data in
self.Character.position = CGPoint(x: self.Character.position.x + (data.velocity.x * self.velocityMultiplier),y: self.Character.position.y + (data.velocity.y * self.velocityMultiplier))
self.Character.zRotation = data.angular
}
}
private func didBegin(_ contact: SKPhysicsContact) {
var firstBody = SKPhysicsBody()
var secondBody = SKPhysicsBody()
if contact.bodyA.node?.name == "Character" {
firstBody = contact.bodyA
secondBody = contact.bodyB
}
else{
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if firstBody.node?.name == "Character" && secondBody.node?.name == "Donut"{
print("collison")
score += 20
scoreLabel.text = "Score \(score)"
Donut.position.y = self.frame.height + 150
Donut.position.x = CGFloat.random(in: 100..<(self.frame.width - 100))
}
else if firstBody.node?.name == "Donut" && secondBody.node?.name == "Character"{
print("collison")
score += 20
scoreLabel.text = "Score \(score)"
Donut.position.y = self.frame.height + 150
Donut.position.x = CGFloat.random(in: 100..<(self.frame.width - 100))
}
}
func touchDown(atPoint pos : CGPoint) {
}
func touchMoved(toPoint pos : CGPoint) {
}
func touchUp(atPoint pos : CGPoint) {
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches ){
let location = touch.location(in: self)
if (BoostButton.frame.contains(location)){
BoostActive = true
}
else{
BoostActive = false
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func update(_ currentTime: TimeInterval) {
// if (BoostActive == true){
// velocityMultiplier = velocityMultiplier * 1.5
// }
// else{
// velocityMultiplier = 0.15
//}
FoodY = Donut.position.y
KnifeX = Knife.position.x
if (Knife.position.x < (self.frame.width - 20) && Knife.position.x > 20){
Knife.physicsBody = SKPhysicsBody(texture: Knife.texture!, size: Knife.size)
Knife.physicsBody?.affectedByGravity = false
Knife.physicsBody?.restitution = 0
Knife.physicsBody?.pinned = false
Knife.physicsBody?.isDynamic = true
Knife.physicsBody?.allowsRotation = false
Knife.physicsBody?.categoryBitMask = BodyType.Knife
Knife.physicsBody?.collisionBitMask = BodyType.Character
Knife.physicsBody?.contactTestBitMask = BodyType.Character
}
else{
Knife.physicsBody = nil
}
if (Donut.position.y < self.frame.height && Donut.position.y > 50){
Donut.physicsBody = SKPhysicsBody(texture: Donut.texture!, size: Donut.size)
Donut.physicsBody?.affectedByGravity = false
Donut.physicsBody?.isDynamic = false
Donut.physicsBody?.categoryBitMask = BodyType.Donut
Donut.physicsBody?.collisionBitMask = BodyType.Character
Donut.physicsBody?.contactTestBitMask = BodyType.Character
}
else{
Donut.physicsBody = nil
}
if (FoodY < 30){
Donut.position.y = self.frame.height + 150
Donut.position.x = CGFloat.random(in: 100..<(self.frame.width - 100))
}
else{
Donut.position.y = Donut.position.y - FoodVel
}
if (KnifeX < 0){
Knife.position.x = self.frame.width + 150
Knife.position.y = CGFloat.random(in: 50..<(self.frame.height - 50))
}
else{
Knife.position.x = Knife.position.x - KnifeVel
}
}
}

Objects Position Not Matching Up (Spritekit)

I am building a spritekit game and have just started the project. I have a circle on the screen which starts in the center, and when i drag my finger from the circle outward, it will show a dotted line/bezierpath connected to the ball which will help the user see where it is aiming the ball. When the user lifts their finger, the ball will shoot in the opposite direction of the aim line. (Think a game like soccer stars or pool). The issue is that the maneuver works the first time when everything starts in the middle: I drag my finger and the ball shoots in opposite direction then stops. But when I try it again, the position of the aiming line says it is the same as the ball (It should be), but then it shows up like an inch away from the ball on the screen. I feel like this may be an issue that the scene(s) behind the objects may not be the same size? But I'm confused because I think I'm only using one scene.
GameViewController viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
scene.size = view.bounds.size
//scene.anchorPoint = CGPoint(x: 0.0, y: 0.0)
// Present the scene
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
GameScene Code (Doubt you need all of it but whatever):
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var ball = SKShapeNode(circleOfRadius: 35)
var touchingBall = false
var aimLine = SKShapeNode()
var startAimPoint = CGPoint()
var endAimPoint = CGPoint()
let damping:CGFloat = 0.94
override func didMove(to view: SKView) {
ball.fillColor = SKColor.orange
ball.name = "ball"
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
var physicsBody = SKPhysicsBody(circleOfRadius: 35)
ball.physicsBody = physicsBody
ball.physicsBody?.affectedByGravity = false
ball.physicsBody?.friction = 10.0
ball.position = CGPoint(x: frame.midX, y: frame.midY)
self.addChild(ball)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("TOUCHES BEGAN.")
for touch in touches {
print("TB: \(touchingBall)")
let location = touch.location(in: self)
let node : SKNode = self.atPoint(location)
if node.name == "ball" {
// touched inside node
if ball.physicsBody!.angularVelocity <= 0.0{
touchingBall = true
startAimPoint = ball.position
print(touchingBall)
}
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
print("TOCUHES MOVED.")
for touch in touches {
let location = touch.location(in: self)
if touchingBall{
endAimPoint = location
assignAimLine(start: startAimPoint, end: endAimPoint)
print("Moving touched ball")
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches ended. \(touchingBall)")
if touchingBall == true{
ball.physicsBody!.applyImpulse(CGVector(dx: -(endAimPoint.x - startAimPoint.x) * 3, dy: -(endAimPoint.y - startAimPoint.y) * 3))
}
touchingBall = false
aimLine.removeFromParent()
print(touchingBall)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches cancelled. \(touchingBall)")
if touchingBall == true{
ball.physicsBody!.applyImpulse(CGVector(dx: -(endAimPoint.x - startAimPoint.x) * 3, dy: -(endAimPoint.y - startAimPoint.y) * 3))
}
touchingBall = false
aimLine.removeFromParent()
print(touchingBall)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
print(ball.physicsBody!.velocity)
let dx2 = ball.physicsBody!.velocity.dx * damping
let dy2 = ball.physicsBody!.velocity.dy * damping
ball.physicsBody!.velocity = CGVector(dx: dx2, dy: dy2)
}
func assignAimLine(start: CGPoint, end: CGPoint){
aimLine.removeFromParent()
var bezierPath = UIBezierPath()
bezierPath.move(to: start)
bezierPath.addLine(to: shortenedEnd(startPoint: start, endPoint: end))
var pattern : [CGFloat] = [10.0, 10.0]
let dashed = SKShapeNode(path: bezierPath.cgPath.copy(dashingWithPhase: 2, lengths: pattern))
aimLine = dashed
aimLine.position = ball.position
aimLine.zPosition = 0
self.addChild(aimLine)
}
func hypotenuse(bp: UIBezierPath) -> Double{
var a2 = bp.cgPath.boundingBox.height * bp.cgPath.boundingBox.height
var b2 = bp.cgPath.boundingBox.width * bp.cgPath.boundingBox.width
return Double(sqrt(a2 + b2))
}
func hypotenuse(startP: CGPoint, endP: CGPoint) -> Double{
var bezierPath = UIBezierPath()
bezierPath.move(to: startP)
bezierPath.addLine(to: endP)
return hypotenuse(bp: bezierPath)
}
func shortenedEnd(startPoint: CGPoint, endPoint: CGPoint) -> CGPoint{
var endTemp = endPoint
//while hypotenuse(startP: startPoint, endP: endTemp) > 150{
endTemp = CGPoint(x: endTemp.x / 1.01, y: endTemp.y / 1.01)
//}
return endTemp
}
func addTestPoint(loc: CGPoint, color: UIColor){
var temp = SKShapeNode(circleOfRadius: 45)
temp.fillColor = color
temp.position = loc
self.addChild(temp)
}
}
I tried printing the frame size for the scene and it says 400 something x 700 something (I am testing on iPhone 6 Plus), and it says the UIScreen is same size so i don't know what issue is. Overall, I just need the aiming line to be on the center of the circle more than just the first time I try the maneuver. Thanks.
Like I mentioned in the comments, your problem was how you were laying out your paths. The code below makes the path relative to the ball instead of absolute to the scene. I also fixed the issue with creating new shapes every time.
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var ball = SKShapeNode(circleOfRadius: 35)
var touchingBall = false
var aimLine = SKShapeNode()
var endAimPoint = CGPoint()
override func didMove(to view: SKView) {
ball.fillColor = SKColor.orange
ball.name = "ball"
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
ball.position = CGPoint(x: frame.midX, y: frame.midY)
let physicsBody = SKPhysicsBody(circleOfRadius: 35)
physicsBody.affectedByGravity = false
physicsBody.friction = 10.0
physicsBody.linearDamping = 0.94
ball.physicsBody = physicsBody
self.addChild(ball)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("TOUCHES BEGAN.")
for touch in touches {
print("TB: \(touchingBall)")
let location = touch.location(in: self)
let node : SKNode = self.atPoint(location)
if node.name == "ball" {
// touched inside node
if ball.physicsBody!.angularVelocity <= 0.0{
touchingBall = true
aimLine.path = nil
self.addChild(aimLine)
print(touchingBall)
}
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
print("TOCUHES MOVED.")
for touch in touches {
let location = touch.location(in: self)
if touchingBall{
endAimPoint = self.convert(location, to: ball)
assignAimLine(end: endAimPoint)
print("Moving touched ball")
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches ended. \(touchingBall)")
if touchingBall == true{
ball.physicsBody!.applyImpulse(CGVector(dx: -(endAimPoint.x) * 3, dy: -(endAimPoint.y) * 3))
}
touchingBall = false
aimLine.removeFromParent()
print(touchingBall)
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
print("Touches cancelled. \(touchingBall)")
if touchingBall == true{
ball.physicsBody!.applyImpulse(CGVector(dx: -(endAimPoint.x) * 3, dy: -(endAimPoint.y) * 3))
}
touchingBall = false
aimLine.removeFromParent()
print(touchingBall)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
print(ball.physicsBody!.velocity)
//let dx2 = ball.physicsBody!.velocity.dx * damping
//let dy2 = ball.physicsBody!.velocity.dy * damping
//ball.physicsBody!.velocity = CGVector(dx: dx2, dy: dy2)
}
func assignAimLine(end: CGPoint){
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint.zero)
bezierPath.addLine(to: end)
let pattern : [CGFloat] = [10.0, 10.0]
aimLine.position = ball.position
aimLine.path = bezierPath.cgPath.copy(dashingWithPhase: 2, lengths: pattern)
aimLine.zPosition = 0
}
func addTestPoint(loc: CGPoint, color: UIColor){
var temp = SKShapeNode(circleOfRadius: 45)
temp.fillColor = color
temp.position = loc
self.addChild(temp)
}
}

My initial pipes are different from the following pipes

I'm making a game where the ball is suppose to go through some pipes, and when the player touches the pipes, the game stops. Kind of like flappy bird. The only problem I have is that the initial pipes blocks the entire screen, while the rest of the pipes are placed and randomized exactly as I want. How is this possible?
This is the ball class:
import SpriteKit
struct ColliderType {
static let Ball: UInt32 = 1
static let Pipes: UInt32 = 2
static let Score: UInt32 = 3
}
class Ball: SKSpriteNode {
func initialize() {
self.name = "Ball"
self.zPosition = 1
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.physicsBody = SKPhysicsBody(circleOfRadius: self.size.height /
2)
self.setScale(0.7)
self.physicsBody?.affectedByGravity = false
self.physicsBody?.categoryBitMask = ColliderType.Ball
self.physicsBody?.collisionBitMask = ColliderType.Pipes
self.physicsBody?.contactTestBitMask = ColliderType.Pipes |
ColliderType.Score
}
}
This is the Random Class:
import Foundation
import CoreGraphics
public extension CGFloat {
public static func randomBetweenNumbers(firstNum: CGFloat, secondNum:
CGFloat) -> CGFloat {
return CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(firstNum -
secondNum) + firstNum
}
}
This is the GameplayScene:
import SpriteKit
class GameplayScene: SKScene {
var ball = Ball()
var pipesHolder = SKNode()
var touched: Bool = false
var location = CGPoint.zero
override func didMove(to view: SKView) {
initialize()
}
override func update(_ currentTime: TimeInterval) {
moveBackgrounds()
if (touched) {
moveNodeToLocation()
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event:
UIEvent?) {
touched = true
for touch in touches {
location = touch.location(in:self)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event:
UIEvent?) {
touched = false
}
override func touchesMoved(_ touches: Set<UITouch>, with event:
UIEvent?) {
for touch in touches {
location = touch.location(in: self)
}
}
func initialize() {
createBall()
createBackgrounds()
createPipes()
spawnObstacles()
}
func createBall() {
ball = Ball(imageNamed: "Ball")
ball.initialize()
ball.position = CGPoint(x: 0, y: 0)
self.addChild(ball)
}
func createBackgrounds() {
for i in 0...2 {
let bg = SKSpriteNode(imageNamed: "BG")
bg.anchorPoint = CGPoint(x: 0.5, y: 0.5)
bg.zPosition = 0
bg.name = "BG"
bg.position = CGPoint(x: 0, y: CGFloat(i) * bg.size.height)
self.addChild(bg)
}
}
func moveBackgrounds() {
enumerateChildNodes(withName: "BG", using: ({
(node, error) in
node.position.y -= 15
if node.position.y < -(self.frame.height) {
node.position.y += self.frame.height * 3
}
}))
}
func createPipes() {
pipesHolder = SKNode()
pipesHolder.name = "Holder"
let pipeLeft = SKSpriteNode(imageNamed: "Pipe")
let pipeRight = SKSpriteNode(imageNamed: "Pipe")
pipeLeft.name = "Pipe"
pipeLeft.anchorPoint = CGPoint(x: 0.5, y: 0.5)
pipeLeft.position = CGPoint(x: 350, y: 0)
pipeLeft.xScale = 1.5
pipeLeft.physicsBody = SKPhysicsBody(rectangleOf: pipeLeft.size)
pipeLeft.physicsBody?.categoryBitMask = ColliderType.Pipes
pipeLeft.physicsBody?.affectedByGravity = false
pipeLeft.physicsBody?.isDynamic = false
pipeRight.name = "Pipe"
pipeRight.anchorPoint = CGPoint(x: 0.5, y: 0.5)
pipeRight.position = CGPoint(x: -350, y: 0)
pipeRight.xScale = 1.5
pipeRight.physicsBody = SKPhysicsBody(rectangleOf: pipeRight.size)
pipeRight.physicsBody?.categoryBitMask = ColliderType.Pipes
pipeRight.physicsBody?.affectedByGravity = false
pipeRight.physicsBody?.isDynamic = false
pipesHolder.zPosition = 5
pipesHolder.position.y = self.frame.height + 100
pipesHolder.position.x = CGFloat.randomBetweenNumbers(firstNum:
-250, secondNum: 250)
pipesHolder.addChild(pipeLeft)
pipesHolder.addChild(pipeRight)
self.addChild(pipesHolder)
let destination = self.frame.height * 3
let move = SKAction.moveTo(y: -destination, duration:
TimeInterval(10))
let remove = SKAction.removeFromParent()
pipesHolder.run(SKAction.sequence([move, remove]), withKey: "Move")
}
func spawnObstacles() {
let spawn = SKAction.run({ () -> Void in
self.createPipes()
})
let delay = SKAction.wait(forDuration: TimeInterval(1.5))
let sequence = SKAction.sequence([spawn, delay])
self.run(SKAction.repeatForever(sequence), withKey: "Spawn")
}
func moveNodeToLocation() {
// Compute vector components in direction of the touch
var dx = location.x - ball.position.x
// How fast to move the node. Adjust this as needed
let speed:CGFloat = 0.1
// Scale vector
dx = dx * speed
ball.position = CGPoint(x:ball.position.x+dx, y: 0)
}
}