Простая система частиц в виде размножения клеток

Опубликовал – 02.12.2010

Дело было вечером, делать было нечего. Решил вот для разминки заняться делом, написать некое подобие системы частиц, которые бы размножались методом деления клетки.

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

Итак, для начала определим константы:

const
SCREEN_WIDTH = 800; //размеры формы
SCREEN_HEIGHT = 700;

OBJECT_SIZE_X = 16; //размеры клетки
OBJECT_SIZE_Y = 16;
OBJECT_COLOR = $0; //ее цвет
OBJECT_RAISE = 2; //скорость деления
OBJECT_BORN = 30; //период времени рождения клетки
OBJECT_SPEED = 1; //скорость

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

type
  TGameObject = class
  private
    pos, dist, oldpos, size: TPoint; //координаты, направление, размеры
    vecborn: Integer; //в какую сторону будем размножаться 0 в стороны, 1 вверх\низ
    canborn: Integer; //сколько раз может размножаться
    timeborn: Integer; //пришло ли время
    color: TColor; //цвет
    speed: Integer; //скорость
    procedure Born(); //тут мы размножаемся
  public
    constructor Create(pos: TPoint; dist: TPoint); //создаем
    procedure Draw(canvas: TCanvas); //рисуемся
    procedure MakeStep(); //обработка AI
  end;

Опишем каждую функцию класса TGameObject:

constructor TGameObject.Create(pos: TPoint; dist: TPoint);
begin
  //конструктор, постараемся внести как можно больше хаотичности в процесс
  size := Point(OBJECT_SIZE_X, OBJECT_SIZE_Y);
  self.dist := dist;
  self.pos := pos;
  oldpos := dist;
  vecborn := Round(Random*1);
  canborn := 1; //тут можно сделать так, например: Round(Random*2)+1;
  timeborn := Round(Random*OBJECT_BORN)+10;
  color := OBJECT_COLOR;
  speed := OBJECT_SPEED;
end;

procedure TGameObject.Draw(canvas: TCanvas);
begin
  //все просто, определяем цвет, рисуем эллипс
  canvas.Brush.Color := color;
  canvas.Ellipse(pos.X, pos.Y, pos.X + size.X, pos.Y + size.Y);
end;

procedure TGameObject.MakeStep();
begin
  if (Int64(dist) <> Int64(pos)) then begin
    //если заданный курс не достигнут двигаемся..
    if (dist.x <> pos.x) then begin
      if (pos.x > dist.x) then Dec(pos.x,speed) else Inc(pos.x,speed);
    end;
    if (dist.y <> pos.y) then begin
      if (pos.y > dist.y) then Dec(pos.y,speed) else Inc(pos.y,speed);
    end;
  end else begin
    //если достигли, размножаемся либо выбираем новый курс
    if (canborn > 0) then begin
      if (vecborn = 0) then begin
        Inc(size.x, OBJECT_RAISE);
        dec(pos.x, OBJECT_RAISE shr 1);
        Dec(dist.x, OBJECT_RAISE shr 1);
        if (size.x > OBJECT_SIZE_X shl 1) then Born();
      end else begin
        Inc(size.y, OBJECT_RAISE);
        dec(pos.y, OBJECT_RAISE shr 1);
        Dec(dist.y, OBJECT_RAISE shr 1);
        if (size.y > OBJECT_SIZE_Y shl 1) then Born();
      end;
    end else dist := Point(pos.x + round(-OBJECT_SIZE_X + random * (OBJECT_SIZE_X shl 1)), pos.y + round(-OBJECT_SIZE_Y + random * (OBJECT_SIZE_Y shl 1)));

    //проверяем время деления клетки
    if (timeborn > 0) then Dec(timeborn) else begin
      Inc(canborn);
      timeborn := Round(Random*OBJECT_BORN)+10;
    end;
  end;

end;

procedure TGameObject.Born();
begin
  //процедура рождения новой клетки
  size := Point(OBJECT_SIZE_X, OBJECT_SIZE_Y);
  dist := oldpos;
  Dec(canborn); //уменьшаем возможность родить 

  //создаем новый экземпляр клетки и добавляем в общий список
  if (vecborn = 0) then begin
    objlst.AddObj(TGameObject.Create(Point(pos.x + size.x + 1, pos.y),
      Point(
        pos.x + round(-OBJECT_SIZE_X+random*(OBJECT_SIZE_X * 3)),
        pos.y + round(-OBJECT_SIZE_Y+random*(OBJECT_SIZE_Y * 3))
      )
    ));
  end else begin
    objlst.AddObj(TGameObject.Create(Point(pos.x, pos.y + size.y + 1),
      Point(
        pos.x + round(-OBJECT_SIZE_X+random*(OBJECT_SIZE_X * 3)),
        pos.y + round(-OBJECT_SIZE_Y+random*(OBJECT_SIZE_Y * 3))
      )
    ));
  end;
  vecborn := Round(Random*1);
end;

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

  TGameList = class(TList) //будем использовать ф-ционал компонента TList
  public
    //общий буфер на нем будем рисовать все клетки
    //это конечно не очень правильно с точки зрения архитектуры, но я не стал
    //выносить буфер в отдельный класс отрисовки
    buffer: TBitmap;
    constructor Create(); //создаем список
    procedure AddObj(gobj: TGameObject); //добавляем объект
    procedure Draw(); //рисуем все объекты в наш буфер
    procedure MakeStep(); //обработка действий всех клеток
    destructor Destroy(); override; //уничтожаем все клетки
  end;

Этот класс гораздо меньше предыдущего. Давайте его посмотрим:

constructor TGameList.Create();
begin
  inherited Create(); //запускаем конструктор TList

  //создаем буфер и задаем размеры
  buffer := TBitmap.Create();
  buffer.Width := frmMain.ClientWidth;
  buffer.Height := frmMain.ClientHeight;
end;

procedure TGameList.AddObj(gobj: TGameObject);
begin
  Self.Add(gobj); //добавляем указатель на объект в список
end;

procedure TGameList.Draw();
var i:Integer;
begin
  //заливаем фон белым цветом
  PatBlt(buffer.Canvas.Handle, 0, 0, frmMain.ClientWidth, frmMain.ClientHeight, WHITENESS);
  //рисуем наши клетки
  for i := 0 to pred(Self.Count) do
    TGameObject(Self.Get(i)).Draw(buffer.Canvas);
end;

procedure TGameList.MakeStep();
var i:Integer;
begin
  //проходим по всем клеткам и обрабатываем действия
  for i := 0 to pred(Self.Count) do
    TGameObject(Self.Get(i)).MakeStep();
end;

destructor TGameList.Destroy();
var i:Integer;
begin
  //удаляем клетки
  for i := 0 to pred(Self.Count) do
    TGameObject(Self.Get(i)).Free();
  //удаляем буфер
  buffer.Free();
  inherited ;
end;

Теперь нам осталось только оживить все это дело, другими словами связать с обработчиками формы некоторые действия. Будем создавать первые клетки по нажатию мышки на форму. На форму поместим TTimer, он будет в нем будет основной жизненный цикл нашей программы. Он, хотя не очень точный, но для данного примера подойдет. Поставим ему Interval: 50 и выключим пока его Enabled: false. Теперь опишем обработчики формы и таймера:

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  //задаем размеры форме
  frmMain.ClientWidth := SCREEN_WIDTH;
  frmMain.ClientHeight := SCREEN_HEIGHT;
  frmMain.Show();
  //обновляем счетчик рандома
  Randomize();
  //создаем список
  objlst := TGameList.Create();
  //и теперь включаем таймер
  tmrMain.Enabled := True;
end;

procedure TfrmMain.FormDestroy(Sender: TObject);
begin
  //выключаем таймер и удаляем список
  tmrMain.Enabled := False;
  objlst.Free();
end;

procedure TfrmMain.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  //создаем объект при клике мышки
  objlst.AddObj(TGameObject.Create(Point(x, y), Point(x, y)));
end;

И наконец обработчик таймера:

procedure TfrmMain.tmrMainTimer(Sender: TObject);
begin
  objlst.MakeStep(); //обрабатываем все объекты
  objlst.Draw(); //рисуем их в наш буфер
  Canvas.Draw(0, 0, objlst.buffer); //отображаем буфер
  Caption := inttostr(objlst.Count); //показываем число объектов на экране
end;

Все, дорогие товарищи, успехов Вам. Тут можно скачать исходные коды к данной статье.

Рассказать друзьям:
  • Добавить ВКонтакте заметку об этой странице
  • Мой Мир
  • Facebook
  • Twitter
  • Яндекс.Закладки
  • В Живую Ленту Google
  • Сто закладок
Комментарии (5) - Простая система частиц в виде размножения клеток

Ответ

  1. Иван:

    Занимательная и понятно разобранная статья, позволяющая совершить сеанс самогипноза после успешного освоения материала. :-D Спасибо, Александр!

    Thumb up Thumb down +2

  2. Саша:

    Александр, круто! Большое спасибо. Жду еще:)

    Thumb up Thumb down 0

  3. TimKruz:

    Интересная идея, интесная статья!

    Thumb up Thumb down +1

Ответить

Comments

Перед отправкой формы: