В статье я напишу рассказ о том, как сделать простенькую игрушку - пятнашки. Пользоваться я буду готовым материалом, найденном в Интернете (ссылка на скачивание). Скачать проект можно по ссылке.
Итак начнём-с...
Code
const H = 4; W = 4; // размер поля - 4х4 CH = 64; CW = 64; // размер клеток - 16х16
var // правильное расположение фишек stp : array[1..H, 1..W] of byte = (( 1, 2, 3, 4), ( 5, 6, 7, 8), ( 9,10,11,12), (13,14,15, 0));
Первым делом выполняется обработка события OnCreate формы. Автор игры задаёт размеры окна, стиль и размер текста, рисуемого на канве. Далее происходит вызов процедуры NewGame (на этом заканчивается обработка события).
Переходим непосредственно к проуедуре NewGame.
Code
procedure NewGame; var i,j: integer; begin // исходное (правильное) положение for i:=0 to H+1 do for j:=0 to W+1 do pole[i,j] := stp[i,j]; Form1.Mixer; // перемешать фишки Form1.ShowPole; // отобразить поле end;
Автор описывает два массива размером 4х4, в одном из которых составлено исходное (правильное) положение всех фишек. В начале процедуры происходит заполнение поля исходным положением фишек. Почему автор заполняет поле за пределами описанного диапазона мне не понятно :). В описанные массивы даный диапазон не влезает и в принципе он не нужен. Поэтому можно оставить массивы от 1 до h/w. Далее следует вызов двух процедур "Миксер" и "Показать поле".
Процедура Миксе/Mixer
Code
procedure TForm1.Mixer; var x1,y1: integer; // пустая клетка x2,y2: integer; // эту переместить в пустую d: integer; // направление, относительно пустой i: integer; begin x1:=4; y1:=4; randomize; for i:= 1 to 150 do begin repeat x2:=x1; y2:=y1; d:=random(4)+1; case d of 1: dec(x2); 2: inc(x2); 3: dec(y2); 4: inc(y2); end; until (x2>=1) and (x2<=4) and (y2>=1) and (y2<=4); // здесь определили фишку, которую // надо переместить в пустую клетку Pole[y1,x1] := Pole[y2,x2]; Pole[y2,x2] := 0; x1:=x2; y1:=y2; end; // запомним координаты пустой клетки ex:= x1; ey:= y1; end;
Определяем пустую клетку, как нижняя правая. Подключаем процедуру Randomize. И начинаем передвигать наши фишки 150 раз. Количество сдвигов вы можете определить самостоятельно. "Рандомно" (случайно) выбираем фишку для сдвига, и если она входит в диапазон нашего поля, то мы её меняем с пустой клеткой. Пустой клетке присваиваем новые координаты. И в конце концов запоминаем координаты пустой клетки.
Процедура ShowPole/Показать поле
Code
procedure TForm1.ShowPole; var i,j: integer; x,y: integer; // x,y - координаты вывода // текста в клетке begin // сетка: вертикальные линии for i:= 1 to W - 1 do begin Canvas.MoveTo(i*CW,0); Canvas.LineTo(i*CW,ClientHeight); end; // сетка: горизонтальные линии for i:= 1 to H - 1 do begin Canvas.MoveTo(0,i*CH); Canvas.LineTo(ClientWidth,i*CH); end;
// содержимое клеток // x,y - координаты вывода текста for i:= 1 to H do begin y:=(i-1)*CH + 15; for j:=1 to W do begin x:= (j-1)*CW + 15; case Pole[i,j] of 0: Canvas.TextOut(x,y,' '); 1..9: Canvas.TextOut(x,y,' '+IntToStr(Pole[i,j])+' '); 10..15: Canvas.TextOut(x,y,IntToStr(Pole[i,j])); end; end; end; end;
Полурусское, полуанглийское название говорит само за себя =). На канве рисуем сетку поля игры. Далее определяя координаты вывода, выводим перемешанные фишки.
На этом все приготовления для игры заканчиваются. Начинается игра =). Пользователь делает щелчок по форме и происходит обработка события MouseDown.
Code
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var cx,cy: integer; // координаты клетки begin // преобразуем координаты мыши в координаты клетки cx := Trunc(X / CW) + 1; cy := Trunc(Y / CH) + 1; Move(cx,cy); end;
В нём определяем координаты нажатой фишки. Если щелчок произошел по сетке, то выбирается правая или нижняя фишка от сетки. Вызываем процедуру Move для сдвига.
Code
procedure Move(cx,cy: integer); // cx,cy - клетка, в которой игрок сделал щелчок var r: integer; // выбор игрока begin // проверим, возможен ли обмен if not (( abs(cx-ex) = 1) and (cy-ey = 0) or ( abs(cy-ey) = 1) and (cx-ex = 0)) then exit; // Обмен. Переместим фишку из x,y в ex,ey Pole[ey,ex] := Pole[cy,cx]; Pole[cy,cx] := 0; ex:=cx; ey:=cy; // отрисовать поле Form1.ShowPole; if Finish then begin r := MessageDlg('Цель достигнута!'+ #13+ 'Еще раз?',mtInformation,[mbYes,mbNo],0); if r = mrNo then Form1.Close; // завершить работу программы end; end;
Происходит простенькая проверка обмена. Если обмен невозможен, то выходим из процедуры. Пользователю при этом можно вывести сообщение, что ход невозможен, но автор решил этого не делать =). Двигаем фишку в массиве. Запоминаем пустую клетку. И вызываем процедуру ShowPole. Но вместо того, чтобы выводить всю сетку и всё поле заново, можно выводить только две фишки!!! Далее проверяем расположены ли фишки в правильном порядке, вызывая Функцию Finish. Если игра закончена, то спрашиваем у пользователя хочет ли он играть ещё раз. Здесь придётся добавить (в случае согласия) вызов процедуры NewGame.
Функция Finish/Конец?
Code
function Finish: boolean; var row,col: integer; i: integer; begin row :=1; col :=1; Finish := True; // пусть фишки в нужном порядке for i:=1 to 15 do begin if pole[row,col] <> i then begin Finish:= False; break; end; // к следующей клетке if col < 4 then inc(col) else begin col :=1; inc(row); end; end; end;
В процедурке пробегаем по полю и смотрим, в правильном порядке расположены фишки или нет. Самостоятельно пробегаем по полю, то есть сами меняем положение поля. Хотя можно сделать полегче и побыстрее!
На этом моя статья заканчивается Жду Ваших вопросов и комментариев.