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

StackOverflow https://stackoverflow.com/questions/342189

Вопрос

Я написал довольно простое 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,8 м/с) из вашего yVector.Когда мяч опускается (yVector уже отрицателен), это заставит его двигаться быстрее.Когда он растет (yVector положителен), это замедляет его.

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

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

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

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

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

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

Другие советы

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

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

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

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

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

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

    // Сохраняем предыдущие значения
    холд = х;
    йолд = у;
    vxold = vx;
    вёльд = вы;

    // Обновление ускорения
    топор = -cvxold^2;
    ай = -c
    выолд^2 - 9,8;

    // Обновляем скорость
    vx = vxold + топордт;
    вы = вйолд + ай
    дт;

    // Обновляем позицию
    x = xold + vxold*dt + 0,5*axдт^2;
    y = йолд + йолд
    дт + 0,5*ау*дт^2;

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

Каждый раз, когда вы используете срезы, вы должны применять эффекты гравитации, ускоряя шар в направлении вниз. Как предположил Билл К, это так же просто, как вычитание из вашего & Quot; yVector & Quot ;. Когда мяч достигает дна, yVector = -yVector, теперь он движется вверх, но все еще ускоряется вниз. Если вы хотите, чтобы шары в конечном итоге перестали подпрыгивать, вам нужно сделать столкновения слегка неэластичными, в основном, удалив некоторую скорость в направлении y-up, возможно, вместо & Quot; yVector = -yVector & Quot; , сделайте это " 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;

Здесь я использую параметризацию времени.Но вы можете использовать Торричелли:

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

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

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

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

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

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

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

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

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

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

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

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

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

  

Что нужно сделать, когда   & Quot; & Земля Quot; так что я могу позволить это   снова подпрыгнуть

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

Например, если мяч попадает в правую или левую стенку, найдите скалярный компонент 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;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top