Функции для создания геометрических объектов

procedure Ellipse(X1, Y1, X2, Y2: Integer);

Метод Ellipse рисует окружность или эллипс с помощью текущих параметров пера Pen. Фигура заполняется текущим значением Brush. Точки (X1, Y1) и (X2, Y2) определяют прямоугольник, описывающий эллипс.

 

procedure Rectangle(X1, Y1, X2, Y2: Integer);

Рисует на канве текущим пером Pen прямоугольник верхний левый угол, которого имеет координаты (X1, Y1), а нижний правый - (X2, Y2). Прямоугольник закрашивается текущей кистью Brush.

 

 

Приложение А

Министерство образования и науки РФ

ФГБОУ ВО «Волгоградский государственный
технический университет»

Себряковский филиал

 

 

Кафедра математических и естественно-научных дисциплин

 

 

Пояснительная записка к курсовой работе

по дисциплине

«Объектно-ориентированное программирование»

 

Тема работы:

 

« Игра “Теннис” с эффектом инверсии управления»

 

 

Выполнил студент гр. <группа>

Иванов И.И.

Проверил <должность >:

Иванов И.И.

Михайловка

 

Приложение Б

Создаем новый проект:

1.Проект → Создать проект

2.Выбираем тип проекта приложение:

3. Добавляем в проект обработчик события нажатия кнопок мыши : OnMouseDown

 

 

4. Добавим в модуль реализации

Form1.Canvas.Ellipse(X+sizeObj, Y+sizeObj, X-sizeObj, Y-sizeObj);

где sizeObj - глобальную константу определяющую размер фигуры: const sizeObj=20;

5. Запускаем созданный нами проект (F9) и тестируем, что у нас получилось.

Вне зависимости от того какой кнопкой мыши мы щелкаем, происходит добавление нового круга на экран.

Но как только вы пробуйте изменить размеры окна. Ваша форма перерисовывается заново и изменения не сохраняются.

Закомментируем код рисования эллипса.

6. Создадим объекты, которые будут оставаться в программе в течение всего времени жизни главного окна. Добавляем в проект обработчик события рисования окна : OnPaint.

Создадим прямоугольники, которые будут располагаться в верхней части окна. Так как размеры окна могут меняться, то пусть количество нарисованных прямоугольников меняется в зависимости от ширины окна (Form1.Width).

 

procedure TForm1.FormPaint(Sender: TObject);

var i:integer;

begin

for i:=0 to Round(Form1.Width/sizeObj) do

Form1.canvas.Rectangle(sizeObj*i, 0, sizeObj*(i+1), sizeObj);

end;

7. Теперь у нас есть проблема. Так как окно может быль любых размеров, то нарисованные прямоугольники помещаются в окне не целое количество раз. Сделаем так, чтобы размеры окна были только кратными переменной sizeObj. Для этого добавляем в проект обработчик события изменения размера окна : OnResize

 

Добавим в модуль реализации :

Form1.Width :=sizeObj*round(Form1.Width/sizeObj);

 

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

Результат:

Обратите особое внимание, что для боковых границ последнее значение счетчика цикла рассчитывается с помощью значения высоты окна(Form1. Height/sizeObj).

Теперь процедура FormPaint выглядит так:

 

procedure TForm1.FormPaint(Sender: TObject);

var i:integer;

begin

for i:=0 to Round(Form1.Width/sizeObj) do {create Top Windows}

Form1.canvas.Rectangle(sizeObj*i, 0, sizeObj*(i+1), sizeObj);

 

for i:=0 to Round(Form1. Height/sizeObj) do {create Left Windows}

Form1.canvas.Rectangle(0,sizeObj*i, sizeObj, sizeObj*(i+1));

 

for i:=0 to Round(Form1. Height/sizeObj) do {create Right Windows}

Form1.canvas.Rectangle(Form1.Width,sizeObj*i,

Form1.Width- sizeObj, sizeObj*(i+1));

 

end;

9. Сделайте такое же, как в пункте 7 ограничение, но еще для высоты окна (Form1. Height)

 

10. Создадим три прямоугольника, которые будут управляться с помощью клавиатуры. Для обработки событий клавиатуры добавляем в проект обработчик события нажатия клавиши: OnKeyDown:

 

Для управления будут использоваться стрелки.

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

ShowMessage(IntToStr(Key)) ;

И теперь запустим нашу программу и нажимая необходимую нам клавишу увидим сообщение с ее кодом . Так определяем «←» = 37, «→» =39. Если же вам нужна какая-то английская буква то можно воспользоваться функцией Ord ('S').

При нажатии на клавишу объект должен перемешаться, т.е. должны изменяться его координаты. Для этого необходимо чтобы значение координат где-то хранилось. Создадим глобальную переменную coorXObj хранящую значение по Х. Тип этой переменной будет Integer, при этом присвоим значение по умолчанию coorXObj:integer=sizeObj;

 

11. Продолжаем работу в обработчике события нажатие клавиши.

При Key=37 значение coorXObj уменьшаем, а при Key=39 значение coorXObj увеличиваем.

if Key=37 then coorXObj:=coorXObj-sizeObj ;

if Key=39 then coorXObj:=coorXObj+sizeObj ;

 

12. Для того чтобы наш объект изображался, необходимо его рисовать в процедуре FormPaint :

 

Form1.canvas.Rectangle(coorXObj, Form1.Height- sizeObj,

coorXObj+sizeObj, Form1.Height);

13. Запустим проект на тестирование. Обнаружим, что наш объект не реагирует на нажатия клавиш. Это связано с тем, что при нажатии клавиш не происходит перерисовка окна. Значит, в процедуре FormKeyDown наверное необходимо вызывать процедуру рисования окна.

FormPaint(Sender);

14. Запустим проект на тестирование. Наблюдаем ошибку, предыдущее положение объекта не стирается с окна. Поэтому необходимо вызывать не процедуру рисования, а процедуру перерисовки окна

Form1.Repaint

15. Однако некоторые аномалии поведения еще остались. Например если объект вывести за приделы экрана, то обратно он возвращается не сразу. Поэтому на значение этой переменной coorXObj должны быть наложены ограничения.

sizeObj <=coorXObj and coorXObj <= (Form1.Width – 2*sizeObj).

Выразим это следующим способом

 

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState );

begin

 

if (Key=37) and not (sizeObj =coorXObj) then coorXObj:=coorXObj-sizeObj ;

if (Key=39) and not (coorXObj = (Form1.Width - 2*sizeObj)) then

coorXObj:=coorXObj+sizeObj ;

Form1.Repaint;

end;

16. Преобразуйте этот объект в три последовательных прямоугольника, и исправьте ограничения переменной coorXObj для этого случая.

 

17. Необходимо реализовать следующее

1. Перемещение шарика по окну программы через определенные промежутки времени.

2. Отражение шарика от стенок.

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

 

18. Теперь создадим объект - шарик, который будет перемешаться самостоятельно. Для этого нам необходим специальный системный компонент таймер располагающийся на выкладке System

 

Добавим его в наш проект.

Для этого добавляем в проект обработчик события времени OnTimer

19. У самостоятельного объекта пусть будет две меняющиеся координаты, два коэффициента, для определения направления движения. Для этого создадим сначала класс, а потом переменную этого типа.

type TBall=class

X,Y, dx,dy:integer;

procedure SetDefault;

end;

 

procedure TBall.SetDefault;

begin

X:=sizeObj;

Y:=sizeObj;

dx:=1;

dy:=2;

end;

 

var

Ball:TBall;

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

Для того чтобы запустить игру опишем в обработчике событий нажатия клавиши следующее действие:

If key = 116 then { код клавиши - F5}

StartNewGame;

где StartNewGame – процедура, которую мы добавим в класс TForm1.

 

И реализуем процедуру:

procedure TForm1.StartNewGame;

begin

Ball:= TBall.Create; {создаем экземпляр класса}

Ball.SetDefault;

Timer1.Enabled:=True; {запускаем таймер}

end;

 

20. Добавим в модуль реализации обработчика событий времени Timer1Timer алгоритм изменения координат нового объекта - Шарика.

Ball.X:= Ball.X+ Ball.dx;

Ball.Y:= Ball.Y+ Ball.dy;

И сразу же изменим интервал времени через который будет происходит запуск этой процедуры Поставим 100.

 

 

21. Для более удобного обозначения размеров рабочей области введем переменные и создадим функцию её вычисляющую.

Переменная будет особого типа record

Который опишем

Type TMinMax=record

min,max:integer;

End;

Type TField=record

X,Y: TMinMax;

End;

 

Var

WorkField: TField;

 

22. Добавим метод procedure CalculationWorkField; в класс TForm1;.

Его реализация будет:

procedure TForm1.CalculationWorkField;

begin

WorkField.X.min:=sizeObj;

WorkField.Y.min:=sizeObj;

WorkField.X.max:=Form1.Width-sizeObj;

WorkField.Y.max:=Form1.Height-sizeObj;

end;

 

23. Будем вызывать процедуру CalculationWorkField при каждом изменении размеров окна т.е. в процедуре FormResize.

 

24. Благодаря только что созданной процедуре есть возможность улучшить читабельность нашей программы в уже написанном коде. Если вспомните что у нас написано в процедуре FormKeyDown. Именно там мы использовали плохо читаемые прямые указания ограничений.

if (Key=37) and not (coorXObj= sizeObj) then coorXObj:=coorXObj-sizeObj ;

if (Key=39) and not (coorXObj = (Form1.Width - 4*sizeObj)) then

coorXObj:=coorXObj+sizeObj ;

 

Так что теперь напишем:

if (Key=37) and not (coorXObj = WorkField.X.min) then coorXObj:=coorXObj-sizeObj ;

if (Key=39) and not (coorXObj = (WorkField.X. max - 3*sizeObj)) then

coorXObj:=coorXObj+sizeObj ;

Дело в том, что мы храним координату самой левой точки объекта, поэтому вычитаем 3* sizeObj – теперь это стало хорошо видно ( с четверкой было не понятно).

Смотрим дальше в процедуре FormPaint мы тоже использовали прямые значения от края окна, поэтому перепишем:

старое По-новому
Form1.Width/sizeObj WorkField.X.max/sizeObj
Form1.Height/sizeObj WorkField.Y.max/sizeObj

Вместо:

for i:=0 to Round(Form1.Height/sizeObj) do {create Right Windows}

Form1.canvas.Rectangle(Form1.Width,sizeObj*i,

Form1.Width- sizeObj, sizeObj*(i+1));

for i:=0 to 2 do

Form1.canvas.Rectangle(coorXObj+sizeObj*i, Form1.Height- sizeObj,

coorXObj+sizeObj*(i+1), Form1.Height);

 

Получилось:

for i:=0 to Round(WorkField.Y.max/sizeObj) do {create Right Windows}

Form1.canvas.Rectangle(WorkField.X.max,sizeObj*i,

WorkField.X.max+ sizeObj, sizeObj*(i+1));

for i:=0 to 2 do

Form1.canvas.Rectangle(coorXObj+sizeObj*i, WorkField.Y.max,

coorXObj+sizeObj*(i+1), WorkField.Y.max+ sizeObj);

 

25.Добавим возможность отражения от стенок шарика реализуем в процедуре Timer1Timer.

If (Ball.X= WorkField.X.min) or (Ball.X= WorkField.X.max- sizeObj) then

Ball.dx:= -1*Ball.dx;

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

 

If (Ball.Y= WorkField.Y.min) then Ball.dy:= -1*Ball.dy; {отражение от верхней границы}

 

If (Ball.Y= WorkField.Y.max -sizeObj) then begin {с пользовательской стенкой}

if ((coorXObj<= Ball.X) and (coorXObj+3* sizeObj >= Ball.X)) then

Ball.dy:= -1*Ball.dy

Else begin

ShowMessage('Game over');

Timer1.Enabled:=false; { останавливаем таймер}

GameIsStart:=false;

Ball.Free;{уничтожить шарик}

end;

end;

 

Дело в том, что мы храним координату самой левой точки объекта, поэтому от максимального вычитаем sizeObj

 

26. Теперь всю реализацию, связанную с шариком в процедуре Timer1Timer, спрячем в метод класса (TBall) function Move():Boolean; возвращающий значение по которому можно определить вышел ли наш шарик за приделы рабочей области или нет. Для удобства чтения создадим глобальную константу: BallInOutWorkField=true; {- шарик вышел за приделы}

 

function TBall.Move():Boolean;

begin

result:=not BallInOutWorkField;

X:= X+ dx;

Y:= Y+ dy;

 

If (X= WorkField.X.min) or (X= WorkField.X.max- sizeObj) then

dx:= -1*dx;

If (Y= WorkField.Y.min) then dy:= -1*dy;

 

If (Y= WorkField.Y.max- sizeObj) then begin {с пользовательской стенкой}

if ((coorXObj<= X) and

(coorXObj+3* sizeObj >= X)) then

dy:= -1*dy

Else result:=BallInOutWorkField;

 

end;

end;

 

27. Делаем соответствующие исправления в процедуре Timer1Timer.

procedure TForm1.Timer1Timer(Sender: TObject);

begin

if Ball.Move()=BallInOutWorkField then begin

ShowMessage('Game over');

Timer1.Enabled:=false; { останавливаемтаймер}

GameIsStart:=false;

Ball.Free;

end;

Form1.Repaint;

end;

 

28. Теперь необходимо рисовать объект –шарик. Создадим специальный метод Draw()

procedure TBall.Draw();

begin

Form1.canvas.Ellipse(X, Y, X+sizeObj, Y+sizeObj);

end;

Будем вызывать её в процедуре FormPaint.

Ball.Draw();

 

29. Запускаем и видим ошибку. Дело в том, что мы еще не запускали процедуру StartNewGame, а значит, у нас еще нет объекта Ball.. Нужно как то ограничить запуск строчки Ball.Draw(), поэтому создадим глобальную переменную (GameIsStart) несущую значение началась наша игра или нет.

Var GameIsStart: Boolean=false;

и в процедуре StartNewGame добавляем GameIsStart:=true; {игра началась}

Исправляем процедуре FormPaint

If GameIsStart then TBall.Draw();

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

if ((coorXObj<= X+sizeObj/2 ) and

(coorXObj+3* sizeObj >= X+sizeObj/2)) then …

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

32. Добавить возможность подсчета очков заработанных пользователем в зависимости от :

а. количества столкновений

б. места столкновения с вашей стенкой (средний или крайний квадраты)

в. размеров рабочей области.

и выведение этого значения во время игры.

33. Реализовать все функциональные возможности, указанные в индивидуальном задании на курсовую работу.

 

 

Приложение B

Настройки проекта для подключения к базе данных MySQL

1. В папку проекта скопируйте библиотеку для работы с MySQL. Скачать ее можно здесь: libmysql.dll.

2. Перейдите в панели инструментов на выкладку SQLbd и разместите на форме компоненты

· TMySQL50Connection (ответственен за подключение к базе данных)

· TSQLTransaction

· TSQLQuery (представляют собой наборы данных, в которые загружается копия таблицы с сервера БД)

3. Перейдите в панели инструментов на выкладку Data Access и разместите на форме компонент TDatasource.

4. Настройте связи между этими компонентами (используются имена объектов, присваиваемые по умолчанию).

Свойство Ссылка на объект
MySQL50Connection1.Transaction SQLTransaction1
SQLTransaction1.Database MySQL50Connection1
SQLQuery1.Database MySQL50Connection1
SQLQuery1.Transaction SQLTransaction1
Datasource1.DataSet SQLQuery1

 

5. Подключаться будем всегда к одной базе данных, поэтому в свойствах объекта MySQL50Connection1 укажем:

DatabaseName=FileSystem

HostName=localhost

UserName=root

где DatabaseName - название базы данных,

HostName - адрес сервера,

UserName - имя пользователя,

Password -пароль для доступа к базе данных (оставим пустым)

6. Процедура подключения к базе данных

procedure TForm1.ConnectToBase();

begin

try

MySQL50Connection1.Connected:=true;

except

ShowMessage('Ошибка подключения к базе данных '+

MySQL50Connection1.DatabaseName);

exit;

end;

try

SQLQuery1.Active:=false;

SQLQuery1.SQL.Clear;

SQLQuery1.sql.add(

'SET character_set_client='+#39+'utf8'+#39+

', character_set_connection='+#39+'utf8'+#39+

',character_set_results='+

#39+'utf8'+#39+';');

SQLQuery1.ExecSQL;

SQLQuery1.SQL.Clear;

except

ShowMessage('Ошибка при настройке соединения.');

end;

end;

Таким образом, сначала производится попытка подключения к MySQL базе, если она прошла успешно, активируется и объект SQLQuery1 выполняется SQL запрос к базе данных, настраивающий соединение.

 

SET character_set_results='utf8'; /*устанавливает кодировку данных отправляемых К клиенту*/

SET character_set_client='utf8'; /*устанавливает кодировку данных отправляемых ОТ клиента*/

SET character_set_connection='utf8' /*устанавливает кодировку, в которую преобразуется информация пришедшая от клиента, перед выполнением запроса на сервере.*/

 

7. После подключения к базе данных, создавайте все необходимые таблицы. У всех таблиц добавьте постфикс из двух букв ваших инициалов (например ваши инициалы D.S., тогда имя таблицы users будет выглядеть как usersDS)

procedure TForm1.CreateAllTable();

begin

if SQLQuery1.Active then SQLQuery1.Close;

SQLQuery1.SQL.Clear;

 

SQLQuery1.SQL.Add(

'create table '+

'if not exists usersDS' +

'('+

' id INTEGER PRIMARY KEY '+

' AUTO_INCREMENT NOT NULL'+

', username varchar(255)'+

', rating INTEGER UNSIGNED'+

', color_ball varchar(20)'+

', color_racket varchar(20)'+

', color_walls varchar(20)'+

', color_background varchar(20)'+

');');

SQLQuery1.ExecSQL;

end;

8. Далее загружаете список пользователей из таблицы. Если список пользователей пуст

 

SQLQuery1.SQL.Clear;

SQLQuery1.SQL.Add('Select * from usersDS;');

SQLQuery1.Open;

SQLQuery1.DataSet.First;

If Datasource1.DataSet.IsEmpty then

ShowMessage('Нет ни одного пользователя');

else begin

with Datasource1.DataSet do begin

username:=FieldByName('username').AsWideString;

rating:=FieldByName('rating').AsInteger;

//загружаете username в элемент интерфейса

// TComboBox или TListBox

end;

end;

9. Процедура корректного отключения от базы данных. Необходимо вызывать при закрытии формы.

 

procedure TForm1.DisConnect();

begin

SQLQuery1.Active:=false;

SQLTransaction1.Commit;

MySQL50Connection1.Connected:=false;

end;

10. Пример запроса на обновление данных

 

try

SQLQuery1.Close;

SQLQuery1.SQL.Clear;

SQLQuery1.sql.add('UPDATE usersDS SET rating=100;');

SQLQuery1.ExecSQL;

except

ShowMessage(' Ошибка при выполнении SQL запроса.');

exit;

end;