Reputation: 1
I am trying to throw a ball in an iOS game in the direction of touchEnded method. I have searched a number of threads but some are old and for the others I could not figure out the issue.
What I'm trying to do is as follows:
1- Add A ball as SKShapeNode - Set affectedByGravity to false.
2- On touchesEnded - I get the location CGPoint (somehow I get the y position reversed - I correct it).
3- I calculate the angle bearing using atan().
4- I calculate the CGVector dx and dy impulses based on the sin() and cos() of the angle.
5- I applyImpulse to the ball based on the CGVector above.
My issue is that the ball is not shot exactly to the touchesEnded direction. If the touchesEnded direction is vertically below the ball, then it works. The further away from vertical, the larger the deviation.
To help troubleshoot and visualize the issue, I added a line and end blue ball in the direction that the ball should follow. The red ball should ideally collide with the blue ball. However, the real direction I get is as per the green ball added. The green ball should ideally be on the line.
I was trying to do exactly like what was done here but I don't get the results: SpriteKit physics applyImpulse in direction
Appreciate your help. '''
import SpriteKit
class GameScene: SKScene {
var sprite = SKShapeNode()
var radius: CGFloat = 10
var ballSpawnPosition: CGPoint = CGPoint(x: 0, y: 0)
override func didMove(to view: SKView) {
self.physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame)
ballSpawnPosition = CGPoint(x: frame.midX, y: frame.maxY - 50)
spawnBall()
}
func spawnBall() {
sprite = SKShapeNode(circleOfRadius: radius)
sprite.physicsBody = SKPhysicsBody(circleOfRadius: radius)
sprite.physicsBody?.collisionBitMask = 0x1
sprite.fillColor = .red
sprite.position = ballSpawnPosition
addChild(sprite)
sprite.physicsBody?.affectedByGravity = false
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: view)
let blueTestTouchPointBall = SKShapeNode(circleOfRadius: radius)
blueTestTouchPointBall.physicsBody = SKPhysicsBody(circleOfRadius: radius)
blueTestTouchPointBall.physicsBody?.categoryBitMask = 0x1
blueTestTouchPointBall.fillColor = .blue
let y = frame.size.height - position.y
blueTestTouchPointBall.position = CGPoint(x: position.x, y: y)
blueTestTouchPointBall.physicsBody?.affectedByGravity = false
addChild(blueTestTouchPointBall)
var path = CGMutablePath()
path.move(to: ballSpawnPosition)
path.addLine(to: CGPoint(x: position.x, y: y))
path.closeSubpath()
let line = SKShapeNode()
line.path = path
line.strokeColor = .white
line.lineWidth = 2
addChild(line)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first { //should not work if dragged.
let position = touch.location(in: view)
let dx = position.x - ballSpawnPosition.x
let dy = frame.size.height - abs( position.y - ballSpawnPosition.y)
let ang = atan(abs(dx)/abs(dy))
//let tang = sqrt(dx*dx + dy*dy)
let impulse: CGFloat = 15.0
let x_impulse = impulse * sin(ang) * dx/abs(dx)
let y_impulse = -1.0 * impulse * cos(ang)
// let x_impulse = impulse * dx/abs(dx) * dx/tang
// let y_impulse = impulse * -1 * dy/tang
sprite.physicsBody?.applyImpulse(CGVector(dx: x_impulse , dy: y_impulse ))
let greenTestBall = SKShapeNode(circleOfRadius: radius/2)
greenTestBall.fillColor = .green
greenTestBall.position = CGPoint(x: ballSpawnPosition.x + 300 * sin(ang) * dx/abs(dx), y: ballSpawnPosition.y - 300 * cos(ang) )
addChild(greenTestBall)
}
}
}
'''
Upvotes: 0
Views: 258
Reputation: 517
You are mixing points in different coordinate systems. The location of the UITouch
should be converted to the node in which the ball resides (typically the scene, but it could be another node) using the location(in:)
method of UITouch
and passing in the SKNode
. See here for more on coordinate conversions in SpriteKit. Since the ball is a child of the scene, change
let position = touch.location(in: view)
to
let position = touch.location(in: self) // i.e. the `GameScene`
The angle calculation also needs to be adjusted supposing that you are using the ball spawn position as the reference point and the take the angle with respect to the vertical. Change
let dy = frame.size.height - abs(position.y - ballSpawnPosition.y)
to
let dy = abs(position.y - ballSpawnPosition.y)
Changing the above creates what I believe you are looking for. Note that momentum is additive: applying an impulse right while the ball is moving left will not necessarily cause the ball to start moving left (unless the impulse is great enough)
Upvotes: 1