Моделирование гравитации и столкновений в C# - Использование кода

ОГЛАВЛЕНИЕ

Использование кода

Сначала надо определить переменные движения для каждого шара в модели.

///////////////////////////////////////// шар /////////////////////////////////////
// xspeed: скорость движения шара по оси X –                                        
//           вычисляется на основе скорости движения мыши.             //
// yspeed: скорость движения шара по оси Y –                                      //
//           вычисляется на основе скорости движения мыши.             //
// newyspeed: обновленная скорость движения шара по оси Y                         //
//              после применения уравнений Ньютона и столкновения.                //
// startingypos: исходная координата Y шара –                             //
//               при прекращении перетаскивания шара.                                     //
// newxpos: обновленная координата X шара                                    //
// newypos: обновленная координата Y шара                                    //
// oldxpos: предыдущая координата X шара                                   //
// oldypos: предыдущая координата Y шара                                   //
// newx: новая координата X мыши после перетаскивания                           //
// oldx: предыдущая координата X мыши после перетаскивания                          //
// newy: новая координата Y мыши после перетаскивания                           //
// oldy: предыдущая координата Y мыши после перетаскивания                           //   
// acc: ускорение = 10                                                     //
// t: время                                                                    //
// xmouse: ось X координат курсора мыши                               //   
// ymouse: ось Y координат курсора мыши                               //
// dragging: логическая переменная, чтобы проверять, перетаскивается шар или нет.  //
// trace: логическая переменная, чтобы проверять, включена или выключена опция слежения.            
// collisiony: логическая переменная, чтобы проверять, ударяется шар о землю или нет.      //
////////////////////////////////////////////////////////////////////////////////////

// переменные шара 1
double xspeed,yspeed,newyspeed,startingypos;
double newxpos,newypos,oldxpos,oldypos;
double newx,oldx,newy,oldy;
double acc,t;
const int ground = 500;
int xmouse,ymouse;
bool dragging=true,trace,collisiony;

int choice = 1;
int numberofballs = 1;

Ballinstance b1 = new Ballinstance();
Дальше движение шара отслеживается и проверяется на столкновение каждые 20 мс в таймере, и в соответствии с этим обновляются координаты шаров.
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    b1.play(ref xspeed,ref yspeed,
            ref newyspeed,ref startingypos,
            ref newxpos,ref newypos,ref oldxpos,ref oldypos,
            ref newx,ref oldx,ref newy,ref oldy,
            ref acc,ref t,
            ref xmouse,ref ymouse,
            ref dragging,ref trace,ref collisiony);
    ball.Left = (int)newxpos;
    ball.Top = (int)(ground - newypos);

    Collision();
}

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

•    Состояние перетаскивания:
Если шар, вызывающий функцию play, был в режиме перетаскивания, то координаты шара будет обновлены в соответствии с координатами курсора мыши, и начальная скорость шара будет вычислена путем измерения изменения координат шара между двумя последовательными вызовами функции play; в пределах 20 мс.

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

public class Ballinstance
{
    int xpos,ypos;
    const int ground = 500;

    public void play(ref double xspeed,
                     ref double yspeed,
                     ref double newyspeed,
                     ref double startingypos,
                     ref double newxpos,
                     ref double newypos,
                     ref double oldxpos,
                     ref double oldypos,
                     ref double newx,
                     ref double oldx,
                     ref double newy,
                     ref double oldy,
                     ref double acc,
                     ref double t,
                     ref int xmouse,
                     ref int ymouse,
                     ref bool dragging,
                     ref bool trace,
                     ref bool collisiony)
    {

        xpos = (int)newxpos;
        ypos = (int)newypos;
   
        // этот код будет вызываться 50 раз в секунду во время перетаскивания
        if (dragging)
        {
            // держать центр шара при перетаскивании
            xpos = xmouse;
            ypos = ymouse;

            // во время перетаскивания начальная координата шара на оси y – верх шара
            startingypos = ground - ypos;

            // вычисляется скорость движения по x и y на основе
            // движения мыши в пределах 20 миллисекунд
            // скорость = расстояние/время  ->  время = 20 миллисекунд
            // скорость – изменение в смещении
            // по отношению к времени, которое
            // уже длится (код находится в пределах
            // таймера), поэтому не надо делить
            // на время
            newx = xpos;
            newy = ground - ypos;
            xspeed = (newx-oldx)/1;
            yspeed = (newy-oldy)/1;
            oldx = newx;
            oldy = newy;

            // время – в ходе перетаскивания – еще не запущено
            t=0;

        }
        else
        {
            // этот код будет вызываться 50 раз в секунду при отсутствии перетаскивания
            // координаты шара находятся там, куда его в последний раз перетащили
            oldxpos = xpos;
            // движение по оси X
            if(xpos < 580 && 0 < xpos)
            {
                newxpos = oldxpos + xspeed;
            }
            else
            {
                // здесь шар ударится о стену
                // скорость движения шара по оси x падает всегда при ударе о стену
                // знак минус: чтобы изменить направление движения шара,
                // когда он сталкивается со стенами.
                // сопротивление стены, шар
                // теряет часть энергии при ударе о стену
                xspeed *= -0.9;       
                newxpos = oldxpos + xspeed;
            }
   
            // движение по оси Y
            if(0 < newypos || collisiony)
            {  
                // первое уравнение движения Ньютона
                newyspeed = yspeed - (acc*t);
                // третье уравнение движения Ньютона
                newypos = startingypos + ((yspeed*t)- 0.5*acc*(t*t));
                // столкновения не произошло
                collisiony = false;
            }
            else
            {
                // здесь шар ударится о землю
                // снова инициализируются переменные шара
                startingypos = -1;   
                // здесь устанавливается startingypos=-1, не 0, так как
                // если 0, newypos будет равна 0 всегда, когда шар
                // ударяется о землю, следовательно, шар не
                // будет подпрыгивать, смотрите на
                // уравнение newypos ниже, когда t = 0
                t = 0;
                // скорость yspeed шара будет падать всегда при ударе шара о землю
                // 0.75 - коэффициент упругости
                // начальная скорость (yspeed)
                // равняется 0.75 от конечной скорости (newyspeed)
                yspeed = newyspeed * -0.75;
                newypos = startingypos + ((yspeed*t)- 0.5*acc*(t*t));
                collisiony = true;
            }
            // всегда
            // скорость xspeed шара всегда будет падать, даже если он не ударился о стену
            xspeed *= 0.99;    // сопротивление воздуха

            #region explination of xspeed condition
            // Чтобы остановить шар, если он движется влево,
            // можно заметить, что исключение
            // этого условия заставит шар вообще не останавливаться
            // во время движения влево, пока он не
            // ударится о левую стену. Чтобы понять причину,
            // запустите моделирование в режиме отладки и следите за
            // значением newxpos
            // newxpos = oldxpos + xspeed
            // когда 0 < xspeed < 1 (шар движется вправо),
            // ball.left = (int)newxpos, приведение типа
            // делает значение левого положения шара
            // таким же, как и его предыдущее значение,
            // потому что oldxpos и newxpos равны,
            // и поэтому шар остановится.
            // но когда -1 < xspeed < 0 (шар движется влево),
            // ball.left = (int)newxpos, приведение типа
            // не работает правильно, поскольку
            // значение oldxpos(целое число)
            // всегда уменьшается на xspeed,
            // что заставляет newxpos всегда
            // уменьшаться на xspeed, а
            // значит ball.left всегда уменьшается
            // на 1 (int) при приведении типа, поэтому шар вообще не остановится.
            #endregion

            if(xspeed > -0.5 && xspeed < 0)
                xspeed = 0;
            // обновляются координаты шара
            xpos = (int)newxpos;
            ypos = (int)(ground - newypos);
            // увеличивается время
            t += 0.3;
        }
    }
}

Вывод

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