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.
Related
I am creating a iOS Game where a player has a bow and arrow that spins in a 360 degree circle, and the player must shoot the bow at the right time to hit the target. Right now I am having trouble getting the arrow to shoot in the direction the bow is facing, as well as getting the arrow to shoot at the right angle towards that direction.
let bullet = SKSpriteNode(fileNamed: "Bullet")
bullet?.size = CGSize(width: 100, height: 100)
bullet.zPosition = -5
bullet.position = CGPointMake(player.position.x, player.position.y)
bullet.zRotation = player.zRotation
let action = SKAction.moveToY(self.size.height + 30, duration: 0.8)
let actionDone = SKAction.removeFromParent()
bullet.runAction(SKAction.sequence([action, actionDone]))
bullet.physicsBody = SKPhysicsBody(rectangleOfSize: bullet.size)
bullet.physicsBody?.affectedByGravity = false
bullet.physicsBody?.dynamic = false
self.addChild(bullet)
The player refers the the bow for reference.
Instead of moving the bullet to self.size.height + 30 in the Y direction and 0 pixels in the X direction, you can rotate that movement direction by the zRotation using trigonometry.
let amount = self.size.height + 30
let action = SKAction.moveTo(CGPointMake(bullet.position.x + amount * sin(bullet.zRotation), bullet.position.y + amount * cos(bullet.zRotation)), duration: 0.8)
You can get the behavior you are looking for by calculating a force vector from the bullet's zRotation and then use it to apply a force to the bullet's physicsBody.
To do this we will use trigonometry.
//adjust rotation by pi/2 radians to match spriteKits rotation system
let adjustedRotation = bullet.zRotation + .pi/2
//intensity scalar
let intensity:CGFloat = 4000 //adjust this value
//find x and y components using adjustedRotation and scale by intensity
let vx = intensity * cos(adjustedRotation)
let vy = intensity * sin(adjustedRotation)
//make vector using vx and vy components
let forceVector = CGVector(dx:vx, dy: vy)
//apply force to physicsBody
bullet.physicsBody?.applyForce(forceVector)
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 am creating an AR app using ARKit in which I have to place a TV on a wall. As vertical plane detection is not supported in ARKit as of now, I have created a wall using the technique like https://github.com/bjarnel/arkit-occlusion did. Now I have to place a TV on this wall. But when I am placing the TV model on this wall, it's not oriented correctly. I am using the same Euler angles for TV model as of wall. But I am not able to place it correctly.
Code for creating the wall -
class func node(from:SCNVector3,
to:SCNVector3) -> SCNNode {
let distance = from.distance(vector: to)
let wall = SCNPlane(width: CGFloat(distance),
height: HEIGHT)
wall.firstMaterial = wallMaterial()
let node = SCNNode(geometry: wall)
// always render before the beachballs
node.renderingOrder = 10
// get center point
node.position = SCNVector3(from.x + (to.x - from.x) * 0.5,
from.y + Float(HEIGHT) * 0.5,
from.z + (to.z - from.z) * 0.5)
node.eulerAngles = SCNVector3(0, -atan2(to.x - node.position.x, from.z - node.position.z) - Float.pi * 0.5, 0)
return node
}
Code for loading the TV model on wall -
let tvScene = SCNScene(named: "art.scnassets/\(name)")
let tvNode = tvScene?.rootNode.childNode(withName: "TV",
recursively: true)
tvNode?.position = SCNVector3(hitResult.worldTransform.columns.3.x, hitResult.worldTransform.columns.3.y, wall.position.z)
tvNode?.eulerAngles = SCNVector3(-90, wall.eulerAngles.y, 180)
self.sceneView.scene.rootNode.addChildNode(tvNode!)
Please help me out.
I don't know about your angle are correctly calculated or not. But SCNNode requires angles in Radians not in degree
SCNVector3(-90, wall.eulerAngles.y, 180)
Should be
SCNVector3(-.pi / 2, wall.eulerAngles.y, pi)
Hope it is helpful
I have made a blue ball that can be dragged by the user only on a red circled path:
I want to check how many times the user make a forward or backward lap (the start & end is at the top of the circle path), for example - If he drag the ball in a clockwise way so the laps is +1, and if he drag it to other way the laps is -1.
I tried to do this(This inculdes the dragging of the ball and my try to count laps):
#IBAction func dragBall(recognizer: UIPanGestureRecognizer) {
let point = recognizer.locationInView(self.view);
let earthX = Double(point.x)
let earthY = Double(point.y)
let midViewXDouble = Double(midViewX)
let midViewYDouble = Double(midViewY)
let angleX = (earthX - midViewXDouble)
let angleY = (earthY - midViewYDouble)
let angle = atan2(angleY, angleX)
let earthX2 = midViewXDouble + cos(angle)*100
let earthY2 = midViewYDouble + sin(angle)*100
circlePath2 = UIBezierPath(arcCenter: CGPoint(x: earthX2,y: earthY2), radius: CGFloat(10), startAngle: CGFloat(0), endAngle:CGFloat(M_PI * 2), clockwise: true)
shapeLayer2.path = circlePath2.CGPath
if degrees == 0 {
laps += 1
print(laps)
}
}
And it worked! but when the user drags the ball very fast it do not calculate, and it do not calculate backwards.
Here is a possible solution, following the approach indicated in the comments. First you need some additional instance variables:
var previousAngle = -M_PI_2 // top position if y-coordinate points down
var totalAngle = 0.0
var laps = 0
In dragBall, you calculate how much the angle has changed.
Since the angle can "jump" from -π to π, or vice versa, the difference
is normalised to the range -π ... π:
var delta = angle - previousAngle
if delta > M_PI {
delta -= 2 * M_PI
} else if delta < -M_PI {
delta += 2 * M_PI
}
previousAngle = angle
Then update the total change in the angle:
totalAngle += delta
and from that you can determine the number of laps:
laps = Int(floor(totalAngle/(2 * M_PI)))
I'm building a 2D game and I'm looking to direct a node in a particular clockwise curve from the sky to the ground (like a bird would swoop down to the ground) in a clockwise motion with some friction to give the effect of a bird landing.
I've been playing around with 'physicsBody' but i cant seem to get the right curve, can anyone help... here is a picture of the curve I'm trying to achieve. http://www.theluxuryteam.com/uploads/shared/images/curved_arrow-black.png
Thank you
func addCrow() {
var crow = SKSpriteNode(imageNamed: "crow")
crow.physicsBody = SKPhysicsBody(rectangleOfSize: crow.size)
crow.physicsBody?.dynamic = true
crow.physicsBody?.collisionBitMask = 0
crow.physicsBody?.velocity = CGVector(dx: -200, dy: 0)
crow.physicsBody?.angularVelocity = 100
crow.physicsBody?.linearDamping = 0
crow.physicsBody?.angularDamping = 100
var random : CGFloat = CGFloat(arc4random_uniform(300))
crow.position = CGPoint(x: self.frame.size.width / 1.4, y: self.frame.size.height / 1.2 ) // 1.4 0.8
self.addChild(crow)
}