пятница, 17 сентября 2010 г.

Динамическое создание объектов на форме

Сегодня мы попробуем написать программу, которая в очень приближенной форме моделирует поведение молекул газа в закрытом сосуде. Для простоты реализации будем считать что молекулы летят с постоянной скоростью и отталкиваются только от стенок сосуда (т.е. в рамках нашей задачи мы пренебрегаем соударениями между молекулами и считаем что они движутся только в 2-х измерениях).
Для реализации собственно молекул было решено взять стандартный компонент TShape в виде круга. По умолчанию объекты данного типа не имеют свойств для хранения величин скорости смешения по оси OX и OY. Для решения этой проблемы построим свой собственный класс (TMyShape) на основании TShape с добавлением свойств SX и SY, которые будут использоваться для хранения величин смещения по осям OX и OY соответственно.

type TMyShape=class(TShape)
  public
    //Добавляем новые свойства объекту (скорости по оси X И Y)
    sx,sy : integer;
end;
В нашей задачи молекулы сталкиваются только со стенками сосуда, который является прямоугольником. Т.о для описания поведения молекул достаточно будет построить условные конструкции, описывающие столкновение с каждой из стенок сосуда. В качестве примера опишем столкновение с верхней границей сосуда: Если верхняя граница молекулы >= верхней границы сосуда, то изменить направление движения. Но этого недостаточно, если молекула будет двигаться со скоростью отличной от 1, то может возникнуть такая ситуация, когда молекула как бы выехала за верхнюю границу сосуда. Чтобы решить эту проблему достаточно добавить в условную конструкцию не только изменения направления движения, но и принудительную установку объекта около верхней границы. Столкновение с остальными границами описывается аналогичным образом.
Кроме этого в нашей программе будет происходить динамическое создание объектов, для этого используется метод Create у созданного нами класса TMyShape. Создание объектов будем проводить в обработчике событий FormCreate.

procedure TForm1.FormCreate(Sender: TObject);
  var i : integer;//Переменная для цикла
        //Переменная для создания объекта
        buf :TMyShape;
begin
randomize;
for i:=0 to Count do begin
  //Создаем объект
  buf:=TMyShape.Create(form1);
  //Задаем ширину
  buf.Width:=20;
  //Задаем высоту
  buf.Height:=20;
  //Задаем положение по оси X
  buf.Left:=Random(form1.Width);
  //Задаем положение по оси Y
  buf.Top:=Random(form1.Height);
  //Задаем случайный цвет
  buf.Brush.Color:=RGBToColor(random(255),random(255),random(255));
  //Задаем случайную скорость по оси X
  buf.SX:=random(3)+5;
  //Задаем случайную скорость по оси Y
  buf.SY:=random(3)+5;
  //Задаем внешниц вид объектов
  buf.Shape:=stCircle;
  //Указываем родительский объект
  buf.Parent:=form1;
end;
end;
Обработка движения объектов и анализ столкновения будет происходить в обработчике событий Timer. Перебор всех компонент типа TMyShape будем производить с помощью циклической конструкции по всем компонентам на форме. Но так как на форме могут присутствовать и другие компоненты, то нам следует проверить является ли очередной компонент нужного типа. Это можно сделать с помощью конструкции (Components[i] is TMyShape), которая возвращает истинное значение, если компонент является указанного типа.
procedure TForm1.Timer1Timer(Sender: TObject);
  var i : integer;
begin
//Цикл по всем компонентам на форме
for i:=0 to Form1.ComponentCount-1 do begin
  //Если компонент I является компонентом с типом TMyShape
  if (Components[i] is TMyShape) then begin
     //Тогда сдвинуть компонент по X и Y
    (Components[i] as TMyShape).left:=(Components[i] as TMyShape).left+(Components[i] as TMyShape).SX;
    (Components[i] as TMyShape).Top:=(Components[i] as TMyShape).Top+(Components[i] as TMyShape).SY;
    //Столкновение с правой границей формы
     if ((Components[i] as TMyShape).left>=form1.ClientWidth-(Components[i] as TMyShape).width) then begin
       (Components[i] as TMyShape).left:=form1.ClientWidth-(Components[i] as TMyShape).width;
       (Components[i] as TMyShape).SX:=-(Components[i] as TMyShape).SX;
     end;
     //Столкновение с левой границей формы
     if ((Components[i] as TMyShape).left<=0) then begin
       (Components[i] as TMyShape).left:=0;
       (Components[i] as TMyShape).SX:=-(Components[i] as TMyShape).SX;
     end;
     //Столкновение с нижней границей формы
     if ((Components[i] as TMyShape).top>=form1.ClientHeight-(Components[i] as TMyShape).height) then begin
       (Components[i] as TMyShape).top:=form1.ClientHeight-(Components[i] as TMyShape).height;
       (Components[i] as TMyShape).SY:=-(Components[i] as TMyShape).SY;
     end;
     //Столкновение с верхней границей формы
     if ((Components[i] as TMyShape).top<=0) then begin
       (Components[i] as TMyShape).top:=0;
       (Components[i] as TMyShape).SY:=-(Components[i] as TMyShape).SY;
      end;
  end;
end;
end;
Собственно на этом все. В качестве самостоятельного задания, можно попробовать реализовать учет столкновений между молекулами. Для этого достаточно знать расстояние между центрами молекул и если оно меньше их двух радиусов, то считать молекулы столкнувшимися и поступить с ними так же как и при столкновении со стенками сосуда.

Комментариев нет: