Как применить гравитацию к моему приложению с прыгающим мячом?

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

вот скриншот:
alt текст http://img222.imageshack.us/img222/3179/ballbouncemf9.png

каждый из кругов на экране является объектом шара. Движение шаров разбивается на 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
    }
}

это прекрасно работает. Все шары прыгают от стены к стене.

в идеале, I хотелось бы добавить в эту программу некоторый гравитационный эффект, а также позволить шарам отскакивать несколько раз, прежде чем осесть на "землю"."

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

любая помощь ценится в указании мне в правильное направление.


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

в моем шаге () я добавляю гравитационную постоянную к моему 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 ответов


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

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

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

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

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

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

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


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

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

  • начните с кинематических уравнений движения:
    x (t) = x0 + vx*t + 0.5*axt^2
    y (t) = y0 + vy
    t + 0.5 * ayt^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 m / S^2
    До этого момента не нужно делать ничего сложного. Мы можем решить для положения каждого шара, используя это уравнение для любого времени.

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

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

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

    // Ускорение обновления
    ax = - cvxold^2;
    ay = - c
    vyold^2 - 9.8;

    // Скорость обновления
    vx = vxold + axdt;
    vy = vyold + ay
    dt;

    // Обновить позицию
    x = xold + vxold*dt + 0.5 * axdt^2;
    y = yold + vyold
    dt + 0.5 * ay * DT^2;

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


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


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

    yVector += g //some constant representing 9.8

    checkCollisions();
}

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


это баллистическое движение. Таким образом, вы получили линейное движение по оси 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 кадров в секунду).

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


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

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

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


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


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

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

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

это масштабирует скорость X и Y немного вниз, позволяя вашим шарам в конечном итоге перестать двигаться, а затем применяет постоянное нисходящее " ускорение "до значения Y, которое будет накапливаться быстрее, чем" замедление", и заставит шары падать.

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


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

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

например, если мяч попадает в правую или левую стенки, восстановите скалярный компонент 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;