Как я могу применить силу тяжести к моему приложению прыгающего шара? - PullRequest
11 голосов
/ 05 декабря 2008

Я написал довольно простое Java-приложение, которое позволяет вам перетаскивать мышь и, исходя из длины перетаскивания мыши, оно будет стрелять мячом в этом направлении, отскакивая от стен по ходу движения.

Вот быстрый скриншот:
альтернативный текст http://img222.imageshack.us/img222/3179/ballbouncemf9.png

Каждый из кругов на экране является объектом Ball. Движение шаров разбивается на вектора x и y;

public class Ball {
    public int xPos;
    public int yPos;
    public int xVector;
    public int yVector;

    public Ball(int xPos, int yPos, int xVector, int yVector) {
        this.xPos = xPos;
        this.yPos = yPos;
        this.xVector = xVector;
        this.yVector = yVector;
    }

    public void step()
    {
        posX += xVector;
        posY += yVector;

        checkCollisions();
    }

    public void checkCollisions()
    {
        // Check if we have collided with a wall
        // If we have, take the negative of the appropriate vector
        // Depending on which wall you hit
    }

    public void draw()
    {
        // draw our circle at it's position
    }
}

Это прекрасно работает. Все шары отскакивают от стены к стене.

Однако я решил, что хочу иметь возможность включать эффекты гравитации. Я знаю, что объекты ускоряются к Земле со скоростью 9,8 м / с, но я не знаю, как это должно переводиться в код. Я понимаю, что на yVector это повлияет, но мои эксперименты не дали желаемого эффекта.

В идеале, Я хотел бы иметь возможность добавить некоторый гравитационный эффект к этой программе, а также позволить шарам отскакивать несколько раз, прежде чем осесть на «земле».

Как я могу создать этот эффект упругости и гравитации? Как я должен манипулировать векторами скорости мяча на каждом шаге? Что нужно сделать, когда он достигнет «земли», чтобы я мог снова подпрыгнуть, но несколько короче, чем в предыдущий раз?

Мы ценим любую помощь, указав мне правильное направление.


Спасибо всем за комментарии! Уже отлично работает!

В моем шаге () я добавляю гравитационную постоянную к своему yVector, как предлагали люди, и это мой checkCollision ():

public void checkCollision()
{
    if (posX - radius < 0)              // Left Wall?
    {
        posX = radius;              // Place ball against edge
        xVector = -(xVector * friction);
    }
    else if (posX + radius > rightBound) // Right Wall?
    {
        posX = rightBound - radius;     // Place ball against edge
        xVector = -(xVector * friction);
    }

    // Same for posY and yVector here.
}

Однако шары будут продолжать скользить по полу. Я предполагаю, что это потому, что я просто беру процент (90%) их векторов за каждый отскок, и он никогда не бывает действительно нулевым. Должен ли я добавить в проверку, что если xVector становится определенным абсолютным значением, я должен просто изменить его на ноль?

Ответы [ 9 ]

15 голосов
/ 05 декабря 2008

То, что вам нужно сделать, это постоянно вычитать небольшую постоянную (то, что представляет ваши 9,8 м / с) из вашего yVector. Когда шарик падает (yVector уже отрицательный), это заставит его двигаться быстрее. Когда он идет вверх (yVector положителен), он замедляет его.

Это не учитывало бы трения, так что вещи должны отскакивать навсегда.

edit1: Чтобы учесть трение, всякий раз, когда оно переворачивается (и вы меняете знак), немного уменьшите абсолютное число. Например, если он достигнет значения yVector = -500, когда вы поменяете знак, сделайте его +480 вместо +500. Вероятно, вы должны сделать то же самое с xVector, чтобы он не подпрыгивал из стороны в сторону.

edit2: Кроме того, если вы хотите, чтобы он реагировал на «воздушное трение», уменьшайте оба вектора на очень небольшую величину при каждой настройке.

Edit3: О том, что вечно валяется на дне - в зависимости от того, насколько высоки ваши цифры, это может быть одна из двух вещей. Либо у вас большие числа, и кажется, что на завершение уйдет целая вечность, либо вы округлили, а ваши векторы всегда равны 5 или чему-то еще. (90% от 5 - это 4,5, поэтому может округляться до 5).

Я бы распечатал оператор отладки и посмотрел, на что похожи числа Vector. Если они идут где-то около 5 и просто остаются там, тогда вы можете использовать функцию, которая усекает вашу дробь до 4 вместо округления обратно до 5. Если она продолжает снижаться и в конечном итоге останавливается, то вам, возможно, придется повысить коэффициент трения. .

Если вы не можете найти простую функцию «округления», вы можете использовать (0,9 * Vector) - 1, вычитание 1 из существующего уравнения должно сделать то же самое.

13 голосов
/ 05 декабря 2008

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

Кстати, то, что вы делаете, называется методом Эйлера для численного интегрирования. Это выглядит так:

  • Начнем с кинематических уравнений движения:
    x (t) = x0 + vx * t + 0.5 * ax t ^ 2
    y (t) = y0 + vy
    t + 0,5 * ay t ^ 2
    vx (t) = vx0 + ax
    t
    vy (t) = vy0 + ay * t
    Где x и y - положение, vx и vy - скорость, ax и ay - ускорение, а t - время. x0, y0, vx0 и vy0 - начальные значения. Это описывает движение объекта в отсутствие какой-либо внешней силы.

  • Теперь примените гравитацию:
    ay = -9,8 м / с ^ 2
    На данный момент нет необходимости делать что-то хитрое. Мы можем определить положение каждого шарика, используя это уравнение в любое время.

  • Теперь добавьте воздушное трение: поскольку это сферический шар, мы можем предположить, что он имеет коэффициент трения c. Как правило, есть два варианта моделирования воздушного трения. Это может быть пропорционально скорости или квадрату скорости. Давайте использовать квадрат:
    ax = -c vx ^ 2
    ay = -c
    vy ^ 2 - 9,8
    Поскольку ускорение теперь зависит от скорости, которая не является постоянной, мы должны интегрировать. Это плохо, потому что нет способа решить это вручную. Придется интегрировать численно.

  • Мы принимаем дискретные временные шаги, DT. Для метода Эйлера мы просто заменяем все вхождения t в приведенных выше уравнениях на dt и используем значение из предыдущего временного шага вместо начальных значений x0, y0 и т. Д. Итак, теперь наши уравнения выглядят так (в псевдокоде) :

    // Сохранить предыдущие значения
    xold = x;
    yold = y;
    vxold = vx;
    vyold = vy;

    // Обновление ускорения
    ax = -c vxold ^ 2;
    ay = -c
    vyold ^ 2 - 9,8;

    // Обновление скорости
    vx = vxold + ax dt;
    vy = vyold + ay
    dt;

    // Обновляем позицию
    x = xold + vxold * dt + 0.5 * ax dt ^ 2;
    y = yold + vyold
    dt + 0.5 * ay * dt ^ 2;

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

1 голос
/ 05 декабря 2010

Это баллистическое движение. Таким образом, вы получили линейное движение по оси X и равномерное ускоренное движение по оси Y.

Основная идея заключается в том, что ось Y будет следовать уравнению:

y = y0 + v0 * t + (0.5)*a*t^2

Или, в коде C, например:

float speed = 10.0f, acceleration = -9.8f, y = [whatever position];

y += speed*t + 0.5f*acceleration*t^2;

Где здесь я использую параметризацию Tiem. Но вы могли бы использовать Торричелли:

v = sqrt(v0^2 + 2*acceleration*(y-y0));

И в этой модели вы должны поддерживать последние значения v и y.

Наконец, я проделал нечто подобное, используя первую модель, в которой dt (разница во времени) была установлена ​​на 1/60 секунды (60 FPS).

Хорошо, обе модели дают хорошие реальные результаты, но sqrt (), например, стоит дорого.

1 голос
/ 05 декабря 2008
public void step()
{
    posX += xVector;
    posY += yVector;

    yVector += g //some constant representing 9.8

    checkCollisions();
}

в checkCollisions (), вы должны инвертировать и умножить yVector на число от 0 до 1, когда оно подпрыгивает на земле. Это должно дать вам желаемый эффект

1 голос
/ 05 декабря 2008

Каждый раз, когда вы срезаете, вы должны применять эффекты гравитации, ускоряя шар в направлении вниз. Как предположил Билл К, это так же просто, как вычитание из вашего «yVector». Когда мяч достигает дна, yVector = -yVector, теперь он движется вверх, но все еще ускоряется вниз. Если вы хотите, чтобы шары в конечном итоге перестали подпрыгивать, вам нужно сделать столкновения слегка неэластичными, в основном, удалив некоторую скорость в направлении y-up, возможно, вместо "yVector = -yVector", сделайте его "yVector = -0.9 * yVector ".

0 голосов
/ 05 декабря 2008

Что нужно сделать, когда «земля», чтобы я мог позволить ему подпрыгнуть снова

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

Например, если мяч попадает в правую или левую стенку, найдите скалярный компонент x и оставьте скалярный компонент y таким же:

 this.xVector = -this.xVector;

Если шар касается верхней или нижней стенок, разверните скалярный компонент y и оставьте скалярный компонент x таким же:

 this.yVector = -this.yVector;

но несколько короче предыдущего время

В этом сценарии часть энергии будет потеряна при столкновении со стеной, поэтому просто добавьте коэффициент потерь, чтобы получить часть скорости при каждом ударе по стене:

 double loss_factor = 0.99;
 this.xVector = -(loss_factor * this.xVector);
 this.yVector = -(loss_factor * this.yVector;
0 голосов
/ 05 декабря 2008

Что вы хотите сделать, это изменить значения xVector и yVector для имитации гравитации и трения. Это действительно довольно просто сделать. (Необходимо изменить все переменные на числа с плавающей точкой. Когда придет время рисовать, просто округлите числа с плавающей точкой.)

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

yVector *= 0.95;
xVector *= 0.95;
yVector -= 2.0;

Это немного уменьшает скорость X и Y, позволяя вашим шарам в конечном итоге перестать двигаться, а затем применяет постоянное «ускорение» вниз к значению Y, которое будет накапливаться быстрее, чем «замедление» и заставит шары падать .

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

0 голосов
/ 05 декабря 2008

Я согласен с тем, что сказал "Билл К", и добавил бы, что, если вы хотите, чтобы они "обосновались", вам нужно будет уменьшать векторы x и y с течением времени (применять сопротивление). Это может быть очень небольшая сумма за раз, поэтому вам, возможно, придется изменить ваши векторы с типа int на тип с плавающей запятой или уменьшать их только на 1 каждые несколько секунд.

0 голосов
/ 05 декабря 2008

Вы действительно хотите смоделировать, что делает гравитация - все, что она делает, это создает силу, которая действует со временем, чтобы изменить скорость объекта. Каждый раз, когда вы делаете шаг, вы немного изменяете скорость вашего шара, чтобы «подтянуть» его к нижней части виджета.

Чтобы решить проблему, связанную с отсутствием трения / отскакивающего шара, необходимо, чтобы «наземное» столкновение оказывало иной эффект, чем просто строгое отражение - оно должно удалять некоторое количество энергии из шара, заставляя его отскакивать назад с меньшей скоростью после того, как он упадет на землю, чем это было бы в противном случае.

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

...