How do I rotate sprites on a perimeter of a circle towards the center? - swift

I have n sprites on the perimeter of a circle:
let radius = 100
let n = 10
let parentNode = SKSpriteNode(color: NSColor.yellowColor(), size: CGSizeMake(CGFloat(radius), CGFloat(radius)))
parentNode.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
parentNode.zPosition = 1.5
for sprite in 0...n {
let aSprite = SKSpriteNode(imageNamed: "image.png")
aSprite.setScale(0.15)
aSprite.name = "sprite"+String(sprite)
let theta = Float(Double(sprite) * ((2 * M_PI)/n))
let x = radius * Double(cosf(theta))
let y = radius * Double(sinf(theta))
aSprite.position = CGPointMake(CGFloat(x), CGFloat(y))
parentNode.addChild(aSprite)
}
I want to rotate each sprite towards the center of the circle:
CGPoint(CGRectGetMidX(parentNode.frame),CGRectGetMidY(parentNode.frame))
I tried using atan2 to accomplish this, but it didn't work:
aSprite.zRotation = atan2(CGRectGetMidY(parentNode.frame)-aSprite.position.y, CGRectGetMidX(parentNode.frame) - aSprite.position.x)

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?

swift shooting objects from circle shape

I have a circle in the middle and I need to fire bullets in direction opposite to where I touch and come out from the player, I wrote the following code but nothing happen
private func fire(position: CGPoint) {
let bullet = SKShapeNode(circleOfRadius: 2)
bullet.strokeColor = SKColor.darkGray
bullet.fillColor = SKColor.darkGray
bullet.physicsBody = SKPhysicsBody(circleOfRadius: 2)
let posX = (position.x) * -1
let posY = (position.y) * -1
let pos = CGPoint(x: posX, y: posY)
bullet.position = pos
bullet.physicsBody?.affectedByGravity = false
bullet.physicsBody?.isDynamic = false
bullet.physicsBody?.pinned = false
numberOfBullets += 1
bullet.name = String(numberOfBullets)
bullet.physicsBody?.collisionBitMask = bodyType.enemy.rawValue
bullet.physicsBody?.categoryBitMask = bodyType.bullet.rawValue
bullet.physicsBody?.contactTestBitMask = bodyType.enemy.rawValue
bullet.physicsBody?.usesPreciseCollisionDetection = true
world.addChild(bullet)
bulletForce(bullet: bullet, position: position)
}
private func bulletForce(bullet: SKShapeNode, position: CGPoint) {
let ddx = Double((bullet.position.x - position.x))
let ddy = Double((bullet.position.y - position.y))
let degree = atan2(ddy, ddx) * (180.0 / M_PI)
let magnitude = 900.0
let angle = degree
let dx = magnitude * cos(angle)
let dy = magnitude * sin(angle)
let vector = CGVector(dx: dx,dy: dy)
bullet.physicsBody?.applyForce(vector)
//bullet.run(fire, withKey: "firing\(bullet.name)")
}
EDIT ::
This solved the problem thanks to the aid from the accepted answer
let posX = (position.x) * -1
let posY = (position.y) * -1
let pos = CGPoint(x: posX, y: posY)
let theta = atan2((pos.y - player.position.y), (pos.x - player.position.x))
let dx = cos(theta) / 4
let dy = sin(theta) / 4
world.addChild(bullet)
let vector = CGVector(dx: dx, dy: dy)
bullet.physicsBody!.applyImpulse(vector)
where theta is the angle i want the bullet to go to and here it is
Note::
The player is in the middle ( circle ).
The posX and posY are the x and y coordinates where the user touched and they are multiplied by -1 to get the opposite direction where my bullet will be placed.
The angle theta is then calculated using the atan2 function that instead of positioning my bullet away from the player ( circle ), it will position it at the edge of circle.
Have you tried switching isDynamic from "false" to "true"? Also, try adding GCVector(dx: 1.0 * dx, dy: 1.0 * dy)

How to scale a radius to fit in all devices?

I am makeing a game in with a ball follows a path, actually is just an illution because the scene is the one that rotates. The problem is that in some devices the ball get out of the screen and in others the radius looks very small. I have tryed to make the radius equal (orbita.size.width / 2) but it doesnt work. (orbita is the orbit that the ball follows)
class GameScene: SKScene {
let sprite = SKSpriteNode(imageNamed: "circulo")
var rotation:CGFloat = CGFloat(M_PI)
let radius:CGFloat = 168
override func didMoveToView(view: SKView) {
/* Setup your scene here */
scaleMode = .ResizeFill
node.position = view.center
// 3) Add the container to the scene
addChild(node)
// 4) Set the sprite's x position
sprite.position = CGPointMake(radius, 0)
// 5) Add the sprite to the container
node.addChild(sprite)
// 6) Rotate the container
rotate()
sprite.color = UIColor.whiteColor()
sprite.colorBlendFactor = 1.0
sprite.zPosition = 4.0
orbita = SKSpriteNode(imageNamed: "orbita")
let padding2:CGFloat = 32.0
orbita.size = CGSize(width:view.frame.size.width - padding2 , height: view.frame.size.width - padding2)
orbita.color = UIColor.whiteColor()
orbita.colorBlendFactor = 1
orbita.alpha = 1
orbita.position = view.center
self.addChild(orbita)
orbita.zPosition = 3
}
If your orbita.size is (it seems a circular orbita by "circulo" name):
orbita.size = CGSize(width:view.frame.size.width - padding2 , height: view.frame.size.width - padding2)
your radius would be:
let radius:CGFloat = (view.frame.size.width - padding2)/2

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.

Spawning down the middle of the screen

Is there a way to spawn an SKSpriteNode continuously down the middle of the screen. For example I have a circle that I want to spawn in the middle of the x axis and then for that node to spawn all the way down the screen?
These circles are different colours so when they spawn is there a way to randomly change the the colour of the circles when they are spawned?
I understand I can spawn them individually but is there a better way? ( I am using SpriteKit on swift)
You need to learn some basics bruh.
Learn about For loops, arrays and arc4random.
var circleDiameter: CGFloat = 80.0
for var y = self.frame.height; y > 0; y = y - circleDiameter
{
let coloroptions = [UIColor.greenColor(), UIColor.blueColor(), UIColor.redColor()]
let randomNumber = Int(arc4random_uniform(UInt32(coloroptions.count)))
let color = coloroptions[randomNumber]
var rect = CGRectMake(self.frame.width / 2, y, circleDiameter, circleDiameter)
var circle = SKShapeNode()
circle.path = UIBezierPath(ovalInRect: rect).CGPath
circle.fillColor = color
circle.lineWidth = 0
circle.physicsBody = SKPhysicsBody(circleOfRadius: circle.frame.width / 2)
circle.physicsBody?.dynamic = false
self.addChild(circle)
}