Сначала вам нужно рассчитать курс и шаг между двумя точками. Полный пост здесь и этот ответ объясняет, как сделать это между любыми двумя точками.
Если у вас есть два угла, если вы попытаетесь использовать ЭйлераУглы на SCNBox, вы заметите, что, когда вы только изменяете высоту (eulerAngles.x
) или изменяете только заголовок (eulerAngles.y
), все работает нормально. Однако, как только вы попытаетесь изменить оба, вы столкнетесь с проблемами . Одно из решений состоит в том, чтобы обернуть один узел внутри другого .
Это казалось таким полезным предложением, что я создал удобный узел-обертку, который должен обрабатывать повороты по всем 3 осям:
import Foundation
import SceneKit
struct HeadingPitchBank {
let heading: Float
let pitch: Float
let bank: Float
/// returns the heading and pitch (bank is 0) represented by the vector
static func from(vector: simd_float3) -> HeadingPitchBank {
let heading = atan2f(vector.x, vector.z)
let pitch = atan2f(sqrt(vector.x*vector.x + vector.z*vector.z), vector.y) - Float.pi / 2.0
return HeadingPitchBank(heading: heading, pitch: pitch, bank: 0)
}
}
class HeadingPitchBankWrapper: SCNNode {
init(wrappedNode: SCNNode) {
headingNode = SCNNode()
pitchNode = SCNNode()
bankNode = SCNNode()
_wrappedNode = wrappedNode
super.init()
addChildNode(headingNode)
headingNode.addChildNode(pitchNode)
pitchNode.addChildNode(bankNode)
bankNode.addChildNode(wrappedNode)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var heading: Float {
get {
return headingNode.eulerAngles.y
}
set {
headingNode.eulerAngles.y = newValue
}
}
var pitch: Float {
get {
return pitchNode.eulerAngles.x
}
set {
pitchNode.eulerAngles.x = newValue
}
}
var bank: Float {
get {
return bankNode.eulerAngles.z
}
set {
bankNode.eulerAngles.z = newValue
}
}
var headingPitchBank: HeadingPitchBank {
get {
return HeadingPitchBank(heading: heading, pitch: pitch, bank: bank)
}
set {
heading = newValue.heading
pitch = newValue.pitch
bank = newValue.bank
}
}
var wrappedNode: SCNNode {
return _wrappedNode
}
private var headingNode: SCNNode
private var pitchNode: SCNNode
private var bankNode: SCNNode
private var _wrappedNode: SCNNode
}
Затем вы можете использовать это, чтобы легко провести линию между двумя точками:
func createLine(start: simd_float3 = simd_float3(), end: simd_float3, color: UIColor, opacity: CGFloat? = nil, radius: CGFloat = 0.005) -> SCNNode {
let length = CGFloat(simd_length(end-start))
let box = SCNNode(geometry: SCNBox(width: radius, height: radius, length: length, chamferRadius: 0))
box.geometry!.firstMaterial!.diffuse.contents = color
let wrapper = HeadingPitchBankWrapper(wrappedNode: box)
wrapper.headingPitchBank = HeadingPitchBank.from(vector: end - start)
wrapper.simdPosition = midpoint(start, end)
if let opacity = opacity {
wrapper.opacity = opacity
}
return wrapper
}