Подтвердить что ты не робот

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

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

Вот скриншот:
alt text 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 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 становится определенным абсолютным значением, я должен просто изменить его на ноль?

4b9b3361

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

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

  • Начнем с кинематических уравнений движения:
    x (t) = x0 + vx * t + 0.5 * axt ^ 2
    y (t) = y0 + vyt + 0,5 * ayt ^ 2
    vx (t) = vx0 + axt
    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 = -cvy ^ 2 - 9.8
    Поскольку ускорение теперь зависит от скорости, которая не является постоянной, мы должны интегрировать. Это плохо, потому что нет возможности решить это вручную. Нам придется интегрировать численно.

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

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

    // Обновление ускорения
    ax = -cvxold ^ 2,
    ay = -cvyold ^ 2 - 9.8;

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

    // Обновить позицию
    x = xold + vxold * dt + 0,5 * axdt ^ 2,
    y = yold + vyolddt + 0,5 * ay * dt ^ 2;

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

Ответ 3

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

Ответ 4

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

    yVector += g //some constant representing 9.8

    checkCollisions();
}

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

Ответ 5

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

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

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

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

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

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

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

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

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

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

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

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

Ответ 9

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

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

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