Попытка обдумать имитацию импульса / инерции с помощью UIRotationGestureController

Итак, я пытаюсь создать эффект типа "Колесо фортуны" с формой колеса в iOS, где я могу схватить и повернуть колесо. В настоящее время я могу перетаскивать колесо и поворачивать его к своему сердцу, но, отпустив палец, оно останавливается. Мне нужно применить какой-то импульс или инерцию, чтобы имитировать естественное вращение колеса.

У меня есть расчет скорости на месте, поэтому, когда я поднимаю палец вверх, я NSLog вне скорости (от 1 до 100), которая колеблется от 1 до 1800 (при моем самом сложном щелчке!), сейчас я просто пытаюсь установить, как мне будет преобразовывать эту скорость в фактическое вращение, применяемое к объекту, и как я буду замедлять его с течением времени.

Мои первоначальные мысли были примерно такими: начать вращать полные круги в цикле с той же скоростью, что и заданная скорость, затем при каждом последующем вращении замедлять скорость на несколько процентов. Это должно дать эффект, что более тяжелое вращение идет быстрее и требует больше времени для замедления.

Я не математик, поэтому мой подход может быть неправильным, но если у кого-нибудь есть какие-либо советы о том, как я могу заставить это работать, по крайней мере в базовом состоянии, я был бы очень благодарен. Здесь есть действительно полезный ответ: iPhone добавляет физику инерции / импульса для анимации "колеса фортуны", например, вращающегося управления, но он более теоретический и лишен практической информации о том, как точно применить расчетную скорость к объекту и т. Д. думая, что мне тоже понадобится помощь в анимации.

РЕДАКТИРОВАТЬ: Мне также нужно будет потренироваться, если они перетаскивают колесо по часовой стрелке или против часовой стрелки.

Большое спасибо!

2 ответа

Решение

Я написал нечто аналогичное для моей программы Bit, но мой случай, мне кажется, немного сложнее, потому что я вращаюсь в 3D: https://itunes.apple.com/ua/app/bit/id366236469?mt=8

По сути, я настраиваю NSTimer, который регулярно вызывает какой-то метод. Я просто беру направление и скорость, чтобы создать матрицу вращения (как я уже сказал, 3D немного неприятнее:P), и я умножаю скорость на некоторое число, меньшее 1, чтобы оно уменьшалось. Причиной умножения вместо вычитания является то, что вы не хотите, чтобы объект вращался в два раза дольше, если вращение от пользователя в два раза сильнее, так как это раздражает, если я нахожу ожидание.

Что касается выяснения, в каком направлении вращается колесо, просто сохраните это в методе touchesEnded:withEvent:, где у вас есть вся информация. Поскольку вы говорите, что у вас уже работает отслеживание, пока пользователь нажимает пальцем, это должно быть очевидно.

Что у меня есть в 3D это что-то вроде:

// MyView.h
@interface MyView : UIView {
    NSTimer *animationTimer;
}
- (void) startAnimation;
@end


// MyAppDelegate.h
@implementation MyAppDelegate

- (void) applicationDidFinishLaunching:(UIApplication *)application {
    [myView startAnimation];
}

@end

// MyView.m
GLfloat rotationMomentum = 0;
GLfloat rotationDeltaX  = 0.0f;
GLfloat rotationDeltaY  = 0.0f;

@implementation MyView
- (void)startAnimation {
    animationTimer = [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)((1.0 / 60.0) * animationFrameInterval) target:self selector:@selector(drawView:) userInfo:nil repeats:TRUE];
}

- (void) drawView:(id)sender {
    addRotationByDegree(rotationMomentum);
    rotationMomentum /= 1.05;
    if (rotationMomentum < 0.1)
        rotationMomentum = 0.1; // never stop rotating completely
    [renderer render];
}

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
}

- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
    UITouch *aTouch = [touches anyObject];
    CGPoint loc = [aTouch locationInView:self];
    CGPoint prevloc = [aTouch previousLocationInView:self];

    rotationDeltaX = loc.x - prevloc.x;
    rotationDeltaY = loc.y - prevloc.y;

    GLfloat distance = sqrt(rotationDeltaX*rotationDeltaX+rotationDeltaY*rotationDeltaY)/4;
    rotationMomentum = distance;
    addRotationByDegree(distance);

    self->moved = TRUE;
}

- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
}

- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event
{
}

Я пропустил функцию addRotationByDegree, но она использует глобальные переменные вращение DeltaX и вращение DeltaY, применяет матрицу вращения к уже сохраненной матрице, а затем сохраняет результат. В вашем примере вы, вероятно, хотите что-то намного более простое, например (я предполагаю, что только движения в направлении X вращают колесо):

- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
    UITouch *aTouch = [touches anyObject];
    CGPoint loc = [aTouch locationInView:self];
    CGPoint prevloc = [aTouch previousLocationInView:self];

    GLfloat distance = loc.x - prevloc.x;
    rotationMomentum = distance;
    addRotationByDegree(distance);

    self->moved = TRUE;
}

void addRotationByDegree(distance) {
    angleOfWheel += distance; // probably need to divide the number with something reasonable here to have the spin be nicer
}

Это будет грубый ответ, поскольку у меня нет подробного примера под рукой.

Если у вас уже есть скорость, когда вы поднимаете палец, она не должна быть жесткой. Скорость у вас в пикселях в секунду или что-то в этом роде. Сначала вам нужно преобразовать эту линейную скорость в угловую скорость. Это можно сделать, зная периметр круга 2*PI*radius а затем сделать 2*PI/perimeter*velocity чтобы получить угловую скорость в радианах в секунду.

Если бы у вашего колеса не было трения в его оси, оно бы продолжало работать с такой скоростью. Ну, вы можете просто определить значение для этого трения, которое является ускорением и может быть представлено в квадратах пикселей в секунду или в радианах в секунду для углового ускорения. Тогда это просто вопрос деления угловой скорости на это угловое ускорение, и вы получаете время, пока оно не остановится.

Со временем анимации вы можете использовать уравнение finalAngle = initialAngle + angularSpeed*animationTime - angularAcceleration/2*animationTime*animationTime чтобы получить последний угол, ваше колесо будет находиться в конце анимации. Затем просто сделайте анимацию трансформации и поверните ее на этот угол на время, которое у вас есть, и скажите, что ваша анимация должна ослабнуть.

Это должно выглядеть достаточно реалистично. Если нет, вам нужно будет указать путь анимации для свойства вращения вашего колеса на основе некоторых примеров из уравнения сверху.

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