简体   繁体   中英

Make a loop with cardinal spline

I'm trying to make an animation for a sprite, which will go on path and it will loop forever. But CardinalSpline doesn't close up as I thought.

Here are example points (it's a square):

pointsRed->addControlPoint(Vec2(350, 350));
pointsRed->addControlPoint(Vec2(350, 150));
pointsRed->addControlPoint(Vec2(150, 150));
pointsRed->addControlPoint(Vec2(150, 350));
pointsRed->addControlPoint(Vec2(350, 350));

Animation creation:

auto redSpline = CardinalSplineTo::create(20, pointsRed, 0);

It looks like that:

在此处输入图片说明

So of course animation doesn't look too good. I want to make a perfect loop (with constant speed), where cannot be seen where's an end or start.

If I will continue adding the same points it will loop nice until it reaches the end where's an breakdown, which you can see on top right corner.

How can I achieve this?

Only thing, which comes to my mind is too double amount of points and at exactly half of animation seek it to the begging or create a new one.

Finally I did it. I had to change cocos2d-x code a bit.

All needed changes:

in startWithTarget:

_deltaT = (float) 1 / (_points->count() - 1);

to:

_deltaT = (float) 1 / (_points->count());

Reimplemented getControlPointAtIndex:

from:

Vec2 PointArray::getControlPointAtIndex(ssize_t index)
{
      index = MIN(static_cast<ssize_t>(_controlPoints->size())-1, MAX(index, 0));  
      return *(_controlPoints->at(index));
}

to:

Vec2 PointArray::getControlPointAtIndex(ssize_t index)
{
    ssize_t tIndex = index;
    ssize_t pSize = static_cast<ssize_t>(_controlPoints->size());
    if(tIndex < 0) tIndex += pSize;
    if(tIndex >= pSize) tIndex -= pSize;    
    return *(_controlPoints->at(tIndex));
}

Then spline is interpolating well, because it always has 4 different points to interpolate. Before that first and last point were cut off.

To draw a spline in CAD programs such as AutoCAD, you must specify not only points, but also the direction (imaginary extension) for the extreme points. Direction affects the behavior of the spline from the extreme point to the next.
Here the direction is not specified, so the extreme point of the spline has a "sharp transition".
Try to add two points to indicate the direction, for example:

pointsRed->addControlPoint(Vec2(300, 360));
pointsRed->addControlPoint(Vec2(350, 350));
pointsRed->addControlPoint(Vec2(350, 150));
pointsRed->addControlPoint(Vec2(150, 150));
pointsRed->addControlPoint(Vec2(150, 350));
pointsRed->addControlPoint(Vec2(350, 350));
pointsRed->addControlPoint(Vec2(360, 300));

I pointed out the approximate position of the extreme points. You should to move them and determine the exact position to achieve precise overlay of a spline.
PS Or you can try to connect the spline is not at the corner and on a flat plot of the spline, for example at the point (250, 360).

My previous answer concerned only spline creation. But now I made my particle rotation by spiral, and know the simplest way which can help you.
So, you need a container for your's object, and you should make two things: rotate container and move object on container. I will show an example with particle as object:

Sprite* container = Sprite::create("background.png");
container->setAnchorPoint(Vec2::ANCHOR_MIDDLE); // the container is rotated about its center 
container->setPosition(winSize.width * 0.5f, winSize.height * 0.5f);
container->setOpacity(255); // it’s only for test. After that you can setOpacity(0)
this->addChild(container);

ParticleSystemQuad* particle = ParticleSystemQuad::create("particle_meteor.plist");
// place object 100 pixels above the center of the container
particle->setPosition(container->getBoundingBox().size.width * 0.5f, container->getBoundingBox().size.height * 0.5f + 100);
container->addChild(particle);

container->runAction(RotateBy::create(4.0f, 360));

EaseInOut* easeUp = EaseInOut::create(MoveBy::create(0.4f, Vec2(0, 35)), 2.0f);
EaseInOut* easeDown = EaseInOut::create(MoveBy::create(0.4f, Vec2(0, -35)), 2.0f);
particle->runAction(Sequence::create(DelayTime::create(0.1f),
                                     easeUp,
                                     easeDown,
                                     DelayTime::create(0.2f),
                                     easeUp,
                                     easeDown,
                                     DelayTime::create(0.2f),
                                     easeUp,
                                     easeDown,
                                     DelayTime::create(0.2f),
                                     easeUp,
                                     easeDown,
                                     DelayTime::create(0.1f),
                                     NULL)); // particle «jumps» in corners

Here I show only one turnover, but you can get it to rotate continuously. There is result of program:

It is not same as your splain, but the principle of the creation should be clear. I think, you can decrease the distance in MoveBy: and experiment with Ease: for a better results.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM