Trying to figure out how to make this little ball spawn anywhere from the center (self.frame.width / 2) to (self.frame.width / 2 - 100) and (self.frame.width / 2 + 100)
I found this code on stackoverflow and it seems fine, but my game crashes when the ball is about to spawn
ball.position = CGPoint(x: self.frame.width / 2 + (CGFloat(arc4random_uniform(201) - 100)), y: self.frame.height)
Help would be greatly appreciated as always <3
arc4random_uniform takes UInt32 as an argument and returns UInt32 as well. Substracting 100 from it will result in underflow eventually, hence the crash.
You can try something like:
ball.position = CGPoint(
x: self.frame.width / 2 + CGFloat(arc4random_uniform(201)) - 100.0,
y: self.frame.height
)
Related
While I am learning Core Graphic by ray wenderlich,
one step is to transform UIBezierPath, var transform = CGAffineTransform(scaleX: 0.8, y: 0.8),
I do not know why the step after is right,which is transform = transform.translatedBy(x: 15, y: 30)?
I don't know how the x and y position is calculated out.
By printing the UIBezierPath currentPoint print(medallionPath.currentPoint), I thought the width should be (x1 - x2) * 0.5, the height should be y1 - y2, I really don't know why it is
(x: 15, y: 30)
The whole code is following , tested in Playground
let size = CGSize(width: 120, height: 200)
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
let context = UIGraphicsGetCurrentContext()!
//Gold colors
let darkGoldColor = UIColor(red: 0.6, green: 0.5, blue: 0.15, alpha: 1.0)
let midGoldColor = UIColor(red: 0.86, green: 0.73, blue: 0.3, alpha: 1.0)
let medallionPath = UIBezierPath(ovalIn: CGRect(x: 8, y: 72, width: 100, height: 100))
print(medallionPath.currentPoint)
// (108.0, 122.0)
print(medallionPath.bounds)
// (8.0, 72.0, 100.0, 100.0)
context.saveGState()
medallionPath.addClip()
darkGoldColor.setFill()
medallionPath.fill()
context.restoreGState()
// question
var transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
// transform = transform.translatedBy(x: 15, y: 30)
medallionPath.lineWidth = 2.0
//apply the transform to the path
medallionPath.apply(transform)
print(medallionPath.currentPoint)
// (86.4, 97.6)
print(medallionPath.bounds)
// (6.4, 57.6, 80.0, 80.0)
medallionPath.stroke()
//This code must always be at the end of the playground
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
transform = transform.translatedBy(x: 15, y: 30)
is a translation. It shifts the entire path to the right by 15 and down by 30. Your question is where did the magic numbers 15 and 30 come from.
The medallion drawing has a circle with dimensions 100 x 100 starting at position (8, 72) as established by this line of code:
let medallionPath = UIBezierPath(ovalIn: CGRect(x: 8, y: 72, width: 100, height: 100))
The code then adds an inner ring by scaling the original path by 0.8, so it will be an 80 x 80 circle. So that the center of this smaller ring lines up with the center of the bigger circle, it will need to be offset an additional 10 in both the horizontal and vertical directions. (The smaller circle is 20 smaller horizontally and vertically, so shifting it by 1/2 of 20 gets it to align properly). The goal then is to have it positioned at (18, 82) with width: 80, height: 80.
So, we need to apply a translation (a shift) in the X and Y directions, such that when the path is scaled we end up with a path anchored at (18, 82). The tricky bit is that the scaling gets applied to the shift values, so that has to be accounted for as well.
So, we are starting with an X position of 8, and we want to apply some translation value dx so that when the result is scaled by 0.8 we end up with the value 18:
(8 + dx) * 0.8 = 18
solving for dx:
dx = (18 / 0.8) - 8
dx = 14.5
Similarly for Y, we are starting with a Y position of 72 and want to apply a translation dy such that when it is scaled by 0.8 we end up with 82:
(72 + dy) * 0.8 = 82
solving for dy:
dy = (82 / 0.8) - 72
dy = 30.5
So, the mathematically correct translation is (14.5, 30.5):
transform = transform.translatedBy(x: 14.5, y: 30.5)
Ray Wenderlich rounded those to (15, 30) for some reason known only to them (because the round numbers look better in the code perhaps?). It's possible that they didn't bother to do the math and just tried values until it looked right.
here scale is 0.8 means 80% of the current so its x, y, width and height all reduce to 80%
so new scale difference is
scaleDifference = ((1/ 0.8) - 1.0) * 100 = 25%
now total size difference is 0.25, here inner circle is in centre so centre point does not change so its x and y position change to calculate x and y
here path rect is (8, 72, 100, 100)
so formula to calculate
circle reduce 10% from both side, to keep it in centre needs to increase 10% x and y and reduce 10% in width and height so circle will be in centre
here scale is set to 80% so what ever x and y translation we are giving all will be calculated to 80%, for example if we give translate x = 10 and y = 10 system converts to its 80% so x = 8 and y = 8.
we have to add width difference to the x and y to keep it in centre
width and height is 100 so
100 * (scaleDifference / 2.0)
100 * (0.25 / 2.0) = 12.5
and due to scaling x and y also reduce to 0.8% so to make it 100% is
xDifference = 8 * scaleDifference = 2.0
yDifference = 72 * scaleDifference = 18.0
to keep circle in centre we have to add width difference with dx and height difference with dy to get final x and y translation value
dx = xDifference + widthDifference = 2.0 + 12.5 = 14.5
dy = yDifference + heightDifference = 18.0 + 12.5 = 30.5
How can I make an arrow swing like in this video?
So far, I can rotate my node back and forth like in this video using the following code in didMove(to:) in my SKScene:
// Ball
let ballNode = SKSpriteNode(imageNamed: "Ball")
let offsetFromCorner: CGFloat = 20
ballNode.position = CGPoint(x: frame.minX + ballNode.size.width / 2 + offsetFromCorner, y: frame.minY + ballNode.size.height / 2 + offsetFromCorner)
addChild(ballNode)
/* ... */
// Aim arrow
let aimArrowNode = SKSpriteNode(imageNamed: "AimArrow")
aimArrowNode.position.y += aimArrowNode.size.height / 2
ballNode.addChild(aimArrowNode)
ballNode.zRotation = -.pi / 18 * 8
let rotateUp = SKAction.rotate(toAngle: -.pi / 18, duration: 1)
let rotateDown = SKAction.rotate(toAngle: -.pi / 18 * 8, duration: 1)
let combinedActions = SKAction.sequence([rotateUp, rotateDown])
ballNode.run(SKAction.repeatForever(combinedActions))
However, I want the arrow to appear to "slow down" as it gets nearer to the edge. How can I achieve this?
If you have any questions, please ask!
Luckily, the answer is actually built into SKAction.
Just add these after you create the actions:
rotateUp.timingMode = .easeInEaseOut
rotateDown.timingMode = .easeInEaseOut
Now you get the effect I was looking for! :)
I have a player that when the user taps I want to be able to spawn 8 bullets around the player (with a 45˚ separation between them) and proceed to move them outwards to the edge of the screen.
The circle from where the bullets originate from is correct, but the bullets in the bottom left of the screen seem to be moving faster than the ones in the top right. Also the bullets are facing sideways, not pointing outwards.
func fireSpecialWeapon() {
stride(from: 0, to: 2 * CGFloat.pi, by: 2 * CGFloat.pi / 10 ).forEach { angle in
let bullet = SKSpriteNode(imageNamed: "bulletCircle")
bullet.setScale(3)
bullet.zRotation = angle
bullet.position = player.position
bullet.zPosition = 2
//move outwards to the edge of the screen
let distance: CGFloat = 2000
let endPoint = CGPoint(x: distance * cos(angle), y: distance * sin(angle))
let move = SKAction.move(to: endPoint, duration: 2)
self.addChild(bullet)
bullet.run(move)
}
}
You should use trig to figure out the end point based on the angle. distance * sin is the y component and distance * cos is the x component. The code looks somethign like this:
stride(from: 0, to: 2 * CGFloat.pi, by: 2 * CGFloat.pi / 8).forEach { angle in
let bullet = SKSpriteNode(imageNamed: "bulletCircle")
bullet.setScale(3)
bullet.zRotation = angle
bullet.position = player.position
bullet.zPosition = 2
//move outwards to the edge of the screen
let distance: CGFloat = 500
let endPoint = CGPoint(x: distance * cos(angle), y: distance * sin(angle))
let move = SKAction.move(to: endPoint, duration: 2)
self.addChild(bullet)
bullet.run(move)
}
The first thing I noticed is that your rotation calculation is not correct.
First the rotation of each bullet should be pi / 4 from its neighbours.
So you should not use pi / I but (pi / 4) * I.
That should fix up the rotation but I’m not sure if that’s everything that’s not working.
I am following some tutorials online but the code is meant for an iPad. I am working with an iPhone and the screen sizes are different. The problem is that the author uses 'magic numbers' that are made specifically for the iPad.
I have been successful in creating conversions so that it will work for any device. For example:
let xMid : CGFloat = CGRectGetMidX(self.frame)
let yMid : CGFloat = CGRectGetMidY(self.frame)
print("x: \(xMid) y: \(yMid)")
let height : CGFloat = CGRectGetHeight(self.frame)
let width : CGFloat = CGRectGetWidth (self.frame)
makeSlotAt(CGPoint(x: xMid / 4.0, y: 0), isGood: true)
makeSlotAt(CGPoint(x: xMid - xMid / 4.0, y: 0), isGood: false)
makeSlotAt(CGPoint(x: xMid + xMid / 4.0, y: 0), isGood: true)
makeSlotAt(CGPoint(x: xMid * 2.0 - xMid / 4.0, y: 0), isGood: false)
makeBouncerAt(CGPoint(x: 0, y: 0))
makeBouncerAt(CGPoint(x: xMid / 2.0, y: 0))
makeBouncerAt(CGPoint(x: xMid, y: 0))
makeBouncerAt(CGPoint(x: xMid * 1.5, y: 0))
makeBouncerAt(CGPoint(x: xMid * 2.0, y: 0))
Anyway, I was just wondering if there was a function or cheat sheet that had numerical conversions between device sizes.
Thanks!
If the tutorial you're using uses magic numbers, then stop using it immediately. If you use autolayout and follow Apple's guidelines for iOS 7 & up, you rarely, if ever, need to use magic numbers. The functions that you created are a good start for in-code measurements.
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)