Ответ 1
Попытка как вручную установить, так и оживить одно и то же свойство может вызвать проблемы. Использование анимации byValue
делает проблему хуже - она объединяется с текущим преобразованием, поэтому сложнее отслеживать, является ли текущее преобразование byValue
анимации.
Вместо этого отделите фиксированную ориентацию пирамиды (ее вершина находится в направлении -z) от анимации (она вращается вокруг оси, на которую она указывает). Есть два хороших способа сделать это:
-
Сделайте
pyramidNode
дочерним элементом другого узла, который получает одноразовый поворот (π/2 вокруг оси x) и применяет анимацию вращения непосредственно кpyramidNode
. (В этом случае вершина пирамиды будет по-прежнему указывать в направлении +y ее локального пространства, поэтому вам нужно будет вращаться вокруг этой оси вместо оси z.) -
Используйте свойство
pivot
чтобы преобразовать локальное пространство содержимогоpyramidNode
и анимироватьpyramidNode
относительно его содержащего пространства.
Вот какой код, чтобы показать второй подход:
let pyramid = SCNPyramid(width: 1.0, height: 1.0, length: 1.0)
let pyramidNode = SCNNode(geometry: pyramid)
pyramidNode.position = SCNVector3(x: 0, y: 0, z: 0)
// Point the pyramid in the -z direction
pyramidNode.pivot = SCNMatrix4MakeRotation(CGFloat(M_PI_2), 1, 0, 0)
scene.rootNode.addChildNode(pyramidNode)
let spin = CABasicAnimation(keyPath: "rotation")
// Use from-to to explicitly make a full rotation around z
spin.fromValue = NSValue(SCNVector4: SCNVector4(x: 0, y: 0, z: 1, w: 0))
spin.toValue = NSValue(SCNVector4: SCNVector4(x: 0, y: 0, z: 1, w: CGFloat(2 * M_PI)))
spin.duration = 3
spin.repeatCount = .infinity
pyramidNode.addAnimation(spin, forKey: "spin around")
Некоторые несвязанные изменения для улучшения качества кода:
- Используйте
CGFloat
когда требуется явное преобразование для инициализации компонентаSCNVector
; используяFloat
илиDouble
будут разбиты на 32 или 64-битную архитектуру. - Используйте
.infinity
вместо устаревшей математической константы BSDHUGE
. Этот тип - относится к типуspin.repeatCount
и использует постоянное значение, определенное для всех типов с плавающей точкой. - Используйте
M_PI_2
для π/2, чтобы быть педантичным относительно точности. - Используйте
let
вместоvar
для анимации, так как мы никогда не назначаем другое значение дляspin
.
Подробнее о CGFloat
ошибкой CGFloat
: в Swift числовые литералы не имеют типа, пока выражение, в котором они нуждаются, нуждается в одном. Вот почему вы можете делать такие вещи, как spin.duration = 3
- хотя duration
- это значение с плавающей запятой, Swift позволяет передавать "целочисленный литерал". Но если вы let d = 3; spin.duration = d
let d = 3; spin.duration = d
вы получите ошибку. Зачем? Поскольку переменные/константы имеют явные типы, а Swift не выполняет неявное преобразование типов. 3
является бесприбыльным, но когда ему присваивается d
, введите вывод по умолчанию для выбора Int
потому что вы ничего не указали.
Если вы видите ошибки преобразования типов, у вас, вероятно, есть код, который смешивает литералы, константы и/или значения, возвращаемые из функций. Вероятно, вы можете просто убрать ошибки, преобразовывая все в выражение в CGFloat
(или независимо от того типа, которым вы передаете это выражение). Конечно, это сделает ваш код нечитаемым и уродливым, поэтому, как только вы его начнете работать, вы можете начать удаление конверсий по одному, пока не найдете тот, который выполняет эту работу.