Пишем игрушку

Пишем игру "Змейка". Часть 4

Выпуск № 4

В этом выпуске:
  1. Добавляем квадратики
  2. Алгоритм проверки

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

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

TDot     = object (TObject)
   fNotDot      : boolean;  {Нет ли на поле квадратиков?}
   fDotCol      : byte;        {Его цвет} 
   fMaxDot      : byte;{Максимальное количество на одном уровне} 
   fDotX, fDotY : byte; {Координаты квадратика}
   constructor  Init(m:byte);
   procedure    DrawDot; {рисуем квадратик}  
   destructor   Done;virtual;
end;  

Количество собранных квадратиков на одном уровне записывается в поле fCurDot. Очевидно, что если координаты головы совпали с координатами квадратика, то мы увеличиваем значение fCurDot на 1, далее смотрим: переходить на новый уровень, или установить новый квадратик. Установить новый квадратик легко. Нужно лишь случайным образом назначить новые координаты квадратика, но то и смущает, что "случайным образом". А вдруг получится, что новый квадратик окажется на теле (если так можно выразиться) змейки. Представляете, что произойдет? И я не представляю. Но очевидно, что этого нельзя допустить. Т.е. после того, как случайно назначили новые координаты, необходимо их проверить. Сделать это несложно.

Все это, включая назначение новых координат и проверку, "лежит на совести" метода AddDot. Его суть заключается в том, что назначенные координаты проверяются с координатами каждой клеткой змейки, начиная с хвоста. Здесь применен следующий алгоритм проверки (счетчик длины и счетчик коллекции - это локальные переменные, не путать их с полями метода TSoliter):

Алгоритм 1.

       1.   Назначаем   временные   координаты,  которые  в  
            начале соответствуют координатам хвоста;
       2.   Устанавливаем счетчик длины в единицу, а счетчик
            коллекции в ноль;   
       3.   Если счетчик длины меньше текущей длины змейки, 
            и назначенные координаты квадратика не совпадают
            с временными координатами, то перейти к пункту 4,
            иначе - 7;
       4.   Если счетчик коллекции не равен количеству углов 
            минус один, и временные координаты совпадают с 
            координатами угла, стоящего в коллекции на месте 
            (отсчет - с конца) равному счетчику коллекции,то
            увеличить счетчик коллекции;
       5.   Меняем    временные   координаты,  так чтобы они 
            соответствовали   координатам  следующей  клетки 
            змейки. Для этого используем входное направление 
            угла, стоящего в коллекции на  месте (отсчет - с
            конца) равном счетчику коллекции;
       6.   Перейти к пункту 2;
       7.   Конец. 

Вот текст функции Ok, являющейся локальной функцией метода AddDot, в которой и реализован вышеприведенный алгоритм:

function    Ok:boolean;
    var
      bufOk        : boolean;
      i : byte; {счетчик длины}
	cor : byte; {счетчик коллекции}	
      x, y : byte; {временные координаты}
begin
 x:=fXZ; y:=fYZ; {назначаем временным коор-
                 динатам координаты  хвоста}
 bufOk:=(X<>fDot.fDotX) or (Y<>fDot.fDotY);
 i:=1; 
 cor:=0;

{Продолжаем,     если     счетчик     длины     меньше    1,
и         временные        координаты        не        равны
назначенным координатам квадратика}
 While (i < fLen) and (bufOk) do
  begin

     with fCorCol do

     {пункт 4, алгоритма 1 }
     If (Count-1<>cor)and(PCorner(At(Count-cor-1))^.fX=x)
                  and(PCorner(At(Count-cor-1))^.fY=y)then
                               begin
                                    Inc(cor);
                               end;

                           {пункт 5 алгоритма 1}
    Case PCorner(fCorCol.At(fCorCol.Count-cor-1))^.fInDir of
                kbUp   : Dec(Y);
                kbDown : Inc(Y);
                kbLeft : Dec(X);
                kbRight: Inc(X);
   end;

  {увеличиваем счетчик длины}
  inc(i);
  {Временные координаты равны координатам квадратика?}
  bufOk:=(Y<>fDot.fDotY) or (X<>fDot.fDotX);
 end;
 Ok:=bufOk;
end;     

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

Некоторые из вас могут заметить, что данный алгоритм можно было бы применить и в методе Check. Можно, но не нужно, так как он более медленный. В тоже время, алгоритм метода Check, нельзя применить к функции Ok, так как в первом случае мы используем направление движения головы, чего во втором случае быть не может.

Поля fScore, fLife, fLevel отвечают за текущий счет, количество жизней, и уровень игры соответственно. В их задачу входит и отображение всей этой информации на экране. Как они это делают это змейку не касается. Разобраться в механизмах их работы, думаю не составит вам труда.

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

На этом все. Будьте здоровы.

[в начало] [Исходник игры]

© Олег Чарышкин

 

Hosted by uCoz