Динамическое создание объектов на форме
Автор Олег Ляш   
23.05.2011 г.
Сегодня мы попробуем написать программу, которая в очень приближенной форме моделирует поведение молекул газа в закрытом сосуде. Для простоты реализации будем считать что молекулы летят с постоянной скоростью и отталкиваются только от стенок сосуда (т.е. в рамках нашей задачи мы пренебрегаем соударениями между молекулами и считаем что они движутся только в 2-х измерениях).
Для реализации собственно молекул было решено взять стандартный компонент TShape в виде круга. По умолчанию объекты данного типа не имеют свойств для хранения величин скорости смешения по оси OX и OY. Для решения этой проблемы построим свой собственный класс (TMyShape) на основании TShape с добавлением свойств SX и SY, которые будут использоваться для хранения величин смещения по осям OX и OY соответственно.
Сегодня мы попробуем написать программу, которая в очень приближенной форме моделирует поведение молекул газа в закрытом сосуде. Для простоты реализации будем считать что молекулы летят с постоянной скоростью и отталкиваются только от стенок сосуда (т.е. в рамках нашей задачи мы пренебрегаем соударениями между молекулами и считаем что они движутся только в 2-х измерениях).
Для реализации собственно молекул было решено взять стандартный компонент TShape в виде круга. По умолчанию объекты данного типа не имеют свойств для хранения величин скорости смешения по оси OX и OY. Для решения этой проблемы построим свой собственный класс (TMyShape) на основании TShape с добавлением свойств SX и SY, которые будут использоваться для хранения величин смещения по осям OX и OY соответственно.
  1. type  TMyShape=class(TShape)
  2. public
  3. sx,sy : integer; //Добавляем новые свойства объекту (скорости по оси X И Y)
  4. end;
В нашей задачи молекулы сталкиваются только со стенками сосуда, который является прямоугольником. Т.о для описания поведения молекул достаточно будет построить условные конструкции, описывающие столкновение с каждой из стенок сосуда. В качестве примера опишем столкновение с верхней границей сосуда: Если верхняя граница молекулы >= верхней границы сосуда, то изменить направление движения. Но этого недостаточно, если молекула будет двигаться со скоростью отличной от 1, то может возникнуть такая ситуация, когда молекула как бы выехала за верхнюю границу сосуда. Чтобы решить эту проблему достаточно добавить в условную конструкцию не только изменения направления движения, но и принудительную установку объекта около верхней границы. Столкновение с остальными границами описывается аналогичным образом.

Кроме этого в нашей программе будет происходить динамическое создание объектов, для этого используется метод Create у созданного нами класса TMyShape. Создание объектов будем проводить в обработчике событий FormCreate.

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

 

Исходных код программы

 
  1. unit Unit1; 
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls;
  5.  
  6. type
  7. { TForm1 }
  8. TForm1 = class(TForm)
  9. Timer1: TTimer;
  10. procedure FormCreate(Sender: TObject);
  11. procedure Timer1Timer(Sender: TObject);
  12. private
  13. { private declarations }
  14. public
  15. { public declarations }
  16. end;
  17. //Создаем новый объект на основе стандартного шейпа
  18. type TMyShape=class(TShape)
  19. public
  20. sx,sy : integer; //Добавляем новые свойства объекту (скорости по оси X И Y)
  21. end;
  22.  
  23. const Count=20;//Количество объектов, которые будем создавать
  24.  
  25. var Form1: TForm1;
  26.  
  27. implementation
  28. { TForm1 }
  29. procedure TForm1.FormCreate(Sender: TObject);
  30. var i : integer;//Переменная для цикла
  31. buf :TMyShape;//Переменная для создания объекта
  32. begin
  33. randomize;
  34. for i:=0 to Count do begin
  35. buf:=TMyShape.Create(form1);//Создаем объект
  36. buf.Width:=20;//Задаем ширину
  37. buf.Height:=20;//Задаем высоту
  38. buf.Left:=Random(form1.Width);//Задаем положение по оси X
  39. buf.Top:=Random(form1.Height);//Задаем положение по оси Y
  40. buf.Brush.Color:=RGBToColor(random(255),random(255),random(255));//Задаем случайный цвет
  41. buf.SX:=random(3)+5;//Задаем случайную скорость по оси X
  42. buf.SY:=random(3)+5;//Задаем случайную скорость по оси Y
  43. buf.Shape:=stCircle;//Задаем внешниц вид объектов
  44. buf.Parent:=form1;//Указываем родительский объект
  45. end;
  46. end;
  47.  
  48. procedure TForm1.Timer1Timer(Sender: TObject);
  49. var i : integer;
  50. begin
  51. //Цикл по всем компонентам на форме
  52. for i:=0 to Form1.ComponentCount-1 do begin
  53. //Если компонент I является компонентом с типом TMyShape
  54. if (Components[i] is TMyShape) then begin //Тогда сдвинуть компонент по X и Y
  55. (Components[i] as TMyShape).left:=(Components[i] as TMyShape).left+(Components[i] as TMyShape).SX;
  56. (Components[i] as TMyShape).Top:=(Components[i] as TMyShape).Top+(Components[i] as TMyShape).SY;
  57. //Столкновение с правой границей формы
  58. if ((Components[i] as TMyShape).left>=form1.ClientWidth-(Components[i] as TMyShape).width) then begin
  59. (Components[i] as TMyShape).left:=form1.ClientWidth-(Components[i] as TMyShape).width;
  60. (Components[i] as TMyShape).SX:=-(Components[i] as TMyShape).SX;
  61. end;
  62. //Столкновение с левой границей формы
  63. if ((Components[i] as TMyShape).left<=0) then begin
  64. (Components[i] as TMyShape).left:=0;
  65. (Components[i] as TMyShape).SX:=-(Components[i] as TMyShape).SX;
  66. end;
  67. //Столкновение с нижней границей формы
  68. if ((Components[i] as TMyShape).top>=form1.ClientHeight-(Components[i] as TMyShape).height) then begin
  69. (Components[i] as TMyShape).top:=form1.ClientHeight-(Components[i] as TMyShape).height;
  70. (Components[i] as TMyShape).SY:=-(Components[i] as TMyShape).SY;
  71. end;
  72. //Столкновение с верхней границей формы
  73. if ((Components[i] as TMyShape).top<=0) then begin
  74. (Components[i] as TMyShape).top:=0;
  75. (Components[i] as TMyShape).SY:=-(Components[i] as TMyShape).SY;
  76. end;
  77. end;
  78. end;
  79. end;
  80. initialization
  81. {$I unit1.lrs}
  82. end.