Какой алгоритм доступен для корреляции позиции SKSpriteNode с currentPoint UIBezierPath?

Какой алгоритм доступен для корреляции позиции 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, мне нужно, чтобы positionSKSpriteNode оставался там, где он был до того, как был представлен новый SKScene.

Каков наилучший способ сделать это?

«поднимите еще один SKScene» — неясно, что это значит… вы уничтожаете текущую сцену, а затем перестраиваете ее, когда пользователь возвращается? Если у вас есть узел, следующий по овальному пути, ваша цель — сохранить «состояние» анимации, а затем восстановить/возобновить ее по возвращении? Или по возвращении перезапустить анимацию с начальной точки пути?

DonMag 19.04.2024 14:13

1) «разрушить» текущую сцену и перестроить ее. Разные сцены в одном GameViewController. 2) перед заменой текущей сцены с овалом, сохраняя положение движущегося поезда в глобальной переменной saveTrainPosition и перезапуская движение с того места, где оно было остановлено. Практически решено, за исключением того, что точка перезапуска изменилась с той, что была.

John Love 19.04.2024 22:35

Я знаю, что анимацию «следования по пути» можно приостановить/возобновить... но это не сработает, если вы уничтожаете текущую сцену. Вы уже нашли способ запустить анимацию «следования по пути» в точке, отличной от начальной точки? То есть вы пытаетесь сделать что-то вроде «сохранить процент выполнения (скажем, 38% вокруг овала), а затем вернуться и создать новый овальный путь и начать с 38% вокруг овала»?

DonMag 19.04.2024 23:11

Да, на последний вопрос

John Love 19.04.2024 23:57

Я близко, но новая отправная точка отличается от старой точки остановки или паузы. мне нужно равное, а не почти

John Love 20.04.2024 00:00

Я не верю, что можно сказать «начать анимацию следования по траектории с 38%». Я помню, как отвечал на ваш предыдущий вопрос, где разместил ссылку на полный проект на GitHub (github.com/DonMag/SpriteKitRotation) — он включал пример генерации массива точек, определяющих овал/эллипс (как в отличие от ovalIn:). Итак, вы можете «найти индекс точки в массиве, ближайшей к текущей позиции, а затем запустить новую анимацию для этого эллипса, начиная с индекса».

DonMag 20.04.2024 01:20

ЕСЛИ я остановлю следующее точно на одном из ваших thePoints, не возникнет проблем с перезапуском на том же CGPoint. Однако, если я остановлюсь в точке между двумя соседними thePoints, мне придется перестраивать массив [CGPoint] каждый раз, когда я останавливаюсь, а затем перезапускать движущийся поезд. Такая перестройка неизбежна при вращении iOS-устройства. Позвольте мне попробовать перестроить каждый раз, когда я останавливаюсь, а затем возобновляю движение. Кажется, что это большие накладные расходы, но это может быть неизбежным.

John Love 20.04.2024 21:05

Мне бы хотелось избежать повторного создания железной дороги SKScene, но я не вижу, как это сделать, если я не использую Xcode для добавления путей и поезда в файл GameScene.sks, как я это делаю для фоновых изображений. Но мне нужно, чтобы размеры и положение путей изменялись при каждом повороте (UIScreen.bounds), и мне нужно, чтобы изображение поезда располагалось вертикально в соответствии с вертикальным положением пути. Итак, похоже, я застрял, перестраивая SKScene с поездом каждый раз, когда переключаюсь SKScenes. Кстати, эти другие сцены — это CreditsScene и DirectionsScene.

John Love 20.04.2024 21:11

Извините, но мне сложно по-настоящему понять, как поток вашей программы связан с «вызовом другого SKScene» и «возвратом от просмотра другого SKScene»… Возможно, я смогу вам еще немного помочь, но во-первых... какова ваша цель, когда ваш «поезд» находится в нижней половине овала? Это: i.sstatic.net/lddDC.png ... или это: i.sstatic.net/Ul1Az.png

DonMag 21.04.2024 22:22

Второе – это цель

John Love 22.04.2024 03:34
i.sstatic.net/Ii645.png
John Love 22.04.2024 17:38

WRT последней картинки, переместите шлейф на 180 градусов прямо внутрь нижнего края овала (как на вашем изображении)

John Love 23.04.2024 01:34
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
12
104
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Потому что вы будете менять размер овала при вращении устройства, и когда вы показываете другую сцену, а затем «вернетесь», вы хотите:

  • «сохранить» текущую позицию поезда
  • покажи новую сцену
  • показать сцену с поездом (возможно, после поворота устройства во время просмотра другой сцены)
  • перезапустить анимацию с сохраненной позиции

Вероятно, вам нужно создать массив точек, определяющих овальный путь (вместо использования 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 просто неверны.

John Love 24.04.2024 02:35

Ваш блеск неоспорим. Документов Apple не так уж и много

John Love 24.04.2024 02:47

@JohnLove - Не уверен, что вы называете «серьезным зависанием» ... не связанным с этим, я добавил в свой ответ еще одну гифку, показывающую, что вы можете сделать с одним (или несколькими) дополнительными «верхними» изображениями вашего поезда (см. обновленный репозиторий GitHub).

DonMag 24.04.2024 14:49

Моя основная проблема заключается в том, что, согласно документам Apple, использование SKAction.follow с orientToPath = true должно автоматически выполнять обнимание с необходимым наклоном овала без каких-либо особых усилий. Ваш mp3 должен быть автоматическим, если документы были точными. За вашими гениальными усилиями действительно приятно наблюдать. Но изменение orientToPath = true занимает 2 секунды по сравнению с вашим многочасовым чудом. Поскольку ваше чудо явно необходимо, почему бы в документах Apple просто не сказать об этом без фальши orientToPath?

John Love 25.04.2024 01:02

@JohnLove - см. мою (длинную) редакцию 2 в моем ответе.

DonMag 25.04.2024 15:54

Я понимаю учебник по ориентации изображения с портретной и альбомной ориентацией. Чего я не понимаю, так это почему в вашем проекте ресурс поезда направлен ВВЕРХ, а не просто горизонтально. Потому что у меня актив поезда горизонтальный, и поезд движется по рельсам носом вниз?? Где-то в истории я повернул нос этого актива вверх, а мой поезд все равно двигался носом вниз.

John Love 27.04.2024 01:36

@JohnLove - вы исследовали пример кода в этом проекте? Я использовал изображения «естественной ориентации», поэтому поезд при рисовании выглядел правильно, затем код поворачивает изображение так, чтобы оно «указывало правильное направление», и переворачивает его для нижней части овала.

DonMag 28.04.2024 14:59

Да! Я найду его

John Love 29.04.2024 17:37

Ваш код работает. Мой нет. Я заставлю это работать. Кстати, я отнес все к GameViewController, потому что мои долгосрочные планы состоят в том, чтобы иметь имитацию набора датчиков HO с несколькими железнодорожными путями и несколькими вагонами - вагон с углем и т. д.

John Love 29.04.2024 17:45

ПОЧЕМУ это работает при подклассе NSObject, но не работает при подклассе GameViewController?

John Love 03.05.2024 11:51

@JohnLove - понятия не имею, о чем вы говорите... Что вы создаете подкласс как NSObject, а что вы создаете как GameViewController?

DonMag 03.05.2024 15:25

Ваш MyEllipse — это класс NSObject + вы помещаете thePoints в него как переменную. Это работает, потому что у вас есть только 1 овальная дорожка/эллипс и 1 объект = поезд. Задумываясь на будущее, я вижу несколько thePoints и paths, например, несколько компонентов поезда = вагон с углем и т. д. + несколько путей (большой экран диагональю 55 дюймов предлагает массу возможностей для этих вещей). В качестве примера такого преобразования Эллипса: У меня есть: funcgeneratePointsForPath(_path:UIBezierPath, inRect: CGRect, withNumberOfPoints: Int) {} Поэтому мне нужно создать ваш эллипс как расширение GameViewController.

John Love 04.05.2024 02:41

Я успешно добавил пройденный путь и вращение + запуск/остановку работы. Но если сделать ваш код Ellipse расширением GameViewController, поезд будет следовать с опущенным носом. К вашему сведению, поезд + очки теперь являются переменными в моем GameViewController.

John Love 04.05.2024 03:01

@JohnLove - пример, который я собрал, уже использует более одной «овальной дорожки/эллипса». С небольшими изменениями — добавлением еще одного узла спрайта, сохранением его «начального индекса» немного позади первого, запуском двух анимаций и т. д. — мы получаем анимацию внизу моего ответа (Редактировать 3). Если вам нужен другой путь — может быть, прямоугольник с закругленными углами? — затем пишем новый класс, аналогичный классу MyEllipse, для генерации неовальных путей.

DonMag 04.05.2024 15:56

@JohnLove - это выходит далеко за рамки вопроса в твоем посте (моя вина). Здесь, на Stack Overflow, чтобы другим пользователям было проще найти ответы... Попробуйте опубликовать конкретный вопрос. Если этот конкретный вопрос решен, отметьте его как принятый и двигайтесь дальше. «Должен ли я использовать расширение контроллера или независимый класс?» совершенно не имеет отношения к этому посту.

DonMag 05.05.2024 15:15

СДЕЛАНО и СДЕЛАНО!

John Love 05.05.2024 16:39

Моя основная проблема заключается в том, что, согласно документам Apple, использование SKAction.follow с orientToPath = true должно автоматически выполнять обнимание с необходимым наклоном овала без каких-либо особых усилий.

Ваш mp3 должен быть автоматическим, если документы были точными.

За вашими гениальными усилиями действительно приятно наблюдать. Но изменение orientToPath = true занимает 2 секунды по сравнению с вашим многочасовым чудом.

Поскольку ваше чудо явно необходимо, почему бы в документах Apple просто не сказать об этом без фальши orientToPath?

Другие вопросы по теме