Cocoa touch 查询沿UIBezier曲线的特定点以进行动画?

Cocoa touch 查询沿UIBezier曲线的特定点以进行动画?,cocoa-touch,animation,uikit,bezier,uibezierpath,Cocoa Touch,Animation,Uikit,Bezier,Uibezierpath,我最近为Maya编写了一个脚本,该脚本导出了一个包含Bezier路径(一系列xy点和xy控制点)数据的文件 此贝塞尔路径表示3D“轨道”或路径,我的角色将在应用程序内以恒定速度行进 我知道如何构造UIBEZIER曲线,但我似乎找不到任何可靠的信息,说明在给定沿曲线移动的距离的情况下,是否可能/如何获得曲线上某个点的x/y位置 我在苹果上找到了以下列表: 但是我不太明白这个函数返回了什么,我将如何使用它来实现我的目标 如有任何帮助/建议,将不胜感激 谢谢, -亚当·艾斯菲尔德(Adam Eisf

我最近为Maya编写了一个脚本,该脚本导出了一个包含Bezier路径(一系列xy点和xy控制点)数据的文件

此贝塞尔路径表示3D“轨道”或路径,我的角色将在应用程序内以恒定速度行进

我知道如何构造UIBEZIER曲线,但我似乎找不到任何可靠的信息,说明在给定沿曲线移动的距离的情况下,是否可能/如何获得曲线上某个点的x/y位置

我在苹果上找到了以下列表:

但是我不太明白这个函数返回了什么,我将如何使用它来实现我的目标

如有任何帮助/建议,将不胜感激

谢谢,
-亚当·艾斯菲尔德(Adam Eisfeld)

你引用的链接暗示的是,贝塞尔曲线的每一段都描绘出一条路径(x(t),y(t)),其中t从0到1

我不熟悉
UIBezierCurve
,但我敢打赌,您可以从中获得
NSBezierPath
,从中您可以手动迭代这些段。每段都是一个移动到、线到、曲线到或闭合(相当于最后一个移动到位置的线)。唯一的非平凡路径类型是curveTo,您可以在此处了解更多信息:

如果您只想设置沿曲线移动的动画,给每个一个固定的时间量,这将很简单;您可以迭代这些段,然后在每个段中,从0到1逐步运行t,并插入到方程中

棘手的部分将以恒定的速度移动。为此,您需要实际测量每个片段的长度,并将该长度拆分为每个帧的部分。在这个问题中,您可以阅读更多关于这方面的内容:

我已经有一段时间没有使用Cocoa了,但是我有一些Java代码,您可以很容易地移植它们(这只是数学,在任何语言中都是一样的):


好的,这将是一个很长的答案。以下是我所做的:

  • 我编写了一个MEL脚本,允许您在Maya中绘制贝塞尔曲线,然后-选择该曲线-运行我的脚本,该脚本将通过曲线分析曲线的每个贝塞尔部分,计算每个部分的长度和曲线点/控制点的位置。计算完所有这些数据后,它会将所有内容导出到一个.bezier文件,该文件的结构如下:

    第1行:整个贝塞尔路径中包含的单个贝塞尔曲线数 第2行:第一条贝塞尔曲线的长度 ... X线:最后一条贝塞尔曲线的长度

    第一个曲线点的第一个控制点的X位置 第一个曲线点的第一个控制点的Y位置 第一个曲线点的第一个控制点的Z位置

    第一个曲线点的X位置 第一个曲线点的Y位置 第一个曲线点的Z位置

    第一个曲线点的第二个控制点的X位置 第一个曲线点的第二个控制点的Y位置 第一个曲线点的第二个控制点的Z位置

    最后一个曲线点的第一个控制点的X位置 最后一个曲线点的第一个控制点的Y位置 最后一个曲线点的第一个控制点的Z位置

    最后一个曲线点的X位置 最后一个曲线点的Y位置 最后一个曲线点的Z位置

    最后一个曲线点的第二个控制点的X位置 最后一个曲线点的第二个控制点的Y位置 最后一个曲线点的第二个控制点的Z位置

  • 因此,要使这组类正常工作,您需要一个这样结构的文件

    下面是我编程用来处理.bezier文件的三个类:

    AEBezierPath:

    .h文件:

    #import <Foundation/Foundation.h>
    #import "AEBezierVertex.h"
    #import "AEBezierLine.h"
    
    @interface AEBezierPath : NSObject
    {
        NSMutableArray *vertices;
        NSMutableArray *lines;
        UIBezierPath *path;
    }
    
    @property (strong) NSMutableArray *vertices;
    @property (strong) NSMutableArray *lines;
    @property (strong) UIBezierPath *path;
    
    -(id) initFromFile: (NSString*) file;
    -(CGPoint) positionFromDistance: (float) fromDistance;
    
    @end
    
    #import <Foundation/Foundation.h>
    
    @interface AEBezierVertex : NSObject
    {
        CGPoint controlIn;
        CGPoint controlOut;
        CGPoint control;
    }
    @property CGPoint controlIn;
    @property CGPoint controlOut;
    @property CGPoint control;
    
    -(id) initWithControl: (CGPoint) setControl In: (CGPoint) setIn Out: (CGPoint) setOut;
    
    @end
    
    #import <Foundation/Foundation.h>
    
    @interface AEBezierLine : NSObject
    {
        float length;
    }
    @property float length;
    
    -(id) initWithLength: (float) setLength;
    
    @end
    
    AEBezierLine:

    .h文件:

    #import <Foundation/Foundation.h>
    #import "AEBezierVertex.h"
    #import "AEBezierLine.h"
    
    @interface AEBezierPath : NSObject
    {
        NSMutableArray *vertices;
        NSMutableArray *lines;
        UIBezierPath *path;
    }
    
    @property (strong) NSMutableArray *vertices;
    @property (strong) NSMutableArray *lines;
    @property (strong) UIBezierPath *path;
    
    -(id) initFromFile: (NSString*) file;
    -(CGPoint) positionFromDistance: (float) fromDistance;
    
    @end
    
    #import <Foundation/Foundation.h>
    
    @interface AEBezierVertex : NSObject
    {
        CGPoint controlIn;
        CGPoint controlOut;
        CGPoint control;
    }
    @property CGPoint controlIn;
    @property CGPoint controlOut;
    @property CGPoint control;
    
    -(id) initWithControl: (CGPoint) setControl In: (CGPoint) setIn Out: (CGPoint) setOut;
    
    @end
    
    #import <Foundation/Foundation.h>
    
    @interface AEBezierLine : NSObject
    {
        float length;
    }
    @property float length;
    
    -(id) initWithLength: (float) setLength;
    
    @end
    
    工作原理:

  • 确保您已经创建了一个符合上面所示结构的.bezier文件,并将其保存在应用程序包中

  • 通过以下方式实例化新的AEBezierPath实例:

    -(id)initFromFile:(NSString*)文件

  • 这将从名为*file的.bezier文件中读取所有数据,并从中构造UIBezierPath,并将必要的长度信息存储到AEBezierPath中

  • 通过使用以下方法向AEBezierPath发送距离值,以CGPoint的形式查询AEBezierPath的x/y位置:

    -(CGPoint)位置from距离:(float)from距离

  • 此方法将首先通过使用先前从.bezier文件中检索到的每个bezier段的长度来确定该距离位于哪个bezier段上。在此之后,该方法将使用前面关于SO问题的文章中提到的bezier插值函数来计算该距离处bezier路径上的x/y位置,并将其作为CGPoint返回

    它并不完美,在长贝塞尔曲线和短紧角上行驶的距离仍然存在一些明显的差异,但它肯定远不如根本不使用此系统,而是依靠百分比值沿贝塞尔曲线行驶

    我知道代码肯定是可以优化的,这只是第一次运行,让一切都正常工作,但我认为它已经足够好了,现在可以作为一个答案发布

    • 亚当·艾斯菲尔德

    NSBezierPath是AppKit的一部分,因此在Cocoa Touch中不可用。你是说CGPath吗?是的,在UIKit中,你必须使用
    CGPath
    ,而遍历它的唯一方法是通过回调和从
    cgpathpapply
    。对不起;我多年前在桌面上的可可体验。谢谢。经过大量的实验和阅读,我想我终于想出了两个方法