Александр Григорьев
В учебных и игровых проектах часто приходится заниматься моделированием столкновений объектов. Например, нам может понадобиться сделать так, чтобы виртуальный робот был способен толкать и перемещать груз по игровому полигону.
Моделирование столкновений требуется и при написании компьютерных игр, аркадных или логических. Многим знакома симпатичная игра "Snail and Sokoban", в которой необходимо расставить ящики по нужным местам. Реализация подобного проекта в среде Snap4arduino может оказаться весьма интересной и познавательной задачей .
https://www.youtube.com/watch?v=g2D0oWxeEWo
В Scratch для проверки столкновений зачастую используют блок "касается цвета". Подобный метод, несмотря на свою простоту, имеет много недостатков. Существуют иной, гораздо более универсальный способ путём проверки касания отдельного спрайта или касания одного из элементов в списке спрайтов. Среда Snap4arduino содержит множество средств, облегчающих нам работу. Давайте посмотрим, насколько удобно ими пользоваться.
Итак, определим задачи. Вначале мы создадим спрайт компьютерного персонажа ("робота"), управляемый при помощи клавиатурных кнопок. Научим его останавливаться перед препятствиями (любыми другими спрайтами). А затем добавим в проект перемещаемые спрайты - "бочки" и сделаем так, чтобы робот мог их двигать по полю.
Будем решать всё шаг за шагом, поэтапно.
- Вина доказана, - произнесла Королева. - Рубите ему...
- Ничего подобного! - возразила Алиса. - Вы даже не знаете, о чем стихи.
- Читай их! - сказал Король Кролику.
Кролик надел очки.
- С чего начинать, Ваше Величество? - спросил он.
ЭТАП 1. Подготовка.
Если необходимо, установите Snap4arduino на свой компьютер, используя подходящий инсталляционный пакет.
Нам понадобится четыре изображения для наших объектов, загрузите их на свой компьютер (щёлкните правой кнопкой мыши по изображению и воспользуйтесь контекстным меню браузера).
робот.png захват.png бочка.png стенка.png
Запустите Snap4arduino. Выберите сцену и добавьте ей фон, закрашенный каким-нибудь цветом светлого оттенка.
Ты старик, - продолжал любопытный юнец, -
Этот факт я отметил вначале.
Почему ж ты так ловко проделал, отец,
Троекратное сальто-мортале?
- В ранней юности, - сыну ответил старик, -
Натирался я мазью особой.
На два шиллинга банка - один золотник,
Вот, не купишь ли банку на пробу?
ЭТАП 2. Создаём робота. Робот останавливается, когда врезается в препятствия.
Выберите спрайт с названием "Спрайт", переименуйте его в "Робот1" (это будет первый робот в нашем проекте, в будущем мы можем добавить дополнительных ).
Добавьте новый костюм из файла " робот.png", перетащив его на вкладку "Костюмы".
Переключитесь на вкладку "Скрипты". Обеспечим возможность ручного управления роботом с использованием компьютерной клавиатуры.
апустите программу и проверьте управление роботом. Имейте ввиду, что он может уехать далеко за пределы отображаемого пространства.
Используйте для возврата блок "перейти в точку X (0) Y (0)".
Добавьте в проект ещё один спрайт (любой). Например, нажмите кнопку "создать новый стандартный спрайт".
Запустите программу ещё раз и проверьте, будет ли робот останавливаться, врезавшись в этот спрайт. Как видите, спрайты пока никак не взаимодействуют.
Будем дорабатывать скрипт робота. Обратите внимание, что в настоящий момент выбран новый спрайт, и у него пока нет собственного скрипта.
Переключитесь на спрайт робота. Теперь найдите на палитре скриптов в разделе "Сенсоры" блок "атрибут" (английский вариант названия - "my") и вытащите его на рабочее поле. По умолчанию выбран атрибут "соседи" ("my neighbors").
Данный блок способен возвращать разные свойства объекта, в том числе список расположенных поблизости спрайтов. Причём это не просто имена, а ссылки, позволяющие данные объекты использовать, что может оказаться очень мощным инструментом в самых разных ситуациях.
Щёлкните по блоку. Вы увидите, что список состоит лишь из одного объекта, это добавленный нами новый спрайт. Он попал в список, поскольку располагается на сцене рядом со спрайтом "Робот1".
Если "стрелочку" отодвинуть подальше, список окажется пустым.
Вытащите на рабочее пространство блок "касается" и поместите внутрь блок "атрибут". Теперь мы можем проверить, касается ли один спрайт другого, без указания их имён. Это очень важно с точки зрения универсальности, результаты нашей работы мы сможем использовать во всех будущих проектах.
Давайте теперь создадим специальный новый блок, который будет перемещать робота, проверять, не коснулся ли он чего-либо, и если это произошло, отодвигать робота назад (и даже на всякий случай чуть дальше). Кроме того, для удобства сделаем так, чтобы скорость перемещения робота была пропорциональна масштабу его отображения.
Выберите категорию "Прочее", нажмите кнопку "Новый блок". Вы увидите диалоговое окно.
Создаваемый блок мог бы работать одинаково хорошо с любыми спрайтами, однако выберите опцию "только для текущего спрайта". Это упростит нам в будущем использование данного спрайта в других проектах. Дайте имя "переместиться", нажмите на кнопку "ОК".
Появилось окно редактора блоков.
Нажмите на "плюсик" и добавьте числовой параметр, назовите его "дистанция".
Теперь можно собирать скрипт. Вам понадобится блок "размер" из категории "Внешность", это текущий масштаб отображения спрайта. Блок "ждать" служит для того, чтобы другие спрайты успевали отреагировать на касание робота, если это предусмотрено их скриптами.
Отредактируйте основной скрипт спрайта "Робот1".
Запустите программу, проверьте, как ведёт себя робот. Теперь он "отказывается" перемещаться за границы экрана и туда, где расположен какой-нибудь спрайт. Причём этот спрайт может иметь любой название и костюм.
Поначалу Алиса никак не могла справиться со своим фламинго: только сунет его вниз головой под мышку, отведет ему ноги назад, нацелится и соберется ударить им по ежу, как он изогнет шею и поглядит ей прямо в глаза, да так удивленно, что она начинает смеяться; а когда ей удастся снова опустить его вниз головой, глядь! - ежа уже нет, он развернулся и тихонько трусит себе прочь.
ЭТАП 3. Создаём спрайт "Бочка1", который можно будет толкать по сцене.
Добавим в наш проект полезный груз. У нас уже есть спрайт со "стрелочкой", замените ему костюм с использованием файла "бочка.png". Дайте спрайту название "Бочка1".
Скрипт данного спрайта должен предусматривать отодвигание от любого другого спрайта, которого он касается, а в случае касания нескольких - отодвигание от каждого из них. Поэтому придётся немножко поработать со списками. Мы будем проверять список спрайтов-соседей элемент за элементом. Блок "сразу" заставляет эту часть скрипта выполняться максимально быстро.
Проверьте, как всё функционирует. Робот толкает груз!
Алиса робко подошла к двери и постучала.
- Не к чему стучать, - сказал Лакей. - По двум причинам не к чему. Во-первых, я с той же стороны двери, что и ты. А во-вторых, они там так шумят, что никто тебя все равно не услышит.
И правда, в доме стоял страшный шум - кто-то визжал, кто-то чихал, а временами слышался оглушительный звон, будто там били посуду.
ЭТАП 4. Добавляем копии спрайта "Бочка1", и спрайты препятствий.
На основе созданной нами заготовки уже можно реализовать множество проектов, в которых робот должен будет отодвинуть препятствие в сторону, например. Но оказывается, наши спрайты уже готовы к тому, чтобы взаимодействовать друг с другом более сложным образом.
Добавьте в проект копии спрайта "Бочка1". Неважно, какие имена они при этом получат. Запустите проект и проверьте, что выйдет, если бочки "нагромождены" одна на другую.
Что будет, если толкать одну из бочек на другую, или сразу на несколько?
Наконец, добавьте спрайты статических препятствий. Используемый нами алгоритм работает оптимально со спрайтами округлой формы, но слегка вытянутые свою роль тоже выполняют. Спрайт "Стенка" препятствует сдвиганию груза вбок при перемещении.
Из подготовленных спрайтов как из конструктора можно собирать самые разнообразные игровые задания. Теперь мы в состоянии построить лабиринт, по которому робот должен будет перемещать бочки.
– ...Ты когда-нибудь видела, как рисуют множество?
– Множество чего? – спросила Алиса.
– Ничего, – отвечала Соня. – Просто множество!
– Не знаю, – начала Алиса, – может…
– А не знаешь – молчи, – оборвал ее Болванщик.
ЭТАП 5. Робот с захватами. Осваиваем операции с множествами.
Было бы неплохо снабдить нашего робота "лапками" или "захватами", чтобы он был способен без проблем перемещать груз в произвольном направлении по свободному полю сцены. Захваты нужны, чтобы груз не смещался в сторону при движении. Добавить роботу "лапки" можно путём объединения нескольких спрайтов в единый взаимосвязанный объект.
Метод объединения нескольких спрайтов уже был рассмотрен в уроке "Ша г за шагом моделируем поведение робота в среде Snap4arduino ". Повторим.
Добавьте новый стандартный спрайт, переименуйте, замените костюм.
Подберите подходящий масштаб отображения, например, 50%. Щёлкните по соответствующему блоку.
Разместите "Захват1" так, чтобы он располагался спереди слева по направлению движения робота.
Сдублируйте спрайт, назовите "Захват2". Разместите спереди справа.
Ухватите мышкой иконку спрайта "Захват1" и бросьте на изображение робота на сцене.
Теперь на панели спрайтов иконки "Робот1" и "Захват1" располагаются рядом. Кроме того, появилась маркировка, что эти спрайты связаны.
Повторите операцию для спрайта "Захват2".
Готово, спрайт "Робот1" является хозяином для спрайтов "Захват1" и "Захват2".
Запустите программу, проверьте возможность управления роботом. Он поворачивается вместе со своими захватами, но сдвигаться с места отказывается. Всё выглядит так, как будто робот касается препятствия.
Так и есть: робот теперь непрерывно "касается" собственных захватов. Это можно проверить, выбрав спрайт робота и используя соответствующие блоки.
Чтобы восстановить работоспособность программы и сохранить её универсальность (отсутствие зависимости от имён подчинённых спрайтов и даже от их количества), нам придётся освоить операции с множествами.
Блок "атрибут соседи" возвращает список расположенных поблизости спрайтов, то есть множество некоторых объектов.
Блок "атрибут части" возвращает множество объектов, которые принадлежат роботу.
Осталось вычесть одно множество из другого, чтобы появилась возможность узнать, касается ли робот какого-то объекта, который не является его собственной частью.
Мы создадим специальный блок с названием "касается_препятствия", который будет возвращать одно из двух значений: "истина" или "ложь". Внутри этого блока будет происходить вычитание одного множества из другого.
Проверьте, что выбран спрайт "Робот1". Создайте блок, указав опцию "только для текущего спрайта" и тип блока "Предикат".
Скрипт "касается_препятствия" выглядит так. Для изменения количества "окошечек" в блоках "список" и "переменные скрипта" пользуйтесь стрелочками. Блок "список" с нулевым количеством элементов возвращает пустой список (пустое множество).
Проверьте функционирование блока "касается_препятствия", перетаскивая спрайты и щёлкая по блоку курсором мыши.
Поправьте скрипт блока "переместиться".
Робот снова начал выполнять наши команды. И бочка при перемещении не пытается "уползти" в сторону. Теперь можно будет запрограммировать робота для самостоятельного передвижения и выполнения какого-нибудь игрового задания.
- Что это ты выдумываешь? - строго спросила Гусеница. - Да ты в своем уме?
- Не знаю, - отвечала Алиса. - Должно быть, в чужом. Видите ли...
- Не вижу, - сказала Гусеница.
- Боюсь, что не сумею вам все это объяснить, - учтиво промолвила Алиса. - Я и сама ничего не понимаю. Столько превращений в один день хоть кого собьет с толку.
ЭТАП 6. Сохраняем результаты.
Мы проделали много работы. Плодами нашего труда можно будет пользоваться словно элементами конструктора, собирая из готовых спрайтов и блоков новые проекты.
Для пользования созданными программными компонентами не обязательно даже чётко понимать, как именно они работают. Например, мы можем кому-нибудь предоставить файлы с экспортированными спрайтами, он придумает дизайн игрового задания и разместит все объекты, после чего передаст кому-то третьему со словами: "Составь такой алгоритм, чтобы робот, перемещаясь по полю, выполнил своё задание".
Итак, приступим. Первым делом сохраним сам проект на свой жёсткий диск. Следует задать имя и выбрать "Computer". Полученный файл имеет расширение "xml".
Теперь сохраним ещё и отдельные спрайты "Робот1" и "Бочка1", поскольку они подготовлены нами для самостоятельной работы в любом новом окружении. Щёлкните правой кнопкой мыши по спрайту и воспользуйтесь контекстным меню.
При экспорте спрайта "Робот1" в файл будут автоматически добавлены и спрайты "Захват1" и "Захват2".
Файлы экспорта, содержащие спрайты, тоже получают расширение "xml". Рекомендуется в имя файла включать упоминание о том, что это именно спрайт, а не целый проект.
В любой новый частично или полностью созданный проект можно будет добавить готовые спрайты (а также блоки, см. ниже) командой "Импорт".
Следует помнить о том, что если при импорте выбрать файл, содержащий не спрайт или библиотеку блоков, а обычный проект, прежний проект, открытый в среде Snap4arduino, будет без предупреждений заменён новым. Рекомендуется сохранять результаты своей работы перед попыткой импортировать дополнительные объекты.
Можно ли созданные нами сегодня блоки со скриптами использовать с любыми другими спрайтами? Да, можно, но требуется их сохранить с опцией "для всех спрайтов". Кроме того, в некоторых случаях будет полезно подготовить эти блоки таким образом, чтобы их запуск был возможен даже Сценой.
Давайте это сделаем. Создадим универсальный блок, возвращающий список (с типом "Генератор значений") и названием "разность_множеств". Не обязательно собирать его полностью заново, можно просто скопировать и немного поправить скрипт блока "касается_препятствия".
Проверим работу на элементах одного типа.
А теперь на элементах другого типа.
Как видите, блок работает независимо от типа элементов. Точно таким же образом при необходимости можно создать "объединение_множеств" и "пересечение_множеств".
Все необходимые блоки можно экспортировать для дальнейшего использования командой меню "Экспортировать блоки". Можно выбрать именно те, которые мы хотим сохранить в отдельном xml-файле.
Алиса просунула ногу подальше в камин и стала ждать. Наконец, она услышала, что в дымоходе прямо над ней кто-то шуршит и скребется (что это был за зверек, она не могла догадаться).
- А вот и Билль! - сказала она про себя и изо всех сил поддала ногой. - Интересно, что теперь будет!
Сначала она услышала, как все закричали:
- Билль! Билль! Вон летит Билль!
Потом голос Кролика:
- Эй, там, у кустов! Ловите его!
Итоги, планы на будущее.
Используемая нами модель столкновений ещё далека от совершенства. Бочки стремятся отодвинуться от центра препятствия, между тем как со спрайтами вытянутой формы это работает плохо. Если усовершенствовать модель столкновений, можно будет добавлять в проекты объекты, перемещающиеся по инерции, и правдоподобным образом отскакивающие от препятствий произвольной формы.
Тем не менее, даже с учётом существующих недостатков, подготовленные спрайты с запрограммированной реакцией на столкновения позволяют реализовать много интересных учебных и игровых проектов.
- С позволения Вашего Величества, - сказал Валет, - я этого письма не писал, и они этого не докажут. Там нет подписи.
- Тем хуже, - сказал Король. - Значит, ты что-то дурное задумал, а не то подписался бы, как все честные люди.
Все зааплодировали: впервые за весь день Король сказал что-то действительно умное.
- Вина доказана, - произнесла Королева. - Рубите ему...