Spawning sprites on screen edges while keeping anchor point in center - swift

I'm developing a simple game where there's a player sprite in the middle of my screen and monster sprites spawn at random locations on the screen edge and slowly move toward the player sprite. Originally, this worked just fine by setting the GameScene anchor point to the lower left with self.anchorPoint = CGPoint(x: 0.0, y: 0.0), and the positioning the player in the center with player.position = CGPoint(x: size.width * 0.5, y: size.height * 0.5). However, because I changed the anchor point, when I rotate my iPad, the player ends up being off center.
I think it's easier to work with a (0,0) anchor point, because then I can spawn monsters randomly from the edges using lines like randomEdgePosition = CGPoint(x: 0.0, y: randomFloat(min: 0.0, max: 1.0) * size.height). But it's easier for my player's positioning to keep the anchor point centered. How can the screen edges be calculated with a central anchor point? Alternatively, how could I keep my player centered with a (0,0) anchor point?

Keep the anchor point in the center of your screen and spawn your monsters using this
randomEdgePosition = CGPoint(x: 0 - size.width / 2, y: randomFloat(min: 0.0, max: 1.0) * size.height)

The problem with randomizing on an edge is that you will see more enemies spawning in the corners than in the centers of the edges because there is more edge on the corners. To evenly distribute around the entire scene, you want to position based on a circle, and use the longest edge / 2 + the longest edge / 2 of the enemy (I assume you want to spawn off screen) as your radius
let theta = CGFloat(randomFloat(min: 0.0, max: 1.0) * Float.pi * 2)
let radius = max(size.width, size.height)/2 + max(enemy.size.width, enemy.size.height)/2
let randomPosition = CGPoint(x: cos(theta) * radius, y: sin(theta) * radius)
This will also solve your issue with the anchorPoint, and allows you to keep it at the center of the screen (0.5,0.5), making it easier to detect which way the enemies are coming from without having to do additional subtraction. (E.G. enemy at -x is left of the player, no need to check if x < player.x0

Related

How can I make an SKPhysicsBody for this image using CGPath?

I am trying to make an SKPhysics body for this SKSpriteNode using a CGPath polygon.
The problem is that when I check for a collision between this node and the player node, the didBeginContact method is executed even though they did not touch each other. I believe their is something wrong with the coordinates but I cannot see the polygon lines, which makes it difficult for me too see the accuracy of the lines.
Here is the code that I am using:
let triangle = SKSpriteNode(imageNamed: "Triangle_ZigZag")
let trianglePath = CGMutablePath()
trianglePath.addLines(between: [CGPoint(x: triangle.size.width,
y: triangle.size.height),
CGPoint(x: triangle.size.width,
y: - triangle.size.height),
CGPoint(x: -triangle.size.width,
y: triangle.size.height / 2)])
trianglePath.closeSubpath()
triangle.physicsBody = SKPhysicsBody(polygonFrom: trianglePath)
Can someone please help me figure out what am I doing wrong ?
Thank You
FYI physics lines are green, so a green sprite probably isn't the best choice you can't see the lines very well.
your sprite has a centre anchorPoint or an anchorPoint of (0, 0) by default. Therefore your physics points need to take that into account. top right corner would be half the width from centre and half the height from centre etc. You have full width from centre and full height from centre, that is the issue.
trianglePath.addLines(between: [CGPoint(x: triangle.size.width / 2, y: triangle.size.height / 2), CGPoint(x: triangle.size.width / 2, y: -triangle.size.height / 2), CGPoint(x: -triangle.size.width / 2, y: 0)])

How to rotate a sprite along a circular path?

This is a wheel I have consisting of four individual sprites. My aim is to rotate them as a wheel. I have tried 'UIBezier' arccenter class but the end result is the individual sprite moving along the center but rotating its shape too, leading to an unwanted result. Here is the relavant code. the 'orientToPath' when set to true, rotates the sprite itself.
//path rotation
let dx = whlPurple.position.x - self.size.width/2
let dy = whlPurple.position.y - (whlRed.size.height/2 + (whlPurple.position.y - whlRed.size.height))
let rad = atan2(dy, dx)
let radius = sqrt((dx*dx) + (dy*dy))
let path = UIBezierPath(arcCenter: CGPoint(x: self.size.width/2, y: (whlRed.size.height/2 + (whlPurple.position.y - whlRed.size.height))), radius: radius, startAngle: rad, endAngle: rad + CGFloat(M_PI * 6), clockwise: true)
let follow = SKAction.follow(path.cgPath, asOffset: false, orientToPath: true, speed: 200)
whlPurple.run(SKAction.repeatForever(follow))
Create an SKNode and place it at the center of your circle.
Then add your sprite to it (addChild) at a position of CGPoint(x:radius,y:0). you can set the zRotation of the SKNode to chose your starting position on the circle.
When you rotate the SKNode, your sprite will move in a perfect circle. For example, to have the spites go around in 5 seconds, you can use SKAction.rotate(by: 2*pi, duration:5) and repeat that forever (on the SKNode containing the sprite (not on the sprite itself).

How do i code in SpriteKit Spawning balls from the top that are now appearing from the side?

I am working on a simple game for the iPhone in SpriteKit and am trying to spawn balls coming from the top of the screen. However, the balls keep spawning from the side and I don't know how to make them appear from the top....Anyone an idea on how to change this? (see code). Also is this a good way to randomly spawn? Or should I spawn them and let them fall with gravity rules?
//ENEMIES
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max - min) + min
}
// 1
func spawnEnemy() {
// 2
let enemy = SKSpriteNode(imageNamed: "ball")
// 3
enemy.name = "enemy"
// 4
enemy.position = CGPoint(x: frame.size.width + enemy.size.width/2,
y: frame.size.height * random(min: 0, max: 1))
// 5
addChild(enemy)
enemy.run(
SKAction.moveBy(x: -size.width - enemy.size.width, y: 0.0,
duration: TimeInterval(random(min: 1, max: 2))))
}
Remember that location (0,0) is the bottom left of the screen, with increasing x values going across the screen from left to right and increasing y values going up the screen from bottom to top.
Your spawn point is always
(x: frame.size.width + enemy.size.width/2, y: frame.size.height * random(min: 0, max: 1)
which is off the right-hand side of the screen, with a random y value (i.e. a random height). Your x value needs to be random and your y value needs to be:
(x: frame.size.width * random(min: 0, max: 1), y: frame.size.height + enemy.size.height/2)
Then, as Dreamer said, your movement SKAction needs to move the enemy from its current y value to 0 whilst keeping its x value constant (assuming you want it to move straight down the screen.
As to your question about spawning and gravity - do you want your objects to move down at a constant speed (which is what your corrected SKAction will do) or to look as though they are falling under gravity, in which case they'd appear, then start moving slowly but constantly accelerating.
Edit: As KnightOfDragons said - the actual location of the (0, 0) co-ordinate is governed by a property known as the anchorPoint (which itself is a CGPoint, but this doesn't relate to an actual co-ordinate). It used to always be (0, 0) which means the bottom left-hand corner, but now it may to be (0.5, 0.5), which means the centre of the screen i.e. a sprite placed at (0,0) will be in the middle of the screen. Increasing x or y will move the sprite right or up respectively, and decreasing x or y will move the sprite left or down.
This means that if your scene has an anchorPoint of (0.5, 0.5), then the edges of the screen will have a co-ordinates of:
top of screen - y: frame.size.height/2
bottom of screen - y: -frame.size.height/2
right-hand edge of screen - x: frame.size.width/2
left-hand edge of screen - x: -frame.size.height/2
Whether or not the anchorPont is (0, 0) or (0.5, 0.5) may be dependant upon if your scene came from an .sks file or through code.
Instead of
enemy.run(
SKAction.moveBy(x: -size.width - enemy.size.width, y: 0.0,
duration: TimeInterval(random(min: 1, max: 2))))
You need to be moving by -Y. X is left and right, Y is up and down. So it would be....
enemy.run(
SKAction.moveBy(x: 0.0 , y: -size.height - enemy.size.height,
duration: TimeInterval(random(min: 1, max: 2))))

SKSpriteNode animation to appear bottom-up

According to the following code, i am displaying a mountain (hill) in the scene at x = 400 and y = self.frame.height / 2
However, I want the hill to appear from bottom-up (from below the scene) like an animation. How can I code this ?
let hill = SKSpriteNode(imageNamed: "hill")
hill.position = CGPoint(x: 400, y: self.frame.height / 2 )
hill.setScale(1)
hill.physicsBody = SKPhysicsBody(rectangleOfSize: hill.size)
hill.physicsBody?.categoryBitMask = 1
hill.physicsBody?.collisionBitMask = 2
self.addChild(hill)
To create an animation that makes the sprite slide up from "under" the scene, do this:
Set its initial position to below the scene:
hill.position = CGPoint(x: 400, y: self.frame.height / CGFloat(2) - self.frame.height)
Note: - self.frame.height is used to place the node 1 full frame height below the desired location
Then start an action that moves it up to its desired position in didMoveToView, or wherever you want the animation to start:
let moveUpAnimation = SKAction.move(to: CGPoint(x: 400, y: self.frame.height / CGFloat(2)), duration: 0.75)
hill.run(moveUpAnimation)
This example creates an action that takes 0.75 seconds to move the sprite up one full frame height. You can set its initial position to anywhere below the visible scene to create this effect, and change the duration to any desired speed.

How to fix some physics bodies behavior, when some bodies go off the physics edges?

I am working with a game, which has a physics world. I create physics box around the whole scene for prevent the sprites go off the screen. But the issue is, when i add too many bodies to the scene, some of them go off this physics box.
This is the physics box around the scene:
homeArea.size = CGSizeMake(self.frame.size.width, self.frame.size.height)
homeArea.position = CGPointMake(self.frame.size.width / 2.0, self.frame.size.height / 2.0)
homeLayer.addChild(homeArea)
homeArea.physicsBody = SKPhysicsBody(edgeLoopFromRect: CGRectMake(-homeArea.frame.width / 2.0, -homeArea.frame.height / 2.0, homeArea.frame.size.width, homeArea.frame.size.height))
This is these too many bodies:
func addSprites() {
for i in 0...20 {
for j in 0...20 {
let shape = SKShapeNode(rect: CGRectMake(-5.0, -5.0, 10.0, 10.0))
shape.fillColor = SKColor.redColor()
shape.position = CGPointMake(self.frame.size.width - (shape.frame.size.width * CGFloat(i)), 0.0 + (shape.frame.size.height * CGFloat(j)))
shape.physicsBody = SKPhysicsBody(rectangleOfSize: shape.frame.size)
shape.physicsBody?.dynamic = true
addChild(shape)
}
}
}
And i create a cannon shooting to these bodies. There i want to create something like splash from cannon's bullet. I shoot to this wall from sprites, and notice from nodes count, that because of my bullet, some sprites go off the screen. I added the method usesPreciseCollisionDetection. But it's all the same.