gpt4 book ai didi

ios - UIImageView 的动画旋转变化

转载 作者:行者123 更新时间:2023-12-01 19:03:52 25 4
gpt4 key购买 nike

我正在制作一个应用程序,该应用程序(除其他外)显示一个简化的指南针图像,该图像根据设备的旋转而旋转。问题是简单地这样做:

float heading = -1.0f * M_PI * trueHeading / 180.0f; //trueHeading is always between 0 and 359, never 360
self.compassNeedle.transform = CGAffineTransformMakeRotation(heading);

在 CLLocationManager 的 didUpdateHeading 方法中,动画变得丑陋且不稳定。
我已经使用 Instruments 来确定它是否只是我的应用程序无法以超过 30-48 fps 的速度进行渲染,但事实并非如此。

如何平滑 ImageView 的旋转,使其更像 Apple 自己的 Compass 应用程序?

最佳答案

不要使用当前的即时值,而是尝试使用最后 N 个值的平均值作为真实航向。该值可能会在一瞬间跳动很多,但会“平均”稳定下来。

假设你有一个成员变量storedReadings,它是一个NSMutableArray:

-(void)addReading(float):newReading
{
[storedReadings addObject:[NSNumber numberWithFloat:newReading]];
while([storedReadings count] > MAX_READINGS)
{
[storedReadings removeObjectAtIndex:0];
}
}

那么当你需要平均值时(定时器更新?)
-(float)calcReading
{
float result = 0.0f;
if([storedReadings count] > 0)
{
foreach(NSNumber* reading in storedReadings)
{
result += [reading floatValue];
}
result /= [storedReadings count];
}
return result;
}

您可以先验地选择 MAX_READINGS。

上一级

如果读数没有跳动太多但动画仍然不稳定,您可能需要执行“平滑”旋转之类的操作。在任何给定时间,您都有当前显示的角度,theta(将其存储在您的类中,从 0 开始)。你也有你的目标角度,叫它 目标 .这是您从平滑的 calcReading 函数中获得的值。 错误 被定义为两者之间的差异:
error = target-theta;

设置一个时间为 0.05 秒(每秒 20 次)的计时器回调。你要做的是调整 theta使 error趋向于 0。您可以通过以下几种方式执行此操作:
  • thetaNext += kProp * (target - theta);//这是比例反馈。
  • thetaNext += kStep * sign(target-theta);//这会在每次更新时将 theta 移动一个固定的量。如果 x >= 0,则符号(x) = +1,如果 x < 0,则为 -1。

  • 第一种解决方案将导致旋转距离目标越远,旋转就会急剧变化。当它摆动超过“零”点时,它也可能会有点振荡。较大的 kProp 值将产生更快的响应,但也会产生更多的振荡。需要进行一些调整。

    第二种解决方案将更容易控制......它每次只是“滴答”指南针。您可以将 kStep 设置为 1/4 度,这为您提供大约(1/4 度/更新)*(20 次更新/秒)= 5 度/秒的旋转“速度”。这有点慢,但您可以查看数学并更改 kStep 以满足您的需要。请注意,您可以“绑定(bind)”“错误”值,以便在错误 < kStep(或类似的东西)时不采取任何措施。这可以防止您的指南针在角度非​​常接近目标时发生偏移。您可以在误差较小时更改 kStep,使其“滑动”到结束位置(即,当误差较小时,kStep 较小)。

    为了处理角度问题(环绕),我将角度“标准化”,使其始终在 -Pi/Pi 范围内。我不保证这是做到这一点的完美方式,但它似乎完成了工作:
       // Takes an angle greater than +/- M_PI and converts it back
    // to +/- M_PI. Useful in Box2D where angles continuously
    // increase/decrease.
    static inline float32 AdjustAngle(float32 angleRads)
    {
    if(angleRads > M_PI)
    {
    while(angleRads > M_PI)
    {
    angleRads -= 2*M_PI;
    }
    }
    else if(angleRads < -M_PI)
    {
    while(angleRads < -M_PI)
    {
    angleRads += 2*M_PI;
    }
    }
    return angleRads;
    }

    通过这种方式,-pi 是您在继续向左/向右旋转时从任一方向到达的角度。也就是说,从 0 度到 359 度的数字没有不连续性。

    所以把这一切放在一起
    static inline float Sign(float value)
    {
    if(value >= 0)
    return 1.0f;
    return -1.0f;
    }

    //#define ROTATION_OPTION_1
    //#define ROTATION_OPTION_2
    #define ROTATION_OPTION_3

    -(void)updateArrow
    {
    // Calculate the angle to the player
    CGPoint toPlayer = ccpSub(self.player.position,self.arrow.position);
    // Calculate the angle of this...Note there are some inversions
    // and the actual image is rotated 90 degrees so I had to offset it
    // a bit.
    float angleToPlayerRads = -atan2f(toPlayer.y, toPlayer.x);
    angleToPlayerRads = AdjustAngle(angleToPlayerRads);

    // This is the angle we "wish" the arrow would be pointing.
    float targetAngle = CC_RADIANS_TO_DEGREES(angleToPlayerRads)+90;
    float errorAngle = targetAngle-self.arrow.rotation;

    CCLOG(@"Error Angle = %f",errorAngle);


    #ifdef ROTATION_OPTION_1
    // In this option, we just set the angle of the rotated sprite directly.
    self.arrow.rotation = CC_RADIANS_TO_DEGREES(angleToPlayerRads)+90;
    #endif


    #ifdef ROTATION_OPTION_2
    // In this option, we apply proportional feedback to the angle
    // difference.
    const float kProp = 0.05f;
    self.arrow.rotation += kProp * (errorAngle);
    #endif

    #ifdef ROTATION_OPTION_3
    // The step to take each update in degrees.
    const float kStep = 4.0f;
    // NOTE: Without the "if(fabs(...)) check, the angle
    // can "dither" around the zero point when it is very close.
    if(fabs(errorAngle) > kStep)
    {
    self.arrow.rotation += Sign(errorAngle)*kStep;
    }
    #endif
    }

    我将此代码放入我为 Cocos2d 编写的演示程序中。它显示了一个角色(大盒子)被一些怪物(小盒子)追逐,并且中心有一个箭头,始终指向角色。 updateArrow 调用定期在计时器滴答声(update(dt) 函数)上进行。玩家在屏幕上的位置由用户点击屏幕设置,角度基于从箭头到玩家的矢量。在函数中,我展示了设置箭头角度的所有三个选项:

    选项 1

    只需根据玩家所在的位置设置它(即,只需设置它)。

    选项 2

    使用比例反馈来调整每个时间步的箭头角度。

    选项 3

    如果误差角大于步长,则每个时间步长一点箭头的角度。

    这是一张大致显示其外观的图片:

    enter image description here

    并且, all the code is available here on github .只需查看 HelloWorldLayer.m 文件即可。

    这个有帮助吗?

    关于ios - UIImageView 的动画旋转变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21103325/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com