Shooting balls from a rotating cannon - swift

I'm making a SpriteKit game. I have six cannons that I've made rotate back and fourth. I want to shoot cannonballs from each cannon that sync with the rotation of each cannon. I want a duration of one second between each cannonball.
So basically: A cannon that is in constant rotation is shooting cannonballs in the same direction as the rotation.
For the cannons i'm using an extension:
extension CGFloat {
func degreesToRadians() -> CGFloat {
return self * CGFloat.pi / 180
}
}
I'm gonna put the code for just one cannon, since I should be able to figure out how to adjust one of the cannonball movements to the others. Here is one:
func createCannons() {
let cannonLeftBottom = SKSpriteNode(imageNamed: "Cannon")
cannonLeftBottom.anchorPoint = CGPoint(x: 0.5, y: 0.5)
cannonLeftBottom.position = CGPoint(x: -325, y: -420)
cannonLeftBottom.zPosition = 4
cannonLeftBottom.setScale(0.4)
cannonLeftBottom.zRotation = CGFloat(65).degreesToRadians()
let rotateLB = SKAction.rotate(byAngle:
CGFloat(-65).degreesToRadians(), duration: 2)
let rotateBackLB = SKAction.rotate(byAngle:
CGFloat(65).degreesToRadians(), duration: 2)
let repeatRotationLB =
SKAction.repeatForever(SKAction.sequence([rotateLB,rotateBackLB]))
cannonLeftBottom.run(repeatRotationLB)
self.addChild(cannonLeftBottom)
}
Here is my function for the cannonball:
func createBalls() {
let cannonBallLB = SKSpriteNode(imageNamed: "Ball")
cannonBallLB.name = "CannonBall"
cannonBallLB.position = CGPoint(x: -325, y: -420)
cannonBallLB.physicsBody = SKPhysicsBody(circleOfRadius:
cannonBallLB.size.height / 2)
cannonBallLB.physicsBody?.affectedByGravity = false
cannonBallLB.zPosition = 3
cannonBallLB.setScale(0.1)
self.addChild(cannonBallLB)
}
THX!

You need to convert from Polar Coordinates to Rectangular Coordinates.
You do this by using sin and cos
E.G.
let speed = 100 //This would mean move 100 points per second
let force = CGVector(dx:cos(cannon.zRotation) * speed,dy:sin(cannon.zRotation) * speed)
cannonBall.applyForce(force)
Note: Now unless they changed this, force used to be in units of points, if they fixed it to units of meters, then you need to divide your speed by 150, since 150 points = 1 meter in Spritekit

Related

SceneKit SCNPhysicsBody performance

SceneKit SCNPhysicsBody performance doesn't seem to take advantage of the multiple cores.
I created a quick bit of code that puts a bunch of small spheres on top of a cube where they drop under gravity. As the number of spheres increases things get very slow. I don't see a way of getting optimal multicore performance. Anyone have any ideas?
I did some quick tests with a stopwatch and observing the simulation to a specific point launching 1,5 and 8 instances of the app.
1 instance running : 15 seconds
2 instances running : 25 seconds
8 instances running : 35 seconds
It is clear that the multcores are not being used optimally for simulation with just one instance.
func createCubeWithSpheres(bottomLeft: SCNVector3, topRight: SCNVector3, radiusOfSpheres: Double) -> SCNNode {
// Create the box using the bottomLeft and topRight coordinates
let box = SCNBox(
width: topRight.x - bottomLeft.x,
height: topRight.y - bottomLeft.y,
length: topRight.z - bottomLeft.z,
chamferRadius: 0.0
)
let node = SCNNode(geometry: box)
// Add a static physics body to the box
node.physicsBody = SCNPhysicsBody(type: .static, shape: nil)
// Calculate the dimensions of the box
let width = box.width
let height = box.height
let depth = box.length
// Calculate the maximum number of spheres that can fit inside the box
let maxNumSpheresWidth = Int(width / (radiusOfSpheres * 2))
let maxNumSpheresHeight = Int(height / (radiusOfSpheres * 2))
let maxNumSpheresDepth = Int(depth / (radiusOfSpheres * 2))
let maxNumSpheres = maxNumSpheresWidth * maxNumSpheresHeight * maxNumSpheresDepth
// Create an array to hold the spheres
var spheres = [Sphere]()
// Create the spheres and add them to the array
for zIndex in 0..<maxNumSpheresDepth {
for yIndex in 0..<maxNumSpheresHeight {
for xIndex in 0..<maxNumSpheresWidth {
let xPos = bottomLeft.x + radiusOfSpheres + (radiusOfSpheres * 2) * CGFloat(xIndex)
let yPos = bottomLeft.y + radiusOfSpheres + (radiusOfSpheres * 2) * CGFloat(yIndex)
let zPos = bottomLeft.z + radiusOfSpheres + (radiusOfSpheres * 2) * CGFloat(zIndex)
let sphere = Sphere(radius: radiusOfSpheres, position: SCNVector3(xPos, yPos, zPos))
spheres.append(sphere)
}
}
}
// Add the spheres to the box's node
for sphere in spheres {
node.addChildNode(sphere.node)
}
return node
}
class Sphere {
let node = SCNNode()
// Initialize the sphere with a radius and position.
init(radius: CGFloat, position: SCNVector3? = nil) {
node.geometry = SCNSphere(radius: radius)
if let position = position {
node.position = position
}
// Add a physics body to the sphere so that it can interact with other objects.
node.physicsBody = SCNPhysicsBody(type: .dynamic, shape: nil)
// Add a "stickiness" to the sphere by applying a static electric field to it.
// let electricField = SCNPhysicsField.electric()
// electricField.strength = 10 // Adjust this value to increase or decrease the stickiness of the sphere.
// node.physicsField = electricField
}
}
let box = createCubeWithSpheres(bottomLeft: SCNVector3(x: 0.0, y: 0.0, z: 0.0), topRight: SCNVector3(x: 5.0, y: 5.0, z: 5.0), radiusOfSpheres: 0.1)
scene.rootNode.addChildNode(box)
Anyway to speed this up with one instance running without giving up collision accuracy?

How to connect two SCNSpheres in 3D space using Bezier path in Swift?

I have the following code:
func createScene(){
count += 1
let sphereGeom = SCNSphere(radius: 1.5)
sphereGeom.firstMaterial?.diffuse.contents = UIColor.redColor()
let path = UIBezierPath()
path.moveToPoint(CGPoint(x: 0, y: 0))
let radius = 3.0
var radians = Double(0)
var yPosition = Float(5.4)
while count <= 20 {
if radians >= 2{
radians -= 2
}
let sphereNode = SCNNode(geometry: sphereGeom)
let angle = Double(radians * M_PI)
let xPosition = Float(radius * cos(angle))
let zPosition = Float(radius * sin(angle))
sphereNode.position = SCNVector3(xPosition, yPosition, zPosition)
let cgX = CGFloat(xPosition)
let cgY = CGFloat(yPosition)
path.addQuadCurveToPoint(CGPoint(x: cgX, y: cgY), controlPoint: CGPoint(x: (cgX / 2), y: (cgY / 2)))
path.addLineToPoint(CGPoint(x: (cgX - (cgX * 0.01)), y: cgY))
path.addQuadCurveToPoint(CGPoint(x: 1, y: 0), controlPoint: CGPoint(x: (cgX / 2), y: ((cgY / 2) - (cgY * 0.01))))
let shape = SCNShape(path: path, extrusionDepth: 3.0)
shape.firstMaterial?.diffuse.contents = UIColor.blueColor()
let shapeNode = SCNNode(geometry: shape)
shapeNode.eulerAngles.y = Float(-M_PI_4)
self.rootNode.addChildNode(shapeNode)
count += 1
radians += 0.5556
yPosition -= 1.35
self.rootNode.addChildNode(sphereNode)
}
I want to add a Bezier path connecting each sphere to the next one, creating a spiral going down the helix. For some reason, when I add this code, the shape doesn't even appear. But when I use larger x and y values, I see the path fine, but it is no way oriented to the size of the spheres. I don't understand why it disappears when I try to make it smaller.
Your SCNShape doesn't ever get extruded. Per Apple doc,
An extrusion depth of zero creates a flat, one-sided shape.
With larger X/Y values your flat shape happens to become visible. You can't build a 3D helix with SCNShape, though: the start and end planes of the extrusion are parallel.
You'll have to use custom geometry, or approximate your helix with a series of elongated SCNBox nodes. And I bet someone out there knows how to do this with a shader.

shooting moving location in swift

I'm working in a game for my college and i stuck in one thing.
when i try to shoot with my hero the bullet go's up only or side i need my bullet go's where ever my hero rotate plz help me this is my code
func spownBullet() {
let bullet = SKSpriteNode(imageNamed: "Bullet")
let hero = self.childNodeWithName("hero") as! SKSpriteNode
bullet.zPosition = 1
bullet.position = CGPointMake(hero.position.x, hero.position.y)
bullet.size = CGSize(width: 20, height: 30)
let bulletact = SKAction.moveToY(self.size.height + 300, duration: 1)
bullet.runAction(SKAction.repeatActionForever(bulletact))
self.addChild(bullet)
}
If I understand correctly you want your bullet to go a certain distance (e.g. 300) in the same angle as your "hero" sprite is rotated.
Here's a function to compute the destination point:
// destination point moving at angle
// ---------------------------------
func endPointMovingSpriteFrom(origin:CGPoint, atAngle angle:CGFloat,forDistance distance:CGFloat ) -> CGPoint
{
let deltaX = distance / sin(angle + CGFloat(M_PI)/2)
let deltaY = distance / cos(angle + CGFloat(M_PI)/2)
return CGPoint(x: origin.x - deltaX, y:origin.y - deltaY)
}
You can use it to compute the destination and then use the destination in a moveTo action:
let bulletDestination = endPointMovingSpriteFrom(hero.position, atAngle:hero.zRotation, forDistance:300)
let bulletact = SKAction.moveTo(bulletDestination, duration: 1)

Swift - SKAction moveByX, How to get my node to move from the top/down

please forgive me as I'm a bit of a Swift noob and am creating my first game :D
Ok, so I have two nodes... one is Guava and the other is Pepper. Guava moves from the bottom of the screen to the top. I want the Pepper to move from the top of the screen to the bottom. Here's the code I have, which works fine for the Guava.
// movement of guavas
let guavaToMove = CGFloat(self.frame.size.width + 2.0 * guavaNodeTexture.size().width)
let moveGuava = SKAction.moveByX(-guavaToMove, y: self.frame.size.width, duration: NSTimeInterval(0.01 * guavaToMove))
let removeGuava = SKAction.removeFromParent()
// movement of peppers
let pepperToMove = CGFloat(self.frame.size.width + 2.0 * pepperNodeTexture.size().width)
let movePepper = SKAction.moveByX(-pepperToMove, y: self.frame.size.width, duration: NSTimeInterval(0.01 * pepperToMove))
let removePepper = SKAction.removeFromParent()
GuavaMoveAndRemove = SKAction.sequence([moveGuava,removeGuava])
PepperMoveAndRemove = SKAction.sequence([movePepper,removePepper])
Now I have the Guava Node anchored to the bottom of the screen and it floats up perfectly. I have the Pepper Node anchored to the top of the screen and am trying to get it to float down in a similar fashion.
Here is the code for both functions, spawnGuava and spawnPepper where both nodes/textures are anchored:
func spawnPepper(){
//The way the Guava spawn and move
let pepperNode = SKNode()
let pepper = SKSpriteNode(texture: pepperNodeTexture)
pepper.setScale(0.30)
pepper.position = CGPointMake (self.size.width + 140, self.size.height * 0.75)
pepper.alpha = 0.75
pepper.physicsBody = SKPhysicsBody(rectangleOfSize: pepper.size)
pepper.physicsBody?.affectedByGravity = false
pepper.physicsBody?.dynamic = true
pepper.physicsBody?.categoryBitMask = PhysicsCatagory.Guava
pepper.physicsBody?.collisionBitMask = 1
pepper.physicsBody?.contactTestBitMask = PhysicsCatagory.Boognish
pepper.zPosition = 50
pepperNode.addChild(pepper)
pepper.runAction(PepperMoveAndRemove)
self.addChild(pepperNode)
if scoreIncreased == true {
}
//the way the pepper collide
}
func spawnGuava(){
//The way the Guava spawn and move
let guavaNode = SKNode()
let guava = SKSpriteNode(texture: guavaNodeTexture)
guava.setScale(0.75)
guava.position = CGPointMake (self.size.width - 40, self.size.height * 0.05)
guava.physicsBody = SKPhysicsBody(rectangleOfSize: guava.size)
guava.physicsBody?.affectedByGravity = false
guava.physicsBody?.dynamic = true
guava.alpha = 0.75
guava.physicsBody?.categoryBitMask = PhysicsCatagory.Guava
guava.physicsBody?.collisionBitMask = 1
guava.physicsBody?.contactTestBitMask = PhysicsCatagory.Boognish
guava.zPosition = 0
guavaNode.addChild(guava)
guava.runAction(GuavaMoveAndRemove)
self.addChild(guavaNode)
if scoreIncreased == true {
}
//the way the guava collide
}
Please help me configure this code accordingly so that the peppers fall from the sky :)
THANKS in advance. First post :)
FYI: I have tried modifying the code many times myself to no avail, so I have reverted it to the baseline you see now. IF I anchored the pepper to where the Guava is anchored then both would float up at the same time.
You're trying to move guavas and peppers in the opposite direction, but it seems like you are moving them in the same direction. Try removing or adding negative signs in movePepper:
let movePepper = SKAction.moveByX(-pepperToMove, y: -self.frame.size.width, duration: NSTimeInterval(0.01 * pepperToMove))
// movement of peppers
let pepperToMove = CGFloat(self.frame.size.width + 2.0 * pepperNodeTexture.size().width)
let movePepper = SKAction.moveByX(-pepperToMove, y: self.frame.size.height / -1, duration: NSTimeInterval(0.01 * pepperToMove))
let removePepper = SKAction.removeFromParent()
just has to divide the y value by a negative, derp... :D

(SWIFT) Sprite not giving me the correct zRotation value after being added

Details of program: by looking at this picture(http://i.stack.imgur.com/QOZ53.png), what I'm trying to do is have the spaceship circle the planet. I achieved this by making a line node and changes its anchor point to the top and then position it to the centre of the planet which then upon impact of the spaceship and planet, the line is angled towards the ship in which then the ship is removed from the view and added to the line node as a child and together they rotate around the planet, as the ship rotates around the the planet, the ship it self also rotates on the spot (hopefully that makes sense)
Problem: the problem is im not getting the correct zRotation value of the ship. Right now I got the code to draw another ship at the location where it was tapped and set the zRotation of that image to the zRotation of the ship but i keep getting different angles. (refer to picture). Is this because I have ship added to line node as a child? Im running a rotating animation on both the line and the ship. In the picture, the line is rotating around the planet counter clockwise dragging the ship along with it and the ship itself is also rotating counter clockwise on the spot. In the picture the left ship; the one that's touching the line is the one thats rotating, the ship on the right is just drawn to angle of the rotating ship but by looking at the picture you can see the angle of the ship is pretty opposite of the one on the left. Why is this so? what I noticed is that when the ship is at the bottom half of the planet, the angles are fine but when it comes to the top half, the angles are kinda opposite(refer to picture)
Picture
Console Log:
I'm Touched -
ship angle in degrees: 83.6418545381942
PLAYER POSITION X: 100.0 Y: 100.0
PLAYER ZROTATION: 1.45982575416565
WE'RE TOUCHING -
PLANET POSITION X*: 120.0 Y: 230.000015258789
PLAYER-SHIP POSITION X: 107.998710632324 Y: 171.783294677734
ANGLE OF LINE RADIANS*: -1.77409685090117 DEGRESS: -101.648262004087
PLAYER-SHIP ROTATION: 1.57079637050629
I'm Touched -
ship angle in degrees: 314.660859137531
TEMP POS X: 136.535125732422 Y: 287.094879150391
TEMP ZROTATION: 5.491868019104
PLAYER POSITION X: 136.535125732422 Y: 287.094879150391
PLAYER ZROTATION: 5.491868019104
Collision Code:
func didBeginContact(contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask == planetGroup || contact.bodyB.categoryBitMask == planetGroup {
print(" WE'RE TOUCHING ")
moving = true
touching = true
let degrees = 45.0
let radians = degrees * M_PI / 180.0
//rotate Line
var rotateLine = SKAction.rotateByAngle(CGFloat(radians), duration: 0.5)
var repeatLine = SKAction.repeatActionForever(rotateLine)
//rotates Ship
var rotateShip = SKAction.rotateByAngle(CGFloat(radians), duration: 0.4)
var repeatShip = SKAction.repeatActionForever(rotateShip)
playerShip.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
planetNode = contact.bodyA.node as! SKSpriteNode
planetX = planetNode.position.x
planetY = planetNode.position.y
playerX = playerShip.position.x
playerY = playerShip.position.y
var angleOfAnchor = AngleBetweenPoints(planetNode.position, endPoint: playerShip.position)
var three60 = 360 * CGFloat(M_PI) / 180.0
var nintey = 90 * CGFloat(M_PI) / 180.0
var inDegree = angleOfAnchor * 180.0 / CGFloat(M_PI)
var shipPlanetDistance = SDistanceBetweenPoints(planetNode.position, p2: playerShip.position)
line = SKSpriteNode(color: UIColor.blackColor(), size: CGSize(width: 2, height: planetNode.size.height))
line.anchorPoint = CGPoint(x: 0.5, y: 1)
line.position = CGPoint(x: planetX, y: planetY)
line.zRotation = -(three60 - nintey - angleOfAnchor)
self.addChild(line)
tempShip = playerShip
playerShip.removeFromParent()
line.runAction(repeatLine, withKey: "rotateLine")
//playerShip.position = CGPoint(x: playerX, y: playerY)
line.addChild(playerShip)
playerShip.zRotation = (90 * CGFloat(M_PI) / 180.0)
playerShip.runAction(repeatShip, withKey: "rotateShip")
print("*PLANET POSITION* X: \(planetX) Y: \(planetY) \r *PLAYER-SHIP POSITION* X: \(playerX) Y: \(playerY) \r *ANGLE OF LINE* RADIANS: \(angleOfAnchor) DEGRESS: \(inDegree) *PLAYER-SHIP ROTATION: \(playerShip.zRotation)")
}
}
Screen Touch Code:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
print(" I'm Touched ")
var radians:CGFloat = playerShip.zRotation
var degrees = radians * 180.0 / CGFloat(M_PI)
var dx = cos(radians)
var dy = sin(radians)
print(" ship angle in degrees: \(degrees) ")
var tempAngle = playerShip.zRotation
var shipPosition = convertPoint(playerShip.position, fromNode: line)
playerX = shipPosition.x
playerY = shipPosition.y
if startMove == true {
playerShip.removeActionForKey("rotateShip")
playerShip.physicsBody?.velocity = CGVector(dx: 100*dx, dy: 100*dy)// speed of direction
startMove = false
}
if moving == true{
//playerShip.removeActionForKey("rotateShip")
//playerShip.removeFromParent()
//self.addChild(playerShip)
var radians:CGFloat = playerShip.zRotation
var degrees = radians * 180.0 / CGFloat(M_PI)
var dx = cos(radians)
var dy = sin(radians)
print(" ship angle in degrees: \(degrees) ")
//playerShip.zRotation = tempShip.zRotation
//playerShip.position = CGPoint(x: playerX, y: playerY)
//playerShip.physicsBody?.velocity = CGVector(dx: 100*dx, dy: 100*dy)// speed of direction
// this is the ship that gets drawn
var temp = SKSpriteNode(imageNamed: "img/ship/2.png")
temp.position = CGPoint(x: playerX, y: playerY)
temp.zRotation = playerShip.zRotation
self.addChild(temp)
//moving = false
print("*TEMP POS* X: \(temp.position.x) Y: \(temp.position.y) *TEMP ZROTATION*: \(temp.zRotation)")
}
print("*PLAYER POSITION* X: \(playerX) Y: \(playerY) *PLAYER ZROTATION: \(playerShip.zRotation)")
}
I managed to solve this problem, I added the line.zrotation with the playerShip.zrotation and it ended up working perfectly