SceneKit - Rotate camera with mouse - swift

I'm trying to achieve an fps control mechanism. The problem is that my camera starts moving in the wanted direction then jumps back and forth... also I can not figure out how to connect 2 or more axis. Here is my code:
func gameView(didReceiveMouseMovedEvent event: NSEvent) {
if let p = self.lastMouseP{
var x:CGFloat = 0
x = event.locationInWindow.x - p.x
var y:CGFloat = 0
y = event.locationInWindow.y - p.y
self.player.move(cameraByX: 0, y: x, z: y, w: 0)
}
self.lastMouseP = event.locationInWindow
}
And in player file:
func move(cameraByX x: CGFloat, y: CGFloat, z:CGFloat, w:CGFloat){
let rot = self.head.rotation
let xx = (x)*CGFloat(M_PI)/180.0 + rot.x
let yy = (y)*CGFloat(M_PI)/180.0 + rot.y
let zz = (z)*CGFloat(M_PI)/180.0 + rot.z
//let xr = SCNMatrix4MakeRotation(xx, 1, 0, 0)
let yr = SCNMatrix4MakeRotation(yy, 0, 1, 0)
let zr = SCNMatrix4MakeRotation(zz, 0, 0, 1)
self.head.transform = SCNMatrix4Mult(self.head.transform, zr)
}

Related

Keep mouse within area in Swift

I'm trying to prevent the mouse cursor from leaving a specific area of the screen. I can't find a native method to do this, so I'm trying to do it manually.
So far I have this:
NSEvent.addGlobalMonitorForEvents(matching: [.mouseMoved, .leftMouseDragged], handler: {(event: NSEvent) in
let x = event.locationInWindow.flipped.x;
let y = event.locationInWindow.flipped.y;
if (x <= 100) {
CGWarpMouseCursorPosition(CGPoint(x: 100, y: y))
}
})
// elsewhere to flip y coordinates
extension NSPoint {
var flipped: NSPoint {
let screenFrame = (NSScreen.main?.frame)!
let screenY = screenFrame.size.height - self.y
return NSPoint(x: self.x, y: screenY)
}
}
This stops the cursor from going off the X axis. Great. But it also stops the cursor from sliding along the y axis at X=100.
So I tried to add the delta:
NSEvent.addGlobalMonitorForEvents(matching: [.mouseMoved, .leftMouseDragged], handler: {(event: NSEvent) in
let x = event.locationInWindow.flipped.x;
let y = event.locationInWindow.flipped.y;
let deltaY = event.deltaY;
if (x <= 100) {
CGWarpMouseCursorPosition(CGPoint(x: 100, y: y + deltaY))
}
})
Now it does slide along the Y axis. But the acceleration is way off, it's too fast. What I don't get is that if I try to do y - deltaY it slides like I expect, but reversed:
NSEvent.addGlobalMonitorForEvents(matching: [.mouseMoved, .leftMouseDragged], handler: {(event: NSEvent) in
let x = event.locationInWindow.flipped.x;
let y = event.locationInWindow.flipped.y;
let deltaY = event.deltaY;
if (x <= 100) {
CGWarpMouseCursorPosition(CGPoint(x: 100, y: y - deltaY))
}
})
Now the cursor is sliding along the Y axis at X=100 with proper acceleration (like sliding the cursor against the edge of the screen), but it's reversed. Moving the mouse up, moves the cursor down.
How do I get proper smooth sliding of the cursor, in the proper direction, at the edge of my custom area?
Or is there a better way to achieve what I'm trying to do?
I figured it out. I need to subtract the previous deltas.
So now I have this instead:
var oldDeltaX: CGFloat = 0;
var oldDeltaY: CGFloat = 0;
NSEvent.addGlobalMonitorForEvents(matching: [.mouseMoved, .leftMouseDragged, .rightMouseDragged], handler: {(event: NSEvent) in
let deltaX = event.deltaX - oldDeltaX;
let deltaY = event.deltaY - oldDeltaY;
let x = event.locationInWindow.flipped.x;
let y = event.locationInWindow.flipped.y;
let window = (NSScreen.main?.frame.size)!;
let width = CGFloat(1920);
let height = CGFloat(1080);
let widthCut = (window.width - width) / 2;
let heightCut = (window.height - height) / 2;
let xPoint = clamp(x + deltaX, minValue: widthCut, maxValue: window.width - widthCut);
let yPoint = clamp(y + deltaY, minValue: heightCut, maxValue: window.height - heightCut);
oldDeltaX = xPoint - x;
oldDeltaY = yPoint - y;
CGWarpMouseCursorPosition(CGPoint(x: xPoint, y: yPoint));
});
public func clamp<T>(_ value: T, minValue: T, maxValue: T) -> T where T : Comparable {
return min(max(value, minValue), maxValue)
}
extension NSPoint {
var flipped: NSPoint {
let screenFrame = (NSScreen.main?.frame)!
let screenY = screenFrame.size.height - self.y
return NSPoint(x: self.x, y: screenY)
}
}
This will restrict the mouse in a 1920x1080 square of the display.
I found Godot's source code to be good resource: https://github.com/godotengine/godot/blob/51a00c2855009ce4cd6475c09209ebd22641f448/platform/osx/display_server_osx.mm#L1087
Is this the best or most perfomant way to do it? I don't know, but it works.

Arrange SCNNodes in Circle

I'm creating multiple nodes automatically and I want to arrange them around me, because at the moment I'm just increasing of 0.1 the current X location.
capsuleNode.geometry?.firstMaterial?.diffuse.contents = imageView
capsuleNode.position = SCNVector3(self.counterX, self.counterY, self.counterZ)
capsuleNode.name = topic.name
self.sceneLocationView.scene.rootNode.addChildNode(capsuleNode)
self.counterX += 0.1
So the question is, how can I have all of them around me instead of just in one line?
Did someone of you have some math function for this? Thank you!
Use this code (macOS version) to test it:
import SceneKit
class GameViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scene = SCNScene()
let scnView = self.view as! SCNView
scnView.scene = scene
scnView.allowsCameraControl = true
scnView.backgroundColor = NSColor.black
for i in 1...12 { // HERE ARE 12 SPHERES
let sphereNode = SCNNode(geometry: SCNSphere(radius: 1))
sphereNode.position = SCNVector3(0, 0, 0)
// ROTATE ABOUT THIS OFFSET PIVOT POINT
sphereNode.simdPivot.columns.3.x = 5
sphereNode.geometry?.firstMaterial?.diffuse.contents = NSColor(calibratedHue: CGFloat(i)/12,
saturation: 1,
brightness: 1,
alpha: 1)
// ROTATE ABOUT Y AXIS (STEP is 30 DEGREES EXPRESSED IN RADIANS)
sphereNode.rotation = SCNVector4(0, 1, 0, (-CGFloat.pi * CGFloat(i))/6)
scene.rootNode.addChildNode(sphereNode)
}
}
}
P.S. Here's a code for creating 90 spheres:
for i in 1...90 {
let sphereNode = SCNNode(geometry: SCNSphere(radius: 0.1))
sphereNode.position = SCNVector3(0, 0, 0)
sphereNode.simdPivot.columns.3.x = 5
sphereNode.geometry?.firstMaterial?.diffuse.contents = NSColor(calibratedHue: CGFloat(i)/90, saturation: 1, brightness: 1, alpha: 1)
sphereNode.rotation = SCNVector4(0, 1, 0, (-CGFloat.pi * (CGFloat(i))/6)/7.5)
scene.rootNode.addChildNode(sphereNode)
}
You need some math here
How I prepare the circle nodes
var nodes = [SCNNode]()
for i in 1...20 {
let node = createSphereNode(withRadius: 0.05, color: .yellow)
nodes.append(node)
node.position = SCNVector3(0,0,-1 * 1 / i)
scene.rootNode.addChildNode(node)
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.arrangeNode(nodes: nodes)
}
func createSphereNode(withRadius radius: CGFloat, color: UIColor) -> SCNNode {
let geometry = SCNSphere(radius: radius)
geometry.firstMaterial?.diffuse.contents = color
let sphereNode = SCNNode(geometry: geometry)
return sphereNode
}
Math behind arrange the view into circle
func arrangeNode(nodes:[SCNNode]) {
let radius:CGFloat = 1;
let angleStep = 2.0 * CGFloat.pi / CGFloat(nodes.count)
var count:Int = 0
for node in nodes {
let xPos:CGFloat = CGFloat(self.sceneView.pointOfView?.position.x ?? 0) + CGFloat(cosf(Float(angleStep) * Float(count))) * (radius - 0)
let zPos:CGFloat = CGFloat(self.sceneView.pointOfView?.position.z ?? 0) + CGFloat(sinf(Float(angleStep) * Float(count))) * (radius - 0)
node.position = SCNVector3(xPos, 0, zPos)
count = count + 1
}
}
Note: In third image I have set.
let xPos:CGFloat = -1 + CGFloat(cosf(Float(angleStep) * Float(count))) * (radius - 0)
let zPos:CGFloat = -1 + CGFloat(sinf(Float(angleStep) * Float(count))) * (radius - 0)
That means if you need view around the camera then
use CGFloat(self.sceneView.pointOfView?.position.x ?? 0) or at the random place then provide the value
Output

How to bend a SCNShape “line”?

I’m trying to create a lighting bolt using scenekit and I’m following this guide. So far I’ve got a vertical line in my scene using UIBezierPath with an extrusion to make it 3d but I’m not sure how to bend the “line” at the midpoint as described in the link.
func createBolt() {
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 0, y: 1))
path.close()
let shape = SCNShape(path: path, extrusionDepth 0.2)
let color = UIColor.red
shape.firstMaterial?.diffuse.contents = color
let boltNode = SCNNode(geometry: shape)
boltNode.position.z = 0
sceneView.scene.rootNode.addChildNode(boltNode)
}
Algorithm is pretty straightforward:
You start with list of 1 segment from A to B, then on each generation you split each segment on 2 segments by shifting middle point on random offset on his norm
struct Segment {
let start: CGPoint
let end: CGPoint
}
/// Calculate norm of 2d vector
func norm(_ v: CGPoint) -> CGPoint {
let d = max(sqrt(v.x * v.x + v.y * v.y), 0.0001)
return CGPoint(x: v.x / d, y: v.y / -d)
}
/// Splitting segment on 2 segments with middle point be shifted by `offset` on norm
func split(_ segment: Segment, by offset: CGFloat) -> [Segment] {
var midPoint = (segment.start + segment.end) / 2
midPoint = norm(segment.end - segment.start) * offset + midPoint
return [
Segment(start: segment.start, end: midPoint),
Segment(start: midPoint, end: segment.end)
]
}
/// Generate bolt-like line from `start` to `end` with maximal started frequence of `maxOffset`
/// and `generation` of split loops
func generate(from start: CGPoint, to end: CGPoint, withOffset maxOffset: CGFloat, generations: Int = 6) -> UIBezierPath {
var segments = [Segment(start: start, end: end)]
var offset = maxOffset
for _ in 0 ..< generations {
segments = segments.flatMap { split($0, by: CGFloat.random(in: -offset...offset)) }
offset /= 2
}
let path = UIBezierPath()
path.move(to: start)
segments.forEach { path.addLine(to: $0.end) }
return path
}
// MARK: - Example
let start = CGPoint(x: 10, y: 10)
let end = CGPoint(x: 90, y: 90)
let path = generate(from: start, to: end, withOffset: 30, generations: 5)
// MARK: - Helpers
func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint {
return CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
}
func - (lhs: CGPoint, rhs: CGPoint) -> CGPoint {
return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y)
}
func / (lhs: CGPoint, rhs: CGFloat) -> CGPoint {
return CGPoint(x: lhs.x / rhs, y: lhs.y / rhs)
}
func * (lhs: CGPoint, rhs: CGFloat) -> CGPoint {
return CGPoint(x: lhs.x * rhs, y: lhs.y * rhs)
}
SceneKit make Lightning Bolts
This here gives you another approach how to create randomized, full 3D Lightning Bolts in SceneKit (thank you Harry!).
Create a new SceneKit project (for iOS) in Xcode using the default Game template - the one that shows the aircraft in 3D space - delete the aircraft and create an empty scene with a black background. Also globally define your sceneView (to be able to access it from other classes).
Add the following classes and extensions to a new Swift file (import SceneKit):
Classes
class LightningStrike:
class LightningStrike : Geometry {
var bolt:[Lightning] = []
var start = SCNVector3() // stores start position of the Bolt
var end = SCNVector3() // stores end position of the Bolt
static var delayTime = 0.0
override init() {
start = SCNVector3(0.0, +5.0, 0.0) // default, to be changed
end = SCNVector3(0.0, -5.0, 0.0) // default, to be changed
print("Lightning Strike initialized")
}
private func fadeOutBolt() {
for b in bolt {
SCNTransaction.begin()
SCNTransaction.animationDuration = 2.0
b.face.geometry?.firstMaterial?.transparency = 0.0
SCNTransaction.commit()
}
}
func strike() {
for b in bolt { b.face.removeFromParentNode() }
bolt.removeAll()
// Create Main Bolt
bolt.append(Lightning())
bolt[0].createBolt(start,end)
sceneView.scene?.rootNode.addChildNode(bolt[0].face)
// Create child Bolts
for _ in 0 ..< 15 { // number of child bolts
// let parent = Int.random(in: 0 ..< bolt.count) // random parent bolt, an other method
let parent : Int = 0
let start = bolt[parent].centerLine[10 + Int.random(in: 0 ..< 15)] // random node to start from off of parent, pay attention to: numSegments - changing numbers here can cause out of index crash
let length:SCNVector3 = bolt[parent].end.minus(start) // length from our start to end of parent
var end = SCNVector3()
end.x = start.x + length.x / 1.5 + Float.random(in: 0 ... abs(length.x) / 3) // adjust by playing with this numbers
end.y = start.y + length.y / 1.5 + Float.random(in: 0 ... abs(length.y) / 3) // adjust by playing with this numbers
end.z = start.z + length.z / 1.5 + Float.random(in: 0 ... abs(length.z) / 3) // adjust by playing with this numbers
bolt.append(Lightning())
let index = bolt.count-1
bolt[index].width = bolt[parent].width * 0.2
bolt[index].deviation = bolt[parent].deviation * 0.3
bolt[index].createBolt(start,end)
sceneView.scene?.rootNode.addChildNode(bolt[0].face)
}
// Reset delay time and schedule fadeOut
LightningStrike.delayTime = 0.0 // reset delay time
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { self.fadeOutBolt() }
// Here you can add a Sound Effect
}
deinit {
for b in bolt { b.face.removeFromParentNode() }
bolt.removeAll()
print("Lightning Strike deinitialized")
}
}
class Lightning:
class Lightning : Geometry {
let UNASSIGNED:Float = 999
var start = SCNVector3()
var end = SCNVector3()
var numSegments = Int() // use => 3,5,9,17,33,65
var width = Float()
var deviation = Float()
var vertices:[SCNVector3] = []
var normals:[SCNVector3] = []
var indices:[Int32] = []
var centerLine:[SCNVector3] = []
var face:SCNNode! = nil
override init() {
numSegments = 33 // 17
width = 0.1
deviation = 1.5
centerLine = Array(repeating: SCNVector3(), count: numSegments)
// indexed indices never change
var j:Int = 0
for i in 0 ..< numSegments-1 {
j = i * 3
indices.append(Int32(j + 0)) // 2 triangles on side #1
indices.append(Int32(j + 2))
indices.append(Int32(j + 3))
indices.append(Int32(j + 2))
indices.append(Int32(j + 5))
indices.append(Int32(j + 3))
indices.append(Int32(j + 2)) // side #2
indices.append(Int32(j + 1))
indices.append(Int32(j + 5))
indices.append(Int32(j + 1))
indices.append(Int32(j + 4))
indices.append(Int32(j + 5))
indices.append(Int32(j + 1)) // side #3
indices.append(Int32(j + 0))
indices.append(Int32(j + 4))
indices.append(Int32(j + 0))
indices.append(Int32(j + 3))
indices.append(Int32(j + 4))
}
}
func createNode() -> SCNGeometry {
for i in 0 ..< numSegments { centerLine[i].x = UNASSIGNED }
centerLine[0] = start
centerLine[numSegments-1] = end
var hop:Int = max(numSegments / 2,1)
var currentDeviation = deviation
while true {
for i in stride(from:0, to: numSegments, by:hop) {
if centerLine[i].x != UNASSIGNED { continue }
let p1 = centerLine[i-hop]
let p2 = centerLine[i+hop]
centerLine[i] = SCNVector3(
(p1.x + p2.x)/2 + Float.random(in: -currentDeviation ... currentDeviation),
(p1.y + p2.y)/2 + Float.random(in: -currentDeviation ... currentDeviation),
(p1.z + p2.z)/2 + Float.random(in: -currentDeviation ... currentDeviation))
}
if hop == 1 { break }
hop /= 2
currentDeviation *= 0.6
}
vertices.removeAll()
normals.removeAll()
// triangle of vertices at each centerLine node on XZ plane
let ss:[Float] = [ sin(0), sin(Float.pi * 2/3), sin(Float.pi * 4/3)]
let cc:[Float] = [ cos(0), cos(Float.pi * 2/3), cos(Float.pi * 4/3)]
var w = width
for i in 0 ..< numSegments {
for j in 0 ..< 3 {
vertices.append(SCNVector3(centerLine[i].x + cc[j] * w, centerLine[i].y, centerLine[i].z + ss[j] * w))
}
w *= 0.90 // bolt gets thinner towards endings
}
// normal for each vertex: position vs. position of neighbor on next node
var index1 = Int()
var index2 = Int()
func norm(_ v: SCNVector3) -> SCNVector3 {
let d = max(sqrt(v.x * v.x + v.y * v.y + v.z * v.z), 0.0001)
return SCNVector3(v.x / d, v.y / -d, v.z / d)
}
for i in 0 ..< numSegments {
for j in 0 ..< 3 {
index1 = i * 3 + j // point on current node
index2 = index1 + 3 // neighboring point on next node
if index2 >= vertices.count { index2 -= 6 } // last node references previous node instead
normals.append(norm(vertices[index1].minus(vertices[index2])))
}
}
let geoBolt = self.createGeometry(
vertices: vertices,
normals: normals,
indices: indices,
primitiveType: SCNGeometryPrimitiveType.triangles)
let boltMaterial : SCNMaterial = {
let material = SCNMaterial()
material.name = "bolt"
material.diffuse.contents = UIColor.init(hex: "#BAB1FFFF") // this is a very clear, almost white purple
material.roughness.contents = 1.0
material.emission.contents = UIColor.init(hex: "#BAB1FFFF") // this is a very clear, almost white purple
material.lightingModel = .physicallyBased
material.isDoubleSided = true
material.transparency = 0.0
return material
}()
geoBolt.firstMaterial = boltMaterial
// this makes the bolt not appearing all geometry at the same time - it's an animation effect
DispatchQueue.main.asyncAfter(deadline: .now() + LightningStrike.delayTime) {
boltMaterial.transparency = 1.0
}
LightningStrike.delayTime += 0.01665
// geoBolt.subdivisionLevel = 1 // give it a try or not...
return geoBolt
}
// Creates a Branch of the entire Bolt
func createBolt(_ nstart:SCNVector3, _ nend:SCNVector3) {
start = nstart
end = nend
face = SCNNode(geometry:createNode())
// This will add some glow around the Bolt,
// but it is **enourmous** performence and memory intense,
// you could try to add some SCNTechnique instead
// let gaussianBlur = CIFilter(name: "CIGaussianBlur")
// gaussianBlur?.name = "blur"
// gaussianBlur?.setValue(2, forKey: "inputRadius")
// face.filters = [gaussianBlur] as? [CIFilter]
sceneView.scene?.rootNode.addChildNode(face)
}
}
class Geometry:
class Geometry : NSObject {
internal func createGeometry(
vertices:[SCNVector3],
normals:[SCNVector3],
indices:[Int32],
primitiveType:SCNGeometryPrimitiveType) -> SCNGeometry
{
// Computed property that indicates the number of primitives to create based on primitive type
var primitiveCount:Int {
get {
switch primitiveType {
case SCNGeometryPrimitiveType.line:
return indices.count / 2
case SCNGeometryPrimitiveType.point:
return indices.count
case SCNGeometryPrimitiveType.triangles,
SCNGeometryPrimitiveType.triangleStrip:
return indices.count / 3
default : return 0
}
}
}
//------------------------
let vdata = NSData(bytes: vertices, length: MemoryLayout<SCNVector3>.size * vertices.count)
let vertexSource = SCNGeometrySource(
data: vdata as Data,
semantic: SCNGeometrySource.Semantic.vertex,
vectorCount: vertices.count,
usesFloatComponents: true,
componentsPerVector: 3,
bytesPerComponent: MemoryLayout<Float>.size,
dataOffset: 0,
dataStride: MemoryLayout<SCNVector3>.size)
//------------------------
let ndata = NSData(bytes: normals, length: MemoryLayout<SCNVector3>.size * normals.count)
let normalSource = SCNGeometrySource(
data: ndata as Data,
semantic: SCNGeometrySource.Semantic.normal,
vectorCount: normals.count,
usesFloatComponents: true,
componentsPerVector: 3,
bytesPerComponent: MemoryLayout<Float>.size,
dataOffset: 0,
dataStride: MemoryLayout<SCNVector3>.size)
let indexData = NSData(bytes: indices, length: MemoryLayout<Int32>.size * indices.count)
let element = SCNGeometryElement(
data: indexData as Data, primitiveType: primitiveType,
primitiveCount: primitiveCount, bytesPerIndex: MemoryLayout<Int32>.size)
return SCNGeometry(sources: [vertexSource, normalSource], elements: [element])
}
}
Extensions:
for SCNVector3:
extension SCNVector3
{
func length() -> Float { return sqrtf(x*x + y*y + z*z) }
func minus(_ other:SCNVector3) -> SCNVector3 { return SCNVector3(x - other.x, y - other.y, z - other.z) }
func normalized() -> SCNVector3 {
let len = length()
var ans = SCNVector3()
ans.x = self.x / len
ans.y = self.y / len
ans.z = self.z / len
return ans
}
}
for UIColor:
extension UIColor {
public convenience init?(hex: String) {
let r, g, b, a: CGFloat
if hex.hasPrefix("#") {
let start = hex.index(hex.startIndex, offsetBy: 1)
let hexColor = String(hex[start...])
if hexColor.count == 8 {
let scanner = Scanner(string: hexColor)
var hexNumber: UInt64 = 0
if scanner.scanHexInt64(&hexNumber) {
r = CGFloat((hexNumber & 0xff000000) >> 24) / 255
g = CGFloat((hexNumber & 0x00ff0000) >> 16) / 255
b = CGFloat((hexNumber & 0x0000ff00) >> 8) / 255
a = CGFloat(hexNumber & 0x000000ff) / 255
self.init(red: r, green: g, blue: b, alpha: a)
return
}
}
}
return nil
}
}
Usage:
Init the class in your ViewController like so:
let lightningStrike = LightningStrike()
Also add a tap gesture recogniser (in viewDidLoad) for easy testing:
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
sceneView.addGestureRecognizer(tapGesture)
And the corresponding function that will trigger the Lightning Bolt:
#objc func handleTap(_ gestureRecognize: UIGestureRecognizer) {
lightningStrike.strike() // will fire a Lighting Bolt
}
Results:
Have fun with it.

How to align 2 SCNNode's together horizontally?

I'm trying to align two SCNNodes together but can't seem to figure out how. This is what I have right now:
cube.scale = SCNVector3(x: 0.1, y: 0.1, z: 0.1)
cube.position = SCNVector3(0, 3, -3)
cubeTwo.scale = SCNVector3(x: 0.15, y: 0.15, z: 0.15)
cubeTwo.position = SCNVector3(0.5, 3, -3)
cubeThree.scale = SCNVector3(x: 0.2, y: 0.2, z: 0.2)
cubeThree.position = SCNVector3(1, 3, -3)
How can I achieve this? Thank you!!
To align them horizontally, set the position Y values to be the same. X is left-right, Y is up-down, Z is near-far (until you start moving the camera around!).
parentNode.addChildNode(cube)
parentNode.addChildNode(cubeTwo)
parentNode.addChildNode(cubeThree)
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
parentNode.position = self.getPositionRelativeToCameraView(distance: 1.0).position
}
func getPositionRelativeToCameraView(distance: Float) -> (position: SCNVector3, rotation: SCNVector4) {
var x = Float()
var y = Float()
var z = Float()
let cameraLocation = self.sceneView.pointOfView!.position //else { return (SCNVector3Zero) }
let rotation = self.sceneView.pointOfView!.rotation //else { return (SCNVector3Zero) }
let direction = calculateCameraDirection(cameraNode: rotation)
x = cameraLocation.x + distance * direction.x
y = cameraLocation.y + distance * direction.y
z = cameraLocation.z + distance * direction.z
let position = SCNVector3Make(x, y, z)
return (position, rotation)
}
func calculateCameraDirection(cameraNode: SCNVector4) -> SCNVector3 {
let x = -cameraNode.x
let y = -cameraNode.y
let z = -cameraNode.z
let w = cameraNode.w
let cameraRotationMatrix = GLKMatrix3Make(cos(w) + pow(x, 2) * (1 - cos(w)),
x * y * (1 - cos(w)) - z * sin(w),
x * z * (1 - cos(w)) + y*sin(w),
y*x*(1-cos(w)) + z*sin(w),
cos(w) + pow(y, 2) * (1 - cos(w)),
y*z*(1-cos(w)) - x*sin(w),
z*x*(1 - cos(w)) - y*sin(w),
z*y*(1 - cos(w)) + x*sin(w),
cos(w) + pow(z, 2) * ( 1 - cos(w)))
let cameraDirection = GLKMatrix3MultiplyVector3(cameraRotationMatrix, GLKVector3Make(0.0, 0.0, -1.0))
return SCNVector3FromGLKVector3(cameraDirection)
}

change position after calling function

I'm using swift and I got that function.
func spawnTwo() -> SKSpriteNode {
let two = SKSpriteNode(imageNamed: "landPic1")
two.anchorPoint = CGPoint(x: 0.5, y: 0.5)
two.name = "Obs"
two.zPosition = 5;
two.anchorPoint = CGPoint(x: 0.5 , y: 0.5);
two.position.x = 0
two.position.y = 0
self.scene?.addChild(two)
return two
}
now I want, that every time I call that function, 1280 are added the position.x. How do I do that?
Simply keep a variable outside of the function:
var n = 0
func spawnTwo() -> SKSpriteNode {
//...
}
In the function, you multiply 1280 by n and increment n:
let two = SKSpriteNode(imageNamed: "landPic1")
two.anchorPoint = CGPoint(x: 0.5, y: 0.5)
two.name = "\(n)bs" // you might want to change the name each time too
two.zPosition = 5;
two.anchorPoint = CGPoint(x: 0.5 , y: 0.5);
two.position.x = 1280 * n
two.position.y = 0
self.scene?.addChild(two)
n += 1
return two