В этом уроке вам придется иметь дело с некоторой техникой, такой как заглавные буквы и рифма. Для того чтобы создать хорошую игру, вам потребуется добавить к рецепту талант. Игра, которую вы реализуете, проста. Имеется червь, быстро ползущий по экрану. Игрок должен изменять его направление, для того чтобы предохранить червя от ударов о препятствия и стены. Вам необходимо реализовать следующие элементы:
1. Окно, по которому червь будет ползать. Окно должно иметь три кнопки для управления игрой и четыре кнопки для управления движением червя.
2. Класс состояний, который описывает направление движения и положение червя. Он содержит:
• предикат init
для инициализации червя, т.е. для начального состояния;
• предикат mov
для изменения положения червя;
• предикат turn
, который используется для выполнения поворотов.
3. Класс, который рисует изображение положения червя. Он содержит предикат snapshot(Win)
. Обратите внимание на следующую деталь: предикат snap-shot
не может нарисовать червя прямо в окне. Это бы заставило мерцать изображение. Он должен построить изображение и только затем нарисовать его на экране.
4. Таймер, который вызывает mov и snapshot через фиксированные интервалы, для того чтобы создать иллюзию движения.
Мы знаем, что делать – давайте сделаем это. До этого момента, каждый раз, когда вы создавали класс, вам рекомендовалось снимать галочку Creates Interface. Однако если мы жестко будем следовать этой установке, наша игра будет иметь очень неприятное свойство. Если вы проиграете, выйдете из текущей игры и попробуете начать новую, то обнаружите, что новая игра не находится в исходном состоянии. Вместо этого она будет в последнем состоянии, в котором вы ее оставили. Проблема заключается в том, что переменные, которые используются для описания состояния, имеют память, которая сохраняется от одной формы к другой. Объекты были изобретены для предотвращения такого неприятного поведения. Поэтому давайте используем прекрасную возможность изучить, как создавать объекты и работать с их состояниями.
Для начала создайте проект «game», в поле Project Kind нужно указать MDI. Теперь создадим форму lawn
. На ней удалим все кнопки, которые были созданы по умолчанию и добавим необходимые кнопки для нашей игры:
• «Start», рекомендуемое имя (далее Name) «start_ctl»;
• «Stop», Name – «stop_ctl»;
• «Up», Name – «up_ctl»;
• «Left», Name – «left_ctl»;
• «Right», Name – «right_ctl»;
• «Down», Name – «down_ctl».
Постройте приложение, для того чтобы включить в него эту форму. Затем, так же как и в предыдущем уроке включите File > New и добавьте код:
clauses
onFileNew(S, _MenuTag) :- X = lawn::new(S), X:show().
Для того, чтобы отделить собственно созданные файлы создадим новый пакет playground
.
Далее добавим класс objstate
внутри playground
, не отключая Create Interface. В файл objstate.pro поместите код:
implement objstate
open core, vpiDomains
facts
w:(integer, pnt) nondeterm. % worm
d:(integer, integer) single. % direction
clauses
init() :-
assert(w(1, pnt(110, 100))),
assert(w(2, pnt(120, 100))),
assert(w(3, pnt(130, 100))),
assert(w(4, pnt(140, 100))),
assert(w(5, pnt(140, 100))).
d(10, 0).
mov() :-
retract(w(1, P)),
P = pnt(X1, Y1),
d(DX, DY),
P1 = pnt(X1+DX, Y1+DY),
assert(w(1, P1)),
retract(w(2, P2)),
assert(w(2, P)),
retract(w(3, P3)),
assert(w(3, P2)),
retract(w(4, P4)),
assert(w(4, P3)),
retract(w(5, _)),
assert(w(5, P4)), !.
mov().
segm(rct(X, Y, X+10, Y+10)) :-
w(_, pnt(X, Y)).
turn(DX, DY) :-
assert(d(DX, DY)).
end implement objstate
Класс objstate
должен иметь интерфейс в файле objstate.i:
interface objstate
open core, vpiDomains
predicates
init:().
turn:(integer, integer).
mov:().
segm:(rct) nondeterm (o).
end interface objstate
Постройте приложение.
Для прорисовки червя создадим класс draw
внутри playground
. В данном случае отключим Create Interface. В файл draw.cl вставим следующий код:
class draw
open core, vpiDomains
predicates
snapshot:(windowGDI Win, objstate).
end class draw
А также изменим файл draw.pro:
implement draw
open core, vpiDomains, vpi
class predicates
draw:(windowHandle, objstate) procedure.
clauses
draw(W, S) :-
S:segm(Rectangle),
vpi::drawEllipse(W, Rectangle),
fail.
draw(_W, _S).
snapshot(Win, S) :-
S:mov(), !,
Rectangle= rct(0, 0, 200, 200),
W= pictOpen(Rectangle),
draw(W, S), Pict= pictClose(W),
Win:pictDraw(Pict, pnt(10, 10), rop_SrcCopy).
end implement draw
Постройте приложение для включения класса draw в проект.
Для обработчика ShowListener
диалогового окна Properties формы lawn.frm добавим код, который будет создавать объект класса objstate
и отображать начальное состояние червя:
class facts
stt:objstate := objstate::new().
clauses
onShow(Parent, _CreationData) :-
stt := objstate::new(),
stt:init().
Теперь сделаем рабочими наши кнопки. Для этого нужно добавить код для обработчика ClickResponder
, соответствующего каждой из кнопок:
facts
tm: timerHandle := erroneous.
predicates
onStartClick : button::clickResponder.
clauses
onStartClick(_Source) = button::defaultAction() :-
tm := timerSet(500),
start_ctl:setEnabled(false),
pause_ctl:setEnabled(true),
stop_ctl:setEnabled(true).
predicates
onPauseClick : button::clickResponder.
clauses
onPauseClick(_Source) = button::defaultAction() :-
timerKill(tm),
start_ctl:setEnabled(true),
stop_ctl:setEnabled(true),
pause_ctl:setEnabled(false).
predicates
onStopClick : button::clickResponder.
clauses
onStopClick(_Source) = button::defaultAction() :-
timerKill(tm),
stt := objstate::new(),
stt:mov(),
R = rct(10, 10, 210, 210), invalidate(R),
stt:init(),
start_ctl:setEnabled(true),
stop_ctl:setEnabled(false),
pause_ctl:setEnabled(false).
predicates
onDownClick : button::clickResponder.
clauses
onDownClick(_S) = button::defaultAction() :- stt:turn(0, 10).
predicates
onLeftClick : button::clickResponder.
clauses
onLeftClick(_S) = button::defaultAction() :- stt:turn(-10, 0).
predicates
onRightClick : button::clickResponder.
clauses
onRightClick(_S) = button::defaultAction() :- stt:turn(10, 0).
predicates
onUpClick : button::clickResponder.
clauses
onUpClick(_S) = button::defaultAction() :- stt:turn(0, -10).
Также нужно добавить код для обработчиков TimeListener
и PaintResponder
из диалогового окна Properties формы lawn.frm:
predicates
onTimer : window::timerListener.
clauses
onTimer(_Source, _TimerID) :- stt:mov(), R= rct(10, 10, 210, 210), invalidate(R).
predicates
onPaint : window::paintResponder.
clauses
onPaint(_Source, _Rectangle, GDIObject) :- draw::snapshot(GDIObject, stt).
Постройте и запустите программу. При выборе пункта меню File > New появится окно с червем. Для начала игры необходимо нажать кнопку Start, соответственно для паузы – Pause. Для того, чтобы начать новую игру достаточно нажать Stop – червь переместится в начальное состояние. Вы можете управлять червем с помощью четырех кнопок.
Улучшение игры остаётся за вами. Вы можете добавить препятствия, например стены. Вы также можете положить маленькие яблоки для питания червя и т.д.
А для тех, кто еще сам не умеет писать игры, тоже есть решение - установите себе на Андроид игру Word Dices и в игровой форме пополняйте свой запас английских слов. Игра особенно полезна программистам, изучающим английский. Учись играя.
<< предыдущий | к содержанию >>