Какой алгоритм доступен для корреляции позиции SKSpriteNode с currentPoint UIBezierPath?
У меня есть код (GameViewController
), который реализует следующее из SKSpriteNode
вместе с UIBezierPath
:
func createTrainPath() {
trackRect = CGRect(x: tracksPosX - tracksWidth/2,
y: tracksPosY,
width: tracksWidth,
height: tracksHeight)
trainPath = UIBezierPath(ovalIn: trackRect)
} // createTrainPath
func startFollowTrainPath() {
var trainAction = SKAction.follow(
trainPath.cgPath,
asOffset: false,
orientToPath: true,
speed: theSpeed)
trainAction = SKAction.repeatForever(trainAction)
myTrain.run(trainAction, withKey: runTrainKey)
} // startFollowTrainPath
func stopFollowTrainPath() {
guard myTrain == nil else {
myTrain.removeAction(forKey: runTrainKey)
savedTrainPosition = getPositionFor(myTrain, orPath: trainPath)
return
}
} // stopFollowTrainPath
Пользователь этого сегмента может вызвать другой SKScene
.. при этом действии я вызываю stopFollowTrainPath()
и сохраняю position
из SKSpriteNode
в глобальном savedTrainPosition
через:
func getPositionFor(_ node: SKSpriteNode, orPath: UIBezierPath) -> CGPoint {
if (useNode) {
return node.position
}
else {
return orPath.currentPoint
}
} // getPositionFor
(1) Когда пользователь возвращается после просмотра другого SKScene
, я хочу, чтобы движущийся (следующий) SKSpriteNode
продолжал двигаться вокруг UIBezierPath
, начиная с того места, где я остановил Node
, прежде чем представить другой SKScene
.
(2) Если SKSpriteNode
не перемещается до того, как будет поднят другой SKScene
, мне нужно, чтобы position
SKSpriteNode
оставался там, где он был до того, как был представлен новый SKScene
.
Каков наилучший способ сделать это?
1) «разрушить» текущую сцену и перестроить ее. Разные сцены в одном GameViewController. 2) перед заменой текущей сцены с овалом, сохраняя положение движущегося поезда в глобальной переменной saveTrainPosition и перезапуская движение с того места, где оно было остановлено. Практически решено, за исключением того, что точка перезапуска изменилась с той, что была.
Я знаю, что анимацию «следования по пути» можно приостановить/возобновить... но это не сработает, если вы уничтожаете текущую сцену. Вы уже нашли способ запустить анимацию «следования по пути» в точке, отличной от начальной точки? То есть вы пытаетесь сделать что-то вроде «сохранить процент выполнения (скажем, 38% вокруг овала), а затем вернуться и создать новый овальный путь и начать с 38% вокруг овала»?
Да, на последний вопрос
Я близко, но новая отправная точка отличается от старой точки остановки или паузы. мне нужно равное, а не почти
Я не верю, что можно сказать «начать анимацию следования по траектории с 38%». Я помню, как отвечал на ваш предыдущий вопрос, где разместил ссылку на полный проект на GitHub (github.com/DonMag/SpriteKitRotation) — он включал пример генерации массива точек, определяющих овал/эллипс (как в отличие от ovalIn:
). Итак, вы можете «найти индекс точки в массиве, ближайшей к текущей позиции, а затем запустить новую анимацию для этого эллипса, начиная с индекса».
ЕСЛИ я остановлю следующее точно на одном из ваших thePoints
, не возникнет проблем с перезапуском на том же CGPoint
. Однако, если я остановлюсь в точке между двумя соседними thePoints
, мне придется перестраивать массив [CGPoint]
каждый раз, когда я останавливаюсь, а затем перезапускать движущийся поезд. Такая перестройка неизбежна при вращении iOS-устройства. Позвольте мне попробовать перестроить каждый раз, когда я останавливаюсь, а затем возобновляю движение. Кажется, что это большие накладные расходы, но это может быть неизбежным.
Мне бы хотелось избежать повторного создания железной дороги SKScene
, но я не вижу, как это сделать, если я не использую Xcode для добавления путей и поезда в файл GameScene.sks
, как я это делаю для фоновых изображений. Но мне нужно, чтобы размеры и положение путей изменялись при каждом повороте (UIScreen.bounds
), и мне нужно, чтобы изображение поезда располагалось вертикально в соответствии с вертикальным положением пути. Итак, похоже, я застрял, перестраивая SKScene
с поездом каждый раз, когда переключаюсь SKScenes
. Кстати, эти другие сцены — это CreditsScene и DirectionsScene.
Извините, но мне сложно по-настоящему понять, как поток вашей программы связан с «вызовом другого SKScene
» и «возвратом от просмотра другого SKScene
»… Возможно, я смогу вам еще немного помочь, но во-первых... какова ваша цель, когда ваш «поезд» находится в нижней половине овала? Это: i.sstatic.net/lddDC.png ... или это: i.sstatic.net/Ul1Az.png
Второе – это цель
WRT последней картинки, переместите шлейф на 180 градусов прямо внутрь нижнего края овала (как на вашем изображении)
Потому что вы будете менять размер овала при вращении устройства, и когда вы показываете другую сцену, а затем «вернетесь», вы хотите:
Вероятно, вам нужно создать массив точек, определяющих овальный путь (вместо использования ovalIn:
).
Давайте посмотрим на овал, используя всего 10 точек, с array[0]
в 3 часа:
анимация всегда начинается с первой точки пути:
теперь поезд немного анимируется:
и пользователь нажимает, чтобы перейти к сцене «Маршруты».
Мы находим индекс ближайшей точки в массиве, сохраняем его, а затем по возвращении заново создаем путь, используя этот индекс в качестве начальной точки:
Мы запускаем анимацию, и пользователь нажимает, чтобы увидеть сцену «Кредиты»:
Опять же, мы находим индекс ближайшей точки в массиве, сохраняем его, а затем по возвращении заново создаем путь, используя этот индекс в качестве начальной точки:
Чтобы получить более гладкий овал, мы будем использовать больше точек:
Мы по-прежнему можем использовать текущую позицию поезда, чтобы найти ближайшую точку, и воссоздать путь, используя эту точку в качестве отправной точки:
Чем больше точек мы используем, тем более плавным будет овал. Вот он со 180 баллами (с метками будет путаница):
Чтобы поезд оказался внутри овала «правой стороной вверх», мы можем заменить текстуру перевернутым по горизонтали изображением, когда узел находится ниже центральной линии овала:
Воссоздание пути из нового индекса происходит очень быстро:
var ptsCopy = Array(thePoints[startIDX...])
ptsCopy.append(contentsOf: thePoints[0..<startIDX])
let pth = UIBezierPath()
pth.move(to: ptsCopy.removeFirst())
while !ptsCopy.isEmpty {
pth.addLine(to: ptsCopy.removeFirst())
}
pth.close()
и, если вы не делаете что-то, о чем не говорили в этой и других похожих публикациях, нам не нужно повторно генерировать «овальный массив точек»… У вас будет только два размера — один для портретная ориентация и одна для альбомной.
Я обновил репозиторий GitHub, который предоставил вам ранее: https://github.com/DonMag/SpriteKitRotation
а вот как это выглядит при запуске: https://thewikihow.com/video_ziDqt-ApuSU
Редактировать:
С небольшим изменением кода и одним (или несколькими) видами поезда «сверху» вы также можете сделать то же самое с анимацией, чтобы сделать ее немного менее резкой:
Редактировать 2:
Иногда нам приходится экспериментировать, чтобы понять, как все работает.
Давайте попробуем использовать эти два изображения:
Желто-красная стрелка — это ваш «поезд». Черная стрелка указывает на верхнюю часть изображения.
Когда мы анимируем узел вдоль пути и используем orientToPath: true
, SceneKit позиционирует центр узла на пути и ориентирует верхнюю часть узла в направлении движения.
Итак, давайте определим восьмиугольный путь (чтобы мы могли видеть изменения направления/угла):
Мы создадим два из них и будем использовать «горизонтальное» изображение для одного пути и «вертикальное» изображение для другого. Располагаем узлы в начальных точках путей, но анимацию пока не добавляем:
Как только мы запускаем анимацию, мы видим, что Черная стрелка, указывающая на верхнюю часть изображения/узла, ориентирована по направлению движения:
Как он «путешествует»:
Мы видим, что он вращается как надо.
Однако для вашего использования вам не нужно встроенное поведение... вы хотите, чтобы узел был перевернут - на основе вашего желаемого поведения. Вы хотите, чтобы «желтая вершина поезда» была ориентирована «вверх» в разных местах пути:
Итак, дело не в том, что SceneKit не делает то, что написано в документах Apple... это неправильное понимание того, как эти вещи работают. И, как я уже сказал, иногда нам приходится экспериментировать, чтобы достичь этого понимания.
Редактировать 3:
Несколько узлов спрайтов, использующих один и тот же овальный путь, определяемый точками, начиная с разных «индексных» точек (игнорируйте «прыжок» через верх... пришлось сохранять gif менее 2 МБ):
Я до сих пор не могу отказаться от SKAction.follow(trainPath.cgPath, asOffset: false, orientToPath: true, скорость: 10) BezierPath: true. В документах Apple указано, что мой поезд будет наклоняться, ориентироваться или охватывать овал. Это серьезная проблема, которая меня беспокоит. Если посмотреть на центральную линию и т. д., это означает, что документы Apple просто неверны.
Ваш блеск неоспорим. Документов Apple не так уж и много
@JohnLove - Не уверен, что вы называете «серьезным зависанием» ... не связанным с этим, я добавил в свой ответ еще одну гифку, показывающую, что вы можете сделать с одним (или несколькими) дополнительными «верхними» изображениями вашего поезда (см. обновленный репозиторий GitHub).
Моя основная проблема заключается в том, что, согласно документам Apple, использование SKAction.follow с orientToPath = true должно автоматически выполнять обнимание с необходимым наклоном овала без каких-либо особых усилий. Ваш mp3 должен быть автоматическим, если документы были точными. За вашими гениальными усилиями действительно приятно наблюдать. Но изменение orientToPath = true занимает 2 секунды по сравнению с вашим многочасовым чудом. Поскольку ваше чудо явно необходимо, почему бы в документах Apple просто не сказать об этом без фальши orientToPath?
@JohnLove - см. мою (длинную) редакцию 2 в моем ответе.
Я понимаю учебник по ориентации изображения с портретной и альбомной ориентацией. Чего я не понимаю, так это почему в вашем проекте ресурс поезда направлен ВВЕРХ, а не просто горизонтально. Потому что у меня актив поезда горизонтальный, и поезд движется по рельсам носом вниз?? Где-то в истории я повернул нос этого актива вверх, а мой поезд все равно двигался носом вниз.
@JohnLove - вы исследовали пример кода в этом проекте? Я использовал изображения «естественной ориентации», поэтому поезд при рисовании выглядел правильно, затем код поворачивает изображение так, чтобы оно «указывало правильное направление», и переворачивает его для нижней части овала.
Да! Я найду его
Ваш код работает. Мой нет. Я заставлю это работать. Кстати, я отнес все к GameViewController, потому что мои долгосрочные планы состоят в том, чтобы иметь имитацию набора датчиков HO с несколькими железнодорожными путями и несколькими вагонами - вагон с углем и т. д.
ПОЧЕМУ это работает при подклассе NSObject, но не работает при подклассе GameViewController?
@JohnLove - понятия не имею, о чем вы говорите... Что вы создаете подкласс как NSObject, а что вы создаете как GameViewController?
Ваш MyEllipse
— это класс NSObject + вы помещаете thePoints
в него как переменную. Это работает, потому что у вас есть только 1 овальная дорожка/эллипс и 1 объект = поезд. Задумываясь на будущее, я вижу несколько thePoints
и paths
, например, несколько компонентов поезда = вагон с углем и т. д. + несколько путей (большой экран диагональю 55 дюймов предлагает массу возможностей для этих вещей). В качестве примера такого преобразования Эллипса: У меня есть: funcgeneratePointsForPath(_path:UIBezierPath, inRect: CGRect, withNumberOfPoints: Int) {} Поэтому мне нужно создать ваш эллипс как расширение GameViewController
.
Я успешно добавил пройденный путь и вращение + запуск/остановку работы. Но если сделать ваш код Ellipse расширением GameViewController, поезд будет следовать с опущенным носом. К вашему сведению, поезд + очки теперь являются переменными в моем GameViewController.
@JohnLove - пример, который я собрал, уже использует более одной «овальной дорожки/эллипса». С небольшими изменениями — добавлением еще одного узла спрайта, сохранением его «начального индекса» немного позади первого, запуском двух анимаций и т. д. — мы получаем анимацию внизу моего ответа (Редактировать 3). Если вам нужен другой путь — может быть, прямоугольник с закругленными углами? — затем пишем новый класс, аналогичный классу MyEllipse
, для генерации неовальных путей.
@JohnLove - это выходит далеко за рамки вопроса в твоем посте (моя вина). Здесь, на Stack Overflow, чтобы другим пользователям было проще найти ответы... Попробуйте опубликовать конкретный вопрос. Если этот конкретный вопрос решен, отметьте его как принятый и двигайтесь дальше. «Должен ли я использовать расширение контроллера или независимый класс?» совершенно не имеет отношения к этому посту.
СДЕЛАНО и СДЕЛАНО!
Моя основная проблема заключается в том, что, согласно документам Apple, использование SKAction.follow с orientToPath = true должно автоматически выполнять обнимание с необходимым наклоном овала без каких-либо особых усилий.
Ваш mp3 должен быть автоматическим, если документы были точными.
За вашими гениальными усилиями действительно приятно наблюдать. Но изменение orientToPath = true занимает 2 секунды по сравнению с вашим многочасовым чудом.
Поскольку ваше чудо явно необходимо, почему бы в документах Apple просто не сказать об этом без фальши orientToPath?
«поднимите еще один
SKScene
» — неясно, что это значит… вы уничтожаете текущую сцену, а затем перестраиваете ее, когда пользователь возвращается? Если у вас есть узел, следующий по овальному пути, ваша цель — сохранить «состояние» анимации, а затем восстановить/возобновить ее по возвращении? Или по возвращении перезапустить анимацию с начальной точки пути?