|
“Волк, коза и капуста”: реализация в среде FlashИстория, побудившая меня написать эту заметку, случилась пару лет назад. На перемене ко мне подошла взволнованная коллега: — У меня через два урока поставили замену. Можешь быстро найти мне “Волка, козу и капусту”? Любой учитель информатики, конечно, понимает, что речь идет не о реальных животных и кочане, а о классической алгоритмической игре :). Коллегу надо было выручать, но, на ее и мою беду, я как-то не мог с ходу собразить, где мне взять требуемую программу. Зато у меня как раз был час свободного времени и Flash — я готовился к курсам повышения квалификации на очных курсах нашего Педагогического университета. Эх, была не была — рискну, неужели за час я не запрограммирую простую задачку? Я успел. Правда, закончил всего за пару минут до звонка, но урок не сорвался. А код, пусть и написанный в цейтноте, мне нравится до сих пор, и я буду рад поделиться им с коллегами. ЗадачаИтак, мне требовалось “нарисовать” среду и запрограммировать исполнителя, выполняющего допустимые действия (и только их!). Ученики должны были интерактивно выдавать команды исполнителю, перетаскивая персонажей на поле и переправляя лодку. В игре должны были распознаваться терминальные (конечные) состояния — успешная переправа или съедение кого-либо. Одна из визуализаций среды показана на рис. 1. Сказать по правде, “настоящие” картинки персонажей я нашел позже — в описываемой ситуации мне пришлось ограничиться просто прямоугольниками с соответствующими надписями.
Рис. 1. Визуализация среды При наступлении одного из терминальных состояний на экране возникает соответствующее сообщение (см. рис. 2). Рис. 2 Рис. 3 Почти все время, пока я в спешке программировал игру, за окном школы в чьей-то машине хрипели песни какого-то популярного исполнителя, среди которых была и “Я убью тебя, лодочник!”. Это музыкальное сопровождение меня изрядно раздражало, но сама идея мне понравилась — лодочника я рисовать не стал. Он ведь все равно из лодки не выходит, так что с алгоритмической точки зрения лодка и лодочник — единое целое. Поэтому на рис. 1 лодочника нет. Если кого-то это смущает, можно его пририсовать. Ну, или ограничиться пояснением, что он есть, но спрятался в лодке :). Отрисовка и расположение объектовВзглянем на рис. 3. Как видите, я расположил основные объекты в четырех слоях. Слои имеют информативные названия, поэтому, я надеюсь, понятно, где что. Пятый, пустой, слой я использовал для размещения скрипта. Это стандартный прием программистов, использующих Flash. Также на рис. 3 можно увидеть библиотеку клипа. В ней имеются: · Символ “Коза”, который представлен одним экземпляром (Instance name) koza. · Символ “Волк”, представленный одним экземпляром volk. · Символ “Капуста”, представленный одним экземпляром kapusta. · Символ “Берег”, посредством которого отрисованы берега. Берега удобно сделать клипами, поскольку это позволяет проще проверять факт помещения персонажа на берег — не надо оперировать координатами, достаточно проверить факт соприкосновения двух клипов. Этот символ представлен двумя экземплярами left и right. · Кнопка “Стрелка”, два экземпляра (toright и toleft) которой используются для переправ. · Кнопки “Ура!” и “GameOver”. Дело в том, что надписи в конечных состояниях сделаны кнопками, возвращающими к началу игры. · В символе “Лодка” имеются два кадра, соответствующие различным ориентациям лодки, — нос смотрит влево и нос смотрит вправо. Примечание для эстетов: самому мне, честно говоря, безразлично, как называть, например, капусту — kapusta или cabbage. Но я уверен, что для лучшего и быстрого понимания кода русскоязычной аудиторией название kapusta предпочтительнее. Реализация и скриптыВесь приведенный ниже код я разместил в первом кадре слоя скрипта. Код не слишком длинный, и мне представляется, что подробных комментариев в тексте вполне достаточно для понимания. Надо только отметить ряд технических моментов: · У клипов koza, kapusta, volk и lodka я определелил свойство bereg, которое может принимать два значения — left и right, в зависимости от того, на каком берегу находится персонаж. · У клипа lodka есть также свойство pas. Значение этого свойства — пассажир лодки или null, если лодка пуста. · В смысле перетаскивания с берега в лодку и обратно, и коза, и волк, и капуста ведут себя абсолютно одинаково. Поэтому я определил три функции: OnPress, OnRelease и OnReleaseOutside — и сделал их обработчиками соответствующих событий всех трех клипов. · В обработчиках кнопок “Влево”/“Вправо” имеется “смертельный грех”, заключающийся в перемещении лодки и сидящего в ней персонажа на абсолютное значение 100 пикселей. Конечно, можно все аккуратно посчитать — зная координаты клипов-берегов, перемещать лодку “универсально”. Но я демонстрирую все же не настоящий продукт, а пример, сделанный за час и “на коленке”. Пусть и доработанный позже. И не вижу никакого смысла в данном случае “городить огород” (записывать легко конструируемое, но довольно сложное и не прозрачное выражение для координат). stop(); //Поскольку в нашем клипе три кадра, //надо остановить проигрывание на первом //Начальная конфигурация koza.bereg=kapusta.bereg=volk.bereg="left"; lodka.bereg="left"; //Это присваивание отделено от группы // предшествующих //исключительно по соображениям наглядности lodka.pas=null; //В лодке никого нет //(кроме невидимого лодочника) _root.onEnterFrame=function(){ //В каждом кадре проверяем, //не достигнуто ли заключительное состояние if (((koza.bereg==volk.bereg)&& (koza.bereg!=lodka.bereg))|| ((koza.bereg==kapusta.bereg)&& (koza.bereg!=lodka.bereg))) gotoAndStop(2); //Кого-то съели… if ((koza.bereg=="right")&& (volk.bereg=="right")&& (kapusta.bereg=="right")&& (lodka.pas==null)) gotoAndStop(3); //Все перевезли } //Обработчик кнопки вправо toright.onPress=function() { if (lodka.bereg=="left") { lodka.gotoAndStop(2); //Сменим ориентацию носа лодки lodka.bereg="right"; lodka._x+=100; if (lodka.pas) { lodka.pas.bereg="right" lodka.pas._x+=100; } } } //Обработчик кнопки влево toleft.onPress=function() { if (lodka.bereg=="right") { lodka.gotoAndStop(1); lodka.bereg="left"; lodka._x-=100; if (lodka.pas) { lodka.pas.bereg="left" lodka.pas._x-=100; } } } //Обработчик нажатия кнопки мыши //на любом персонаже function OnPress() { //Запомним координаты персонажа curx=this._x; cury=this._y; //Начинаем перетаскивание startDrag(this); }
//Обработчик опускания кнопки мыши //любого персонажа function OnRelease() { //Закончим перетаскивание stopDrag(); //Если мы высаживаем пассажира //на левый берег… if ((lodka.pas==this)&& (lodka.bereg=="left")&& (this.hitTest(left))) { lodka.pas=null; return; } //Если мы высаживаем пассажира //на правый берег… if ((lodka.pas==this)&& (lodka.bereg=="right")&& (this.hitTest(right))) { lodka.pas=null; return; } //Если мы сажаем пассажира в лодку… if ((this.hitTest(lodka))&& (lodka.pas==null)&& (this.bereg==lodka.bereg)) { lodka.pas=this; return; } this._x=curx; this._y=cury; } koza.onPress=kapusta.onPress=volk.onPress=OnPress; koza.onRelease=kapusta.onRelease=volk.onRelease=OnRelease; koza.onReleaseOutside=kapusta.onReleaseOutside= =volk.onReleaseOutside=OnRelease; Се. Ль. Островский |