перемещение и вращение объектов по синусоидальной волне

#flash #actionscript-3 #math #tween #gsap

#вспышка #actionscript-3 #математика #анимация #gsap

Вопрос:

С этим у меня возникает пара проблем — первая заключается в том, как определить, где находится вершина каждой синусоидальной кривой (то есть частота), а вторая заключается в том, чтобы определить, как поворачивать каждый объект так, чтобы он скользил вбок по синусоидальной волне

(файл примечания имеет размер 800 x 600)

вот мой код: CarouselTest.as:

 package  
{
    import com.greensock.easing.Sine;
    import com.greensock.TweenMax;
    import com.components.CarouselItem;
    import com.theflashblog.fp10.SimpleZSorter;
    import flash.display.Sprite;
    import flash.events.Event;
    import uk.co.thereceptacle.utils.Trig;

    public class CarouselTest extends Sprite 
    {
        public static const SPACING : int = 20;
        public static const XSPACING: int = SPACING * 5;
        public static const SPEED   : Number = .05;
        public static const XSPEED  : Number = 800 / 360 * 2;
        public static const RADIUS  : int = 400;

        private var _items:Array;
        private var _container:Sprite;
        private var _movePerItem:Number;
        private var _noOfItems:int;

        public function CarouselTest() 
        {
            _items = new Array();
            _noOfItems = 200;
            _movePerItem = 360 / _noOfItems; // my attempt at finding how much each item must move in order to bring it to the front

            _container = new Sprite();
            _container.x = 400;
            _container.y = 300;
            _container.z = 200;
            addChild(_container);

            var item:CarouselItem = new CarouselItem();
            for (var i:int = 0; i < _noOfItems; i  )
            {
                item = new CarouselItem();
                item.radius = RADIUS;
                item.angle = (i * SPACING) % 360;
                item.x = i * XSPACING - 300;
                item.speed = SPEED;
                _container.addChild(item);
                _items.push(item);
            }

            moveItems(-1 * _movePerItem);
        }

        private function moveItems(direction:int):void
        {
            TweenMax.allTo(_items, 5, { angle:direction.toString(), x:(direction * XSPACING).toString(), ease:Sine.easeInOut } );
            TweenMax.to(this, 5, { onUpdate:SimpleZSorter.sortClips, onUpdateParams:[_container] } );
        }
    }
}
  

CarouselItem.as

 package com.components 
{
    import flash.display.Sprite;
    import flash.text.TextField;
    import uk.co.thereceptacle.components.HTMLTextField;
    import uk.co.thereceptacle.utils.Trig;

    public class CarouselItem extends Sprite
    {
        private var _angle  : Number;
        private var _prevX  : Number;
        private var _prevZ  : Number;

        public var speed    : int;
        public var radius   : Number;

        public function CarouselItem() 
        {
            graphics.beginFill(0x99ff00);
            graphics.lineStyle(1);
            graphics.drawRect( -100, -150, 200, 300);

            var tf:TextField = new HTMLTextField();
            tf.embedFonts = false;
            tf.wordWrap = false;
            tf.htmlText = "<p>THIS IS A TEST</p>";
            tf.x = -100;
            tf.y = -tf.textHeight / 2;
            addChild(tf);
        }

        public function get angle():Number { return _angle; }
        public function set angle(value:Number):void 
        {
            _angle = value;

            z = Math.sin(angle) * radius;

            var deltaX:Number = x - _prevX;
            var deltaZ:Number = z - _prevZ;
            var rotate:Number = Trig.radiansToDegrees(Math.atan2(deltaZ, deltaX));
            rotationY = rotate; <strike>// this is getting close but always rotates the item away from the center and i need it to rotate towards the center on the positive side of the sine wave</strike>
// edit: i've updated this as it's much closer but still flaky - it feels like i haven't done it right

        }

        override public function set x(value:Number):void 
        {
            _prevX = x;
            super.x = value;
        }

        override public function set z(value:Number):void 
        {
            _prevZ = z;
            super.z = value;
        }
    }
}
  

надеюсь, вы сможете помочь
obie

Комментарии:

1. привет, ребята — я решил проблему с частотой (то есть с кончиками синусоидальной волны). Если один оборот равен 2 * ПИ, то, пока я всегда использую дробную часть ПИ, я в ударе, так что теперь все, с чем мне действительно нужна помощь, — это вычисление угла наклона линии

2. Синус имеет максимальное значение для числа пи / 2 или 90 градусов в зависимости от ваших единиц измерения. Я не могу запустить ваш код, но ясно, что вам нужно выяснить, в какой момент поворот должен быть равен 0, а в какой — 90 градусам (или как вы хотите). в этом руководстве объясняется, как работает atan2

3. Можете ли вы немного подробнее объяснить, чего вы хотите достичь? В какой момент вы хотите, чтобы спрайт был повернут на сколько градусов?

4. я бы хотел, чтобы объект был повернут на 90 градусов по отношению к траектории, по которой он движется (в основном, как если бы он скользил по ней) в ходе его перемещения (поэтому я помещу его в angle mutator и обновлю угол с помощью анимации)

5. я думаю, что нашел кое-что в википедии , что может быть ответом — теперь все, что мне нужно сделать, это разобраться, о чем это говорит

Ответ №1:

Я предполагаю, что вы хотите анимировать что-то вроде скейтборда на холме (или лодки в море, или чего-то с похожим движением).

Все тригонометрические вычисления, которые вам нужно знать для решения этой задачи: линия, расположенная на кривой так, как вы хотите, является касательной линией. Это описывается производной кривой. Производной синусоидальной кривой является косинус. Магия исчисления: если ваша кривая равна a * sin (b * x), касательная равна a * b * cos (b * x). Это дает вам (подъем / бег). Если вы хотите, чтобы это было выражено в виде угла, вам понадобится atan (или atan2) этого. Обратите внимание, вам нужно будет придерживаться ваших единиц измерения угла. Скорее всего, atan выдает вам радианы, а вы хотите градусы.

Комментарии:

1. спасибо за вашу помощь, ребята 🙂 Я попытался использовать Trig.radiansToDegrees (Math.atan2 (deltaY, deltaZ)), но я обнаружил, что это довольно мерцающий, и если объекты не переместили предыдущий кадр (например, из-за того, что они находятся в конце анимации), они в конечном итоге получили бы значение deltaX / deltaZ равным 0 и остались бы плоскими. Если это правильный путь, то я посмотрю, смогу ли я попытаться объяснить это.

2. Спасибо, это было действительно полезно! Я создал изолированную среду для кода, демонстрирующую технику, когда я выяснял реализацию: editor.p5js.org/fadookie/sketches/7yvMfs7cz