ББК 32.973.23-018.9 УДК 004.388.4 К70 К70 Корягин Андрей Roblox: играй, программируй и создавай свои миры. — СПб.: Питер, 2021. — 240 с.: ил. — (Серия «Программирование для детей»). ISBN 978-5-4461-1491-7 Что такое Roblox? Это красочная компьютерная платформа для создания и публикации своих игр. Мир Roblox изо дня в день становится все популярней, и ты можешь стать его частью! Если ты мечтаешь стать геймдизайнером и создать свою игру, но не знаешь, с чего начать, эта книга поможет тебе! yy Не требуется предварительных знаний — ты можешь начать делать игру сразу! yy Мир Roblox интуитивно понятен и очень нравится детям и взрослым. yy В книге даны очень подробные пошаговые инструкции по установке и работе с Roblox. yy Есть введение в программирование и язык Lua. yy Наглядные инструкции по созданию игровых объектов (каждое действие иллюстрируется скриншотом). yy Интересные задания для самостоятельной работы. «Roblox: играй, программируй и создавай свои миры» станет верным другом, поможет в кратчайшие сроки освоить игровую среду, удивлять друзей и родителей крутыми играми и даже заработать! 16+ (В соответствии с Федеральным законом от 29 декабря 2010 г. № 436-ФЗ.) ББК 32.973.23-018.9 УДК 004.388.4 Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав. ISBN 978-5-4461-1491-7 © ООО Издательство «Питер», 2021 © Серия «Программирование для детей», 2021 © Андрей Корягин, 2021 ОГЛАВЛЕНИЕ Введение . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 От издательства . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Глава 1. Roblox и Roblox Studio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 Обзор Roblox и регистрация аккаунта . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Установка Roblox Studio. Знакомство с инструментарием . . . . . . . . . . . 18 Процесс создания мира (локации) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 Глава 2. Основы языка Lua в Roblox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 История Lua и предназначение . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 Типы данных и операции над типами данных . . . . . . . . . . . . . . . . . . . . . . 34 Математические операторы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 Таблицы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Условия . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Циклы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Функции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 Библиотеки Lua . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 Векторы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Глава 3. Оcновы игровой механики в Roblox . . . . . . . . . . . . . . . . . . . . . . . 55 Физические особенности игровых объектов . . . . . . . . . . . . . . . . . . . . . . . 56 Создание линейного движения объекта . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 Вращение объекта . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 Настройка взаимодействия игрока с объектами . . . . . . . . . . . . . . . . . . . . 90 Нанесение урона . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 Смена дня и ночи . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 Левитирующий лифт . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 Телепортация . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 Строительство . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 Сбор объектов на время . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 3 Взрывы и разрушения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 Создание инвентаря . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 Создание canvas. GUI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 Анимация персонажа или бота . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 Диалоги . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 Сохранения достижений игрока . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 Управление персонажем — тачскрин . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 Звуки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214 Движение NPС . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 Стрельба . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231 Заключение . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236 Предметный указатель . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237 ВВЕДЕНИЕ Привет! Ты держишь в руках книгу, которая поможет тебе научиться создавать свои игры в Roblox, а еще изучить основы программирования на языке Lua. Содержание книги будет понятно как новичкам в изучении Lua, так и начинающим геймдизайнерам. Вместе мы научимся применять язык Lua для создания разных игровых механик, распространенных в Roblox, а затем опубликуем готовую игру в сообществе платформы. Стиль повествования и уровень сложности содержания книги адаптированы для детей 10 лет. Книга ведет читателя от простого к сложному, и каждая глава является продолжением предыдущей. Почти в каждой теме есть задания для закрепления, которые делятся на три категории по степени сложности. Эта книга может быть использована преподавателями информатики или программирования как учебное пособие для изучения программирования на языке Lua. Все проекты в книге разрабатываются в Roblox Studio. Это специальная программа для разработки игр. Но перед тем как воспользоваться этой программой, нужно пройти регистрацию на сайте Roblox. Что же такое Roblox? Roblox — это онлайн-платформа, где каждый пользователь может создать свою игру, а также играть в игры, опубликованные другими участниками. На платформе собраны виртуальные миры, охватывающие множество жанров, от традиционных гонок и ролевых игр до симуляций и полос препятствий. Программа появилась в 2004 году и начала бурно развиваться. Сегодня сообщество Roblox — это более 100 миллионов пользователей! А количество опуб­ ликованных игр составляет несколько десятков миллионов. Прочитав книгу, ты сможешь не просто играть в игры, а стать их разработчиком и даже зарабатывать. В игры, которые создаются в Roblox, можно играть на самых популярных платформах: Windows, MacOS, iOS, Android и Xbox One. Такая мультиплатформенность избавляет разработчика от необходимости адаптировать свою игру под разные операционные системы. Это, конечно, большой плюс для начинающего создателя игр. Все представленные проекты можно будет скачать по ссылке с GitHub https:// github.com/Antipat/Roblox_LUA. 5 В книге используются следующие условные обозначения: Таким шрифтом в тексте выделены элементы программного кода. Таким шрифтом в тексте выделены элементы интерфейса Roblox Studio, адреса ссылок и названия файлов. ! Важная информация. ? Вопросы для самопроверки. Задание легкого уровня. Задание среднего уровня. Задание повышенной сложности. ОТ ИЗДАТЕЛЬСТВА Ваши замечания, предложения, вопросы отправляйте по адресу comp@piter.com (издательство «Питер», компьютерная редакция). Мы будем рады узнать ваше мнение! На веб-сайте издательства www.piter.com вы найдете подробную информа­цию о наших книгах. 1 ROBLOX И ROBLOX STUDIO В этой главе мы рассмотрим программы компании Roblox и их инструментарий. Мы изучим процедуру регистрации, установки приложений на компьютер и телефон. Познакомимся с инструментами Roblox Studio на примере создания локации. ОБЗОР ROBLOX И РЕГИСТРАЦИЯ АККАУНТА Как мы уже говорили, Roblox — многопользовательская онлайн-платформа, на которой можно играть в игры или создавать игры и размещать их. Игры разрабатываются исключительно в Roblox Studio. Играть можно только в игры, созданные на этом игровом движке. Таким образом, разработка игр и их выбор ограничиваются возможностями движка Roblox Studio и правилами самой компании. Но Roblox дает и ряд преимуществ, среди которых легкая публикация игр и несложная разработка, мультиплатформенность для охвата большого количества пользователей, неограниченность жанров, за исключением тех, которые противоречат политике организации. Для начала работы нужно зарегистрироваться в Roblox. Для этого необходимо перейти на сайт платформы. РИС. 1.1. РЕГИСТРАЦИЯ В ROBLOX 8 РИС. 1.2. РЕГИСТРАЦИЯ В ROBLOX Если ты впервые на сайте Roblox, тебе будет предложена регистрация аккаунта. Регистрация нужна, чтобы ты мог пользоваться всеми доступными инструментами, а также привязывать свои игры к собственному аккаунту и размещать их в сообществе Roblox. Также аккаунт нужен, чтобы хранить свои достижения в играх, в которые ты играешь. Если ты уже указал дату рождения и пол, придумал имя для аккаунта и пароль, пора нажать на зеленую кнопку Sign up. Теперь нужно пройти аутентификацию на подтверждение, что ты не бот. Тут нужно быстро вращать животных так, чтобы они располагались горизонтально. Нажимай на стрелки по бокам, чтобы вращать картинку. После того как ты зарегистрировался и выбрал аватар, нужно рассмотреть основной инструментарий в личном кабинете пользователя. 9 РИС. 1.3. ПРОХОЖДЕНИЕ АУТЕНТИФИКАЦИИ В зависимости от пола, выбранного при регистрации, ты получишь шаблон аватара (мальчик или девочка). Ты можешь настроить его внешность. Нажми левой кнопкой мыши (ЛКМ) на фотографию аватара либо ЛКМ на слово Avatar. Первая настройка быстрее, зато вторая позволит детальнее проработать персонаж (рис. 1.4). РИС. 1.4. ЛИЧНЫЙ КАБИНЕТ ПОЛЬЗОВАТЕЛЯ ROBLOX 10 На рис. 1.5 изображены аватар и инструменты для его редактирования. РИС. 1.5. НАСТРОЙКА АВАТАРА В ROBLOX Ты можешь собрать аватар с любой внешностью, последовательно меняя его по частям: сначала волосы, потом одежду, лицо, конечности и структуру тела. Выбери какую-нибудь одежду для аватара из рекомендованного. РИС. 1.6. РЕДАКТИРОВАНИЕ ВНЕШНОСТИ АВАТАРА 11 После того как одежда выбрана, нажми Try on, чтобы посмотреть, как выглядит аватар. Если тебе не нравится, нажимай кнопку Take off. Если тебе все нравится, то щелкни по значку «…» рядом с названием одежды, а затем на слове Wear. Сразу после этого костюм привяжется к аватару (рис. 1.7). Одежда Снять одежду РИС. 1.7. ВЫБОР ОДЕЖДЫ ДЛЯ АВАТАРА После завершения настройки аватара можно перейти на вкладку Home. РИС. 1.8. ДОМАШНЯЯ СТРАНИЦА ЛИЧНОГО КАБИНЕТА Здесь будут представлены два списка: игры, в которые ты играл (1), и рекомендованные игры (2) (рис. 1.8). 12 Теперь стоит быстро изучить основное меню, которое располагается слева. Вкладка Profile содержит информацию о профиле игрока и средства редактирования. РИС. 1.9. ПРОФИЛЬ ПОЛЬЗОВАТЕЛЯ Вкладка Messages содержит информацию о сообщениях, которые ты получаешь от пользователей либо от администраторов Roblox. РИС. 1.10. СООБЩЕНИЯ ДЛЯ ПОЛЬЗОВАТЕЛЯ 13 На вкладке Friends ты можешь найти своих виртуальных друзей, с которыми познакомился, играя в игры. РИС. 1.11. ДРУЗЬЯ ПОЛЬЗОВАТЕЛЯ На вкладке Avatar содержатся все данные о детальной настройке твоего аватара, в том числе о приобретении нужных частей тела или одежды с аксессуарами. РИС. 1.12. НАСТРОЙКА АВАТАРА ПОЛЬЗОВАТЕЛЯ 14 На вкладке Inventory описан твой инвентарь, который можно получить как бесплатно, так и в специальных магазинах за деньги. РИС. 1.13. ВКЛАДКА ИНВЕНТАРЯ На вкладке Trade ты найдешь информацию о твоих покупках с местной валютой Roblox. Эта валюта называется Robux. Справа в профиле указано твое количество Robux. Валюта Robux РИС. 1.14. ТОРГОВЛЯ ROBUX Вкладка Groups содержит информацию о существующих группах. Все группы распределены по тематике, и здесь ты можешь выбрать группы, темы которых тебе понравились. 15 РИС. 1.15. ГРУППЫ ПО ИНТЕРЕСАМ На вкладке My Feed собрана информация о каналах, посвященных гайдам (по­ дробным руководствам) по Roblox. РИС. 1.16. КАНАЛ ПО РУКОВОДСТВУ На вкладке Blog содержится информация о блогах разработчиков Roblox. 16 РИС. 1.17. БЛОГ На вкладке Official Store — информация о магазине на Amazon. Если нажать на вкладку, тебя перебросит на страницу интернет-магазина. Там ты сможешь за реальные деньги купить сувениры или коллекционные элементы, связанные с Roblox. РИС. 1.18. ИНТЕРНЕТ-МАГАЗИН ROBLOX На вкладке Gift Cards хранятся подарочные карты, которые можно купить. С их помощью можно приобрести сувениры или игровые элементы со скидкой. 17 РИС. 1.19. ПОДАРОЧНЫЕ КАРТЫ В ROBLOX Мы изучили основные элементы меню пользователя Roblox. Это полезная информация для новичков. Освоившись в Roblox, приступай к следующему шагу — установке Roblox Studio. УСТАНОВКА ROBLOX STUDIO. ЗНАКОМСТВО С ИНСТРУМЕНТАРИЕМ Перед тем как приступить к установке Roblox Studio, рекомендую установить Roblox Player. Roblox Player — это программа для запуска игр и отображения игрового процесса на мониторе. Для установки достаточно запустить хотя бы одну игру. Чтобы выбрать игру, нажми одну из вкладок — Games или Play Games. РИС. 1.20. ЗАПУСК ИГРЫ И УСТАНОВКА ROBLOX PLAYER 18 При попытке запуска игры тебе придет уведомление с предложением скачать и установить Roblox Player. РИС. 1.21. УСТАНОВКА ROBLOX PLAYER Щелкни на кнопке Download and Install Roblox и подожди загрузки установщика. Для разных браузеров загрузка отображается по-разному, но ты всегда можешь увидеть скачанный файл в папке Загрузки. Для установки плеера запусти файл установки, щелкнув на нем ЛКМ. РИС. 1.22. УСТАНОВКА ROBLOX PLAYER 19 После завершения установки Roblox Player можно запустить выбранную игру. Тебе сразу предложат дать разрешение на запуск приложения — нужно его дать. РИС. 1.23. ЗАПУСК ИГРЫ В ROBLOX РИС. 1.24. ЗАПУСК ИГРЫ В ROBLOX 20 Теперь ты можешь запускать любые игры Roblox и тестировать их. Рекомендую делать это почаще, так как при этом ты: отмечаешь интересные игровые решения: сценарий, локацию, игровые возможности; обращаешь внимание на основные элементы игровой механики, которые пользуются большим спросом: приобретение оружия и одежды, общение с игроками и т. д. Чтобы выйти из игры, нужно либо закрыть окно Player, нажав на крестик в правом углу окна, либо нажать клавишу Esc. Остался важный шаг — установка Roblox Studio. Чтобы сделать это, перейди на вкладку Create на странице своего профиля и щелкни на Start Creating. РИС. 1.25. УСТАНОВКА ROBLOX STUDIO Тебе будет предложено скачать установщик Roblox Studio. Как только загрузка завершится, запусти его и дождись полной установки. 21 РИС. 1.26. УСТАНОВКА ROBLOX STUDIO РИС. 1.27. УСТАНОВКА ROBLOX STUDIO Когда установка завершится, на рабочем столе появится синий рассеченный квадрат — это ярлык программы Roblox Studio. Roblox Studio — это та среда, которая поможет тебе создавать игры любого жанра. Запусти программу (рис. 1.28). Поздравляю! Ты успешно прошел все процессы установки! Теперь приступим к созданию твоего первого виртуального мира. 22 РИС. 1.28. НАЧАЛЬНОЕ ОКНО ROBLOX STUDIO ПРОЦЕСС СОЗДАНИЯ МИРА (ЛОКАЦИИ) Сначала Roblox Studio предложит тебе выбрать основу под локацию. Как видно на рис. 1.28, существуют уже готовые шаблоны для определенного сюжета игры. Чтобы более полно рассмотреть процесс создания виртуального мира, выберем пустую локацию под названием Baseplate. На ней мы и попробуем создать свое игровое окружение. Однако, чтобы создавать свой мир осмысленно, стоит изучить инструментарий. РИС. 1.29. ИНСТРУМЕНТАРИЙ ROBLOX STUDIO 23 Обрати внимание на рис. 1.29, изображающий рабочее пространство студии. Он разбит на основные части инструментария. Кратко пройдемся по ним. 1. Основная панель инструментов. Здесь ты можешь: ààсоздавать новый проект и сохранять его; ààсоздавать локацию с помощью встроенных инструментов: использовать основные геометрические фигуры, настраивать цвета, изменять размеры и положение тел, а также их вращать; ààнастраивать физические свойства созданных тел; ààтестировать и отлаживать игры; ààдобавлять плагины. 2. Панель инструментов. Здесь собраны готовые: ààмодели с настроенной физикой; ààизображения; ààмеши (3D-объекты); ààзвуки; ààплагины для игры. 3. Инструмент для редактирования поверхности локации: ààсоздание поверхности земли с помощью параметров; ààсоздание поверхности с помощью импортированных моделей; ààсброс настроек. 4. Рабочее поле — окно 3D-сцены твоей игры (одной из сцен игры). 5. Окно отладки. Здесь отображаются работа твоих скриптов в игре, продуктивность самой игры и ее ошибки. 6. Окно свойств. Здесь отображаются редактируемые свойства объектов, которые располагаются на 3D-сцене. 7. Структура твоего проекта. Здесь есть шаблон структуры проекта, разбитый на папки, в которых размещаются модели, скрипты, эффекты, изображения, звуки и т. д. Теперь у тебя есть краткое представление о возможностях среды! Более подробно мы изучим каждый инструмент на практике. СОЗДАЕМ ЗЕМЛЮ Создать свой биом (землю с различной почвой, ландшафтом, водой, лавой и горными породами) можно несколькими способами: сгенерировать биом; импортировать биом; создать биом вручную. 24 Все три способа доступны в одном окне Terrain Edit — редактирования поверхности (ландшафта) локации (окно 3 на рис. 1.29). Рассмотрим два способа: генерацию ландшафта и ручное моделирование. ГЕНЕРАЦИЯ ЛАНДШАФТА Чтобы сгенерировать ландшафт, пользуясь возможностями среды, достаточно перейти на вкладку Create окна Terrain Edit. До начала работы ты можешь сделать предварительные настройки параметров: Position — устанавливает центр ландшафта по координатам x, y и z; Size — устанавливает размер земли (локации) также по координатам x, y и z; Material Setting — отвечает за особенности твоего ландшафта (наличие равнин, холмов, гор, пустыни (песка), воды, лавы, заснеженных районов, каньонов, болота); Biome size — устанавливает глубину распределения выбранных параметров ландшафта (Material Setting); Seed — регулирует «шум» распределения элементов биома. Чем меньше значение, тем более резкие будут участки перехода ландшафта. РИС. 1.30. РАЗМЕР И ПОЗИЦИЯ ЛАНДШАФТА РИС. 1.31. НАСТРОЙКА ПАРАМЕТРОВ ГЕНЕРАТОРА ЗЕМЛИ На рис. 1.32–1.34 показаны различные биомы в зависимости от значений Biome Size и Seed. 25 РИС. 1.32. ГЕНЕРАЦИЯ ЛАНДШАФТА ПРИ БОЛЬШЕМ ЗНАЧЕНИИ BIOM SIZE И SEED РИС. 1.33. ГЕНЕРАЦИЯ ЛАНДШАФТА ПРИ МАЛОМ ЗНАЧЕНИИ BIOME SIZE И БОЛЬШОМ ЗНАЧЕНИИ SEED РИС. 1.34. ГЕНЕРАЦИЯ ЛАНДШАФТА ПРИ НАИМЕНЬШИХ ЗНАЧЕНИЯХ BIOME SIZE И SEED 26 Изменяя данные параметры, ты можешь создать свой уникальный ландшафт! Возможно, после генерации ландшафта стандартная платформа серого цвета Baseplate будет смотреться некрасиво. Ее можно удалить или сделать прозрачной. Для удаления нужно в окне структуры твоего проекта щелкнуть на названии объекта Baseplate и вызвать контекстное меню правой кнопкой мыши. Среди появившихся команд выбери Delete. Команда Delete РИС. 1.35. УДАЛЕНИЕ ОБЪЕКТА СО СЦЕНЫ Теперь, когда ты создал свой ландшафт, можно протестировать его! Для этого нужно запустить игру. Так ты можешь протестировать ландшафт РИС. 1.36. ЗАПУСК ИГРЫ Как только игра запустится, твой аватар перебросят на эту локацию в координаты x = 0, y = 0, z = 0. Для завершения игры просто нажми красный квадрат. 27 Stop — останавливает тестирование игры РИС. 1.37. ТЕСТИРОВАНИЕ ПЕРВОЙ ИГРЫ Рекомендую протестировать управление персонажем и изучить игровую механику, связанную с ним. Камеру можно перемещать, вращая колесико мыши. Тем самым ты можешь настроить управление и вид от первого или третьего лица. СОЗДАНИЕ ЛАНДШАФТА ВРУЧНУЮ Кроме автоматической генерации ландшафта, можно создать его вручную. Для этого перейди на вкладку Edit окна Terrain Edit. Здесь можно отредактировать локацию РИС. 1.38. РУЧНОЕ РЕДАКТИРОВАНИЕ ЛОКАЦИИ 28 Здесь представлены инструменты для ручного редактирования, давай изучим их. РИС. 1.39. ИНСТРУМЕНТ ДОБАВЛЕНИЯ ЭЛЕМЕНТА ЛАНДШАФТА Область 1 Add — добавление элемента ландшафта (один из ключевых инструментов). Subtract — обрезание (понижение) частей созданного ландшафта. Grow — создание возвышенностей из добавленных элементов ландшафта. Erode — создание углублений из добавленных элементов ландшафта. Smooth — сглаживание элементов ландшафта. Flatten — выравнивание, процедура, похожая на Smooth. Paint — рисование разных текстур на уже созданном ландшафте. Sea level — уровень океана (моря). Область 2 Этот раздел есть во всех инструментах области 1, за исключением Sea level. Здесь можно выбрать форму кисти, которой ты будешь работать: создавать, сглаживать, выравнивать. Кисть имеет три формы: сфера, куб и цилиндр. Base Size — настраивает размер кисти. Pivot Position — точка создания элемента ландшафта. Это хорошо видно на Baseplate. 29 РИС. 1.40. РАСПОЛОЖЕНИЕ ЛАНДШАФТА СОГЛАСНО PIVOT Snap to Grid — при активации этого пункта создание ландшафта будет проходить строго по сетке (в одной плоскости). Создание ландшафта строго по сетке РИС. 1.41. ПОСТРОЕНИЕ В ОДНОЙ ПЛОСКОСТИ Ignore Water — если активизировать этот пункт, то созданные элементы будут игнорировать расположение воды и замещать ее. Этот параметр не позволяет редактировать воду никакими доступными инструментами. Область 3 Material Settings — нужно выбрать материал, из которого будет состоять ландшафт. Ландшафт с указанным материалом создается выбранной кистью при нажатой ЛКМ. Пример создания локации вручную ты можешь увидеть на рис. 1.42. 30 РИС. 1.42. ЛОКАЦИЯ, СОЗДАННАЯ ВРУЧНУЮ ? 1. Как можно настроить внешность твоего аватара? 2. Что отображает окно Properties в Roblox Studio? 3. За что отвечает окно Terrain Edit? 4. Опиши процесс случайной генерации ландшафта. 5. За что отвечает функция Grow в окне Edit при создании ландшафта? Создай локацию: остров посреди моря. В качестве примера смотри рис. 1.43. РИС. 1.43. ОСТРОВ В ОКЕАНЕ 31 Создай локацию: вулкан, извергающий лаву в море. Пример см. на рис. 1.44. РИС. 1.44. ВУЛКАН Мы подробно рассмотрели инструментарий Roblox Studio и научились проектировать локации. Чтобы материал усвоился лучше, выполни задания и ответь на вопросы главы. Перед тем как приступить к созданию своих игр, тебе нужно познакомиться с языком программирования Lua, который используется в Roblox для создания игр. 2 ОСНОВЫ ЯЗЫКА LUA В ROBLOX Ты можешь создавать не только свои локации, но и полноценные игры. Для этого нужно познакомиться с языком программирования Lua. С его помощью создается логика взаимодействия персонажа с объектами или поведения объектов с другими объектами игры. Чем глубже ты вникнешь в процесс программирования и создания алгоритмов, тем лучше и качественнее будет игра (приложение). ИСТОРИЯ LUA И ПРЕДНАЗНАЧЕНИЕ Язык Lua был разработан в Католическом университете Рио-де-Жанейро на базе языка С, первая версия появилась в 1993 году. Этот язык относится к виду скриптовых языков наравне с JavaScript и Python, то есть код такого языка выполняется покомандно. Язык Lua, как и Python, популярен благодаря простой и краткой записи кода. В первую очередь Lua предназначен для начинающих программистов или пользователей, которых нельзя назвать профессиональными программистами. Lua — язык с динамической типизацией. Динамическая типизация — это тип работы с переменными. При динамической типизации тип переменной определяется уже в момент выполнения программы. При статической это происходит заранее. Языки с динамической типизацией: Lua, Python, JavaScript, Perl, PHP… Языки со статической типизацией: C, C++, C#, Java, Delphi. Lua стал широко известен как язык программирования уровней и расширений в компьютерных играх. ТИПЫ ДАННЫХ И ОПЕРАЦИИ НАД ТИПАМИ ДАННЫХ ПЕРЕМЕННЫЕ ! Первым делом нужно рассмотреть основы любого языка — это типы данных, с которыми он работает. Числа: целые, числа с плавающей точкой, экспоненциальная форма записи числа. Строки: текстовые символы. Логический: имеет два логических значения — true и false. Таблицы: массивы, списки, словари. Функции: подпрограмма. Nill: пустое значение (что-то, что не имеет значения). Переменные — именованная ячейка памяти. Разные значения разных типов данных мы можем присвоить ячейке памяти с именем и в дальнейшем вызывать ее. РИС. 2.1. ДВА ТИПА ПЕРЕМЕННЫХ 34 Глобальная переменная может быть использована в любой части кода программы. Локальная переменная может использоваться только с начала появления в коде программы. Если нужно указать, что переменная локальная, то пропиши перед ней оператор local. Какие могут быть переменные? Переменные могут быть записаны так: a, b, c, d, e, f; A, B, C, D, E, F; a1, a2, a3, a4, a5… x1, x2, x3; Name, long, age, La_Grange. Рассмотрим на примерах в Roblox Studio эти типы данных и переменные. Для этого запусти пустое окно локации Baseplate. В окне Explorer напротив слова Workspace нажми на значок + и создай Script. Рабочая область РИС. 2.2. СОЗДАНИЕ СКРИПТА Script будет содержать код нашей программы, который при запуске игры будет вы- полняться. Результат выполнения программы в первую очередь будет отображаться в окне Output. После создания скрипта откроется окно Script. 1 2 print("Hello world!") РИС. 2.3. ОКНО SCRIPT 35 По умолчанию здесь будет функция print(). Ее задача — выводить значения, указанные в скобках. Для примера указана строка "Hello world!". Как можно заметить, строка записана в кавычках. Запусти игру, нажав Play, и ты увидишь, что после загрузки в окне Output появится Hello world!, но уже без кавычек. РИС. 2.4. ЗАПУСК ИГРЫ СО СКРИПТОМ Выйди из игры, нажав Stop (красный квадрат), и перейди обратно в скрипт. Рассмотри различные варианты чисел, которые можно использовать в Lua. Для этого нужно создать несколько переменных и присвоить им числовые значения, а затем вывести их в игре. 1 2 3 4 5 6 7 8 9 10 11 a=2 b=0.5 c=2.234564 d=-5 f=34.55e+6 print(a) print(b) print(c) print(d) print(f) РИС. 2.5. ЧИСЛОВОЙ ТИП ДАННЫХ Обрати внимание, что в записи дробных чисел целую часть отделяют от дробной с помощью точки. Пробежимся по числам. В переменную a записано целое число, в b — дробь с точностью до десятых, в с — дробь с точностью до шестого знака, в d — целое отрицательное число, в f — экспоненциальное число. 36 С какими-то типами чисел ты, наверное, уже знаком, а с какими-то только предстоит познакомиться на уроках математики. Запустим наш пример в Roblox Studio и посмотрим результат. 2 0.5 2.234564 –5 34550000 РИС. 2.6. РЕЗУЛЬТАТ РАБОТЫ СКРИПТА На фото видно, что числа выведены так же, как они были записаны, за исключением последнего варианта. В этом варианте программа преобразовала эту запись в число. Теперь рассмотрим, что такое строки. Самые внимательные читатели, скорее всего, заметили, что строки — это любые символы, заключенные в кавычки. Рассмотрим несколько примеров строковых значений. Для этого очистим предыдущий скрипт и перезапишем его в таком виде. 1 2 3 4 5 6 7 8 9 10 11 a= "Name" Ь="Вася" c="356" d="Lua и Roblox" f="100 игр Roblox" print(a) print(b) print(c) print(d) print(f) РИС. 2.7. СТРОКИ Для разнообразия используем символы кириллицы, английского алфавита и числа. Запустим игру и посмотрим результат. Name Вася 356 Lua и Roblox 100 игр Roblox РИС. 2.8. ВЫВОД СТРОК 37 Ошибок не возникло. Приведем для наглядности пример с самой распространенной ошибкой. 1 2 3 4 а=Вася print(a) 12:39:29.952 - Baseplate auto-recovery file was created 12:39:30.442 - Workspace.Script:1: Unexpected Unicode character: U+412. Did you mean 'B'? РИС. 2.9. ОШИБКА В КОДЕ Здесь, как ты видишь, забыли кавычки. Заметь, что текст об ошибке содержит информацию о скрипте, в котором произошла ошибка, и строку, на которой она произошла. Рассмотрим теперь логический тип данных. Логический тип данных имеет два значения — true и false. true = истина = 1. false = ложь = 0. Для логического типа данных существует таблица истинности, которая строится на логических операторах: not, and, or. Эти операторы называются так: «отрицание», «конъюнкция» и «дизъюнкция». A B A and B A or B not A 1 1 1 1 0 0 1 0 1 1 1 0 0 1 0 0 0 0 0 1 Ниже представлен пример логической операции из таблицы истинности. 1 2 3 4 5 6 a = true b = false c= a and b print(c) 13:41:04.423 - Baseplate auto-recovery file was created false РИС. 2.10. ПРИМЕР ЛОГИЧЕСКОЙ ОПЕРАЦИИ 38 Как видно из примера, переменная a присвоила значение true (истина), а переменная b — false (ложь). При конъюнкции (a and b) согласно таблице истинности мы получим false. Такой же результат нам дает программа в Roblox. Логические операции очень важны при создании игр. Любые действия персонажей или объектов постоянно проверяются на истинность или ложность. Теперь, когда ты познакомился с основными типами данных, посмотрим, как переводить один тип в другой. Речь пойдет о переходе из числа в строку и обратно. Есть две функции: tostring() и tonumber(). Первая преобразует числовой тип данных в строковый, а вторая — строковый в числовой. Но есть один нюанс: перевести строковый тип в числовой можно, если строкой является число. Чтобы совсем не запутаться, давай перейдем к примеру. Возьмем переменную a и присвоим ей значение 10, а затем с помощью функции tostring() переведем в строковый тип данных. Чтобы удостовериться в том, что тип данных изменен, нужно получить информацию о типе данных. Это делается с помощью функции type(). 1 2 3 4 5 а = 10 b = tostring(a) print(type(b)) Тип данных — строка 19:22:44.821 - Baseplate auto-recovery file was created string РИС. 2.11. ОПЕРАЦИЯ НАД ТИПОМ ДАННЫХ Рассмотрим обратную задачу. 1 2 3 4 5 а = "10" b = tonumber(а) print (type(b)) Тип данных — число 19:34:09.048 - Baseplate auto-recovery file was created number РИС. 2.12. ОПЕРАЦИЯ НАД ТИПОМ ДАННЫХ Слово number указывает, что строка "10" была преобразована в число 10. В следующей главе эти две операции еще раз проверятся на достоверность. Мы рассмотрели основные типы данных и можем перейти к математическим операциям, которые не менее важны, чем сами данные. 39 МАТЕМАТИЧЕСКИЕ ОПЕРАТОРЫ Эти операторы не просто так называются математическими. Они тесно связаны с математикой и символьной записью математических действий и законов. + Сложение % Остаток от деления - Вычитание ^ Возведение в степень * Умножение .. Конкатенация / Деление Все математические операторы применимы только к числам, за исключением операции конкатенации. Конкатенацией связывают (складывают) строки. Рассмотрим операторы на примере в Roblox Studio. Создадим математические выражения для всех представленных операторов. Код программы — на рис. 2.13. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 a b c d e f k = 5 + 12 = 10 – 3 = 4*3 = 5/2 = 5%2 = 2^3 ="Вася ".. "любит roblox" print(a) print(b) print(c) print(d) print(e) print(f) print(k) РИС. 2.13. МАТЕМАТИЧЕСКИЕ ОПЕРАТОРЫ Посмотрим результат. 17 7 12 2.5 1 8 Вася любит roblox Кроме основных математических операторов, есть операторы сравнения. С одним из них мы уже познакомились — с оператором «присвоить» ( = ). 40 = Присвоить >= Больше либо равно == Равно <= Меньше либо равно > Больше ~= Не равно < Меньше Проверим, как работают эти операторы. 1 2 3 4 5 6 7 8 a=2 print(a) print(3==5) print(3>2) print(5<1) print(10>=8) print(10<=8) print(5~=6) 2 false true false true false true РИС. 2.14. ОПЕРАТОРЫ СРАВНЕНИЯ Проверь правильность вывода ответов. Теперь, чтобы лучше запомнить изученное, выполни несколько заданий. 1. Составь программу в Roblox Studio, которая вычислит математические выражения: a 2 12 1; 20 b 56 1440.5 ; ac 235 43 (126%5) ; 5 2 7 2 d (34 25 122 )1.5 . 5 2. Проверь истинность логического выражения, если a = true, b = false, c = true: v = (a and b or (a and c)) or (not b or c and (a or b)). 41 ТАБЛИЦЫ ! Таблицы — это обобщение таких данных, как списки, массивы, словари. Если ты уже изучал какой-нибудь язык программирования, то такое определение будет тебе понятно. Но все же ниже поясним, что такое список, массив и словарь. Список — это упорядоченная изменяемая коллекция объектов произвольных типов: a={ll, "23", 34, –8.1, "вася"} Массив — это упорядоченная структура данных, которая используется для хранения однотипных объектов: a={ll, 251, 1, 8, 5} Словарь — это неупорядоченные коллекции произвольных объектов с доступом по ключу: a={["apple"] = 1, ["raspberry"] = 2, ["orange"] = 3} b={height = 164, age = "десять", name = "Вася"} Все эти примеры имеют общее название — таблицы. Словари могут записываться двумя способами, но всегда первый элемент объекта — это ключ, а второй — значение. Например: ["apple"] — ключ, а 1 — значение. Рассмотрим процедуру создания таблицы, добавление элемента в таблицу и удаление элемента из таблицы. Скопируй представленный ниже код в скрипт Roblox Studio: а={} a[l]=33 a[2]=–55 а[3]="Имя" Сначала мы создали пустую таблицу a. Оставшиеся три строчки добавляют элементы в таблицу. Числа в квадратных скобочках — это индексы порядка расположения. В отличие от многих общеизвестных языков программирования нумерация в таб­ лицах идет с единицы, а не с нуля. Чтобы посмотреть количество элементов в таблице, достаточно написать такую строчку: print(#a) Знак # указывает на полученное количество элементов таблицы. Если добавить в наш код в Roblox Studio такую строчку, то после запуска программы в окне Output появится ответ 3. Еще можно вызвать определенный элемент из этой таблицы по индексу; если элемента нет при этом индексе, то будет выводиться ответ nil. 42 2 3 4 5 6 7 8 а = {} a[1]=33 a[2]=-55 а[3]="Имя" print(а[1]) print(а[5]) 12:46:03.123 - table.rbxl auto-recovery file was created 33 nil РИС. 2.15. ДОБАВЛЕНИЕ ЭЛЕМЕНТОВ В ТАБЛИЦУ Если нужно удалить элемент, то просто напиши nil для индекса, где он располагался. Пример представлен на рис. 2.16. 2 3 4 5 6 7 8 9 10 а = {} a[1]=33 a[2]=-55 а[3]="Имя" a[3]=nil print(а[1]) print(а[3]) print(#a) 12:56:38.367 - table.rbxl auto-recovery file was created 33 nil 2 РИС. 2.16. УДАЛЕНИЕ ЭЛЕМЕНТОВ ТАБЛИЦЫ И последнее, что мы должны рассмотреть, — это вывод информации из словарей. Так как словарь можно записать двумя способами, то и вызывать элементы словаря можно тоже двумя способами. На рис. 2.17 показан первый способ получения информации из словаря. 43 2 3 4 5 6 а = {["apple"]=1, ["raspberry"]=2, ["orange"]=3} print(a.apple) print(a.raspberry) print(a.orange) 13:03:09.561 - table.rbxl auto-recovery file was created 1 2 3 РИС. 2.17. СЛОВАРЬ И РАБОТА С НИМ Чтобы получить значения для каждого ключа, достаточно через точку указать ключ этого словаря. Такой же результат можно получить, если словарь записан так, как на рис. 2.18. 2 3 4 5 6 а = {apple=1, raspberry=2, orange=3} print(a.apple) print(a.raspberry) print(a.orange)| 13:06:41.003 - table.rbxl auto-recovery file was created 1 2 3 РИС. 2.18. СЛОВАРЬ И РАБОТА С НИМ Есть альтернативный способ вывода информации из словаря. Он ничем не лучше и не хуже предыдущего. Этот способ представлен на рис. 2.19. 2 3 4 5 6 7 а = {apple=1, raspberry=2, orange=3} print(a["apple"]) print(a["raspberry"]) print(a["orange"]) 13:11:14.849 - table.rbxl auto-recovery file was created 1 2 3 РИС. 2.19. СЛОВАРЬ И РАБОТА С НИМ 44 УСЛОВИЯ Ты уже познакомился с одним из основных типов алгоритма в программировании — с линейным алгоритмом. Линейный алгоритм — это такой алгоритм, когда все действия идут строго друг за другом. Наглядные примеры были представлены на рис. 2.13–2.14. Там есть ввод данных и действия с ними, а также вывод результата этих действий. И каждое действие начинается только после завершения предыдущего. Это самый простой алгоритм, но, конечно, есть и другие. Использовать их или нет — зависит от сложности задачи, которую нужно решить. Давай рассмотрим второй основной тип алгоритма — ветвление (условие). Ветвление — это алгоритм, в котором есть как минимум два альтернативных действия по решению задачи. Эти действия будут выполняться в зависимости от истинности поставленного условия. Характерный вид ветвления определяют операторы условия: if — если; elseif — иначе если; else — иначе; then — тогда; end — конец. Рассмотрим простой пример алгоритма на определение отрицательного или положительного числа (рис. 2.20). 2 3 4 5 6 7 8 а = 10 if а>0 then print("Положительное число") elseif а<0 then print("Отрицательное число") end 13:43:32.373 - table.rbxl auto-recovery file was created Положительное число РИС. 2.20. АЛГОРИТМ ВЕТВЛЕНИЯ Согласно представленному алгоритму переменная a со значением 10 проверяется на положительность и отрицательность. Так как 10 больше нуля, то ответ — «Положительное число». Дополнительных подусловий elseif можно ставить очень много в зависимости от задачи. Все операторы в Roblox Studio подсвечиваются темно-синим цветом. В конце ветвления обязательно нужно ставить оператор end. Давай рассмотрим задачу для закрепления: программа должна проверить возраст человека и соотнести его с примерным социальным статусом. 45 Задача. Программа проверяет возраст человека. Если возраст больше 0, но меньше 7 лет, то ответ: «Ты ходишь в детский сад». Если возраст больше или равен 7, но меньше 18 лет, то ответ: «Ты школьник». Если возраст больше или равен 18, но меньше 26, то ответ: «Ты студент». Если возраст больше или равен 26, но меньше 65, то ответ: «Ты работник». Если возраст больше либо равен 65: «Ты пенсионер». В противном случае ответ будет: «Ты не родился(ась)». Ниже представлена реализация кода для этой задачи. Попробуй создать программу самостоятельно, а если возникнут сложности, подсмотри в решение. Вместо 10 можно поставить любой возраст и проверить программу. а = 10 if а>0 and а<7 then print("Ты ходишь в детский сад") elseif а>=7 and а<18 then print("Ты школьник") elseif а>=18 and а<26 then print("Ты студент") elseif а>=26 and а<65 then print("Ты работник") elseif а>=65 then print("Ты пенсионер") else print("Ты не родился(ась)") end Усложним задачу. Пусть программа выведет примерный класс, если возраст человека будет варьироваться от 7 до 18 лет. Это дополнение покажет нам принцип вложенных условий: в одном условии может быть одно или несколько подусловий. Пример вложенного условия представлен ниже. а = 10 if а>0 and а<7 then print("Ты ходишь в детский сад") elseif а>=7 and а<18 then print("Ты школьник") if а ==7 then print("Учишься в первом классе") elseif а==8 then print ("Учишься во втором классе") elseif а==9 then print ("Учишься в третьем классе") elseif а==10 then print ("Учишься в четвертом классе") elseif а==11 then print ("Учишься в пятом классе") elseif а==12 then print ("Учишься в шестом классе") elseif а==13 then print ("Учишься в седьмом классе") 46 elseif а==14 then print ("Учишься в восьмом классе") elseif а==15 then print ("Учишься в девятом классе") elseif а==16 then print ("Учишься в десятом классе") elseif а==17 then print ("Учишься в одиннадцатом классе") end elseif а>=18 and а<26 then print("Ты студент") Все эти примеры можно опробовать в Roblox Studio. Создай программу-калькулятор — она должна вычислять, сколько плиток нужно для покрытия площади пола в ванной комнате. Для расчетов нужно указать площадь плитки и пола и количество плиток. Если количество будет достаточным, то выводится ответ: «Вы замостите пол». Если нет, то в ответе: «Вы не замостите пол, не хватает …» идет вывод числа плиток, которых не хватает. ЦИКЛЫ Циклы — еще один из основных видов алгоритмов. Цикл — это повторяющиеся во времени действия. Действия в цикле могут повторяться конечное количество раз, а могут повторяться бесконечно. В зависимости от задачи применяются различные операторы цикла. Таких операторов несколько. КОНЕЧНЫЕ ЦИКЛЫ For, do, end — операторы конечного цикла. Пример: for i= 0, 10 do print ("Roblox") end В цикле вводится локальная переменная i, которой присваивается значение 0 и через запятую указывается конечное значение. В данном случае это 10, то есть цикл должен повториться от 0 до 10. Иными словами, он должен вывести слово Roblox 11 раз. Как видно из программы, каждый раз значение i увеличивалось на 1. Такое число называется шагом цикла. Если нам нужно, чтобы шаг отличался от единицы, то достаточно написать его после конечного значения переменной i. Пример: for i= 0, 10, 0.5 do print ("Roblox") end for i= 0, 10, 5 do print("Lua") end 47 В первом цикле шаг равняется 0.5, а во втором — 5. Согласно программе слово Roblox выведут 21 раз, а слово Lua — 3 раза (рис. 2.21). Roblox Lua (x21) (x3) РИС. 2.21. КОНЕЧНЫЕ ЦИКЛЫ Рассмотрим циклы, которые могут повторяться достаточно долго или бесконечно: операторы while, do, end. Приведу пример бесконечного цикла. Настоятельно рекомендую подобный цикл использовать только с временной задержкой, иначе Roblox Studio зависнет. Временная задержка — wait(t), где t — это время в секундах. а=0 while а>–1 do а=а+1 print (а) wait(1) end Программа будет выполнять действия бесконечно, так как значение переменной a никогда не станет отрицательным. Запусти эту программу в Roblox Studio и убедись сам. Но благодаря этим же операторам можно ограничить действия цикла. Например, так: а=0 while a<10 do a=a+l print(a) wait(1) end Согласно последней программе цикл будет выполняться, пока значение переменной a будет меньше 10. Как только значение достигнет 10, цикл прервется. Еще один возможный вариант прервать цикл — прописать условие по достижению какого-то значения и дать команду break: а=0 while а>–1 do а=а+1 print(а) wait(0.5) if а>=20 then break end end 48 Приведенный выше код программы схож с бесконечным циклом, за исключением того, что тут добавлено условие, которое определяет границу работы цикла. В результате при запуске этой программы будут выводиться числа от 0 до 20. По достижении значения, равного 20, цикл прервется и программа остановиться. ФУНКЦИИ Функция — это часть кода, в которой прописан некий алгоритм. Она может стоять обособленно и вызываться в определенных местах кода. Такая конструкция уменьшает размер кода и позволяет его оптимизировать. В Lua уже есть специальные готовые функции, например, такие, как: print(); ipairs(). С функцией print() мы уже подробно познакомились. Теперь давай рассмотрим функцию ipairs(). Эта функция получает таблицу и при первом вызове возвращает ее первое значение, а при последующих — второе, третье и т. д. Рассмотрим ее на примере программы по выводу элементов таблицы. Программу напишем в Roblox Studio. 1 2 3 4 5 6 а = {1,5,67,3,5} for i in ipairs(a)do print(i) end 12:04:39.788 - Baseplate auto-recovery file was created 1 2 3 4 5 РИС. 2.22. ВЫВОД ИНДЕКСОВ ЭЛЕМЕНТОВ ТАБЛИЦЫ Цикл пробегает строгое количество элементов в таблице и останавливается после прохождения последнего. Выводятся индексы, которые закреплены за каждым элементом таблицы. Вместо них можем вывести сами элементы. 49 1 2 3 4 5 6 а = {1,5,67,3,5} for i in ipairs(a)do print(a[i]) end 12:12:26.828 - Baseplate auto-recovery file was created 1 5 67 3 5 РИС. 2.23. ВЫВОД ЭЛЕМЕНТОВ ТАБЛИЦЫ Рассмотрим вариант, когда нужно создать свою функцию. Например, эта функция будет находить максимальное число среди двух. Откроем script в Roblox Studio и создадим такую функцию: а = 10 b=5 function max() if a>b then print(a," – Наибольшее число") else print(b, "– Наибольшее число") end end max() Для создания своей функции необходимо воспользоваться оператором function и после этого создать имя функции, в нашем случае max(). Обязательное условие для функций — наличие круглых скобок. После этого внутри созданной конструкции пишется алгоритм. Здесь используется алгоритм ветвления, который проверяет, какое из двух чисел наибольшее. СКРИПТЫ. ПРАВИЛА СОЗДАНИЯ В ROBLOX STUDIO Есть три основных варианта размещения скриптов: в рабочей области Workspace; непосредственно внутри созданного игрового объекта; в папке ServerScriptService. Есть и другие варианты размещения скриптов, но в этой книге мы не будем их рассматривать. Напиши программу, используя конструкцию функции для распределения введенного числа к четным или нечетным. 50 БИБЛИОТЕКИ LUA У Lua есть стандартные библиотеки, которые можно подключать в программу. Название библиотеки Краткое содержание math Математическая библиотека с набором самых распространенных математических функций table Библиотека по работе с таблицами как с массивами: вставка, удаление, поиск, замена и сортировка элементов таблиц string Библиотека по работе со строками: поиск, замена, комбинация, подсчет элементов строк io Библиотека по работе с файлами os Библиотека по работе с функциями операционной системы — запуск и закрытие программ, работа с системным временем и т. д. debug Библиотека по отладке работы программы coroutine Библиотека по работе с сопрограммами Подробнее про них ты можешь прочитать здесь: https://www.sites.google.com/site/ ltwood/projects/lua/lua-lib#TOC--9. Сейчас важна пока одна библиотека, которую можно и нужно использовать в Roblox Studio, — библиотека math. Ниже представлены функции math. math.abs (x) — модуль x. math.acos(x) — возвращает арккосинус x. math.asin(x) — возвращает арксинус x (в радианах). math.atan(x) — возвращает арктангенс x (в радианах). math.atan2(x, y) — возвращает арктангенс x/y (в радианах), но использует знаки обоих параметров для вычисления «четверти» на плоскости. (Также корректно обрабатывает случай, когда y равен нулю.) math.ceil(x) — возвращает наименьшее целое число, большее или равное x. (Окру- гление «вверх».) math.cos(x) — возвращает косинус x. (Угол — в радианах.) math.cosh(x) — возвращает гиперболический косинус x. math.deg(x) — переводит угол, заданный в радианах (x), в градусы. math.exp(x) — возвращает e^x. math.floor(x) — возвращает наибольшее целое число, меньшее или равное x . (Округление «вниз».) 51 math.fmod(x, y) — возвращает остаток от деления x на y. math.frexp(x) — возвращает m и e такие, что x = m*2^e, e — целое, а модуль m находится в интервале [0.5, 1], либо ноль, если x равен нулю (разложение числа с фиксированной запятой). math.huge — значение HUGE_VAL, больше либо равно любого числового значения. math.ldexp(m, e) — возвращает m^2e (e должно быть целым). Восстановление зна- чения по мантиссе (дробной части логарифма числа) и показателю. math.log(x) — возвращает натуральный логарифм x. math.log10(x) — возвращает логарифм x по основанию 10. math.max(x,...) — возвращает максимальный из аргументов. math.min(x,...) — возвращает минимальный из аргументов. math.modf(x) — возвращает два числа: целую часть x и дробную часть x. math.pi — значение pi. math.pow(x, y) — возвращает x^y. math.rad(x) — конвертирует угол x, заданный в градусах, в радианы. math.random() — возвращает псевдослучайное число из генератора. math.randomseed(x) — инициализирует генератор псевдослучайных чисел параме- тром, каждый параметр порождает соответствующую (но одну и ту же) последовательность псевдослучайных чисел. math.sin(x) — возвращает синус x (аргумент — в радианах). math.sinh(x) — возвращает гиперболический синус x. math.sqrt(x) — возвращает квадратный корень x. math.tan(x) — возвращает тангенс угла x (аргумент — в радианах). math.tanh(x) — возвращает гиперболический тангенс x. Как видишь, эта библиотека содержит достаточно неплохую базу полезных функций. Рассмотрим пример реализации библиотеки. Выведем с помощью нее значения числа π. print (math.pi) 11:42:20.476 - Baseplate autorecovery file was created 3.1415926535898 На этом примере видно, с какой точностью функция выводит значение числа π. В дальнейшем мы будем неоднократно обращаться к этой библиотеке. 52 ВЕКТОРЫ Векторы играют важную роль в Roblox, особенно в программировании игр. При этом вектор сложен в описании. Есть несколько вариантов определения, что такое вектор. Не будем вдаваться в тонкости высшей математики и ограничимся определением, которое часто пишут в школьных учебниках. Вектор — направленный отрезок прямой, то есть отрезок, для которого указано, какая из его точек является началом, а какая — концом. Пример вектора: РИС. 2.24. ВЕКТОР У вектора есть два характерных параметра — это направление и длина. В Roblox используется радиус-вектор. Радиус-вектор — это вектор, начало которого всегда совпадает с началом системы координат. Так как Roblox — трехмерная игровая среда, здесь присутствуют три оси декартовой системы координат. РИС. 2.25. ТРЕХМЕРНАЯ ДЕКАРТОВА СИСТЕМА КООРДИНАТ 53 На рис. 2.26 показан радиус-вектор в декартовой системе координат. РИС. 2.26. РАДИУС-ВЕКТОР «Для чего используют векторы в Roblox?» — спросишь ты. И это хороший вопрос! Вектор напрямую связан с положением и перемещением, а также со скоростью и направлением (особенно с направлением сил). Многие математические темы не могут обходиться без введения понятия «вектор», а огромное количество физических величин определяются как векторы. Например, векторами являются: перемещение, скорость, сила, ускорение, угловая скорость, момент сил и т. д. Все это есть в Roblox. А чтобы можно было работать с векторами и управлять ими, нужна математика. Она поможет нам, если на тело действуют несколько сил, и определит, в какую сторону тело начнет движение или в какой точке пространства окажется. Но не будем углубляться в математику и физику, так как книга адресована начинающим создателям игр. Далее мы изучим свойства игровых объектов, которые тесно связаны с математикой и физикой. ? И если ты хочешь создать мегакрутую игру, не пренебрегай этими дисциплинами! 1. Перечисли основные математические операторы. 2. Какая функция переводит число в строку? 3. Какие операторы должны быть в алгоритме ветвления? 4. Какие операторы должны быть в алгоритме конечного цикла? 5. Перечисли операторы сравнения. 6. Какой оператор отвечает за создание функции? 7. Что такое конкатенация и как она осуществляется? 3 ОСНОВЫ ИГРОВОЙ МЕХАНИКИ В ROBLOX В предыдущих главах мы рассмотрели структуру Roblox Studio и познакомились с основами программирования на языке Lua. Теперь ты можешь смело погрузиться в разработку игр в Roblox! Каждый пункт этой главы будет охватывать определенную часть важной игровой механики. Сочетая и комбинируя их, ты сможешь создать свою уникальную игру. ФИЗИЧЕСКИЕ ОСОБЕННОСТИ ИГРОВЫХ ОБЪЕКТОВ ЦВЕТ Откроем пустой проект в Roblox Studio и перейдем на вкладку MODEL. Здесь можно выбрать стандартные игровые объекты. Раскроем вкладку Part и выберем элемент Block. РИС. 3.1. СОЗДАНИЕ БЛОКА Если выделить этот блок ЛКМ, то на вкладке Properties отобразятся параметры его свойств. Изменим цвет блока с помощью параметра BrickColor (рис. 3.2). 56 РИС. 3.2. НАСТРОЙКА ЦВЕТА Если нужна более тонкая настройка цвета, можно воспользоваться параметром Color (рис. 3.3). РИС. 3.3. ТОНКАЯ НАСТРОЙКА ЦВЕТА 57 Если посмотреть в окно Explorer, можно заметить, что этот блок находится на вкладке Workspace. Вкладка Workspace хранит все элементы, принадлежащие этой локации. Кроме ручной настройки, цвет можно задать с помощью скрипта. Для этого создадим скрипт блока, нажав значок + напротив вкладки Part в Explorer и выбрав Script. Редактирование элемента РИС. 3.4. СОЗДАНИЕ СКРИПТА ДЛЯ ИГРОВОГО ЭЛЕМЕНТА ! Откроется окно редактора скрипта, где можно удалить уже известную нам стандартную строчку. Перед тем как приступить к написанию скрипта, проверь, чтобы все используемые функции были взяты из официальной документации Roblox на сайте https://developer. roblox.com/en-us/. Этот сайт содержит общедоступную информацию об API-Roblox с примерами использования. Инструменты Roblox с каждым годом совершенствуются и модернизируются. Поэтому рекомендуем периодически посещать этот сайт и изучать обновления. Цвет блока можно вызвать с помощью функции BrickColor, причем тремя способами. Информация о цвете — https://developer.roblox.com/en-us/articles/BrickColor-Codes (в поисковике можно вбить BrickColor Codes Roblox). Чтобы воздействовать с помощью кода на объект игры, нужно указать, с каким объектом игры мы будем иметь дело. Для этого можно ввести локальную переменную, которой присвоим вызываемый объект: local f = game.Workspace.Part Переменной f присваиваем объект Part , который принадлежит классу game и подклассу Workspace. Следующая строчка непосредственно присвоит цвет этому объекту. 58 f.BrickColor=BrickColor. При написании кода будут появляться подсказки, в том числе на этом этапе. Roblox сразу предлагает выбрать основной цвет. Выберем Black и нажмем клавишу Enter. f.BrickColor=BrickColor.Black() Запустив игру, программа выдаст результат как на рис. 3.5. РИС. 3.5. НАЛОЖЕНИЕ ЦВЕТА С ПОМОЩЬЮ СКРИПТА Как видно, две строчки кода смогли изменить цвет блока. Обрати внимание: название блока в окне Explorer должно совпадать с названием в скрипте. В нашем случае это Part. Перейдем к другому варианту вызова цвета. Представленный выше способ ограничивает нас в выборе цветов, так как предлагает список основных цветов. Чтобы расширить список цветов, можно воспользоваться либо вызовом номера цвета, либо его именем, которые представлены в таблице BrickColor Codes. 59 РИС. 3.6. ФРАГМЕНТ ТАБЛИЦЫ ЦВЕТОВЫХ КОДОВ Для сравнения создадим еще один блок со скриптом и изменим имя на Part1. РИС. 3.7. ДВА БЛОКА СО СКРИПТАМИ Присвоим каждому из них темно-зеленый цвет. В таблице он назван Dark green и записан под номером 28. Для блока с именем Part напишем программу с вызовом цвета по имени, а для блока с именем Part1 — с вызовом цвета по номеру. 60 Скрипт для Part: local f = game.Workspace.Part f.BrickColor=BrickColor.new("Dark green") Скрипт для Part1: local f = game.Workspace.Part1 f.BrickColor=BrickColor.new(28) Если запустить игру, то результат должен быть как на рис. 3.8. РИС. 3.8. НАЗНАЧЕНИЕ ЦВЕТА РАЗНЫМИ СПОСОБАМИ На картинке видно, что блоки одинаковые. Какой способ лучше — решать только тебе. Ты мог заметить, что мы не затронули способ с указанием RGB Value. Рассмотрим его как отдельный вариант. Последний способ, который мы рассмотрим, — универсальный. Чаще всего профессиональные программисты работают именно с ним. То же можно сказать и про художников и дизайнеров. Лучше всегда иметь под рукой таблицу цветовых кодов в RGB- или HSV-формате. Эти форматы еще называются цветовыми схемами или диаграммами. Мы не будем изучать их подробно, просто рассмотрим, что же такое RGB-схемы. RGB расшифровывается как Red (красный), Green (зеленый), Blue (синий). РИС. 3.9. RGB-СХЕМА 61 Эта схема может быть тебе знакома. Как видно на рисунке, есть три основных цвета: красный, зеленый и синий. При смешивании их в определенных пропорциях мы получаем другой цвет. Количество того или иного основного цвета определяется диапазоном от 0 до 255. Согласно RGB-схеме цвет должен определяться тремя основными цветами, взятыми в определенном соотношении. Поэтому цвет записывается тремя числами, которые по порядку определяют количество составляющих его цветов. Сначала красного, затем зеленого, а потом синего. Рассмотрим примеры. 1. (255, 255, 255) — это белый цвет. 2. (0, 0, 0) — это черный цвет. 3. (255, 0, 0) — это красный цвет. 4. (0, 255, 0) — это зеленый цвет. 5. (0, 0, 255) — это синий цвет. Благодаря такой тонкой настройке можно получить 65 536 разных цветов. Многие из них будут казаться почти одинаковыми для человеческого глаза, но не для компьютера. Для наглядности создадим сферу, выбрав ее на вкладке Part, и напишем для нее скрипт по установке цвета последним способом. Перед тем как написать скрипт, изменим имя сферы в окне Explorer на Part2: local f = game.Workspace.Part2 f.BrickColor=BrickColor.new(255, 255, 255) Программа придаст сфере белый цвет. РИС. 3.10. БЕЛЫЙ ЦВЕТ СФЕРЫ 62 Углубимся в программирование и создадим программу, которая будет постепенно изменять цвет сферы, но только в диапазоне красного цвета — от 0 до 255. Воспользуемся циклом: local f = game.Workspace.Part2 for i =0, 255 do f.BrickColor=BrickColor.new(i,0,0) wait(0.1) print(i) end Если запустить игру, то можно увидеть, что сфера сразу стала красной, а анимации с переходом от черного к красному нет. Это связано с тем, что именно в циклах значение цвета воспринимается как интервал от 0 до 1, то есть он нормирован. Для того чтобы увидеть процесс анимации, необходимо значение i разделить на 255: local f = game.Workspace.Part2 for i =0, 255 do f.BrickColor=BrickColor.new(i/255,0,0) wait(0.1) print(i) end В этом случае при запуске игры мы увидим изменение цвета сферы от черного к ярко-красному. РАЗМЕР, МАТЕРИАЛ И МАССА Длину, ширину и высоту каждого объекта можно менять. Для этого есть множество способов, например с помощью инструмента Scale. Параметры объекта Изменение материала РИС. 3.11. ИЗМЕНЕНИЕ РАЗМЕРА 63 Двигая красный, синий или зеленый шарик, можно менять длину, ширину и высоту объекта. В данном случае был изменен размер блока. Необходимо заметить, что каждому объекту можно присвоить материал: пластик, металл, дерево, лед, стекло, кирпич и т. д. Материал можно настроить либо на панели инструментов на вкладке Material, либо в окне Properties, выбрав параметр Material. Почему это важно? Все дело в расчете массы созданного тела. В зависимости от материала и размера вычисляется его примерная масса. Значение массы можно посмотреть в окне Properties у параметра Data. РИС. 3.12. МАССА ОБЪЕКТА В дальнейшем, когда мы коснемся тем взаимодействия с объектами, этот параметр будет играть ключевую роль. Важно отметить, что изменение размера тела может проходить по-разному, в зависимости от значения единицы измерения. Единицей измерения линейных размеров является studs. Чем больше это значение, тем сильнее и грубее будут изменения размеров. Рекомендуем ставить значение, равное нулю. РИС. 3.13. НАСТРОЙКА МАСШТАБА РЕДАКТИРОВАНИЯ Кроме этого способа, есть вариант изменения размера объекта в окне Properties. В этом окне есть параметр Size, который содержит три значения по осям x, y и z. Так как создаваемые игры трехмерны (есть длина, ширина и высота), то для описания положения тела в пространстве используется декартова система координат, которая определяется тремя вышеуказанными осями. Для лучшего запоминания расположения осей можно сказать так: ось x отвечает за длину; ось y отвечает за высоту; ось z отвечает за ширину. 64 РИС. 3.14. ДЕКАРТОВА СИСТЕМА КООРДИНАТ В ROBLOX Создадим блок и вытянем его по высоте, то есть по оси y. Вытягивание по высоте РИС. 3.15. ИЗМЕНЕНИЕ РАЗМЕРА С ПОМОЩЬЮ SIZE Как только ты изменишь значение y на 10, то увидишь, что тело вытянулось, но не сильно. Это связано с тем, что тело растягивается равномерно по оси y вверх и вниз относительно своего центра. Поэтому, чтобы увидеть реальный размер полученного тела, нужно схватить его ЛКМ и приподнять. Теперь рассмотрим вариант изменения размера тела и установки материала с помощью программы. Script мы можем расположить в разных местах, например в Workspace. Имя тела остается тем же — Part. local telo = game.Workspace.Part telo.Material= Enum.Material.Wood telo.Size=Vector3.new (1,10,1) 65 Для того чтобы изменить материал, достаточно вызвать класс Enum, в котором содержится функция Material. В данном случае мы присвоили телу материал "Дерево". Список всех доступных материалов можно найти здесь: https://developer.roblox.com/ en-us/api-reference/enum/Material. РИС. 3.16. ФРАГМЕНТ ТАБЛИЦЫ МАТЕРИАЛОВ В ROBLOX Чтобы изменить размер объекта, нужно присвоить параметру Size радиус-вектор для трехмерного пространства. Он укажет границу построения тела. В этом примере взят такой размер: x = 1, y = 10, z = 1. 66 РИС. 3.17. РЕЗУЛЬТАТ РАБОТЫ ПРОГРАММЫ Как видно, при запуске игры появился деревянный столб размером 10 studs. Добавим для красоты светло-коричневый цвет и растянем блок по оси x: local telo = game.Workspace.Part telo.Material= Enum.Material.Wood telo.BrickColor = BrickColor.new(12) telo.Size=Vector3.new (10,10,1) Мы добавили одну строчку с BrickColor и изменили 1 на 10 в Vector3.new. РИС. 3.18. НАЛОЖЕНИЕ ЦВЕТА И МАТЕРИАЛА С ПОМОЩЬЮ СКРИПТА 67 Мы рассмотрели множество вариантов изменения размеров и материала игрового объекта, а наши программы с каждым разом становятся больше. Пришло время поговорить о комментариях в программе. В программировании принято писать емкие комментарии для важных пунктов программы. Комментарии в программе не выполняются, программа их игнорирует. Они нужны в первую очередь для самих разработчиков, так как программисты делятся своими программами друг с другом. Комментарии оформляются двумя знаками тире (--). Пример: -- Создадим переменную для взаимодействия с Part local telo = game.Workspace.Part -- Присвоим материал "Дерево" telo.Material = Enum.Material.Wood -- Присвоим светло-коричневый цвет telo.BrickColor = BrickColor.new(12) -- Установим размер 10, 10, 1 telo.Size=Vector3.new (10,10,1) Теперь, когда ты знаешь, как настраивать объект, перейдем к взаимодействию. ПРЕПЯТСТВИЕ Любой созданный объект по умолчанию обладает непроницаемой границей и является динамическим телом, то есть телом, которое может смещаться при соударении с другими телами. Для примера возьмем тот же блок. Можно убрать из скрипта цвет и материал. Если запустить игру, то при попытке пройти сквозь стену ничего не выйдет. Да и сама стена не сдвинется. Это связано с процедурой срастания границ. Если при перемещении тела появляется белая рамка на месте соприкосновения с другим телом — это как раз процесс сращивания. Здесь происходит сращивание РИС. 3.19. ФИКСИРОВАНИЕ ПОЛОЖЕНИЯ ОБЪЕКТА 68 Чтобы этого избежать, достаточно слегка приподнять тело. РИС. 3.20. ТЕЛО НАД ПОВЕРХНОСТЬЮ Если запустить игру и попробовать столкнуть стену, то она упадет. РИС. 3.21. СТОЛКНОВЕНИЕ ТЕЛА У каждого тела активизирован по умолчанию параметр Collisions (рис. 3.22). РИС. 3.22. COLLISION 69 Если данный параметр активен, то при сочетании с несколькими телами границы соприкосновения нарушаться не будут. РИС. 3.23. ГРАНИЦЫ СОПРИКОСНОВЕНИЯ Если параметр неактивен, то границы смыты. Границы смыты РИС. 3.24. COLLISIONS НЕАКТИВЕН Если запустить игру, мы увидим эффект выдавливания одного тела из другого. РИС. 3.25. ВЫДАВЛИВАНИЕ СИРЕНЕВОГО БЛОКА 70 Чтобы этого не произошло, нужно активизировать параметр Anchor у сиреневого блока. Активизация параметра Anchor РИС. 3.26. ЗАКРЕПЛЯЕМ ПОЛОЖЕНИЕ БЛОКА РИС. 3.27. РЕЗУЛЬТАТ ЗАКРЕПЛЕНИЯ Также процедура закрепления дублируется в окне Properties, пункт Behavior, параметр Anchored. РИС. 3.28. ЗАКРЕПЛЕНИЕ ПОЛОЖЕНИЯ В ОКНЕ СВОЙСТВА 71 Закрепить положение тела можно и с помощью программы: -- Создадим переменную для взаимодействия с Part local telo = game.Workspace.Part -- Закрепим положение тела telo.Anchored = true Как видно из кода, параметр Anchored принадлежит логическому типу данных. Поэтому значение можно менять на true или false. ПЕРЕМЕЩЕНИЕ И ПОВОРОТ Давай рассмотрим процедуру перемещения и поворота тела в Roblox. Мы уже упоминали о том, что тело можно перемещать ЛКМ. Также ты можешь воспользоваться инструментом Move. С его помощью объекты перемещаются строго по осям x, y и z. Нужно заметить, что перемещение по осям будет на ту единицу длины, которая задана в Snap to Grid. На рис. 3.29 указано, что перемещение будет с точностью 0 studs. Перемещение объекта РИС. 3.29. РЕЖИМ MOVE Для того чтобы вращать игровой объект, нужно воспользоваться инструментом Rotate. 72 Вращение объекта РИС. 3.30. ИНСТРУМЕНТ ROTATE Обрати внимание, что точность вращения будет зависеть от значения, указанного в окне Snap to Grid. На рис. 3.30 указано значение 45°, следовательно, повороты будут осуществляться на градусы, кратные 45. Ты можешь указать другое значение в градусах. Значения в окне Snap to Grid будут оказывать влияние только в том случае, если напротив слов Rotate и Move стоят галочки. В противном случае эти значения игнорируются инструментами Rotate и Move. Фиксированные значения для поворота и перемещения важны для точности позицио­ нирования. Например, нам нужно повернуть объекты строго на 90° и разместить их на расстоянии друг от друга 10 studs, тогда нужно прописать данные значения в окне Snap to Grid и поставить галочки. Кроме основных инструментов, перемещение и поворот тела можно регулировать с помощью параметров Position и Orientation в окне Properties. 73 РИС. 3.31. НАСТРОЙКА ПОВОРОТА И ПОЗИЦИИ ТЕЛА В СВОЙСТВАХ При изменении значений в параметре Position регулируется положение тела в пространстве. Изменяя значения в параметре Orientation, можно поворачивать тело. Нельзя обойти вниманием настройку расположения тела и его поворот в пространстве с помощью программы. Ниже представлен код для задания положения тела в пространстве: local g = game.Workspace.Part g.Position = Vector3.new(0,3,0) В этом коде в параметре Position указывается точка с помощью пространственного вектора. В данном случае блок переместится на 3 studs вверх. РИС. 3.32. ПЕРЕМЕЩЕНИЕ БЛОКА С ПОМОЩЬЮ КОДА 74 Если необходимо повернуть блок на 90° вокруг оси y (тело повернется в плоскости xoz, параллельной поверхности), то нужно воспользоваться параметром Orientation: local g = game.Workspace.Part g.Position = Vector3.new(0,3,0) g.Orientation=Vector3.new(0,90,0) К предыдущему коду добавим строчку для поворота тела вокруг оси y. Запусти программу и проверь правильность работы кода. Выполни следующие задания для закрепления материала. Составь программу, которая будет каждую секунду перемещать блок на координаты (0, 0, 0), (5, 0, 0), (–5, 0, 0), (10, 5, 0), (–10, 2.5, 0), (5, 3, 5), (3, 0, –6). Составь программу, которая будет каждую секунду поворачивать блок на: àà 30° по оси x; àà 60° по оси z; àà –45° по оси y; àà 180° по оси x; àà 270° по оси y. Составь программу, которая каждую секунду будет присваивать сфере поочередно три цвета: àà Pastel blue; àà Bright bluish green; àà Neon orange. Составь программу, которая сразу трем блокам присваивает разные материал и цвет. Составь программу, которая в зависимости от значения переменной a изменяет размер, цвет и положение блока. àà Если a = 1, то размер (5, 2, 1), цвет — Dark blue, поворот блока вокруг оси z на 60°. àà Если a = 2, то размер (10, 3, 10), цвет — Light yellow, поворот блока вокруг оси y на 135°. ? àà Если a = 3, то размер (1, 10, 5), цвет — Tr. Red, поворот блока вокруг оси x на –45°. 1. За что отвечает параметр Size в окне Properties? 2. Для чего можно использовать функцию BrickColor.new()? 3. За что отвечает инструмент Move? 4. Что можно настраивать с помощью параметра Orientation? 5. Для чего применяются инструменты окна Snap to Grid? 75 СОЗДАНИЕ ЛИНЕЙНОГО ДВИЖЕНИЯ ОБЪЕКТА Эта глава посвящена способам настройки игрового объекта так, чтобы он мог перемещаться. В играх это встречается сплошь и рядом в виде движущихся платформ, работающих дверей (люков), полетов снарядов со статичных мест и т. д. ПЕРВЫЙ СПОСОБ Для начала рассмотрим самый простой вариант. Создадим блок синего цвета размером (10, 1, 5) из материала Plastic. Назовем его Platform. РИС. 3.33. ПЛАТФОРМА Создадим скрипт в папке ServerScriptService и пропишем там код, содержащий два конечных цикла, которые будут перемещать платформу по оси x в отрицательную и положительную сторону. Перед этим лучше посмотреть, какие координаты положения имеет сейчас платформа. В нашем случае это примерно (0, 0.5, 0). -- Создание локальной переменной для платформы local pl = game.Workspace.Platform local x=–15 -- Бесконечный цикл со вложенными циклами while true do for i=0, 15 do pl.Position=Vector3.new(x+i, 0.5, 0) wait(0.1) end x=x+15 print("x = ", x) 76 for i=0, 15 do pl.Position=Vector3.new(x–i, 0.5, 0) wait(0.1) end x=x–15 print("x = ", x) end Сначала создадим переменную и присвоим ей объект Platform. Для начальной позиции платформы создадим локальную переменную x и присвоим ей значение –15. Чтобы платформа перемещалась бесконечно вперед-назад по оси x, следующую часть кода нужно вложить в бесконечный цикл: -- Создание локальной переменной для платформы local pl = game.Workspace.Platform local x=-15 -- Бесконечный цикл со вложенными циклами while true do for i=0, 15 do pl.Position=Vector3.new(x+i, 0.5, 0) wait(0.1) end x=x+15 print("x = ", x) for i=0, 15 do pl.Position=Vector3.new(x–i, 0.5, 0) wait(0.1) end x=x-15 print("x = ", x) end Далее идут два конечных цикла. После их завершения нужно прописать конечное значение x, которое получится в конце каждого цикла. Для того чтобы отследить, как меняется значение переменной x, воспользуемся функцией print(). Положение платформы описывается уже знакомым кодом из раздела «Физические особенности игровых объектов», за исключением одного момента — изменение координаты по оси x (x + i или x – i). Чтобы увидеть это движение, нужно сделать небольшую задержку по времени — wait(0.1). Количество шагов в каждом цикле — 16. Запусти игру, запрыгни на платформу и попробуй проехать на ней. Скорее всего, ты не сможешь на ней удержаться. Таким образом, данный вариант реализации хорошо подойдет для создания движущихся препятствий и дверей. Для примера поставим в конечных точках траектории платформы два массивных блока, а саму платформу преобразуем в дверь, уменьшив размер по оси z и увеличив по оси y — Size (10, 10, 2). Чтобы наш игрок появлялся (спавнился) в нужной нам точке, можно поставить платформу Spawn. Если нам надо, чтобы стены рядом с дверью стояли неподвижно, поставим Якорь (Anchor). Сами стены расположи так, чтобы они не мешали движению двери. 77 Платформа Spawn РИС. 3.34. НАСТРОЙКА ИГРОВОГО ПРОСТРАНСТВА «ДВЕРЬ» Запусти игру, попробуй пройти, пока дверь открыта. Если не получается, то увеличь задержку. Дверь будет перемещаться строго по оси x. В случае столкновения дверь может упасть, но перемещаться будет по оси x. Один из вариантов решения этой проблемы — зафиксировать положение двери с помощью свойства Orientation: -- Создание локальной переменной для платформы local pl = game.Workspace.Platform local x=–15 pl.Orientation= Vector3.new(0, 0, 0) -- Бесконечный цикл со вложенными циклами while true do for i=0, 15 do pl.Position=Vector3.new(x+i,0.5,0) pl.Orientation= Vector3.new(0,0,0) wait(0.1) end x=x+15 print("x = ", x) for i=0, 15 do pl.Position=Vector3.new(x–i,0.5,0) pl.Orientation= Vector3.new(0, 0, 0) wait(0.1) end x=x–15 print("x = ", x) end 78 ВТОРОЙ СПОСОБ Создадим новый проект, возьмем блок и настроим размер (10, 1, 5). Добавим зеленый цвет. Добавим платформу Spawn для загрузки игрока в нужном месте. Переименуй Part в Platform. Теперь нужно добавить свойство платформе под названием BodyPosition, которое можно найти при нажатии значка + напротив имени Platform в окне Explorer. Свойство BodyPosition находится в пункте Legacy Body Movers. РИС. 3.35. СВОЙСТВО BODYPOSITION Теперь создадим скрипт, можно внутри тела Platform. В этом скрипте нужно со­здать локальную переменную и присвоить ей свойство BodyPosition платформы. А далее код, который применялся для двери, можно применить и здесь. Строчки с кодом, содержащие параметр Orientation, уберем. У BodyPosition тоже есть параметр Position: local pl = game.Workspace.Platform.BodyPosition local x=–15 -- Бесконечный цикл со вложенными циклами while true do for i=0, 15 do pl.Position=Vector3.new(x+i,0.5, 0) wait(0.1) end x=x+15 print("x = ", x) for i=0, 15 do pl.Position=Vector3.new(x–i,0.5,0) wait(0.1) end x=x–15 print("x = ", x) end 79 Теперь осталось запустить игру и попытаться запрыгнуть на платформу. В этом случае твой персонаж не будет соскальзывать с платформы, а будет перемещаться вместе с ней. Во время тестирования могут возникнуть проблемы: платформа не совершает перемещение сама по себе или когда персонаж встает на нее. Решается это достаточно просто — нужно увеличить значение параметра P или увеличить одно из значений векторов MaxForce, которые действуют по трем осям: x, y и z. Если у нас платформа перемещается по оси x, то нужно увеличить первое значение. Настройка скорости РИС. 3.36. НАСТРОЙКА BODYPOSITION РИС. 3.37. НАСТРОЙКА BODYPOSITION С ПОМОЩЬЮ MAXFORCE Со вторым способом рекомендую работать аккуратно, так как эти силы также косвенно влияют и на трение персонажа с платформой. 80 ! Расчет силы связан напрямую с массой игрока и платформы. Еще при движении на платформе можно заметить, что она поворачивается. Для устранения этого недостатка можно воспользоваться свойством BodyGyro. Его задача — держать тело в исходном положении относительно вращения. Это свойство находится там же, где и BodyPosition. Добавим его для платформы. Настройка положения тела РИС. 3.38. ДОБАВЛЕНИЕ И НАСТРОЙКА BODYGYRO Чтобы платформу не разворачивало, укажем значения как на рис. 3.38. Параметр MaxTorque содержит в себе три значения трех векторов. Эти значения указывают, до какой максимальной приложенной к платформе силы гироскоп выдержит и сможет держать платформу в равновесии. Параметр P — вес платформы, он тоже важен, так как это вектор, который направлен вниз, и он стабилизирует положение платформы. Запусти игру и проверь устойчивость платформы. Если в скрипте указать в положении платформы 2 studs для оси y: for i=0, 15 do pl.Position=Vector3.new(x+i,2,0) wait(0.1) end x=x+15 print("x = ", x) for i=0, 15 do pl.Position=Vector3.new(x-i,2,0) wait(0.1) end x=x–15 print("x = ", x) end 81 А также значение веса сделать равным силе по оси y в свойстве BodyPosition. Настройка веса и силы РИС. 3.39. НАСТРОЙКА BODYPOSITION Тогда в результате таких манипуляций мы получим левитирующую платформу, на которую ты можешь запрыгнуть и парить на ней. РИС. 3.40. ПЕРЕМЕЩЕНИЕ НА ЛЕВИТИРУЮЩЕЙ ПЛАТФОРМЕ ТРЕТИЙ СПОСОБ Этот вариант основан на добавлении силы или установке и регулировании скорости тела. Создадим новую сцену и добавим сферу, придадим ей красный цвет. Переименуем сферу в Pyla и для зрелищности добавим эффект Fire. Он добавляется нажатием значка + напротив имени Pyla. 82 Пламя РИС. 3.41. ДОБАВЛЕНИЕ ЭФФЕКТА «ПЛАМЯ» Можно настроить пламя так, чтобы оно охватывало шар. Для этого щелкни на созданном эффекте Fire и в окне Properties укажи для параметра Size значение 10, а для Heat — значение 1. РИС. 3.42. НАСТРОЙКА FIRE 83 Как ты уже, наверное, понял, такой способ подходит для создания пуль и снарядов. Щелкнув на Pyla, перейди в свойства (Properties) и найди параметр Velocity. Этот параметр также определяется векторами (их три) и их сложением. Каждый вектор направлен вдоль определенной оси: x, y или z. Укажем для первого вектора значение 10, а для остальных оставим нули. Тогда сфера должна будет перемещаться строго по оси x в положительную сторону. Запусти игру и удостоверься в этом. Такой способ можно реализовать с помощью скрипта, который создается внутри Pyla: local ppl = game.Workspace.Pyla ppi.Velocity=Vector3.new(30,0,0) При запуске игры можно увидеть, как сфера начнет движение с большей скоростью, чем раньше. С помощью скрипта можно сделать так, чтобы сфера остановилась через определенное время. Можно прописать так, чтобы скорость уменьшалась с течением времени, то есть тело двигалось равнозамедленно. Используем конечный цикл: local ppl = game.Workspace.Pyla for i =0, 30 do ppl.Velocity = Vector3.new(30–i,0,0) wait(0.1) end Если запустить игру, мы увидим, как пуля начнет терять скорость, а затем либо остановится, либо будет очень медленно двигаться по инерции. Для того чтобы полностью остановить сферу, после завершения цикла пропишем одну строчку: ppl.RotVelocity=Vector3.new(0,0,0) Параметр RotVelocity отвечает за скорость вращения тела и тоже определяется тремя векторами. Еще одно интересное свойство — это BodyVelocity. Его задача тоже сводится к указанию скорости для тела, но также есть параметры, которые отвечают за нагрузки, которые может выдержать тело. Иными словами, если ты используешь это свойство и при запуске игры пытаешься остановить или повернуть тело так, чтобы оно двигалось в другом направлении, то при недостаточной силе тело будет идти строго по заданному вектору. Это можно проверить, открыв новую сцену и создав в ней сферу с таким свойством. Оно вызывается так же, как и BodyPosition. Если сравнить с предыдущим примером, в первом мы можем менять направление движения тела. 84 Настройка скорости тела РИС. 3.43. НАСТРОЙКА BODYVELOCITY ! Параметры Velocity и BodyVelocity относятся к линейным скоростям, а RotVelocity и BodyAngularVelocity — к угловым. Первые две заставляют тело двигаться вдоль прямой, а последние два — вращаться вокруг определенной оси. Если между соприкасающимися телами есть трение, то даже при вращении они могут двигаться линейно. Кроме непосредственной манипуляции со скоростями тела, можно воздействовать на него с помощью силы. Удалим все свойства, сбросим настройки сферы и добавим следующее свойство — BodyForce. Настройка силы тела РИС. 3.44. НАСТРОЙКА BODYFORCE 85 Указав в значении Force по оси x число 10 и запустив игру, мы сможем наблюдать, как наше тело начнет движение вдоль данной оси. Можно заметить, что при касании персонажа со сферой последняя может поменять направление и даже ускориться. Это связано с тем, что наш персонаж также воздействует на другие тела с определенной силой. Сила (force) — это тоже вектор, а значит, если к одной силе прикладывается другая, два вектора складываются, образуя третий. Мы рассмотрели основные инструменты, позволяющие управлять движением тела. В качестве закрепления материала рекомендую выполнить несколько заданий. Создай дверь, которая открывается и закрывается вертикально. Создай программу для платформы, которая должна двигаться строго по квадрату, прямоугольнику и трапеции. Создай программу, с помощью которой сфера движется по параболе. Используй функцию z = 0.5 × x2. ? Создай несколько сфер и добавь каждой цвет. Напиши для них скрипт так, чтобы они начали двигаться друг к другу. Создай программу, с помощью которой сфера движется по окружности. Уравнение окружности: x = 10 × sin α, z = 10 × cos α, где α — угол в радианах. Для чего нужен параметр Velocity? Чем Velocity отличается от BodyVelocity? Если необходимо настроить угловую скорость, то чем лучше воспользоваться, Velocity или RotVelocity? Что мы настраиваем с помощью свойства BodyForce? ВРАЩЕНИЕ ОБЪЕКТА Разобравшись с линейным перемещением, поговорим о вращении тел. ПЕРВЫЙ СПОСОБ Некоторые параметры из раздела «Создание линейного движения объекта», могут использоваться и для задания вращения тела. Например, RotVelocity. Если мы хотим, чтобы тело вращалось параллельно плоскости пола, то есть по оси y, то можно в параметре RotVelocity написать так, как на рис. 3.45. 86 РИС. 3.45. ВРАЩЕНИЕ ТЕЛА С ПОМОЩЬЮ ROTVELOCITY Для значения y было поставлено число 10. Запусти игру и посмотри результат. Ты увидишь, как тело совершило вращательное движение параллельно полу и остановилось. А остановилось оно из-за трения. Чтобы тело вращалось бесконечно, можно воспользоваться скриптом, который присвоим нашей платформе. Размер платформы ты можешь выбрать произвольно: local h = game.Workspace.Part while true do h.RotVelocity=Vector3.new(0,10,0) wait(0.1) end Как видно, параметр RotVelocity вызывается бесконечное количество раз с задержкой 0.1 секунды. Задержка играет очень большую роль в этих скриптах. При запуске игры можно наблюдать вращение платформы. Если персонаж попытается встать на нее, то его может сбросить, а сама платформа переместится относительно первоначального положения. Чтобы платформа не смещалась, можно зафиксировать ее положение с помощью параметра Position: local h = game.Workspace.Part while true do h.RotVelocity=Vector3.new(0,10,0) h.Position = Vector3.new(0,0.5,–20) wait(0.1) end 87 Теперь в игре при любом воздействии платформа будет оставаться на месте. Такой вариант вращения годится для вентиляторов, ветряных мельниц, самолетов и т. д. ВТОРОЙ СПОСОБ В этом варианте используется свойство BodyAngularVelocity. Добавим его в платформу (рекомендую перед этим удалить скрипт или закомментировать предыдущую запись в нем). Скорости вращения РИС. 3.46. НАСТРОЙКА BODYANGULARVELOCITY Параметр AngularVelocity содержит значение скоростей вращения по осям x, y и z. Чем больше значение, тем быстрее вращение. Параметр MaxTorque отвечает за крутящий момент (момент силы), приложенный к одной из осей. В нашем случае он приложен к оси y. Если запустить игру, можно наблюдать вращение платформы. Если персонаж попадает на нее, начинается его вращение. Можно также увидеть небольшие сдвиги платформы при взаимодействии персонажа с ней. Проблему сдвига можно решить так же, как и в первом случае: local g = game.Workspace.Part while true do g.Position = Vector3.new(–7,0.5,–20) wait(0.01) end 88 Также можно использовать BodyPosition, указав в параметре Position координаты изначального расположения платформы. Координаты платформы РИС. 3.47. НАСТРОЙКА BODYPOSITION В этом случае платформа также не сдвинется с места, если на нее воздействовать персонажем. Создай с помощью двух блоков и скрипта подобие вентилятора, лопасти которого вращаются вокруг оси x. РИС. 3.48. МЕЛЬНИЦА 89 НАСТРОЙКА ВЗАИМОДЕЙСТВИЯ ИГРОКА С ОБЪЕКТАМИ Рассмотрим теперь, как настраивать взаимодействие персонажа с предметами. Для этого создадим два объекта, которые будут играть роль нажимных пластин. Также создадим дверь с двумя элементами, имитирующими стены (см. с. 76). В игре будут две нажимные пластины. Если игрок наступит на первую, то дверь откроется, а если на вторую, то закроется. РИС. 3.49. ИГРОВАЯ ЛОКАЦИЯ С ДВЕРЬЮ Переименуем зеленый и красный цилиндры в knopka1 и knopka2, а сиреневый блок в dver. Сними галочку с Collisions и с помощью инструмента Move углуби красный и зеленый цилиндры в голубые блоки. Если запустить игру, мы увидим, как цилиндры «выплевываются». Чтобы этого не происходило, поставим на них и на голубые блоки якорь Anchor. Теперь перейдем к скрипту. Его можно создать как в Workspace, так и в Server­ ScriptService. Для интереса создадим в последней. 90 РИС. 3.50. СОЗДАНИЕ СКРИПТА Для начала создадим три локальные переменные, которые будут отвечать за кнопки и дверь: local k1 = game.Workspace.knopka1 local k2 = game.Workspace.knopka2 local d = game.Workspace.dver Теперь воспользуемся конструкцией в Lua, которая создает функцию (см. раздел «Функции» главы 2). ! Первая функция будет открывать дверь, а вторая — закрывать. Для этого посмотрим на свойство двери, а именно параметр Position. В примере дверь имеет координаты (9.31, 5, 4). Это положение отсчитывается от центра двери. Левую стену для ориентира мы расположили в координате (0, 5, 0) относительно ее центра. Размер стены Size = (10, 10, 3), а размер двери (15, 10, 3). Размеры и положение стен и дверей можно выбрать произвольно, главное, чтобы тебе было удобно считать: -- Создание первой функции function open1() d.Position=Vector3.new(-5,5,4) end -- Создание второй функции function close1 () d.Position=Vector3.new(9.31,5,4) end Как видно из кода, первая функция названа open1, а вторая — close1. К словам добавлено число, чтобы ненароком не перекрыть зарезервированную функцию в самой среде Lua. Такой способ перемещения двери мы уже рассматривали в разделе «Создание линейного движения объекта» на с. 76. Только здесь нет цикла, и поэтому открытие и закрытие будут резкими. 91 Теперь переходим к последнему шагу — к подключению обработки события. Функции, связанные с событием, обозначаются значком «желтая молния» в Roblox Studio. Здесь понадобится функция Touched — функция касания. Добавим ее для каждой кнопки. И выберем для этой функции параметр Connect — это вызов того действия, которое нам нужно. Мы вызываем функцию open1 и close1. Записывается это так: -- Проверка на касание k1.Touched:Connect(open1) k2.Touched:Connect(close1) Проверь игру, встань на зеленую кнопку и смотри за поведением двери. Затем встань на красную кнопку и смотри, как себя поведет дверь. РИС. 3.51. РЕЗУЛЬТАТ РАБОТЫ КОДА Ниже выложен весь код скрипта: local k1 = game.Workspace.knopka1 local k2 = game.Workspace.knopka2 local d = game.Workspace.dver -- Создание первой функции function open1() d.Position=Vector3.new(-5,5,4) end -- Создание второй функции function close1() d.Position=Vector3.new(9.31,5,4) end -- Проверка на касание k1.Touched:Connect(open1) k2.Touched:Connect(close1) Улучши программу по открытию двери так, чтобы дверь открывалась плавно (см. первый способ в разделе «Cоздание линейного движения объекта» на с. 76). 92 НАНЕСЕНИЕ УРОНА Одни из важных пунктов в игре — количество жизней игрока и нанесение урона, а также восстановление здоровья персонажа. В качестве теста создадим красный блок размером (15,1, 15), а рядом поставим сферу зеленого цвета. Блок красного цвета является зоной, где игрок будет получать урон. Сфера зеленого цвета при касании будет возвращать игроку здоровье. РИС. 3.52. ЛОКАЦИЯ ИГРЫ ! Чтобы нанести урон персонажу, нужно создать скрипт с кодом, где будет указано, что урон будут получать гуманоиды, то есть все игровые персонажи, имеющие строе­ ние гуманоида. А точнее, если в свойствах персонажа будет прописано Humanoid. По умолчанию все игровые объекты гуманоидного типа, в том числе и игровой персонаж, имеют такое свойство. Для примера запусти игру и найди в окне Explorer своего персонажа — у него должно быть имя твоего аккаунта (аватара). Аватар и его имя РИС. 3.53. ПАРАМЕТР HUMANOID 93 Теперь, когда мы разобрались в теме, нужно перейти к написанию скрипта красному блоку. Для этого создадим функцию, которая и будет отнимать здоровье у персонажагуманоида. Перед этим изменим имена красного блока и зеленой сферы на hp и apteka. Созданная функция будет вызываться, когда персонаж коснется красного блока hp: -- Присваем параметры hp к переменной h local h = game.Workspace.hp -- Создадим функцию, отнимающую жизни у персонажей-гуманоидов function pl(n) if n and n.Parent:FindFirstChild("Humanoid") then n.Parent.Humanoid.Health=0 end end -- Пропишем команду для обработки события касания тела hp h.Touched:Connect(pl) Если запустить игру и подойти к красному блоку, произойдет следующее. РИС. 3.54. УНИЧТОЖЕНИЕ ПЕРСОНАЖА Конструкция кода программы опирается на предыдущие разделы. Часто в примерах игровых алгоритмов на Lua в Roblox опускают строчки создания переменной, которой присваивают свойства данного тела. Вместо этого прописывается команда, которая запускает скрипт (если он есть), принадлежащий этому телу. Тот же код, но с другим подходом будет выглядеть так: -- Создадим функцию, отнимающую жизни у персонажей-гуманоидов function pl(n) if n and n.Parent:FindFirstChild("Humanoid") then n.Parent.Humanoid.Health=0 end end -- Пропишем команду для обработки события касания тела hp script.Parent.Touched:Connect(pl) Последняя строчка и указывает на выполнение скрипта, принадлежащего телу, когда этого тела коснутся. Какой выбрать вариант написания кода, решать тебе. 94 С таким кодом игровой персонаж погибает сразу же при касании. Но, кроме моментальной гибели персонажа, можно добиться постепенного нанесения урона. Пример представлен ниже: -- Создадим функцию, отнимающую жизни у персонажей-гуманоидов function pl(n) if n and n.Parent:FindFirstChild("Humanoid") then n.Parent.Humanoid.Health=n.Parent.Humanoid.Health–5 end end -- Пропишем команду для обработки события касания тела hp script.Parent.Touched:Connect(pl) Благодаря этому изменению в коде урон персонажу будет наноситься постепенно и отнимать с каждым касанием по 5 долей здоровья. При максимальном здоровье это значение равно 100, то есть достаточно пяти касаний и персонаж распадется на составные части. Запусти игру и проверь работу кода. По такому же принципу, но с противоположным эффектом должна работать аптечка. Для объекта с именем apteka создадим скрипт, похожий на первый. -- Создадим функцию улучшения здоровья у персонажей-гуманоидов function live(n) if n and n.Parent:FindFirstChild("Humanoid") then n.Parent.Humanoid.Health=100 end end -- Пропишем команду для обработки события касания тела hp script.Parent.Touched:Connect(live) Проверь работу скриптов. Для наглядности коснись сначала красного блока, а затем зеленой сферы. Ты сможешь заметить, как нанесенный урон исчезнет. Во многих играх часто применяют такую игровую механику: персонаж касается аптечки и здоровье восстанавливается, но при этом аптечка удаляется. Для того чтобы это реализовать, достаточно добавить одну строчку в последний скрипт. Эта строчка будет ссылаться на функцию Destroy, которая присутствует для всех объектов Roblox. Функция Destroy удаляет (уничтожает) игровой объект. -- Создадим функцию улучшения здоровья у персонажей-гуманоидов function live(n) if n and n.Parent:FindFirstChild("Humanoid") then n.Parent.Humanoid.Health=100 script.Parent:Destroy() end end -- Пропишем команду для обработки события касания тела hp script.Parent.Touched:Connect(live) Эта строка добавляется в функцию live(n). Запусти игру и проверь работу скриптов. Такой игровой прием используют не только для аптечек, а вообще для разных игровых объектов. Если ты играл в мобильные и компьютерные игры, то знаешь про эти приемы. В дальнейшем мы вернемся к этой теме, когда будем разбирать принцип сбора предметов и подсчет очков. 95 СМЕНА ДНЯ И НОЧИ Интересный игровой элемент — это смена дня и ночи. Такая идея отлично подходит играм, где события связаны с временем, — игры на выживание, ролевые, экшен-игры и т. д. Здесь мы рассмотрим два способа настройки времени в игре. ПЕРВЫЙ СПОСОБ Создадим новый проект и назовем его den_noch. Необходимо открыть свойства у игрового объекта Lighting, который расположен в окне Explorer. В свойствах данного объекта нужно найти параметр ClockTime. РИС. 3.55. ПАРАМЕТР CLOCKTIME ОБЪЕКТА LIGHTING Меняя значения в данном параметре, а оно меняется от 0 до 24, ты можешь регулировать время суток. Для того чтобы автоматизировать этот процесс, нужно создать скрипт и прописать цикл изменения времени. Скрипт можно создать в игровой области Workspace: local d2=0 while wait(0.1) do game.Lighting.ClockTime=d2 print(game.Lighting.ClockTime) d2=d2+0.1 end 96 Согласно коду переменная d2 изменяется каждую 0,1 секунды на 0,1 и присваивается в качестве времени параметру ClockTime. Если запустить игровой процесс, можно наблюдать движение солнца и луны по небосклону. В окне Output во время работы скрипта будут отображаться числа от 0 до 24. Как только число превысит 24, начнет выводиться только значение 0 и смена времени суток прекратится. Чтобы этого избежать и получить непрекращающийся процесс смены дня и ночи, нужно в бесконечный цикл добавить условие, которое будет проверять переменную d2 на достижение значения, большего чем 24. Если переменная d2 достигла такого значения, то необходимо ее обнулить: local d2=0 while wait(0.1) do game.Lighting.ClockTime=d2 end print(game.Lighting.ClockTime) d2=d2+0.1 if d2>24 then d2=0 end Если проверить игру, то все будет как надо. ВТОРОЙ СПОСОБ Этот способ связан с двумя функциями игрового объекта Lighting — GetMinutes­ AfterMidnight и SetMinutesAfterMidnight. Первая функция получает (считывает) значения времени, а вторая устанавливает данные значения. Указывается время в минутах относительно 00:00 (полночь). Для справки, в сутках 1440 минут. Скрипт переделаем таким образом: while wait(0.1) do local d1=game.Lighting:GetMinutesAfterMidnight() end game.Lighting:SetMinutesAfterMidnight (d1+10) print(d1) Запустив игровой процесс, мы увидим изменение игрового времени с движением солнца и луны. В окне Output будет отображаться значение времени в минутах от 0 до 1440. Каждую 0,1 секунды значение будет меняться на 10. Отсчет времени будет идти с того значения, что указано в параметре ClockTime свойства объекта Lighting. Настрой программу так, чтобы через каждый интервал времени игровое время изменялось на 1 час: а) 1 секунда, б) 5 секунд и в)10 секунд. 97 ЛЕВИТИРУЮЩИЙ ЛИФТ В разделе «Создание линейного движения объекта» на с. 76 были рассмотрены перемещения по горизонтали (либо по оси x, либо по оси z). Во многих играх встречается такая механика, как поездка на лифтах — на движущихся вертикально платформах. Поэтому важно разобраться в данном процессе передвижения в играх. Рассмотрим три задачи работы лифта: лифт (платформа) автоматически и бесконечно совершает вертикальные движения вверх-вниз; лифт начинает движение вверх-вниз только при нажатии кнопки на земле; лифт начинает движение вверх-вниз только при нажатии кнопки на лифте. Рассмотрим первую задачу. Для этого создадим новый пустой проект, в котором разместим блок Spawn и блок Block зеленого цвета и размером (10, 1, 10), который будет выполнять роль лифта. Для контраста блоку Baseplate придадим фиолетовый цвет. Также переименуем зеленый блок в Lift. РИС. 3.56. ЛИФТ Добавим лифту свойство BodyPosition и укажем характеристики сил и веса, который он выдержит. 98 Сила и вес РИС. 3.57. НАСТРОЙКА BODYPOSITION Как видно из рис. 3.57, вес тела и силы по осям x и z равны, а вот сила по оси y в 10 раз больше веса. Также для наглядности и в качестве теста установим начальное положение лифта в точке (0, 2, 0), то есть центр лифта будет располагаться на высоте 2 studs. Теперь добавим BodyGyro для сохранения равновесия и настроим силы наклона и вес. Равновесие РИС. 3.58. НАСТРОЙКА BODYGYRO Вес 2000 будет достаточным для нашего персонажа. Для вертикального движения увеличим значение MaxTorque также в 10 раз по сравнению с боковым. Запустим игру и проверим устойчивость лифта под тяжестью персонажа. 99 РИС. 3.59. ПРОВЕРКА УСТОЙЧИВОСТИ ЛИФТА Теперь перейдем к созданию скрипта, который разместим внутри объекта Lift. В скрипте нужно создать программу, которая будет постепенно поднимать и опускать лифт. Это можно сделать с помощью конечных циклов, вложенных в бесконечный цикл. Функция, которая отвечает за перемещение по вертикали, — это изменение вектора по оси y и присвоение данного значения параметру Position свойства BodyPosition. Лифт будет подниматься на высоту 50 studs: -- Создание локальной переменной для привязки свойств Lift local l= game.Workspace.Lift.BodyPosition -- Создадим бесконечный цикл с вложенными циклами while true do -- Подъем вверх for i=2, 50,2 do l.Position=Vector3.new(0,i,0) wait(0.5) end -- Спуск вниз for i=50, 2,–2 do l.Position=Vector3.new(0,i,0) wait(0,5) end end Запусти программу, встань на платформу и проверь работоспособность устройства. РИС. 3.60. ДВИЖЕНИЕ ЛИФТА С ИГРОВЫМ ПЕРСОНАЖЕМ 100 Итак, все работает. Теперь доработаем наш лифт, сделав для него подобие коробочки. Для этого создадим два блока и придадим им синий цвет. Изменим размер так, чтобы они играли роль стенок лифта. Расположим данные стены на платформе и переименуем их в Lift1 и Lift2. РИС. 3.61. СОЗДАНИЕ ФОРМЫ ЛИФТА Осталось связать все три элемента в один. За это отвечает инструмент Union в окне Solid Modeling. Но перед тем, как приступить к объединению в одну модель, рекомендую скопировать код скрипта: при объединении скрипт удалится и свойства платформы тоже. Выделив все элементы (для этого удерживай клавишу Ctrl и щелкни ЛКМ на нужных объектах), выбери инструмент Union. РИС. 3.62. СОЗДАНИЕ ЛИФТА ИЗ ТРЕХ БЛОКОВ Запусти игру и проверь прочность стенок. 101 Теперь переименуем созданный объект в Lift, придадим ему те же свойства с теми же параметрами, что и для предыдущей версии, но за исключением маленькой детали. Так как с дополнительными стенками размер лифта увеличился, то и его центр стал выше — примерно 5–6 studs. Поэтому для свойства BodyPosition зададим параметру Position такие значения — (0, 8, 0). Положение объекта РИС. 3.63. НАСТРОЙКА ПЕРВОНАЧАЛЬНОГО ПОЛОЖЕНИЯ ЛИФТА Приступим к созданию скрипта. Код должен быть таким же, как для первоначального лифта, с небольшим изменением: i должна принимать значение от 8 до 56: local l=game.Workspace.Lift.BodyPosition while true do end for i=8,56,2 do l.Position=Vector3.new(0,i,0) wait(0.5) end for i=56,8,–2 do l.Position=Vector3.new(0,i,0) wait(0.5) end Запусти игру и проверь работу лифта. Рассмотрим второй вариант работы лифта. Для этого создадим кнопку по такому же принципу, как создавали кнопку для двери (см. с. 90). Кнопку в виде цилиндра назовем Knopka. 102 РИС. 3.64. СОЗДАНИЕ КНОПКИ ДЛЯ ЗАПУСКА ЛИФТА Далее осталось подправить скрипт так, чтобы только при нажатии кнопки лифт заработал (см. с. 90). Теперь запишем два конечных цикла, но не в бесконечный цикл, а в виде функции: -- Создаем две переменные для лифта и кнопки local l=game.Workspace.Lift.BodyPosition local k=game.Workspace.Knopka -- Создаем функцию запуска лифта вверх-вниз function g() for i=8,56,2 do 1.Position=Vector3.new(0,i,0) wait(0.5) end for i=56,8,–2 do 1.Position=Vector3.new(0,i,0) wait(0.5) end end -- Прописываем событие касания кнопки и запуска функции k.Touched:Connect(g) Запустив игру, убедись в правильности работы скрипта. Дополни программу, чтобы была небольшая задержка для запрыгивания в лифт после нажатия кнопки. Создай объект, имитирующий здание с тремя этажами. Пусть лифт, перемещаясь вверх и вниз, притормаживает напротив этих этажей, чтобы персонаж мог на них перейти. Пример такого здания представлен на рис. 3.65. 103 РИС. 3.65. ЗДАНИЕ ДЛЯ ЛИФТА Рассмотрим третий вариант. Для этого создадим вторую кнопку из цилиндра и расположим для начала так, как показано на рис. 3.66. Расположение кнопки РИС. 3.66. СОЗДАНИЕ КНОПКИ ДЛЯ ЛИФТА 104 Эту кнопку переименуем в Knopka1. Теперь нужно зафиксировать ее положение на стенке лифта. Для этого понадобится окно под названием Constraints (Ограничители). В нем содержатся инструменты для ограничения каких-либо видов движения. Нужно найти инструмент Weld (Сварка) — см. вкладку Create. Этот инструмент приварит намертво кнопку к стене, но сохранит объект обособленным от общей конструкции лифта. РИС. 3.67. СВАРИВАНИЕ КНОПКИ СО СТЕНОЙ Как только ты выберешь этот инструмент, от кнопки протянется красная линия. Тебе нужно указать точку касания этой линии с поверхностью, двигая мышь и зажав ЛКМ. В нашем случае это стена. Если все сделано правильно, останется сместить кнопку к стене (желательно к точке сварки). 105 РИС. 3.68. РАСПОЛОЖЕНИЕ КНОПКИ Запусти игру и убедись, что твоя кнопка не падает со стены и жестко закреплена. Чтобы кнопка была красивой, можно выбрать материал Neon и красный цвет. Осталось прописать две строчки в уже существующем коде. Нужно создать новую переменную, принимающую значения объекта Knopka1, и прописать событие на касание этой кнопки: -- Создаем две переменные для лифта и кнопки local l=game.Workspace.Lift.BodyPosition local k=game.Workspace.Knopka local kl=game.Workspace.Knopka1 -- Создаем функцию запуска лифта вверх-вниз function g() wait(3) for i=8,56,2 do l.Position=Vector3.new(0,i,0) wait(0.5) end for i=56,8,–2 do l.Position=Vector3.new(0,i,0) wait(0.5) end end -- Прописываем событие касания кнопки и запуска функции k.Touched:Connect(g) -- Прописываем событие касания кнопки в лифте и запуска функции kl.Touched:Connect(g) Добавь вторую кнопку в лифте и измени код так, чтобы при касании первой кнопки лифт поднимался вверх, а при касании второй опускался вниз. 106 ТЕЛЕПОРТАЦИЯ Еще один интересный игровой элемент — это моментальное перемещение персонажа в определенную точку пространства. Такое стремительное перемещение обычно называют телепортацией. Чтобы рассмотреть это свойство, сделаем игровой уровень с платформами, расположенными на больших расстояниях друг от друга и в разных точках пространства. Откроем новый пустой игровой проект и удалим плату Baseplate, добавив вместо нее блок Spawn. РИС. 3.69. ИГРОВАЯ ЛОКАЦИЯ ДЛЯ ТЕЛЕПОРТАЦИИ Создадим пять платформ размером (10, 1, 10) и придадим им контрастный цвет (красно-коричневый). Все платформы должны быть закреплены с помощью Anchor. РИС. 3.70. ПЛАТФОРМЫ ДЛЯ ТЕЛЕПОРТАЦИЙ 107 Теперь приступим к созданию скрипта. Пропишем код, который будет содержать функцию. Сначала создадим пять локальных переменных, которым присвоим свойства пяти платформ: local local local local local t1 t2 t3 t4 t5 = = = = = game.Workspace.Tele1 game.Workspace.Tele2 game.Workspace.Tele3 game.Workspace.Tele4 game.Workspace.Tele5 Теперь перейдем к написанию функции. Перед ней создадим локальную переменную tele00, которая в дальнейшем понадобится для хранения свойств одной из платформ. В функции мы прописываем условие проверки касания платформы гуманоидом. И если платформы коснулся игровой персонаж, его нужно перебросить в точку пространства, где располагается вторая платформа. За перемещение персонажа отвечает функция MoveTo(): local tele00 function hl(m) wait(2) if m and m.Parent:FindFirstChild("Humanoid") then tele00=script.Parent.Tele2 m.Parent:MoveTo (tele00.Position) end end function h2(m) Как видишь, добавилась только строчка перемещения персонажа. Теперь эту функцию должно вызвать касание персонажа о первую платформу: t1.Touched:Connect(h1) t2.Touched:Connect(h2) В функции стоит небольшая задержка в 2 секунды (wait(2)). Это сделано исключительно для того, чтобы игрок мог зафиксировать перемещение и сориентироваться в пространстве. Конечно, для остальных платформ нужно прописать то же, изменяя только название функций и имена платформ. Ниже представлен пример для трех платформ. После создания всех пяти функций так, чтобы с каждой платформы игровой персонаж последовательно перемещался на следующую, нужно прописать, какую функцию вызывать при касании конкретной платформы: t1.Touched:Connect(h1) t2.Touched:Connect(h2) t3.Touched:Connect(h3) t4.Touched:Connect(h4) t5.Touched:Connect(h5) 108 Процесс перехода должен быть таким: 1 — 2 — 3 — 4 — 5 — 1. Числами обозначены номера платформ. Если ты все сделал правильно, твой персонаж каждые 2 секунды будет перемещаться между платформами, как только встанет на первую. Такой пример телепортации — лишь один из многих вариантов. Давай рассмотрим еще один вариант. Для этого нужно удалить первый скрипт или просто пересохранить проект, удалив в нем ранее созданный скрипт. Скопируй и вставь первую платформу. Переименуй ее в Tele0 и совмести с платформой Tele1. Поставь Anchor. Далее в окне Explorer выдели все платформы от Tele1 до Tele5 и вложи в платформу Tele0. РИС. 3.71. ВТОРОЙ ВАРИАНТ ТЕЛЕПОРТА Внутри Tele0 создадим скрипт, в котором будет функция по перемещению нашего персонажа по портам от Tele1 до Tele5. Перемещение начнется только при касании Tele0: function h1(m) wait(2) if m and m.Parent:FindFirstChild("Humanoid") then m.Parent:MoveTo (script.Parent.Tele2.Position) wait(2) m.Parent:MoveTo (script.Parent.ТеlеЗ.Position) wait(2) m.Parent:MoveTo (script.Parent.Tele4.Position) wait(2) 109 end end m.Parent:MoveTo (script.Parent.Теle5.Position) wait(2) m.Parent:MoveTo (script.Parent.Теle1.Position) wait(2) script.Parent.Touched:Connect(hl) Здесь скрипт действует на все объекты, которые вложены (parent) в Tele0. Если запустить игровой процесс, игрок будет перемещаться по платформам бесконечное количество раз с интервалом 2 секунды. Как видно, и в первом и во втором варианте, чтобы начать перемещаться, необходимо коснуться платформы. Но для первого варианта можно коснуться любой платформы, и она перебросит в нужную область, а для второго перемещение произойдет только при касании Tele0. Все предыдущие разделы были рассмотрены с определением гуманоидов, чтобы срабатывал скрипт, но есть и другой вариант. Можно проверять игровые объекты не на наличие свойства Humanoid, а на наличие непосредственно игроков (players). То есть можно сделать так, чтобы телепорт работал для любого игрока, коснувшегося платформы. За это отвечает функция GetPlayerFromCharter, которая получает информацию о персонажах в игре. Пример использования представлен ниже: game.Players:GetPlayerFromCharacter(m.Parent) arent:FindFirstChild("Humanoid") then Давай видоизменим наш последний скрипт, где будет проверяться не гуманоид, а наличие игрока: function hl(m) local player1=game.Players:GetPlayerFromCharacter(m.Parent) wait(2) --if m and m.Parent:FindFirstChild("Humanoid") then if player1 then m.Parent:MoveTo (script.Parent.Tele2.Position) wait(2) m.Parent:MoveTo (script.Parent.Tele3.Position) wait(2) m.Parent:MoveTo (script.Parent.Tele4.Position) wait(2) m.Parent:MoveTo (script.Parent.Tele5.Position) wait(2) m.Parent:MoveTo (script.Parent.Telel.Position) wait(2) end end script.Parent.Touched:Connect(hl) Запусти игровой процесс и убедись в исходном результате. 110 СТРОИТЕЛЬСТВО Один из интересных аспектов Roblox Studio — это возможность создавать свои модели. Причем ты можешь создавать их как вручную с помощью инструментов, так и путем программирования. Давай рассмотрим оба варианта. Конечно, всех аспектов моделирования и строительства не охватить в одной книге, но я постараюсь рассказать о наиболее интересных элементах. МОДЕЛИРОВАНИЕ ИГРОВОГО ОБЪЕКТА С ИСПОЛЬЗОВАНИЕ ROBLOX STUDIO Умение моделировать — один из важных навыков, которыми нужно владеть при наполнении игровой локации. В любой игре большинство элементов составные (состоят из множества частей). В качестве примера попробуем создать простой домик и автомобиль. Домик будет с треугольной крышей, окнами, мебелью. Чтобы понимать, как и что строить, нужно начертить план дома. Возьмем карандаш, линейку и листок бумаги и нарисуем, как должен выглядеть дом. На рис. 3.72 представлен пример плана дома. РИС. 3.72. ПЛАН ДОМА Дом разделен на три комнаты. Первая комната — это гостиная. Здесь есть диван, стол, книжный шкаф и телевизор на тумбе. Вторая комната — ванная, в которой есть ванна и умывальник. Третья комната — спальня, в которой расположены кровать, стул и стол с компьютером. Вход сделаем со стороны гостиной, а окна — со стороны дивана и кровати. Для начала создадим пол из материала Wood Planks и стены, но так, чтобы оставить отверстия для входа и окон. Все это делается из блоков. Результат каркаса представлен на рисунке 3.73. 111 РИС. 3.73. КАРКАС ДОМА Для стен был выбран материал Concrete (Бетон). Высота стен — не больше 15 studs, а стороны дома — не больше 50 studs. Поставим для каждой стены Anchor, чтобы они не падали. Добавим четыре бетонные перекладины для создания отверстий под три окна. Размер окон можно сделать произвольным, но так, чтобы до них мог дотянуться игровой персонаж. РИС. 3.74. МОДЕЛИРУЕМ ОТВЕРСТИЕ ПОД ОКНА Добавим внутренние перегородки согласно плану. 112 РИС. 3.75. ПЕРЕГОРОДКИ В ДОМЕ Пол я назвал Hause. Как видно, основная конструкция дома готова, и составные элементы, за исключением пола, теперь можно объединить с помощью инструмента Union. Если у тебя элементы получились из разного материала, то при объединении с помощью Union все они примут свойство первого выделенного элемента, поэтому пол сюда и не входит. Объединенные стены получат имя Stena. Добавим их в элемент Hause в окне Explorer. РИС. 3.76. ДОБАВЛЕНИЕ UNION В HAUSE Теперь приступим к созданию стеклянных окон. Для этого снова воспользуемся блоками, но сделаем их слегка прозрачными. За прозрачность отвечает параметр Transparency. По умолчанию все создаваемые объекты имеют значение Transparency, равное нулю. Создадим блок и установим его размеры так, чтобы он закрывал проем окна, укажем значение Transparency, равное 0,5, а материал выберем Glass. Создав три окна, также объединим их в один объект с помощью инструмента Union. 113 Прозрачность РИС. 3.77. НАСТРОЙКА ОКОН Переименуем этот элемент в Okno и добавим к элементу Hause в окне Explorer. РИС. 3.78. СБОРКА ДОМА Когда основной элемент дома готов, можно приступить к созданию мебели. На­ чнем с дивана и кровати. Для реализации также воспользуемся прямоугольными блоками. Рекомендую перед моделированием дивана посмотреть на настоящие диваны: чем более подробно ты их изучишь, тем более эстетично они будут выглядеть в игре. Диван строится из составных частей по принципу создания дома, пример см. на рис. 3.79. 114 РИС. 3.79. ДИВАН Как видно, диван состоит из множества элементов. Сначала мы создаем сиденье дивана, которое назовем Divan. Для него выбираем материал Fabric (Ткань) и цвет, близкий к желто-зеленому. Размер объекта Divan — (15, 2, 5). Затем создаем четыре деревянные ножки из цилиндров. Выбираем для них светло-коричневый цвет. Ножки объединяем с помощью Union и называем noski. Этот элемент добавляем в Divan в окне Explorer. Затем берем прямоугольный блок толщиной 1.2, высотой 3 и длиной — 15. Он будет такого же цвета и из такого же материала, что и Divan. Назовем его Spina. Чтобы сгладить верхнюю часть спинки, добавляем цилиндр размером (15, 1.2, 1.2) и размещаем поверх так, чтобы выходила только половина цилиндра. Задаем материал Fabric и темно-коричневый цвет. Называем этот цилиндр spina1. Добавляем Spina и spina1 в элемент Divan в окне Explorer. Осталось сделать подлокотники. Для этого берем прямоугольный блок толщиной 1 studs и глубиной 3 studs и располагаем его на боковом краю сиденья дивана. Материал и цвет придаем такой же, как и сиденью. Теперь осталось скопировать и вставить такой же подлокотник для другой боковой стороны сиденья. Объединяем оба подлокотника с помощью Union и называем их Podlokot. Затем добавляем этот элемент в Divan в окне Explorer. Сверху добавляем цилиндрические деревянные подлокотники, которые размещаем и закрепляем по такому же принципу, что и предыдущие. Назовем их Podlokot1. Все элементы дивана созданы, осталось правильно его разместить. Если это диван, то на него можно сесть. Чтобы можно было это сделать, добавим свойство Seat из пункта Interaction, который появляется, когда мы вызываем дополнительное меню при нажатии значка + напротив игрового элемента в окне Explorer. 115 Теперь на диван можно сесть РИС. 3.80. ДОБАВЛЯЕМ ЭЛЕМЕНТ, ЧТОБЫ МОЖНО БЫЛО СЕСТЬ ! Этот игровой элемент дивана можно сделать невидимым: либо углубить в диван, либо поставить установить Transparency значение 1. В книге представлен второй вариант с небольшим углублением. Не забудь добавить Anchor всем элементам игровых объектов, которые должны быть статичными. Запустим игру и проверим взаимодействие с диваном. Если персонаж садится не спиной к дивану, то достаточно развернуть Seat, чтобы все как надо сработало. РИС. 3.81. ВЗАИМОДЕЙСТВИЕ ПЕРСОНАЖА С ДИВАНОМ Мы не будем подробно описывать алгоритм создания остальных элементов мебели и бытовой техники. Ниже представлены эти элементы и их структура в окне Explorer. Попробуй повторить и создать свои игровые элементы. 116 Создадим стол, который будет состоять из столешницы и четырех ножек. Высота ножек должна быть не больше 3.5 studs. РИС. 3.82. СОЗДАНИЕ СТОЛА Приступим к созданию кровати, высота которой равна примерно 2 studs. У кровати есть ножки, лежанка, одеяло, матрас и подушка. Располагаем кровать в спальне. РИС. 3.83. КРОВАТЬ Теперь перейдем к моделированию тумбы под телевизор. РИС 3.84. СОЗДАНИЕ ТУМБЫ ПОД ТЕЛЕВИЗОР 117 На эту тумбу поставим телевизор, для чего приступим к его созданию. РИС. 3.85. СОЗДАНИЕ ТЕЛЕВИЗОРА Перейдем к созданию компьютерного стола и компьютера в спальню. РИС. 3.86. КОМПЬЮТЕРНЫЙ СТОЛ 118 РИС. 3.87. КОМПЬЮТЕР Поставим стул в спальню и книжный шкаф в гостиную, и две комнаты будут готовы. Это поможет отточить твои навыки в моделировании и будет полезно для тренировки 3D-восприятия (стереометрии). РИС. 3.88. СОЗДАНИЕ СТУЛА Объект «книжный шкаф» будет содержать в себе как сам шкаф, так и книги. Чтобы книги смотрелись красиво, можно раскрасить их в разные цвета. 119 РИС. 3.89. КНИЖНЫЙ ШКАФ Теперь можно приступить к созданию ванны и умывальника. Известно, что ванна, как и умывальник, имеет закругленные края и углубление. Для создания такой модели можно воспользоваться инструментом Negate, который находится в том же окне Solid Modeling. Рассмотрим этот процесс на примере с ванной. Для начала создадим блок размером (10, 4, 5) и две сферы стандартных размеров (4, 4 ,4). Расположим сферы внутри блока так, как показано на рис. 3.90. РИС. 3.90. СОЗДАНИЕ ВАННЫ Выделив две сферы, объединим их в один объект с помощью инструмента Union. После этого выберем инструмент Negate. 120 Сферы изменятся в цвете — это значит, что будет вырезана выделенная область в виде сфер. Чтобы вырезание произошло, нужно выделить совместно со сферами блок и использовать инструмент Union. В результате должно получиться примерно так, как на рис. 3.91. РИС. 3.91. ИСПОЛЬЗОВАНИЕ ИНСТРУМЕНТА NEGATE Осталось сделать маленький штрих — вырезать область между сферическими выемками. Для этого можно воспользоваться цилиндром. Возьмем цилиндр размером (5, 4, 4) и расположим так, как показано на рис. 3.93. РИС. 3.92. ВЫРЕЗАНИЕ ОБЛАСТЕЙ В БЛОКЕ 121 РИС. 3.93. ВЫРЕЗАЕМ ФОРМУ ВАННЫ Воспользуемся инструментом Negate. После этого выделяем цилиндр и блок и применяем инструмент Union. РИС. 3.94. СОЗДАНИЕ ВАННЫ И чтобы ванна выглядела более реалистично, сделаем слив. Для этого возьмем цилиндр и повернем окружностью к полу. Укажем размер (10, 0.5, 0.5) и проткнем насквозь ванну в том месте, где должен будет располагаться слив. РИС. 3.95. СОЗДАНИЕ СЛИВА 122 РИС. 3.96. ВАННА Для реалистичности можно добавить воды, а саму ванну сделать из материала Metal. Воду можно найти в окне Toolbox, в категории Models. В поисковике набери water и выбирай любую понравившуюся модель. РИС. 3.97. СОЗДАНИЕ ВОДЫ ДЛЯ ВАННЫ Осталось подогнать размеры под ванну. Если ты хочешь, чтобы игровой персонаж смог сесть в ванну, то нужно добавить тот же инструмент, что и для дивана, — Seat. Данный элемент можно сделать прозрачным. 123 РИС. 3.98. ВАННА С СИДЕНЬЕМ По такому же принципу создадим раковину. РИС. 3.99. РАКОВИНА Чтобы украсить интерьер, можно добавить в гостиную ковер и две картины на стены. Для этого достаточно создать блок, растянуть по размеру гостиной и установить толщину 0,2. Затем достаточно в окне Toolbox открыть раздел Images и в поисковике написать слово carpet (ковер). Теперь осталось найти нужный рисунок ковра и переместить мышью картинку на блок Kover. 124 РИС. 3.100. СОЗДАНИЕ КОВРА По аналогии сделаем две картины. РИС. 3.101. КВАРТИРА С КАРТИНАМИ И КОВРОМ Интерьер готов, осталось добавить освещение, потолок и крышу. Для освещения создадим лампочку, которая будет состоять из сферы (2, 2, 2) и цилиндра (2, 1, 1). Цилиндр — это цоколь, а сфера — сама лампа. Чтобы лампа светилась, нужно добавить световой эффект PointLight из окна Gameplay. 125 РИС. 3.102. СОЗДАНИЕ ЛАМПЫ Для настройки яркости лампы нужно перейти в свойства эффекта PointLight и отрегулировать два параметра: Brightness (яркость, интенсивность света) и Range (радиус распространения). В примере, который представлен на рис. 3.102, параметр Brightness имеет значение 14, а параметр Range — значение 9. Назовем сферу Lamp, а цоколь — plinth. Цоколь вложим в сферу в окне Explorer. После этого можно создать потолок и разместить лампы (предварительно их скопировав (Ctrl+C)). Потолок назовем Ceiling и добавим ему материал Wood Planks. РИС. 3.103. ДОМ С ПОТОЛКОМ И ОСВЕЩЕНИЕМ 126 Построим угловую крышу. Для этого воспользуемся прямоугольными блоками и инструментами Union и Negate. Возьмем блок и придадим ему размер (50, 30, 2). Затем, скопировав его, увеличим копии по толщине до 10. Теперь копии нужно расположить под углом 50° по обеим сторонам оригинала так, чтобы они коснулись друг друга. Присвоим им материал Wood Planks и темно-коричневый цвет. РИС. 3.104. СОЗДАНИЕ КРЫШИ Две наклоненные копии объединяем с помощью Union и затем используем для них инструмент Negate. После этого выделяем все элементы крыши и применяем инструмент Union. РИС. 3.105. ФРОНТАЛЬНАЯ ЧАСТЬ КРЫШИ 127 Теперь делаем копию этой части крыши и размещаем параллельно на противоположном краю дома. Осталось только по бокам поставить прямоугольные блоки размером (45, 1, 50) под углом 50°. Для красоты можно наложить на боковые крыши рисунок черепицы. Для этого в окне Toolbox в категории Images укажем roof tiles и выберем понравившийся рисунок. Накладывается он так же, как на ковер или на картину. РИС. 3.106. ДОМ С КРЫШЕЙ Чтобы сделать красивым общий вид, добавим дорожку с бетонными бордюрами и растительность (цветы, деревья). Их можно взять из категории Models или Meshes в окне Toolbox. РИС. 3.107. ИГРОВОЙ ДОМ 128 Дом готов! Этот пример поможет тебе научиться моделировать игровые объекты и создавать игровые локации. Смоделируй трубы и краны для ванной. Сделай двери и кнопки для открытия этих дверей. Создай детализированную модель автомобиля, в которую можно сесть. Создай электроплитку и кастрюлю. Создай выключатели для света. С помощью скрипта включай и выключай свет. Смоделируй трехэтажный дом с квартирами так, чтобы на каждом этаже располагались три двухкомнатные квартиры. МОДЕЛИРОВАНИЕ ИГРОВЫХ ОБЪЕКТОВ С ПОМОЩЬЮ СТОРОННИХ ПРОГРАММ ДЛЯ 3D-МОДЕЛИРОВАНИЯ Первый вариант подходит для начинающего дизайнера игровых уровней в Roblox. Чтобы создавать более сложные конструкции, нужно иметь познания в 3D-моде­ лировании. Roblox Studio позволяет импортировать 3D-модели, созданные в других 3D-редак­ торах. В качестве примера рассмотрим вариант экспорта 3D-модели из редактора Blender в Roblox. Blender — это бесплатный 3D-редактор для моделирования и анимации. В нем можно создавать объекты как в 3D, так и в 2D. Благодаря Blender, 3D Max, Maya и другим редакторам можно создавать очень сложные и детализированные объекты как живой, так и неживой природы. РИС. 3.108. BLENDER — ОФИЦИАЛЬНЫЙ САЙТ Скачать Blender можно с официального сайта https://www.blender.org/. 129 Если у тебя есть опыт моделирования в этой или похожей среде, то эта глава тебе будет интересна. Если ты пока не занимался моделированием, то можешь смело пропустить этот раздел и вернуться к нему чуть позже, когда заинтересуешься темой 3D-моделирования и захочешь получить больше информации. Перед тем как экспортировать модель, ее нужно создать. В качестве примера со­ здадим дерево. Для этого откроем установленный редактор Blender и перейдем на вкладку Modeling. Мы не будем углубляться в процесс моделирования, так как эта книга о другом. Потому раздел и посвящен опытным «моделистам». Создадим дерево из куба и нескольких сфер. Для этого применим экструдирование, поворот, масштабирование и, конечно, скульптинг. Что это и как им пользоваться, можно прочитать тут: https://docs.blender.org/manual/ru/dev/. Если тебе нужна полноценная книга для изучения этой темы, есть такие интересные варианты. Моя книга по 3D-моделированию и созданию игр в Blender. Книга «Самоучитель Blender 2.7» Андрея Прахова. РИС. 3.109. СОЗДАНИЕ ДЕРЕВА В BLENDER 2.83 130 Roblox Studio позволяет импортировать 3D-объекты только определенного формата — .fbx и .obj. Blender может экспортировать модели в эти форматы. Но у процесса экспорта и импорта есть подводные камни — о них и пойдет речь. Для начала нужно убедиться, что модель низкополигональная, то есть состоит из полигонов, количество которых не превышает 100 000. РИС. 3.110. ПОЛИГОНЫ МОДЕЛИ Под словом «неправильно» подразумевается неспособность Roblox Studio импортировать модель, так как она высокополигональная. Рекомендую для сравнения экспортировать модель в два представленных формата. Как только экспорт прошел успешно, переходим в Roblox Studio и в новом игровом проекте для Workspace создаем MeshPart. РИС. 3.111. СОЗДАНИЕ ЗАГОТОВКИ ДЛЯ МОДЕЛИ 131 Этот объект является заготовкой для импортируемой модели. Теперь, чтобы добавить нашу модель, необходимо перейти в свойства MeshPart и найти параметр Meshid. С помощью значка Папка найдем нужный файл. РИС. 3.112. MESHID-ИМПОРТ МОДЕЛИ Если все сделано правильно, то в Roblox Studio загрузится модель дерева. Импортируем две модели дерева разного формата. РИС. 3.113. ИМПОРТ МОДЕЛЕЙ РАЗНОГО ФОРМАТА Для формата .fbx Roblox предлагает сохранить реальные размеры модели, а для .obj размер подгоняется под среду. Вот тут-то и был первый подводный камень (количество полигонов и особенности форматов). Второй подводный камень — это структура импортируемой модели. Даже если в 3D-редакторе твоя модель состоит из отдельных независимых частей, то при импортировании в Roblox Studio она будет преобразована в целостную модель. Это 132 несет ограничение на наложение материала и цвета. Теперь и материал, и цвет на все участки модели будут накладываться одни и те же. РИС. 3.114. НАЛОЖЕНИЕ ЦВЕТА И МАТЕРИАЛА Чтобы решить эту проблему, в самом редакторе нужно создать отдельными файлами части 3D-модели и экспортировать отдельно. Тогда при импорте в Roblox Studio это будут уже отдельные части модели, для которых можно настроить индивидуальные цвет и материал, а затем просто сгруппировать. РИС. 3.115. СОЗДАНИЕ ДЕРЕВА ПО ЧАСТЯМ На этом мы закончим обзор процесса импорта моделей в Roblox Studio. 133 СОЗДАНИЕ ИГРОВЫХ ОБЪЕКТОВ С ПОМОЩЬЮ СКРИПТА Теперь рассмотрим последний вариант создания игровых моделей. В нем мы будем использовать язык программирования Lua. Создадим прямоугольный блок стандартного размера. Для этого в скрипте в Workspace вызовем функцию Instance. Эта функция отвечает за создание объекта. Для того чтобы создалась именно деталь, необходимо прописать параметр Part, но, чтобы деталь создалась именно в нашем игровом мире, нужно добавить параметр workspace: --Создание детали блок (4,1.2,2) Instance.new("Part",workspace) При запуске игры под ногами игрового персонажа появится наша деталь, так как по умолчанию детали создаются в точке с координатами (0, 0, 0). Новая деталь РИС. 3.116. СОЗДАНИЕ ДЕТАЛИ С ПОМОЩЬЮ СКРИПТА Чтобы деталь создалась в определенном месте, используем тот же параметр, что и раньше, — Position. Для изменения размера детали воспользуемся параметром Size: -- Создание детали блок (4,1.2,2) local detal=Instance.new ("Part", workspace) -- Устанавливаем положение детали detal.Position=Vector3.new(0,0,5) РИС. 3.117. СОЗДАНИЕ ОБЪЕКТА В ОПРЕДЕЛЕННОЙ ТОЧКЕ ПРОСТРАНСТВА 134 Ниже представлен код с указанием размера детали: -- Создание детали блок (4,1.2,2) local detal=Instance.new("Part",workspace) -- Устанавливаем положение детали detal.Position=Vector3.new(0,0,5) -- Установим размер детали detal.Size=Vector3.new(5,3,5) РИС. 3.118. УСТАНОВКА РАЗМЕРА ДЕТАЛИ По умолчанию при создании детали создается Block, у которого снизу и сверху есть измененная поверхность. Сверху это Studs (шипы), а снизу Inlets (выемки). Они нужны для скрепления с поверхностью. Параметры можно настроить в классе Surface. Этот класс можно найти как в отдельном окне инструментов (в урезанном виде), так и в свойствах конкретной детали. РИС. 3.119. SURFACE НА ПАНЕЛИ ИНСТРУМЕНТОВ 135 РИС. 3.120. SURFACE В ОКНЕ PROPERTIES Всего таких параметров в Surface насчитывается 11, и они применимы к любой стороне детали. Параметры имеют нумерацию от 0 до 10. Это индексы, которые присвоены поверхностям. Ниже представлена таблица поверхностей и индексов. Название поверхности Индекс Название поверхности Индекс Smooth (гладкая) 0 Hinge (шарнир) 6 Glue (клей) 1 Motor (мотор, двигатель) 7 Weld (сварной шов) 2 SteppingMotor (шаговый двигатель) 8 Studs (шипы) 3 Smooth (гладкая) 9 Inlet (выемки) 4 SmoothNoOutlines (гладкая без контуров) 10 Universal (универсальное крепление) 5 Чтобы сгладить верхние и нижние стороны, достаточно прописать для параметра BottomSurface и TopSurface (индекс 0 или 9). -- Создание детали блок (4,1.2,2) local detal=Instance.new("Part",workspace) -- Устанавливаем положение детали detal.Position=Vector3.new(0,0,5) -- Установим размер детали detal.Size=Vector3.new(5,3,5) -- Создаем гладкую поверхность detal.BottomSurface=0 detal.TopSurface=0 136 РИС. 3.121. СГЛАЖИВАНИЕ БЛОКА Кроме Block, можно создать Ball (шар) и Cylinder (цилиндр). Для этого нужно воспользоваться функцией Shape: -- Создание детали блок (4,1.2,2) local detal=Instance.new("Part",workspace) -- Создаем цилиндр detal.Shape="Cylinder" -- Устанавливаем положение детали detal.Position=Vector3.new(0,0,5) -- Установим размер детали detal.Size=Vector3.new(5,3,5) -- Создаем гладкую поверхность detal.BottomSurface=0 detal.TopSurface=0 По такому же принципу можно задать Block и Ball: detal.Shape="Ball" detal.Shape="Cylinder" Из предыдущих глав ты уже знаешь, как накладывать материал и цвет на объект, а также регулировать размер и положение. Сейчас попробуем создать дом с крышей, похожий на предыдущий. Начнем с пола и стен. Для этого создадим новый игровой объект и в Workspace напишем скрипт: print("Создаем дом!") -- Создаем деталь local floor = Instance.new("Part", workspace) -- Переименуем деталь floor.Name="Пол" -- Установим размер пола floor.Size=Vector3.new(50, 1, 50) -- Укажем, что это блок floor.Shape="Block" -- Укажем гладкую поверхность пола floor.TopSurfасе=0 137 Благодаря этой части кода будет создана заготовка для пола размером 50 × 50 и 1 studs в высоту, центр пола будет располагаться в точке (0, 0, 0). Зададим полу светло-коричневый цвет и добавим материал Wood planks: -- Установка цвета пола floor.BrickColor=BrickColor.new(106) -- Установка материала floor.Material=Enum.Material.WoodPlanks -- Закрепим положение пола floor.Anchored=true РИС. 3.122. ПОЛ ДОМА С МАТЕРИАЛОМ И ЦВЕТОМ Далее приступим к созданию стен, но так, чтобы оставались места под дверь и окна. Начнем со сплошной стены: -- Создание первой стены local wall1 = Instance.new("Part", workspace) -- Переименуем деталь wall1.Name="Стена1" -- Установим размер стены wall1.Size=Vector3.new(50, 16, 1) -- Укажем, что это блок wall1.Shape="Block" -- Укажем гладкую поверхность стены wall1.TopSurfасе=0 -- Установим положение стены wall1.Position=Vector3.new(0,8,25) -- Установим цвет стены wall1.BrickColor=BrickColor.new(4) -- Установка материала wall1.Material=Enum.Material.Concrete -- Закрепим положение стены wall1.Anchored=true Как видно, в качестве материала был использован бетон. 138 РИС. 3.123. СОЗДАНИЕ СТЕНЫ Создадим еще одну сплошную стену по оси x. Ее нужно повернуть (или можно просто изменить размер). Но для примера используем поворот: -- Создание второй стены local wall2 = Instance.new ("Part", workspace) -- Переименуем деталь wall2.Name="Cтенa2" -- Установим размер стены wall2.Size=Vector3.new(50, 16, 1) -- Укажем, что это блок wall2.Shape="Block" -- Укажем гладкую поверхность стены wall2.TopSurface=0 -- Поворот стены на 90° wall2.Orientation=Vector3.new(0, 90, 0) -- Установим положение стены wall2.Position=Vector3.new(25,8,0) -- Установим цвет стены wall2.BrickColor=BrickColor.new(4) -- Установка материала wall2.Material=Enum.Material.Concrete -- Закрепим положение стены wall2.Anchored=true РИС. 3.124. СОЗДАНИЕ ВТОРОЙ СТЕНЫ 139 Теперь приступим к созданию более сложной конструкции — стены с проемом для двери. Есть два варианта. 1. Создать три части стены и расположить в определенном порядке, чтобы получилось отверстие. 2. Использовать функцию вырезания отверстия в стене. Для первого варианта строчек кода будет больше, чем для второго. Да и второй вариант создает данную стену цельным объектом. Поэтому лучше использовать второй вариант. Нам нужно создать третью стену и блок, который будет иметь размер дверного проема: -- Создание стены с отверстием под дверь -- Создание третьей стены local wall3 = Instance.new("Part", workspace) -- Переименуем деталь wall3.Name="Cтeнa3" -- Установим размер стены wall3.Size=Vector3.new(50,16,1) -- Укажем, что это блок wall3.Shape="Block" -- Укажем гладкую поверхность стены wall3.TopSurface=0 -- Установим положение стены wall3.Position=Vector3.new(0,8,–25) -- Установим цвет стены wall3.BrickColor=BrickColor.new(4) -- Установка материала wall3.Material=Enum.Material.Concrete -- Закрепим положение стены wall3.Anchored=true -- Создание блока под дверь local door =Instance.new("Part",workspace) door.Name = "Дверь" door.TopSurface=0 door.Size=Vector3.new(10,12,3) door.Position=Vector3.new(0,5,-25) door.Anchored=true Заметь, что положение блока под дверь должно быть строго внутри третьей стены. Запусти игру и посмотри на результат (рис. 3.125). 140 РИС. 3.125. ШАБЛОН ДЛЯ СОЗДАНИЯ ДВЕРИ Осталось вырезать блок в стене, и за это отвечает функция SubtractAsync. Под вырезанием понимается создание уже нового объекта «Стена с отверстием», который должен быть привязан к игровому пространству workspace. Созданный объект наложится на предыдущие (третья стена и дверь), поэтому их нужно удалить: -- Вырезаем в стене отверстие под дверь local newUnion=wall3:SubtractAsync({door}) -- Созданный объект привязываем к игровому пространству newUnion.Parent=game.Workspace -- Удаляем третью стену и блок для двери wall3:Destroy() door:Destroy() Обрати внимание: в функции SubtractAsync содержится тип данных — таблица, и поэтому этот объект заключен в фигурные скобки. Таких объектов может быть много, просто в нашем случае он один. РИС. 3.126. РЕЗУЛЬТАТ СОЗДАНИЯ ДВЕРИ 141 По такому же принципу создадим четвертую стену с двумя окнами: -- Создание четвертой стены с двумя окнами local wall4 = Instance.new ("Part", workspace) -- Переименуем деталь wall4.Name="Cтeнa4" -- Установим размер стены wall4.Size=Vector3.new(50, 16, 1) -- Укажем, что это блок wall4.Shape="Block" -- Укажем гладкую поверхность стены wall4.TopSurfасе=0 -- Поворот стены на 90 wall4.0rientation=Vector3.new(0, 90, 0) -- Установим положение стены wall4.Position=Vector3.new(–25,8,0) -- Установим цвет стены wall4.BrickColor=BrickColor.new(4) -- Установка материала wall4.Material=Enum.Material.Concrete -- Закрепим положение стены wall4.Anchored=true -- Создание блока под окно local window1 =Instance.new("Part" workspace) window1.Name = "Окнo1" window1.TopSurface=0 window1.Size=Vector3.new(3,8,8) window1.Position=Vector3.new(–25,8,–12) window1.Anchored=true window1.Transparency=O.5 -- Создание блока под окно local window2 =Instance.new("Part" workspace) window2.Name = "Окнo2" window2.TopSurface=0 window2.Size=Vector3.new(3,8,8) window2.Position=Vector3.new(–25,8,12) window2.Anchored=true window2.Transparency=O.5 -- Вырезаем в стене отверстие под окна local newUnion1=wall4:SubtractAsync({window1, window2}) -- Созданный объект привязываем к игровому пространству newUnion1.Parent=game.Workspace -- Удаляем третью стену и блок для двери wall4:Destroy() Для окон добавим прозрачность с помощью функции Transparency блокам window1 и window2 и при этом не станем их удалять — они будут имитировать стекла. Подчеркнем снова: код содержится в одном скрипте. 142 РИС. 3.127. СОЗДАНИЕ ВСЕХ СТЕН С ДВЕРНЫМ И ОКОННЫМИ ПРОЕМАМИ А теперь создадим потолок и крышу. Потолок сделать легче, для этого достаточно скопировать код для пола и указать положение по оси y, равное 16: -- Создаем потолок local ceiling = Instance.new("Part", workspace) -- Переименуем деталь ceiling.Name="Потолок" -- Установим размер потолка ceiling.Size=Vector3.new(50, 1, 50) -- Укажем, что это блок ceiling.Shape="Block" -- Укажем гладкую поверхность потолка ceiling.TopSurface=0 -- Установим цвет потолка ceiling.BrickColor=BrickColor.new(106) -- Установка материала ceiling.Material=Enum.Material.WoodPlanks -- Размещение потолка ceiling.Position=Vector3.new(0,16,0) -- Закрепим положение потолка ceiling.Anchored=true Создание крыши — сложное задание. Ниже будет представлен пример создания треугольной крыши по такому алгоритму: создаем все четыре элемента крыши; настраиваем два боковых элемента, они должны находиться под углом 45° и идти от края крыши до соприкосновения друг с другом; делаем размер боковых угловых элементов крыши таким, чтобы закрыть те части оставшихся элементов крыши, которые пересекаются с ними; применяем функцию SubtractAsync, чтобы сделать треугольные элементы крыши; изменяем размеры боковых элементов, которые были наклонены под углом 45°, так, чтобы они стали элементами плоской крыши; подгоняем положение этих угловых элементов крыши. Дом готов. 143 РИС. 3.128. СОЗДАНИЕ ПОТОЛКА Попробуй сделать крышу без подсказки: -- Создание первой части крыши local roof0 = Instance.new("Part", workspace) -- Переименуем деталь roof0.Name="Kpышa0" -- Установим размер крыши roof0.Size=Vector3.new(50, 28, 1) -- Укажем, что это блок roof0.Shape="Block" -- Укажем гладкую поверхность крыши roof0.TopSurfасе=0 -- Установим цвет потолка roof0.BrickColor=BrickColor.new(105) -- Установка материала roof0.Material=Enum.Material.WoodPlanks -- Размещение крыши roof0.Position=Vector3.new(0,30,25) -- Закрепим положение крыши roof0.Anchored=true -- Создание второй части крыши local roof1 = Instance.new("Part", workspace) -- Переименуем деталь roof1.Name="Kpышa1" -- Установим размер крыши roof1.Size=Vector3.new(50, 28, 1) -- Укажем, что это блок roof1.Shape="Block" -- Укажем гладкую поверхность крыши roof1.TopSurfасе=0 144 -- Установим цвет пола roof1.BrickColor=BrickColor.new(105) -- Установка материала roof1.Material=Enum.Material.WoodPlanks -- Размещение крыши roof1.Position=Vector3.new(0,30,–25) -- Закрепим положение крыши roof1.Anchored=true -- Создание третьей части крыши local roof2 = Instance.new("Part", workspace) -- Переименуем деталь roof2.Name="Kpышa2" -- Установим размер крыши roof2.Size=Vector3.new(30, 36, 60) -- Укажем, что это блок roof2.Shape="Block" -- Укажем гладкую поверхность крыши roof2.TopSurface=0 -- Установим цвет крыши roof2.BrickColor=BrickColor.new(105) -- Установка материала roof2.Material=Enum.Material.WoodPlanks -- Размещение крыши roof2.Position=Vector3.new(–23,39,0) -- Закрепим положение крыши roof2.Anchored=true -- Поворот на 50° roof2.0rientation=Vector3.new(0, 0, –45) -- Создание четвертой части крыши local roof3 = Instance.new("Part", workspace) -- Переименуем деталь roof3.Name="Kpышa3" -- Установим размер крыши roof3.Size=Vector3.new(30, 36, 60) -- Укажем, что это блок roof3.Shape="Block" -- Укажем гладкую поверхность крыши roof3.TopSurface=0 -- Установим цвет крыши roof3.BrickColor=BrickColor.new(105) -- Установка материала roof3.Material=Enum.Material.WoodPlanks -- Размещение крыши roof3.Position=Vector3.new(23,39,0) -- Закрепим положение пола roof3.Anchored=true -- Поворот на 50° roof3.0rientation=Vector3.new(0, 0, 45) 145 РИС. 3.129. РЕЗУЛЬТАТ ПРЕДЫДУЩЕГО КОДА -- Вырезаем в треугольник крыши local newUnion2=roof0:SubtractAsync({roof2, roof3}) -- Созданный объект привязываем к игровому пространству newUnion2.Parent=game.Workspace -- Удаляем первую часть крыши roof0:Destroy() -- Вырезаем треугольник крыши local newUnion3=roof1:SubtractAsync({roof2, roof3}) -- Созданный объект привязываем к игровому пространству newUnion3.Parent=game.Workspace -- Удаляем вторую часть крыши roof1:Destroy() -- Установим размер боковой крыши roof2.Size=Vector3.new(1, 36, 50) roof3.Size=Vector3.new(1, 36, 50) РИС. 3.130. РЕЗУЛЬТАТ ВЫРЕЗАНИЯ КРЫШИ 146 Смещаем боковые элементы крыши. -- Устанавливаем новое положение roof2.Position=Vector3.new(–12,29, 0) roof3.Position=Vector3.new(12,29,0) РИС. 3.131. ДОМ, СОЗДАННЫЙ С ПОМОЩЬЮ СКРИПТА НА LUA Поздравляю: дом построен! Чтобы закрепить изученный материал, выполни несколько заданий. Они разделены по уровню сложности. ? Создай с помощью программы стол и стул. Сделай с помощью программы ванну. Создай с помощью программы кружку с круглой ручкой и мобильный телефон. За что отвечает инструмент Union? С помощью какого инструмента можно вырезать что-то из объекта? Как можно объединять элементы в один объект без использования инструментов? Какой формат файлов можно импортировать в Roblox Studio? За что отвечает функция SubtractAsync? Для чего используют функцию Instance? СБОР ОБЪЕКТОВ НА ВРЕМЯ В этом разделе мы рассмотрим интересный элемент игровой механики — сбор предметов на время. Предмет должен быть собран, то есть подсчитан программой. Через какое-то время, если игрок не собрал предметы, персонаж погибает и игра начинается заново. Для начала создадим пустой игровой проект с одной красной сферой и платформой Spawn. 147 РИС. 3.132. ПРЕДМЕТЫ ДЛЯ СБОРА Создадим скрипт в Workspace и назовем его collet. Пропишем там команду для удаления сферы при касании персонажа и подсчета количества собранных сфер. Сами сферы переименуем в Apple, Apple1, Apple2… Создадим дорожку, по которой можно бегать и собирать яблоки. РИС. 3.133. ЛОКАЦИЯ ДЛЯ СБОРА ЯБЛОК В скрипте пропишем локальные переменные, которым присвоим игровые объекты — яблоки: local local local local local a0=game.Workspace.Apple a1=game.Workspace.Apple1 a2=game.Workspace.Apple2 a3=game.Workspace.Apple3 a4=game.Workspace.Apple4 148 После этого введем локальную переменную для подсчета очков и создадим функцию для начисления значений. При каждом вызове функции будет начисляться одно значение: local n=0 function h1() n=n+l print(n) end Затем пропишем несколько условий, в которых будет проверяться касание яблока персонажем. Если произойдет касание, то яблоко удалится, а значение переменной n увеличится на единицу: if aO.Touched:Wait(0) h1() aO:Destroy() end if a1.Touched:Wait(0) h1() a1:Destroy() end if a2.Touched:Wait(0) h1() a2:Destroy() end if a3.Touched:Wait(0) h1() a3:Destroy() end if a4.Touched:Wait(0) h1() a4:Destroy() end then then then then then При запуске игры мы увидим, как яблоки при касании исчезают, а в окне Output отображается счет. Теперь добавим игровой элемент — уничтожение персонажа по истечении 10 секунд. Для этого создадим платформу такого размера, которая позволит закрыть всю игровую локацию. Разместим ее ниже под дорожками на высоте по оси y = –20 studs. 149 РИС. 3.134. СОЗДАНИЕ ПЛАТФОРМЫ ДЛЯ УНИЧТОЖЕНИЯ ИГРОКА Для платформы добавим скрипт, который будет проверять касание Humanoid. Если касание произойдет, игрок уничтожится. Подобный скрипт уже был в разделе «Нанесение урона». script.Parent.Position=Vector3.new(80,–20,–60) function he(m) if m and m.Parent:FindFirstChild('Humanoid') then m.Parent.Humanoid.Health = 0 end end Перед этой функцией укажем исходное положение платформы. Для этого достаточно перейти в окно Properties и найти параметр Position. Затем добавим цикл с отсчетом в 10 секунд. По истечении времени нужно будет переместить платформу вверх, чуть выше дорожки. И там же прописать проверку на касание с персонажем. Если касание происходит, вызывается функция he: wait(10) for i =0, 10 do print(i) if i==10 then script.Parent.Position=Vector3.new(80,2,–60) script.Parent.Touched:Connect(he) end end Запусти игру и проверь работу скриптов. Как видно, перед циклом мы поставили задержку 10 секунд. Это было сделано, чтобы дать время на загрузку игры. Чтобы изображение выглядело красивее, платформу сделаем полностью невидимой с помощью параметра Transparency, поменяв на значение, равное единице. 150 РИС. 3.135. РЕЗУЛЬТАТ РАБОТЫ СКРИПТА ПО ИСТЕЧЕНИИ 10 СЕКУНД После перезагрузки платформа остается на том же месте. Поэтому можно добавить код на возвращение в исходную позицию и зациклить участок движения: while true do wait(10) for i =0, 10 do print(i) if i==10 then script.Parent.Position=Vector3.new(80,2,–60) script.Parent.Touched:Connect(he) wait(1) script.Parent.Position=Vector3.new(80,–20,–60) end end end Если запустить игру, то игрок через каждые 10 секунд будет погибать. Давай улучшим игру. Увеличим таймер до 50 секунд, а уничтожающую платформу переименуем в LLL. script.Parent.Position=Vector3.new(80,–20,–60) function he(m) if m and m.Parent:FindFirstChild('Humanoid') then m.Parent.Humanoid.Health = 0 end end while true do wait(10) for i =0, 50 do print("Время = ", i) wait(1) if i==50 then script.Parent.Position=Vector3.new(80,2,–60) script.Parent.Touched:Connect(he) wait(1) script.Parent.Position=Vector3.new(80,–20,–60) end end end РИС. 3.136. УВЕЛИЧЕНИЕ ВРЕМЕНИ ТАЙМЕРА И ПЕРЕИМЕНОВАНИЕ ПЛАТФОРМЫ 151 Чтобы не запутаться в скриптах, переименуем скрипт платформы в Script1. Теперь осталось перейти в главный скрипт Script и добавить условие, согласно которому по достижении пяти очков игрок выигрывает и таймер останавливается. Пример реализации представлен ниже: local local local local local a0=game.Workspace.Apple a1=game.Workspace.Apple1 a2=game.Workspace.Apple2 a3=game.Workspace.Apple3 a4=game.Workspace.Apple4 local n=0 function hl() n=n+l print(n) if n>=5 then local gg=game.Workspace.LLL.Script1 gg:Destroy() print("Вы выиграли") end end if a0.Touched:Wait(1) then hl() aO:Destroy() end if a1.Touched:Wait(1) then hl() В этой части кода показано, что и куда нужно добавить. Смысл этих строк такой: если n равняется пяти или больше этого значения, то нужно удалить скрипт у платформы LLL и вывести Вы выиграли в окне Output. Запусти и проверь работу игры. ВЗРЫВЫ И РАЗРУШЕНИЯ Еще одна интересная игровая механика — взрывы и разрушения, которые эти взрывы создают. Для начала рассмотрим, что такое взрыв. Взрыв — это некий силовой эффект, который обладает радиусом поражения BlastRadius и силой поражения BlastPressure. Этот игровой элемент можно вызвать из инструмента Effects, щелкнув на параметре Explosion. 152 РИС. 3.137. СОЗДАНИЕ ВЗРЫВА Как только ты добавишь этот элемент, то увидишь, что в игровом окне произойдет взрыв — в окне Explorer отобразится элемент взрыва. По умолчанию он срабатывает один раз, а потом его нельзя увидеть. РИС. 3.138. ВЗРЫВ Активизировав параметр Visible в окне Properties, ты увидишь процесс взрыва. Правда, это можно делать не во время игрового процесса. Чтобы взрывы происходили во время игры, нужно создавать их с помощью функции Instance. Для примера создадим скрипт, в котором пропишем бесконечный цикл, вызывающий ежесекундные взрывы. По умолчанию взрыв будет происходить в точке с координатами (0, 0, 0). Предварительно перед запуском игры создай Spawn в отдаленном от этой точки месте: while true do wait(1) local b = Instance.new("Explosion",workspace) end 153 При запуске игры ты увидишь этот эффект. Поэкспериментируй и попробуй подойти к источнику взрыва. Взрывы могут быть весьма красивыми. Например, можно создать череду взрывов по одной линии. Чтобы взрывы происходили по ровной линии, по кривой или по всей поверхности, необходимо углубляться в математику. Разделы математики, описывающие графики функций и поверхности фигур, называются алгебра, математический анализ и аналитическая геометрия. Рассмотрим простой пример описания прямой линии. Так как игровая поверхность лежит в плоскости xOz, то и записывать будем с помощью координат x и z. К примеру, есть такая линия, описываемая уравнением z = 2 × x. Эту линию можно описать с помощью языка Lua. Для этого в нашем бесконечном цикле создадим вложенный конечный цикл, в котором через каждые 3 studs будем вызывать взрыв. Чтобы взрыв смещался вдоль нужной нам линии, запишем ее математическое выражение, которое было взято в качестве примера. Полученные значения i запишем в координаты оси x, значения z — в координаты оси z. Чтобы эффект был виден, сделаем в конечном цикле задержку 0.01 секунды: while true do wait(1) for i=0, 100, 3 do local b = Instance.new("Explosion",workspace) -- Изменяем координаты по оси Z и X согласно зависимости z=2*i -- Указываем положение точки взрыва согласно зависимости b.Position=Vector3.new(i,1,z) wait(0.01) end end РИС. 3.139. ВЗРЫВЫ ВДОЛЬ ЛИНИИ 154 Теперь рассмотрим особенности разрушения конструкции. Для начала рядом со взрывом поставим блоки разного размера и цвета. Эту конструкцию лучше создать в новом игровом объекте. РИС. 3.140. БЛОКИ, ИМИТИРУЮЩИЕ КОНСТРУКЦИЮ Все блоки не связаны друг с другом. Теперь добавим с помощью скрипта один взрыв так, чтобы он оказался внутри конструкции. Для этого нужно получить координаты, взять их можно на основе координат крыши. РИС. 3.141. ПОЛУЧЕНИЕ КООРДИНАТ ДЛЯ ВЗРЫВА Как было написано ранее, сила взрыва и его радиус влияют на разрушение. С увеличением размеров деталей увеличивается и их масса. Нужно указать радиус поражения и силу воздействия. Чтобы взрыв произошел не сразу и мы смогли запечатлеть его, поставим таймер на 10 секунд. Этого достаточно, чтобы игра загрузилась: 155 -- Обратный отсчет for i=10,0, –1 do print("Осталось до взрыва = ",i) wait(1) end -- Взрыв -- Создание взрыва и его расположение local Bomb=Instance.new("Explosion",workspace) Bomb.Position=Vector3.new(0,1,–8) -- Указываем значения силы ударной волны Bomb.BlastPressure=500000 -- Указываем радиус поражения Bomb.BlastRadius=30 Очень важно настроить два последних параметра, а иначе взрыв может произойти, а разрушения конструкции не будет. Теперь нужно уделить внимание тому, как строить конструкции, чтобы они разрушались. Простые конструкции можно оставить как есть, на примере предыдущей, а можно объединить части конструкции в один объект. Ниже представлены примеры комбинации конструкции из частей, при которой она разрушается при взрыве. РИС. 3.142. ВАРИАНТ СБОРКИ КОНСТРУКЦИИ 156 РИС. 3.143. ПРЕОБРАЗОВАНИЕ КОНСТРУКЦИИ В МОДЕЛЬ С ПОМОЩЬЮ ГРУППИРОВКИ Следующий вариант сборки конструкции не дает объекту разрушиться. Именно инструмент Union свяжет все элементы в единое целое. Такая конструкция может только сместиться от взрыва, но не развалится. РИС. 3.144. ИНСТРУМЕНТ UNION ПОЗВОЛЯЕТ СДЕЛАТЬ КОНСТРУКЦИЮ НЕРАЗРУШИМОЙ Методы, позволяющие разрушить постройку, могут быть использованы для простых вариантов, но часто встречаются сложные модели. И тут на помощь приходит инструмент Create, в котором есть различные типы соединений. Этот инструмент уже рассматривался в разделе «Левитирующий лифт». Для примера воспользуемся соединением Weld (Сварной шов). И с помощью него сварим потолок со стенами. Для наглядности раздвинем стенки, чтобы потолок ни на что не опирался. 157 РИС. 3.145. РАБОТА СВАРНОГО ШВА ДЛЯ КОНСТРУКЦИИ Если запустить игру, то мы увидим левитирующий потолок, однако при взрыве конструкция разлетится. РИС. 3.146. РАЗРУШЕНИЕ КОНСТРУКЦИИ ДАЖЕ С ИСПОЛЬЗОВАНИЕМ СОЕДИНЕНИЯ WELD 158 В качестве закрепления пройденного материала рекомендую выполнить практические задания. 1 2 Напиши программу, чтобы взрывы шли по параболе. Уравнение параболы — z x 4 для интервала [–40, 40]. Напиши программу, где при нажатии на кнопку происходит взрыв. Создай конструкцию с помощью инструментов Create: телевизор на столе, двухэтажное здание, автомобиль. Подбери соответствующие параметры для взрыва в каждом случае так, чтобы конструкция разрушилась. Врыв должен произойти через 10 секунд после запуска игры. Создай программу, где по истечении 15 секунд персонаж взрывается, причем в любой точке пространства, где бы ни стоял. Не используй огромный радиус поражения! СОЗДАНИЕ ИНВЕНТАРЯ В некоторых играх у персонажа бывает заготовленное количество игровых объектов, которыми он может пользоваться. В этом разделе поговорим, как добавлять инвентарь персонажу. Для примера создадим инвентарь из факела и топора. Начнем с создания факела. Создадим Block инструментом Part и зададим размер (0.5, 0.5, 0.5). Инвентарь должен быть стартовым, то есть данные элементы присутствуют у персонажа уже во время появления в игре. Поэтому объект нужно создать в папке StarterPack. Все игровые элементы, которые будут использоваться, называются Tool. РИС. 3.147. СОЗДАНИЕ ИНСТРУМЕНТА ДЛЯ ИНВЕНТАРЯ 159 Переименуем созданный Tool в Torch (факел). Запустим игровой проект и посмотрим результат нашей работы. РИС. 3.148. ИНСТРУМЕНТ ИНВЕНТАРЯ Инструмент добавлен, но при щелчке кнопкой мыши ничего не происходит, кроме выделения Torch синим квадратом. Чтобы что-то происходило, а именно, появлялся инструмент, нужно добавить модельки к инструменту инвентаря. В нашем случае — это Block, которому были заданы материал Wood и коричневый цвет. Добавляется игровой блок простым перетаскиванием из Workspace в StartPack-Torch. Если запустить игру и щелкать на Torch, мы увидим, как наш игровой блок то появляется, то исчезает. И появляется он в том же месте, где изначально был размещен. РИС. 3.149. РАЗМЕЩЕНИЕ ИГРОВОГО БЛОКА В ИНВЕНТАРЬ 160 Часто любой элемент инвентаря необходимо брать в руки. Чтобы этот блок появлялся в руке, его нужно переименовать в Handle (Ручка). Блок в руке РИС. 3.150. УДЕРЖАНИЕ БЛОКА В РУКЕ В нашем случае этот блок может выступать в роли рукоятки для факела. Сам факел создадим из двух цилиндров с помощью Union. РИС. 3.151. СОЗДАНИЕ ФАКЕЛА Чтобы прикрепить факел к ручке (Handle), нужно перенести последнюю опять в Workspace, чтобы она стала видимой для нас. Затем необходимо найти переднюю (фронтальную) грань. Ее можно определить в окне Properties, в параметре Surface, щелкнув на FrontSurface. Если этого параметра нет в окне Properties, то достаточно 161 щелкнуть на инструменте Surface на верхней панели Roblox Studio, выбрать один из параметров и щелкнуть на поверхности рукоятки. В результате в окне Properties появится параметр Surface. РИС. 3.152. ОПРЕДЕЛЕНИЕ ФРОНТАЛЬНОЙ ПОВЕРХНОСТИ К этой грани можно прикрепить наш факел. Изменим размер факела до (4, 1, 1). Затем, чтобы привязать ручку к факелу, можно воспользоваться инструментом Weld (Сварка). Этот процесс описывался ранее. РИС. 3.153. СВАРКА РУЧКИ С ФАКЕЛОМ 162 РИС. 3.154. ПРОЦЕСС СВАРКИ Выделим ручку и факел и переместим их в инструмент Torch. Если запустить игру и щелкнуть на Torch, результат будет такой, как на рис. 3.155. РИС. 3.155. УДЕРЖАНИЕ ФАКЕЛА В РУКАХ Чтобы факел смотрелся в руке более органично, можно уменьшить размер ручки до (0.1, 0.1, 0.1) и сделать ее прозрачной. 163 РИС. 3.156. ПОДСТРАИВАЕМ ФАКЕЛ ПОД РУКУ Чтобы показать, что это действительно факел, добавим эффект Fire (Огонь) к созданной сфере и переместим ее в верхнюю часть факела. Сферу приварим к верхушке факела и сделаем прозрачной. РИС. 3.157. ГОРЯЩИЙ ФАКЕЛ Отредактировав факел, проверь работу. Для лучшего эффекта установи в игре ночное время. 164 РИС. 3.158. ГОРЯЩИЙ ФАКЕЛ Можно заметить, что огонь ничего не освещает, поэтому добавим к сфере еще и лампу PointLight. РИС. 3.159. ОСВЕЩЕНИЕ ОТ ФАКЕЛА На рис. 3.158 и 3.159 видно, чем отличается факел с лампой и без. В качестве закрепления создадим топор по такому же принципу, за исключением самой модели топора. Ее возьмем в окне Toolbox, выбрав параметр Meshes и введя в поисковике Axe. 165 РИС. 3.160. ДОБАВЛЕНИЕ ТОПОРА Создадим ручку с таким же именем Handle, найдем фронтальную грань и приварим к ней топор. Затем в StarterPack создадим второй Tool и переименуем в Ax (топор), в который добавим наш топор с ручкой. РИС. 3.161. ТОПОР В ИНВЕНТАРЕ Добавим на панель инвентаря значки наших инструментов. Для этого в окне Toolbox выбираем параметр Images и в поисковике указываем torch (факел). Щелкаем на понравившейся нам картинке правой кнопкой и копируем адрес URL картинки. 166 РИС. 3.162. ДОБАВЛЕНИЕ ЗНАЧКА ФАКЕЛА ЧЕРЕЗ КОПИРОВАНИЕ АДРЕСА URL Скопировав адрес, переходим к инструменту Torch в окне Explorer и в параметре Textureld окна Properties вставляем скопированный адрес также правой кнопкой мыши в сочетании с левой, выбрав Paste. Такую же процедуру можно проделать и с топором. РИС. 3.163. ДОБАВЛЕНИЕ ЗНАЧКОВ В ИНВЕНТАРЬ Поздравляю, теперь ты умеешь создавать инвентарь инструментов! Создай в инвентаре фонарик, который светится. Создай в инвентаре пистолет. 167 СОЗДАНИЕ CANVAS. GUI Часто в играх можно встретить текст, изображения (как статичные, так и анимированные) и даже кнопки. Такой игровой элемент называется Graphircal User Interface (графический интерфейс пользователя), или сокращенно GUI. GUI создается на специальном полотне, которое во многих игровых редакторах называется Canvas, а в Roblox — ScreenGUI. По умолчанию это полотно с текстом, кнопками и изображением создается в папке StarterGui в окне Explorer. Для примера добавим поле с текстом. РИС. 3.164. ДОБАВЛЕНИЕ ТЕКСТОВОГО ПОЛЯ На рис. 3.164 показано добавление текстового поля под именем TextLabel. Для этого поля мы можем вводить текст заранее в окне Properties, выбрав параметр Text. Работа с текстом РИС. 3.165. СВОЙСТВА TEXTLABEL 168 В примере введено слово Roblox. Тут же можно указать размер шрифта TextSize. Если тебе нужно, чтобы текст растянулся по размеру окна, используй автомасштаб TextScaled. Параметр Font отвечает за тип шрифта. РИС. 3.166. ВЫБИРАЕМ ТИП ШРИФТА ДЛЯ ТЕКСТА В самой игре ты не сможешь редактировать это поле и как-то менять текст. Текст можно менять либо во время редактирования, либо с помощью скрипта. wait(5) local t= game.StarterGui.ScreenGui.TextLabel t.Text="Вася Пупкин" РИС. 3.167. ИЗМЕНЕНИЕ ТЕКСТА С ПОМОЩЬЮ СКРИПТА Скрипт примера разместим в папке ServerScriptService. На рис. 3.165 и 3.166 видно, что при перемещении созданного поля с помощью желтых маркеров показывается его размер и расстояние до ближайшего края. Эти координаты принадлежат точке, расположенной в верхнем левом углу. 169 Рассмотрим другой элемент GUI —TextBox. Щелкнув на ScreenGui, выберем в левом верхнем меню TextBox. РИС. 3.168. СОЗДАНИЕ TEXTBOX Запусти игру: теперь в этом поле можно писать разные сообщения. РИС. 3.169. В TEXTLABEL НЕРЕДАКТИРУЕМЫЙ ТЕКСТ, А В TEXTBOX — РЕДАКТИРУЕМЫЙ Окно TexBox можно применить для считывания введенного текста, его дальнейшей записи, вывода или сравнения с шаблонным текстом. Для обработки такой информации создадим локальный скрипт для TextBox. 170 РИС. 3.170. СОЗДАНИЕ ЛОКАЛЬНОГО СКРИПТА ДЛЯ TEXTBOX Для работы с данными в TextBox используется функция FocusLost, задача которой — отслеживать введение данных в поле и обработать их, как только будет нажата клавиша Enter или другая. Клавиша Enter используется по умолчанию в функции FocusLost. А для того, чтобы применять другую клавишу, необходимо писать KeyCode и имя клавиши. Рассмотрим первый вариант: -- Создание функции, обрабатывающей событие "нажатие Enter", -- и вывод введенного текста function Vvod (enterPressed) if enterPressed then end end print (script.Parent.Text) script.Parent.FocusLost:Connect(Vvod) Запусти программу, введи любой текст в TextBox, а затем нажми Enter. Обрати внимание на окно Output. 171 РИС. 3.171. ВЫВОД ВВЕДЕННОГО ТЕКСТА Важно отметить, что при запуске игры этот пользовательский интерфейс отображается для конкретного игрока. Поэтому во время игры в папке Players появляется папка, хранящая информацию о подключенном пользователе. В ней есть папка PlayerGui, которая содержит элемент ScreenGui с полностью скопированными элементами из папки StarterGui. Поэтому любые изменения текста и его отображение выполняются для конкретного игрока. РИС. 3.172. РАСПОЛОЖЕНИЕ GUI ДЛЯ КОНКРЕТНОГО ИГРОКА Рассмотрим, как работает передача сообщения (текста) в игре. Например, нужно ввести текст в окно TextBox, чтобы после нажатия клавиши Enter этот текст отобразился в окне TextLabel. Для решения этой задачи возьмем тот же локальный скрипт, который мы создали для предыдущей задачи. Добавим строки, где присвоим введенный текст переменной, значение которой затем присвоим текстовому полю окна TextLabel: 172 -- Создание функции, обрабатывающей событие "нажатие Enter", -- и вывод введенного текста b='' -- Переменная b function Vvod (enterPressed) if enterPressed then -- Записываем текст из TextBox в память переменной b b = script.Parent.Text -- Присваиваем текст из b в поле Text окна "TextLabel" script.Parent.Parent.TextLabel.Text = b -- Вывод значений текста из окна "TextBox" и "TextLabel" в окно "Output" print(script.Parent.Text) print(script.Parent.Parent.TextLabel.Text) end end -- Запуск функции "Vvod" после нажатия клавиши "Enter" script.Parent.FocusLost:Connect(Vvod) Строка script.Parent.text ссылается на родителя скрипта, то есть на TextBox, а строка script.Parent.Parent — на родителя, которому принадлежит окно TextBox. Там же находится и TextLabel. РИС. 3.173. РАБОТА СКРИПТА ПО ПЕРЕДАЧЕ СООБЩЕНИЯ МЕЖДУ ОКНАМИ GUI Таким же способом можно работать и с другими элементами GUI. Например, можно добавить картинку в пользовательское меню. За добавление картинки отвечает ImageLabel. Чтобы создать это окно, нужно щелкнуть на папке StarterGui —ScreenGui в окне Explorer. Как и для предыдущих окон, существует два способа вызова. 173 РИС. 3.174. СОЗДАЕМ IMAGELABEL Созданное окно будет пустым, и нужно добавить адрес изображения, которое содержится в базе данных Roblox. Для этого найдем подходящую картинку на вкладке Images окна Toolbox. Щелкнув ПКМ на нем, скопируем адрес (URL) и вставим в параметр Image окна Properties. РИС. 3.175. ДОБАВЛЕНИЕ ИЗОБРАЖЕНИЯ В IMAGELABEL Как видно на рис. 3.175, картинка сразу отобразится, как только мы добавим адрес в параметр Image. 174 Теперь рассмотрим процесс добавления кнопки TextButton. Как понятно из названия — это визуальная кнопка с текстом. Добавляется она стандартным способом, как и другие элементы GUI: TextBox, TextLabel и ImageLabel. РИС. 3.176. ДОБАВЛЕНИЕ TEXTBUTTON Для изменения цвета фона кнопки используют параметр BackgroundColor3, а для изменения текста — параметр Text. Также фон ты можешь поменять для TextBox и TextLabel. В качестве примера создадим скрипт, благодаря которому при нажатии кнопки будет появляться окно ImageLabel. Скрипт создадим локальный: РИС. 3.177. ДОБАВЛЕНИЕ ЛОКАЛЬНОГО СКРИПТА В TEXTBUTTON 175 -- Отключаем видимость окна ImageLabel script.Parent.Parent.ImageLabel.Visible=false -- Функция, где включается видимость окна ImageLabel function v() script.Parent.Parent.ImageLabel.Visible=true end -- При активизации кнопки запускается функция script.Parent.Activated:Connect(v) Если запустить игру, то окна ImageLabel мы не увидим. И если нажать кнопку Открыть картинку, то ImageLabel появится на экране. Картинка отобразится при одной активизации кнопки и станет видимой навсегда. Чтобы отрегулировать отображение картинки, как бы включая и выключая его, необходимо отредактировать скрипт. Добавим глобальную переменную и функцию для подсчета нажатий на кнопку. В функции v добавим условие, при котором в зависимости от значения счетчика картинка будет либо появляться, либо исчезать: -- Отключаем видимость окна ImageLabel script.Parent.Parent.ImageLabel.Visible=false -- Вводим глобальную переменную для счетчика g=0 -- Функция, где включается или отключается видимость -- окна ImageLabel function v() if g==1 then script.Parent.Parent.ImageLabel.Visible=true elseif g==0 then script.Parent.Parent.ImageLabel.Visible=false end end -- Функция счетчика function actol() if g==0 then g=l elseif g==l then g=0 end end Осталось добавить две строчки по вызову данных функций при активизации кнопки: -- При активации кнопки запускаются функции v и actol script.Parent.Activated:Connect(v) script.Parent.Activated:Connect(actol) Запусти игру и проверь работу скриптов. 176 Кроме классической кнопки есть кнопка с рисунком ImageButton, которая вызывается и настраивается так же, как и предыдущие элементы GUI. Для этой кнопки добавляется не текст, а рисунок, который может содержать как текст, так и любое изображение. Рисунок добавляется по той же схеме, как и для ImageLabel. РИС. 3.178. ДОБАВЛЕНИЕ IMAGEBUTTON Еще один интересный GUI-элемент — ScrollingFrame — окна с набором множества GUI-элементов типа TextButton и TextBox. Этот элемент позволяет добавить несколько перечисленных элементов GUI в ограниченную область, что избавляет разработчика от загромождения игрового пространства. РИС. 3.179. ДОБАВЛЕНИЕ SCROLLINGFRAME 177 В качестве примера добавим несколько TextButton в ScrollingFrame. Для этого в окне Explorer щелкнем на значке + напротив ScrollingFrame и выберем TextButton. РИС. 3.180. ДОБАВЛЕНИЕ КНОПОК В SCROLLINGFRAME Затем можно уменьшить размер области ScrollingFrame так, чтобы были видны только две кнопки. Запустим игру и проверим работу скроллинга. Справа окна есть бегунок для перемещения по полю. Также это можно сделать с помощью колесика мыши. И последний элемент, который будет рассмотрен, — это Frame (Рамка). Это окно отвечает за группировку всех остальных перечисленных элементов. Во Frame можно разместить все элементы GUI разом в любом удобном для тебя порядке. Добавление самого окна Frame и размещение других GUI-элементов осуществляются по той же схеме. Создай скрипт для кнопки, с помощью которого будут появляться и исчезать все перечисленные элементы GUI: TextBox, TextLabel, ImageLabel, ImageButton. Создай скрипты для пяти кнопок в ScrollingFrame, чтобы при активизации одной кнопки активизировалась одна из функций: àà появление и исчезновение картинки; àà появление текста в TextLabel; àà смена фона TextBox; àà смена изображения в ImageLabel; àà изменение шрифта текста в TextLabel. 178 АНИМАЦИЯ ПЕРСОНАЖА ИЛИ БОТА В Roblox можно создавать анимации для гуманоидов. Roblox Studio позволяет создавать анимации и присваивать их либо игровому персонажу, либо NPC — неигровому персонажу (боту). Для того чтобы создать анимацию, необходимо перейти на вкладку Plugins и выбрать Build Rig. После появится окно для выбора варианта гуманоида и особенности скелетной конструкции. Согласно документации рекомендуется выбирать R15, так как для этой системы костей поддерживается инверсия. Эти заготовки нужны для создания анимации персонажам гуманоидного типа. Рассмотрим все шесть разновидностей шаблонных персонажей. РИС. 3.181. ГУМАНОИДНЫЕ ЗАГОТОВКИ ДЛЯ АНИМАЦИИ Изучим создание анимации с использованием Mesh Rig. Для начала работы нужно вызвать окно редактирования и записи анимации. Для этого достаточно щелкнуть на инструменте Animation Editor. 179 РИС. 3.182. ВЫЗОВ ANIMATION EDITOR Создадим простую анимацию — персонаж машет рукой в знак приветствия. Для реализации нужно щелкнуть на начале руки (плечо). Сразу после щелчка появится окно, где будет предложено назвать анимацию. Напишем Hello и нажмем кнопку Create. РИС. 3.183. СОЗДАНИЕ АНИМАЦИИ 180 Далее снова щелкнем по плечу, немного сдвинем руки и возвратим в исходное положение. Мы задали исходное положение руки, и в окне Animation Editor появилось указание на руку и ключи для всех элементов руки. РИС. 3.184. ЗАПИСЬ КЛЮЧЕВЫХ КАДРОВ АНИМАЦИИ РУКИ Теперь настроим длительность анимации. Для этого в окне Animation Editor напротив анимации Hello нужно выставить конечное время, например 5:00 (5 секунд). РИС. 3.185. ВЫСТАВЛЕНИЕ ДЛИТЕЛЬНОСТИ АНИМАЦИИ Щелкаем на отметке 1:00 (1 секунда) и поднимаем руку персонажа, поворачивая плечо. 181 Теперь нужно взяться за часть руки перед кистью и повернуть ее так, чтобы ладонь смотрела на тебя. РИС. 3.186. АНИМАЦИЯ РУКИ Если ключи анимации появились на отметке 1 секунда, значит ты сделал все правильно. Теперь будем двигать плечевую кость: одну секунду влево, затем одну секунду вправо — и повторим это движение еще раз. Анимация должна уложиться в пять секунд. Добавим еще одну секунду для последнего движения — возвращения руки в исходное положение. РИС. 3.187. АНИМАЦИЯ ВЗМАХА РУКИ 182 РИС. 3.188. ВОЗВРАЩЕНИЕ РУКИ В ИСХОДНОЕ ПОЛОЖЕНИЕ Для проверки своей анимации щелкни на значке Play () в окне Animation Editor. Если анимация руки работает правильно, нужно ее экспортировать, чтобы использовать. Для этого в окне Animation Editor выбираем инструмент Export (рис. 3.189). РИС. 3.189. ЭКСПОРТ АНИМАЦИИ После этого появится окно с настройками для экспорта. 183 РИС. 3.190. НАСТРОЙКИ ДЛЯ ЭКСПОРТА После нажатия на кнопку Submit произойдет экспорт и появится окно с указанием ID анимации и адресом ее расположения. Именно адрес нам и нужен. Можно перей­ти по нему и скопировать. РИС. 3.191. ЭКСПОРТ АНИМАЦИИ Владельцем анимации будет тот, кто ее создал и экспортировал. 184 РИС. 3.192. ЭКСПОРТИРОВАННАЯ АНИМАЦИЯ Теперь, чтобы наш персонаж мог исполнить действие, создадим игровой элемент Animation в папке ReplicatedStorage. Эта папка дает доступ как локальным скриптам, так и глобальным. Для удобства хранения анимации создай папку Animation11, выбрав инструмент Folder. Хранение анимаций РИС. 3.193. ДОБАВЛЯЕМ ПАПКУ В REPLICATEDSTORAGE 185 Новый элемент РИС. 3.194. ДОБАВЛЕНИЕ ЭЛЕМЕНТА ANIMATION Переименуем анимацию в Hello и в окне Properties для параметра Animationid добавим адрес созданной нами анимации. Адрес созданной анимации РИС. 3.195. ДОБАВЛЕНИЕ СОЗДАННОЙ АНИМАЦИИ В ЗАГОТОВКУ Теперь осталось привязать анимацию к игровому персонажу. Для этого создадим LocalScript в папке StarterPlayer — StarterPlayerScripts. В этих папках хранятся элементы, которые предназначены только для персонажей. 186 РИС. 3.196. СОЗДАНИЕ ЛОКАЛЬНОГО СКРИПТА ДЛЯ ИГРОКА В скрипте создаем переменные для принятия параметров игроков, а именно Humanoid. Затем вызываем анимацию, которую присваиваем нашему игроку, и запускаем проигрывание: -- Создаем переменную, которая принимает значение игроков, подключенных -- к игре local player = game:GetService("Players").LocalPlayer -- Ждем три секунды и проверяем наличие параметра "Humanoid" wait(3) local g = player.Character local hum =g:WaitForChild("Humanoid") -- Ждем три секунды и подключаем анимацию к игровому персонажу wait(3) local а = game.ReplicatedStorage.Animationll.Hello local an = hum:LoadAnimation(a) -- Запускаем проигрывание созданной анимации an:Play() В данном случае задержка по времени wait важна — нужно дать время для загрузки игрока в игру. Если не прописать задержку, то программа будет пытаться найти параметр Humanoid, который еще не загрузился. Возникнет ошибка, и скрипт прервется. Для проверки запусти игру и убедись в работе скрипта: через 6 секунд после запуска персонаж должен помахать рукой. 187 РИС. 3.197. АНИМАЦИЯ ИГРОВОГО ПЕРСОНАЖА В качестве закрепления темы выполни ряд заданий. Создай анимацию танца персонажа и присвой ее ему. Добавь несколько TextButton и напиши программу по запуску анимации для персонажа после активизации данных кнопок. ДИАЛОГИ Часто в играх жанра RPG (Role-Playing Game, ролевая игра) используется механика диалогов. Диалоги позволяют игроку получить информацию в игре. Это может быть: диалог с выбором вариантов ответов; текстовая подсказка при контакте с NPC; информация о товаре; диалог-магазин с выбором товара. Создадим новый игровой проект и возьмем модель NPC со вкладки Meshes в окне Toolbox. Введем в поисковике слово man (или другое) и выберем понравившегося нам персонажа. РИС. 3.198. СОЗДАНИЕ NPC 188 Представленная модель целостная и не имеет отдельных частей. Чтобы создать диалог, нужно добавить нашему NPC этот инструмент в окне Explorer. РИС. 3.199. СОЗДАНИЕ ДИАЛОГА Переименуем созданный диалог в Info и пройдемся по параметрам его свойств. РИС. 3.200. СВОЙСТВА DIALOG Параметр BehaviorType (Тип поведения) имеет два режима поведения: один игрок и несколько игроков одновременно. Имеется в виду взаимодействие NPC с одним персонажем отдельно или одновременно со всеми подключенными к игре. Параметр Conversation Distance настраивает расстояние, с которого начнется диалог. По умолчанию стоит 25 studs. 189 Параметр GoodbyeDialog (Прощальный диалог) можно использовать в качестве завершения диалога. Введем фразу «Я все понял». Параметр InitialPrompt (Первоначальный запрос) необходим для инициализации (начала) диалога. Пропишем его. Например, введем фразу «Здравствуйте, я могу вам помочь». Если запустить игру с такими настройками диалога, то при щелчке на знаке вопроса над NPC мы получим такой результат. РИС. 3.201. ДИАЛОГ В ПЕРВОНАЧАЛЬНЫХ НАСТРОЙКАХ Параметр InUse (Взаимодействие с игроком) показывает, щелкнул игрок на диалоге или нет. Если нет, то клеточка напротив этого параметра будет пуста, и это означает false (нет). Если игрок щелкнет на значке диалога, то напротив параметра появится галочка, то есть он примет значение true (да). РИС. 3.202. ОТОБРАЖЕНИЕ ВЗАИМОДЕЙСТВИЯ ИГРОКА С ДИАЛОГОМ NPC 190 Параметр Name содержит имя диалога, а параметр Parent содержит информацию о родителе, которому принадлежит этот диалог. Параметр Purpose (Назначение) имеет три вида значений. Help — помощь, информация, то, что может помочь игроку в игре. Quest — квест. Запутанная сеть диалогов, где игроку предстоит выбрать нужную нить разговора с NPC. Shop — магазин. Вариант, который предлагает купить товар у NPC. При выборе назначения будет меняться и значок диалога. Для Help — знак вопроса, для Quest — восклицательный знак, а для Shop — знак доллара. В зависимости от того, какую задачу выполняет твой NPC, ты можешь выбирать нужные назначения диалога. Параметр Tone (Тон) настраивает отношение NPC к персонажу посредством диалога. Существует три значения для Tone. Neutral (Нейтральный) — диалоговые окна подсвечиваются слева вертикальной синей линией. Friendly (Дружелюбный) — диалоговые окна подсвечиваются слева вертикальной зеленой линией. Enemy (Враждебный) — диалоговые окна подсвечиваются слева вертикальной красной линией. По умолчанию стоит Neutral. Параметр TriggerDistance (Расстояние до триггера (вызова)) отвечает за автоматический запуск диалога, когда игрок приближается к NPC на расстояние, указанное в studs. Параметр TriggerOffset (Смещение триггера) позволяет сместить триггер по осям x, y и z туда, где по задумке разработчика должен оказаться игрок, чтобы запустился определенный диалог. Основные свойства диалога были рассмотрены, теперь расскажу о добавлении разных вариантов вопросов и ответов. Для этого необходимо диалогу Info добавить элемент DialogChoice (Выбор диалога). 191 РИС. 3.203. ДОБАВЛЯЕМ ВАРИАНТ ДИАЛОГА Переименуем DialogChoice в Quest1. Щелкнув на этом элементе, перейдем в окно Properties, чтобы настроить вопрос и ответ. В параметре UserDialog вводим вопрос, который может выбрать наш игрок. Например: «Где я?». В параметре ResposeDialog введем текст ответа NPC: «Ты в игре Roblox». РИС. 3.204. ДИАЛОГ С NPC 192 РИС. 3.205. ОТВЕТ NPC Добавим к Info еще два вопроса и назовем их Quest2 и Quest3. В UserDialog для второго вопроса впишем фразу «Кто вы?», а для третьего — «Как у тебя дела?». В ResposeDialog для второго вопроса впишем ответ «Я NPC, который дает задание», а для третьего — «Сегодня прекрасный день, и настроение хорошее». РИС. 3.206. ДИАЛОГОВОЕ ОКНО С НЕСКОЛЬКИМИ ВАРИАНТАМИ ВОПРОСОВ Запусти игровой процесс и проверь правильность работы диалога. Обрати внимание на особенность ответа NPC на вопрос «Как у тебя дела?». Ответ не поместился полностью в диалоговое окно. Поэтому нужно учитывать длину предложения, которое помещается в ответе. Ты можешь не останавливаться на этом примере создания диалога, а добавлять еще диалоги внутри других диалогов. Например, в диалоге Quest2 можно добавить еще два диалога, где NPC будет давать задание персонажу. Например, в Quest21 NPC попросит принести ему красное яблоко, а в Quest22 предложит поговорить с волшебником. 193 РИС. 3.207. РАЗВЕТВЛЕНИЕ В ДИАЛОГАХ Если необходимы сложные и красочные диалоги, то лучше воспользоваться ScreenGUI, который будет отображать кнопки или текстовые поля, как только персонаж подходит или касается NPC. Рассмотрим варианты взаимодействия с диалогом через скрипт. Особенность работы игрока с диалогами через скрипт заключается в создании локального скрипта для подключенных игровых персонажей. Локальный скрипт создается в папке StarterPlayer —StarterPlayerScripts. В скрипте мы создадим функцию, которая будет выводить имя игрока, взаимодействующего с диалогом, и название того диалога, который он выбрал. Чтобы вызвать эту локальную функцию, используют встроенную функцию для диалогов DialogChoiceSelected: -- Переменная, принимающая свойства диалога Info local dialog = game.Workspace.hhh.Info -- Фунция, которая выводит имя игрока и вариант диалога, -- который он выбрал local function dialogselection(player, choice) print("Имя игрока – ", player.Name) print("Bыбop диалога – ", choice.Name) end -- Функция выбора любого из вариантов диалога с вызовом -- локальной функции dialogselection dialog.DialogChoiceSelected:Connect(dialogselection) Если запустить игру и щелкать на диалоговых окнах, то в окне Output отобразятся имя персонажа, который работает с диалогом, и имя диалогового окна. 194 РИС. 3.208. РАБОТА СКРИПТА Если нужно вывести сам ответ или записать его в память переменной, то подойдет такая запись, которую можно прописать внутри нашей функции: print("Ответ – ", choice.ResponseDialog) Рассмотрим вариант, когда с помощью скрипта нужно создать диалог. Для этого создадим простой Block и присвоим ему скрипт. В нем создадим Dialog, внутри которого — несколько диалоговых окон с вопросами и ответами: -- Создание диалога для блока local d1 = Instance.new ("Dialog",workspace.Part) -- Переименовка диалога d1.Name="Information" -- Назначение диалога — квест d1.Purpose="Quest" -- Назначение тона d1.Tone = "Friendly" -- Установка дистанции триггера при срабатывании без клика d1.TriggerDistance=25 -- Установка начала диалога d1.InitialPrompt= "Здравствуй друг. Я говорящий камень." -- Создание первого диалогового окна с вопросом и ответом local d2=Instance.new("DialogChoice",d1) d2.Name = "Boпpoc1" d2.UserDialog= "Как ты здесь оказался?" d2.ResponseDialog = "Меня запрограммировали" -- Создание второго диалогового окна с вопросом и ответом local d2=Instance.new("DialogChoice",d1) d2.Name = "Boпpoc2" d2.UserDialog= "Как мне пройти в школу?" d2.ResponseDialog = "Иди прямо" Если ты запустишь игру, то увидишь такой же результат, как на рис. 3.209. Диалоги можно создавать либо с помощью инструментов в окне Explorer, либо с помощью программ. 195 РИС. 3.209. РЕЗУЛЬТАТ РАБОТЫ СКРИПТА Создай диалог с NPC с помощью инструментов Dialog и DialogChoice в окне Explorer. В диалоге должна содержаться просьба показать дорогу к волшебному дереву. При выборе неправильных ответов и вопросов NPC отказывается показывать дорогу, а при правильном — указывает дорогу. С помощью программы создай «дерево» диалога с разными ответвлениями в повествовании. В черных рамках — ответы NPC, а в синих — вариант вопросов и ответов игрока. Погода ужасна Прекрасная погода, не правда ли? Погода прекрасна Вы точно не знаете Как у вас дела? Вы что-то хотели? Где находится меч-кладенец? Я не знаю про этот меч Я вам заплачу Что вы тут делаете? Жду помощи Дай меч, а то рассержусь Как тебе помочь? Накажи Змея Горыныча Хорошо, я помогу У него мечкладенец Нет, с таким биться не буду Очень жаль. Ничем помочь не могу РИС. 3.210. ВЕТКА ДИАЛОГА Разработай диалог с помощью GUI. Можно использовать рис. 3.210. 196 СОХРАНЕНИЯ ДОСТИЖЕНИЙ ИГРОКА Если в твоей игре присутствует рейтинг достижений и важна система хранения очков игроков и других игровых элементов, необходимо использовать модуль Data Stores (Хранилище данных). Если игроки регулярно посещают твою игру для накопления игровых достижений, то такой модуль будет очень полезен. Все игровые достижения хранятся не на компьютере пользователя, а на сервере (в цифровом облаке). Скрипт, который будет обрабатывать данные, должен располагаться в папке ServerScriptService в окне Explorer. Перед тем как приступить к написанию программы, нужно выполнить два обязательных условия: создать игровой проект, сохранить его, можно под именем Data1; опубликовать на сервере, выбрав на вкладке File инструмент Publish Roblox. Щелкнув на вкладке Game Settings (Настройки игры), перейти на вкладку Security и активизировать параметр Enable Studio Access to API Services (Включить доступ студии к службам API). После чего щелкнуть на кнопке Save. РИС. 3.211. ПУБЛИКАЦИЯ ИГРЫ По умолчанию игра будет опубликована с доступом только для разработчика. Изменить доступ можно на сайте Roblox в личном кабинете на вкладке Create. 197 Параметры настройки РИС. 3.212. НАСТРОЙКА ОПУБЛИКОВАННОЙ ИГРЫ РИС. 3.213. НАСТРОЙКА ДОСТУПА К ИГРЕ Также можно настроить доступ к игре и в окне Game Settings, выбрав вкладку Permissions (Разрешения). Можно сделать игру публичной для всех или только для твоих друзей в сообществе Roblox. 198 РИС. 3.214. НАСТРОЙКА ДОСТУПА К ИГРЕ Опубликовав игру и настроив доступ к ней, переходим к подключению API студии для работы с базой данных. РИС. 3.215. ПОДКЛЮЧЕНИЕ К API ROBLOX STUDIO Теперь все предварительные настройки сделаны и можно приступать к работе с данными. Подробнее о Data Stores ты можешь почитать в инструкции по ссылке https:// developer.Roblox.com/en-us/articles/Data-store. Там представлены примеры работы со многими функциями этого модуля. В книге же рассмотрим основные свойства этого модуля, которых будет достаточно для твоих первых игр в Roblox. Все достижения привязываются к игроку и сохраняются в виде словаря (таблицы). Каждый игрок, кроме своего имени и аватара, имеет идентификационный номер (UserId). Он и играет главную роль в записи данных для определенного игрока. 199 Первым делом в скрипте нужно прописать код, благодаря которому программа будет проверять наличие в игре игрока и подключение к модулю Data Stores для последующего создания базы данных под именем Money: -- Подключение игрока local Players = game:GetService("Players") -- Создание базы данных с именем Money local goldDataStore = game:GetService("DataStoreServiсе"): GetDataStore("Money") Переменная goldDataStore будет хранить в себе весь функционал Data Stores для базы данных Money. Теперь нужно создать функцию, которая будет работать с данными при подключении игрока к игре: -- Вызов функции, когда игрок подключается к игре Players.PlayeгAdded:Connect (function(player) end) Для модуля Players есть функция PlayerAdded, задача которой — отслеживать подключение игроков к игре. Внутри этой функции создадим папку с именем leaderstats и переменную с целочисленным типом данных с именем gold: -- Вызов функции, когда игрок подключается к игре Players.PlayerAdded:Connect (function(player) -- Создание папки для хранения достижений игрока local leaderstats = Instance.new("Folder") leaderstats.Name = "leaderstats" leaderstats.Parent = player -- Создание переменной, принимающей целочисленное значение, -- которое будет содержаться в созданной папке local gold = Instance.new("IntValue", leaderstats) gold.Name = "gold" gold.Parent = leaderstats end) Обрати внимание: папка должна принадлежать игроку player, а целочисленное значение gold — папке. Добавим в эту же функцию две строчки, в которых будут созданы две переменные. Одна будет принимать данные об игроке, а вторая — считывать эти данные: 200 -- Вызов функции, когда игрок подключается к игре Players.PlayerAdded:Connect (function(player) -- Создание папки для хранения достижений игрока local leaderstats = Instance.new("Folder") leaderstats.Name = "leaderstats" leaderstats.Parent = player -- Создание переменной, принимающей целочисленное значение, -- которое будет содержаться в созданной папке local gold = Instance.new("IntValue", leaderstats) gold.Name = "gold" gold.Parent = leaderstats -- Создание переменной для хранения сведений об ID игрока local playerKey = "Player_" .. player.UserId -- Создание переменной для чтения из памяти сервера данных игрока local data end) Следующие добавления также будут касаться этой функции. Внутри нее нужно создать еще одну функцию, которая будет связываться с базой данных игрока на сервере и получать их. За получение данных отвечает функция GetAsync. Чтобы обезопасить процесс считывания данных с сервера через интернет от неожиданного прерывания, используют функцию pcall. Если подключение к серверу и чтение данных с него прошли успешно, то присвоим переменной gold значение, полученное с сервера, которое записывается в переменную data: -- Создание функции для чтения данных игрока с сервера с защитой -- от перебоев в сети local success, err = pcall(function() data=goldDataStore:GetAsync(playerKey)-- or 0 end) -- Если подключение к серверу прошло успешно, то -- переменная gold принимает значения, полученные с сервера if success then gold.Value=data else -- Failed to retrieve data end Запусти игру, результат будет такой, как на рис. 3.216. 201 РИС. 3.216. ПОДКЛЮЧЕНИЕ ИГРОКА И ПОЛУЧЕНИЕ ЕГО СВОЙСТВ По умолчанию значение игрового элемента gold равно нулю. Если в окне Properties для параметра Value прописать 100 и нажать клавишу Enter, то в поле достижений игрока значение gold изменится на 100. РИС. 3.217. ИЗМЕНЕНИЕ ЗНАЧЕНИЯ ПЕРЕМЕННОЙ GOLD Эта часть скрипта работает! Теперь осталось написать еще одну функцию, где полученные данные будут сохранены на сервер при выходе игрока из игры: -- Создание функции сохранения данных игрока при его выходе из игры Players.PlayerRemoving:Connect(function (player) -- Создание переменной для хранения сведений об ID игрока local playerKey = "Player_" .. player.Userid -- Создание переменной, принимающей значение переменной gold local data = player.leaderstats.gold.Value 202 -- Создание функции для сохранения данных игрока на сервер с защитой -- от перебоев в сети local success, err = pcall(function() goldDataStore:SetAsync(playerKey, data) end) -- Если сохранение прошло успешно, то вывести текст Data Save if success then print("Data Save") else print ("Ошибка сохранения") warn(err) end end) Функция PlayerRemoving проверяет связь компьютера с облаком данных игры по ID игрока. Если связи нет, то передача данных не осуществляется и игрок не загружается в опубликованной игре Roblox. Для этой функции также нужно создать переменную, принимающую данные об игроке. Можно создать одноименную переменную data, которая будет принимать значения, записанные в переменную gold. Чтобы записать данные на сервер, воспользуемся функцией SetAsync. Вызываться она будет так же — через встроенную функцию с защитой от перебоев в передаче данных. Если передача данных на сервер пройдет успешно, то появится сообщение Data Save в окне Output. Остановимся на тонкостях работы с сервером. Так как игра тестируется в Roblox Studio, а не на сервере при непосредственном подключении к игре, то нужно самостоятельно выбирать режимы работы в Roblox Studio, иначе твои данные могут не сохраниться. Запустим игру и на верхней вкладке Test выберем режим Current: Server (Текущий сервер). Работа с сервером РИС. 3.218. ВЫБОР РЕЖИМА — СЕРВЕР 203 Далее найдем переменную gold в окне Explorer и в окне Properties параметра Value изменим значение с нуля до 100. РИС. 3.219. ИЗМЕНЕНИЕ ЗНАЧЕНИЯ GOLD ВО ВРЕМЯ ИГРЫ Для проверки работы начисления денег перейдем в режим Current: Client. РИС. 3.220. ПРОВЕРКА ИЗМЕНЕНИЯ ЗНАЧЕНИЯ ДЛЯ ИГРОКА Если значение изменилось в поле достижений игроков, то возвращаемся в режим Current: Server. Подождем около шести секунд, так как передача данных идет с шестисекундной задержкой. Теперь выйдем из игры, в окне Output должна появиться надпись Data Save. Если она появилась, значит, данные записались на сервер. 204 РИС. 3.221. СОХРАНЕНИЕ ДАННЫХ НА СЕРВЕР ПРИ ВЫХОДЕ ИЗ ИГРЫ. Если запустить игру еще раз, можно увидеть, что значение игрока изменилось. С основной частью алгоритма передачи и считывания данных для игрока ты справился! И наконец, рассмотрим процесс начисления значения переменной gold, например, при щелчке ЛКМ. Для этого поверхности Baseplate добавим элемент ClickDetector, задача которого — считывать события щелчка. Этому же объекту Baseplate добавим скрипт, где будет вызываться функция, которая будет начислять значение переменной gold каждый раз, как только сработает событие щелчка кнопкой мыши на поверхности. РИС. 3.222. ДОБАВЛЕНИЕ CLICKDETECTOR РИС. 3.223. СОЗДАНИЕ СКРИПТА ДЛЯ BASEPLATE А вот сам скрипт: script.Parent.ClickDetector.MouseClick:Connect(function (player) player.leaderstats.gold.Value=player.leaderstats.gold.Value +1 end) 205 Если запустить игру и щелкать кнопкой мыши на поверхности, ты увидишь изменение значений для переменной gold. После этого перейди в режим Current: Server и по истечении шести секунд выйди из игры. Если ты снова зайдешь в игру, то увидишь, что значение сохранилось. Игровой процесс можно проверить и в самой игре на сервере. Для этого еще раз щелкни на инструменте Publish Roblox на вкладке File, а затем перейди в личный кабинет на странице Roblox. Выбери вкладку Create и найди свою игру. Чтобы запустить игру с сервера, достаточно щелкнуть на названии игры напротив фразы Start Place. РИС. 3.224. ЗАПУСК ИГРЫ С СЕРВЕРА После этого тебя перенаправят на страницу игры, где ты можешь ее запустить. Обрати внимание на ограничение по игре: одновременно подключаться к игре могут 50 игроков. 206 Запускаем игру РИС. 3.225. ЗАПУСК ИГРЫ С СЕРВЕРА Основной принцип работы с данными я постарался изложить подробно, и теперь на основе этой информации можно делать неплохие игры с рейтингом игроков. Создай игру, где нужно собирать яблоки. За сбор яблока начисляется один балл. Количество яблок — 20. Создай игру, где нужно собирать постоянно появляющиеся яблоки с интервалом 5 секунд. За сбор яблока начисляется один балл. Дополни второе задание функциональностью подсчета максимального значения собранных яблок и определения победителя. 207 УПРАВЛЕНИЕ ПЕРСОНАЖЕМ — ТАЧСКРИН Все рассмотренные варианты игровой механики могут правильно работать на компьютере, но на планшетах и смартфонах могут оказаться неработающими, так как там нет мыши и клавиатуры. Для устройств с сенсорным экраном необходимо добавить в игру функционал управления тактильным способом, так называемый тачскрин, touchscreen. Для знакомства с особенностями настройки тачскрина и работы с клавиатурой со­ здадим новый игровой проект. Для тестирования игры на устройствах с сенсорным экраном (смартфонах и планшетах) в Roblox Studio есть инструмент Emulation. Нужно в окне Emulation (Эмуляция, визуализация) активизировать инструмент Device (Устройство). РИС. 3.226. ТЕСТИРОВАНИЕ ИГРЫ НА УСТРОЙСТВАХ С СЕНСОРНЫМ УПРАВЛЕНИЕМ Выбери устройство, на котором хочешь протестировать игру (рис. 3.227). 208 РИС. 3.227. ВЫБОР УСТРОЙСТВА ДЛЯ ТЕСТИРОВАНИЯ Можно настроить масштаб отображения выбранного устройства: реальный размер, фактическое разрешение по отношению к разрешению экрана монитора, автомасштаб по экрану монитора. РИС. 3.228. РАЗМЕР ЭМУЛЯТОРА УСТРОЙСТВА 209 Если запустить игру в эмуляторе, ты увидишь, как будет работать твоя игра на выбранных устройствах. По умолчанию в играх для телефонов и планшетов предусмотрено базовое управление: движение персонажа по xz и прыжки в высоту по оси y. РИС. 3.229. ТЕСТИРОВАНИЕ ИГРЫ НА ЭМУЛЯТОРЕ СМАРТФОНА Если твоя игра простая, где нужно бегать, прыгать и собирать объекты касанием, то такого управления достаточно. Но бывает, что в игре должны быть еще какие-то функции: включение анимации добычи руды, стрельба и перезарядка, выход в меню и т. д. Тогда нужно создать дополнительные сенсорные кнопки. Их можно сделать с помощью GUI — и это будет твоим домашним заданием. Сейчас научимся создавать сенсорные кнопки. Для этого понадобится модуль ContextActionService. Документация к нему находится по адресу https://developer.Roblox.com/en-us/api-reference/class/ContextActionService. У этого модуля множество функций. Рассмотрим функцию BindAction (привязка действий), которая содержит три основных параметра: название кнопки, к которой должно быть привязано действие; функцию, которая вызывает какое-то действие; логическое значение, которое отображает кнопку. Если true, то кнопка отображается на экране. Если false — не отображается. С помощью новых сенсорных кнопок будем изменять цвет поверхности. Важно учесть, что работать с модулем ContextActionService можно только через локальный скрипт. Для правильного отображения рекомендуется размещать этот скрипт в папке StarterGui. 210 РИС. 3.230. СОЗДАНИЕ ЛОКАЛЬНОГО СКРИПТА ДЛЯ ТАЧСКРИНА В локальном скрипте подключаемся к модулю ContextActionService: -- Создание переменной для подключения к модулю ContextActionService local ВВ = game:GetService("ContextActionService") Потом создаем две функции. В одной поверхности цвет меняется на красный, в другой — на синий: -- Создание функции для изменения цвета платформы на красный function f() local v = game.Workspace.Baseplate v.BrickColor=BrickColor.new(255,0,0) end -- Создание функции для изменения цвета платформы на синий function fl () local v = game.Workspace.Baseplate v.BrickColor=BrickColor.new(0,0,100) end Активизируем кнопки, которые будут вызывать эти функции: -- Создание переменной для привязки действия функции f к кнопке local ccc = BB:BindAction("Color1", f,true) -- Добавление текста к кнопке BB:SetTitle("Color1","Красный") -- Установка расположения кнопки BB:SetPosition("Color1", UDim2.new(0,0,0,0)) -- Создание переменной для привязки действия функции f к кнопке local cccl = BB:BindAction("Color2", f1,true) -- Добавление текста к кнопке BB:SetTitle("Color2","Синий") -- Установка расположения кнопки BB:SetPosition("Color2", UDim2.new(0,50,0,0)) Первая кнопка Color1 с текстом Красный, вызывает функцию, окрашивающую поверхность в красный цвет. Вторая кнопка Color2 с текстом Синий вызывает функцию, окрашивающую поверхность в синий цвет. 211 Чтобы кнопки расположились правильно, на дисплее используют функцию Udim2. У нее есть четыре параметра: Udim2(x.scale, x.offset, y.scale, y.offset). Параметры x.scale и y.scale — относительные масштабные сдвиги на дисплее игры. Для сенсорных кнопок на экране отводится определенное место (невидимая рамка справа). Эти параметры изменяются от 0 до 1, они нормированные. Если x.scale=0 и y.scale=0, то кнопка отобразится в верхнем левом углу рамки. Параметры x.offset, y.offset — это координаты кнопки, но в пикселах. Диапазон значений резко различается в зависимости от выбранного устройства, на котором тестируется программа. Для указания положения кнопки можно использовать параметры scale или offset. В коде мы использовали второй вариант описания положения. Запустив игру, ты увидишь такой результат, как на рис. 3.231 и 3.232. РИС. 3.231. ДВЕ КНОПКИ НА УСТРОЙСТВЕ IPAD РИС. 3.232. ДВЕ КНОПКИ НА СМАРТФОНЕ IPHONE 7 212 Создадим кнопку, которая будет вызывать функцию, создающую шар голубоватого цвета: -- Создание функции, вызывающей шар function f2() local v = Instance.new("Part", workspace) v.Shape="Ball" v.BrickColor=BrickColor.new(0,255,100) end -- Создание переменной для привязки действия функции f2 к кнопке local ссс2 = ВВ:BindAction("sphere", f2,true) -- Добавление текста к кнопке ВВ:SetTitle("sphere", "Шар") -- Установка расположения кнопки ВВ:SetPosition("sphere", UDim2.new(0,100,0,0) --local ccc2 = BB:BindAction("Color3", f,true) Если запустить игру, то при нажатии кнопки с текстом Шар будет создаваться шар голубого оттенка точно в центре игрового мира. РИС. 3.233. СОЗДАНИЕ СФЕР КНОПКОЙ TOUCHSCREEN Код можно улучшить так, чтобы шары появлялись там, где находится игрок. Для этого нужно получить координаты игрока: -- Создание функции, вызывающей шар function f2(player) -- Получение данных подключенного игрока player = game.Players.LocalPlayer local character = player.Character local h = character:FindFirstChild("HumanoidRootPart") 213 end -- Создание сферы там, где находится игрок local v = Instance.new("Part",workspace) v.Shape="Ball" v.BrickColor=BrickColor.new(0,255,100) v.Position=Vector3.new(h.Position.x,h.Position. y, h.Position.z) print(v.Position) В функцию был добавлен параметр player, который получал данные подключенного к игре игрока, а именно все параметры элемента HumanoidRootPart, в том числе и его координаты. Их мы и присваиваем шару. Для наглядности выведем в окно Output координаты сферы и персонажа. Создай кнопку и пропиши код, с помощью которого можно открывать меню. Создай механику стрельбы с помощью кнопки и программы. Создай программу для кнопки, такую, чтобы при нажатии персонаж перемещался в разные точки пространства. ЗВУКИ Игры без звуков и музыки успехом не пользуются. В игре должны быть звуковые эффекты и фоновые мелодии. На сайте для разработчиков в Roblox есть несколько страниц, посвященных звукам: https://developer.roblox.com/en-us/api-reference/class/sound; https://developer.roblox.com/en-us/articles/sounds-and-music. Отмечу несколько моментов. 1. Звуки можно прикреплять как к определенным игровым объектам, так и глобально. 2. Звуки можно настраивать через окно свойств или с помощью программ. 3. В игре может проигрываться несколько звуков одновременно или по очереди в определенном порядке. Для начала рассмотрим самый простой пример добавления звука в игру. Создадим новый игровой проект с объектом Block. Для него в окне Explorer добавим элемент Sound (Звук). Вставим адрес звука для этого элемента в параметр Soundid в окне Properties. Звуки можно выбрать в окне Toolbox на вкладке Audio. У элемента Sound есть интересные параметры. Looped — зацикливание. Если значение true, то звук будет повторяться. MaxDistance — максимальная дистанция, с которой начинает проигрываться звук. Ее можно назвать также «радиус охвата звуком». Измеряется в studs. 214 Playing — запуск проигрывания звука. Если true, то звук проигрывается, если false — то нет. TimePosition — указываем временную точку звучания трека. Например, не с начала, а с 5-й секунды. Volume — громкость звучания. Если запустить игру, можно услышать звук. При удалении или приближении звук будет искажаться, так как автоматически вычисляется эффект Доплера. Не будем останавливаться на нем, но если тебе стало интересно, то ты без труда найдешь о нем в Сети много информации. Можно настроить звуковые эффекты для каждого предмета или определенной локации так, чтобы они запускались только при достижении какого-то места. РИС. 3.234. ДОБАВЛЕНИЕ ЗВУКА ИГРОВОМУ ОБЪЕКТУ Рассмотрим другой вариант работы со звуком — запуск с помощью программы. Например, нужно запустить звук с помощью кнопок GUI. Создадим ScreenGui и в нем — три кнопки TextButton, переименуем их в Play, Pause, Stop. Также создадим локальный скрипт в ScreenGui, у элемента Block удалим объект Sound. В скрипте пропишем код, который будет создавать объект Sound для Block. А затем в этом же скрипте создадим три функции, которые будут отвечать за запуск звука, установки паузы и полной остановки проигрывания звука. Затем для каждой кнопки пропишем вызов нужной функции. 215 РИС. 3.235. GUI-КНОПКИ ДЛЯ УПРАВЛЕНИЯ ЗВУКОМ Ниже приведен этот локальный скрипт. Обрати внимание: вся программа заключена в одном скрипте по работе с тремя кнопками: -- Создание объекта Sound с зацикливанием local s = Instance.new("Sound" workspace.Part) s.SoundId = "rbxassetid://301964312" s.Looped = true -- Создание функции для проигрывания звука function pl () s:Play() end -- Создание функции для паузы в проигрывании function ра () s:Pause () end -- Создание функции для полной остановки проигрывания function st () end s:Stop() -- Активация кнопок с вызовом функций script.Parent.Play1.Activated:Connect(pl) script.Parent.Pause.Activated:Connect(pa) script.Parent.Stop.Activated:Connect(st) Можно запустить игру и проверить, как работают кнопки. Если все работает так, как задумано, то ты все сделал правильно. Усложним задачу. Создадим плеер, который проигрывает три трека и пролистывает их. Для этого в нашем скрипте сделаем некоторые дополнения. Первым делом 216 создадим таблицу из списка трех треков по их id. Добавим условие со счетчиком для считывания номера трека и функции для перемотки. Для перемотки добавим две кнопки GUI. РИС. 3.236. ДОБАВЛЕНИЕ КНОПОК GUI ДЛЯ ПЕРЕМОТКИ ТРЕКОВ Рассмотрим по частям измененный код: -- Создание объекта Sound с зацикливанием local s = Instance.new ("Sound", workspace.Part) local d ={"rbxassetid://301964312", "rbxassetid://1838660362", "rbxassetid://1842104602"} s.Looped = true -- Добавляем переменную, хранящую целочисленные значения local n = 1 -- Создание функции для проигрывания звука function pl () if n==1 then s.SoundId=d[1] elseif n==2 then s.SoundId=d[2] elseif n==3 then s.SoundId=d[3] end s:Play() end 217 Мы создали таблицу d, которая содержит адреса звуков. Их можно найти и добавить, используя окно Toolbox и вкладку Audio. Для функции проигрывания добавим условие с ветвлением. В зависимости от значения n будет проигрываться тот или иной трек. Так как переменная d — это таблица, то всем ее элементам присваиваются индексы, начиная с единицы: -- Создание функции для паузы в проигрывании function ра () s:Pause() end -- Создание функции для полной остановки проигрывания function st () s:Stop() end -- Создание функции для счетчика на увеличение function per1 () n=n+1 if n >3 then n=1 end end -- Создание функции для счетчика на уменьшение function per2 () n=n–1 if n <0 then n=3 end end Здесь были добавлены две функции для перемотки треков. В одной увеличиваем индекс, в другой уменьшаем. Пропишем условие, в котором при достижении определенного значения n оно изменяется на исходное начальное: -- Активация кнопок с вызовом функций script.Parent.Play1.Activated:Connect(pl) script.Parent.Pause.Activated:Connect(pa) script.Parent.Stop.Activated:Connect(st) script.Parent.Rig.Activated:Connect(per1) script.Parent.Let.Activated:Connect(per2) В последней части кода добавим вызов функций перемотки для двух новых кнопок — Rig и Lef. Чтобы все проверить, сделай следующее. 1. Запусти игру. 2. Щелкни на кнопке Play. 3. Нажми кнопку >>. 4. Щелкни на кнопке Play. 5. Сделай все то же самое для кнопки <<. 218 Есть еще один вариант запуска звука, который часто используется в играх. А точнее, такой алгоритм активизации звука, анимации или других действий применяется всегда в любых играх. Он основывается на триггерах — некоторых ограниченных областях, при касании которых вызываются действия. Для примера создадим новый игровой проект и добавим в него наш синий Block, который поставим на тонкий Block большего размера. РИС. 3.237. СОЗДАНИЕ TRIG И RADIO Объект Block, который будет проигрывать звук, назовем Radio, а Block, который будет триггером, назовем Trig. Для Trig создадим скрипт: local s = Instance.new ("Sound", workspace.Radio) s.SoundId = "rbxassetid://1838601237" -- Создание переменной для счетчика local n = 0 -- Создание функции проверяющей наличие игрока рядом -- и значение счетчика function b(m) local player = game.Players:GetPlayerFromCharacter(m.Parent) s:Play() end -- При касании триггера запускается мелодия, если коснулся игрок script.Parent.Touched:Connect(b) Код собран на основе предыдущих тем. При запуске будет проигрываться мелодия, а при повторном касании мелодия начинает проигрываться заново. Чтобы избежать такого повтора, можно поставить счетчик и условие, при каком значении запускать мелодию. Пример такой программы: 219 local s = Instance.new ("Sound", workspace.Radio) s.SoundId = "rbxassetid://1838601237" -- Создание переменной для счетчика local n = 0 -- Создание функции, проверяющей наличие игрока рядом -- и значение счетчика function b(m) local player = game.Players:GetPlayerFromCharacter(m.Parent) if player then n=n+1 if n==1 then s:Play() end end end -- При касании триггера запускается мелодия, если коснулся игрок script.Parent.Touched:Connect(b) Теперь мелодия сработает только при одном касании. Если игрок выйдет за пределы триггера, то музыка все равно будет играть. Для определения границ выключения мелодии используют еще один триггер. Им может выступать базовая платформа Baseplate. Создадим еще одну функцию в том же скрипте, которая будет обнулять n и останавливать проигрывание мелодии: -- Функция, которая обнуляет счетчик -- и останавливает проигрывание function d(h) local player = game.Players:GetPlayerFromCharacter(h.Parent) if player then n=0 if n==0 then s:Stop() end end end -- При касании триггера запускается мелодия, если коснулся игрок script.Parent.Touched:Connect(b) script.Parent.Parent.Baseplate.Touched:Connect(d) По такому же примеру можно создавать определенные игровые области, где будут запускаться какие-то действия: диалоги, музыка, действия NPC, анимация или действия игровых объектов. Все будет зависеть от фантазии разработчика игры. Создай GUI-кнопки для настройки громкости звучания мелодии. 220 Создай программу, благодаря которой будет появляться портал в виде светящейся двери, как только игрок подойдет к определенному месту. Создай эквалайзер, определяющий частотные высоты мелодии (используй дополнительные модули). ДВИЖЕНИЕ NPС Интересно, когда в игре твои NPC ведут себя более разумно: ищут тебя или игровые объекты, патрулируют местность и т. д. Для примера создадим новый игровой проект и добавим NPC из окна Toolbox. Алгоритмы поведения будем брать из статьи по Lua для Roblox: https://developer.Roblox.com/en-us/articles/Moving-NPCs-Between-Points, в которой описаны простые операции движения. Начнем с простого. Создадим блок синего цвета, который будет играть роль NPC. Для этого нужно сделать из него не отдельный объект, а группу (модель). Выделим его и щелкнем на инструменте Group в верхнем окне Parts. Созданную модель переименуем в NPC и добавим объект Humanoid. Объект Humanoid позволяет управлять нашим персонажем, даже если он выглядит не как гуманоид, но по умолчанию у него есть основные части гуманоида: голова, туловище, руки и ноги. Чтобы объект Humanoid связался с синим блоком, который располагается в группе NPC, нужно переименовать его в Torso. Создадим элемент, к которому будет перемещаться наш NPC. Сделаем его из цилиндра и назовем Cil. Расположим чуть поодаль от NPC. РИС. 3.238. NPC И ЕГО ЦЕЛЬ 221 Создадим скрипт в Workspace, в котором пропишем две переменные и присвоим им свойства Cil и NPC. Затем подключимся к параметру Humanoid и напишем команду на перемещение его к точке с координатами Cil: local В = game.Workspace.Cil local npc1 = game.Workspace.NPC.Humanoid npc1:MoveTo(B.Position) Функция MoveTo позволяет переместить Humanoid вместе с прикрепленным к ним объектом NPC в точку в трехмерном пространстве Roblox. Запусти игру и увидишь движение NPC к цилиндру. Например, ты хочешь, чтобы твой NPC двигался по заданной траектории. Траектория может быть достаточно сложной, например быть кривой, состоящей из миниотрезков. Такие части траектории можно пометить деталями, к которым должен последовательно перемещаться NPC. Для примера создадим еще один цилиндр, Cil2, а старый переименуем в Cil1. Если мы пропишем последовательность выполнения движения сначала к Cil1, а затем к Cil2 по предыдущей схеме алгоритма, то результат будет совсем другим — NPC сразу начнет движение к Cil2. Чтобы такого не происходило, используют функцию MoveToFinished:Wait(). РИС. 3.239. ДВИЖЕНИЕ NPC ПОСЛЕДОВАТЕЛЬНО К КАЖДОЙ ТОЧКЕ Она позволяет провести действие с первым участком, а затем уже переходить ко второму. local В = game.Workspace.Cil1 local D = game.Workspace.Cil2 local npc1 = game.Workspace.NPC.Humanoid npc1:MoveTo(B.Position) npc1.MoveToFinished:Wait() npc1:MoveTo D.Position 222 Запусти игру: ты увидишь, как NPC сначала движется к Cil1, а затем к Cil2. Если нужно выстроить сложную траекторию, таких Cil можно настроить десятки и запрограммировать NPC обходить каждую. Это самый простой вариант работы как самого NPS и его строения, так и конструкций, к которым он движется. Но для хорошей игры нужны модели из сборных деталей, то есть сгруппированных. Для такого варианта нам нужно изменить код. Например, объекты Cil1 и Cil2 будут состоять не из одной детали, а из двух и более. Добавим для каждого цилиндра по красному кубу и сгруппируем цилиндр с кубом, образуя модель. Назовем их Streetlight1 и Streetlight2 (Уличный фонарь). Эти модели будут уличными фонарями, а наш NPC станет двигаться от первого ко второму. РИС. 3.240. МОДЕЛИ ИЗ НЕСКОЛЬКИХ ОБЪЕКТОВ Для тренировки можно связать модели с помощью различных видов соединений, например Weld. Чтобы NPC двигался от первого ко второму, нужно сделать две вещи. 1. Настроить модели, то есть указать, какой объект модели будет PrimaryPart (Преимущественная часть). В нашем случае это Cil1 и Cil2. 223 РИС. 3.241. ДОБАВЛЕНИЯ ОБЪЕКТА МОДЕЛИ В PRYMARYPART 2. Для задания координат функции MoveTo добавить координаты PrimaryPart: local В = game.Workspace.Streetlight1 local D = game.Workspace.Streetlight2 local npc1 = game.Workspace.NPC.Humanoid npc1:MoveTo(B.PrimaryPart.Position) npc1.MoveToFinished:Wait() npc1:MoveTo(D.PrimaryPart.Position) Также интересен вариант, когда NPC имеет более привычный вид. Его можно создать из примитивов (Parts) и связать детали с помощью инструментов Create. РИС. 3.242. СОЗДАНИЕ NPC-ГУМАНОИДА ИЗ ПРИМИТИВОВ 224 Для тренировки сконструируй NPC, как на рис. 3.242, и назови эту модель NNN. Добавим объект Humanoid, а одну часть тела назовем Torso. РИС. 3.243. СОЗДАНИЕ УПРАВЛЯЕМОГО NPC Теперь осталось дописать его в код. Изменим для него порядок движения — он будет двигаться от Streetlight2 к Streetlight1: local В = game.Workspace.Streetlight1 local D = game.Workspace.Streetlight2 local npc1 = game.Workspace.NPC.Humanoid local npc2 = game.Workspace.NNN.Humanoid -- Движение первого NPC npc1:MoveTo(B.PrimaryPart.Position) npc1.MoveToFinished:Wait() npc1:MoveTo(D.PrimaryPart.Position) -- Движение второго NPC npc2:MoveTo (D.PrimaryPart.Position) npc2.MoveToFinished:Wait() npc2:MoveTo(B.PrimaryPart.Position) Запусти игру и увидишь, как два NPC перемещаются между фонарными столбами. Если столбы не нужны, а важны точки перемещения, то можно поднять их или опустить под землю, чтобы они не мешали движению NPC. 225 РИС. 3.244. ДВИЖЕНИЕ NPC Для наглядности фонарные столбы можно сделать невидимыми. Чтобы столбы не падали, нужно их закрепить, то есть поставить якорь Anchor. Чтобы сделать модель невидимой, нужно каждому объекту модели выставить значение параметра Transparency, равное единице. Прозрачность объектов РИС. 3.245. НАСТРАИВАЕМ НЕВИДИМОСТЬ ОБЪЕКТОВ Теперь NPC будут перемещаться по земле относительно невидимых точек. Сейчас перемещение происходит один раз, но этот процесс можно зациклить: 226 local В = game.Workspace.Streetlight1 local D = game.Workspace.Streetlight2 local npc1 = game.Workspace.NPC.Humanoid local npc2 = game.Workspace.NNN.Humanoid while true do -- Движение первого NPC npc1:MoveTo(B.PrimaryPart.Position) npc1.MoveToFinished:Wait() npc1:MoveTo(D.PrimaryPart.Position) npc1.MoveToFinished:Wait() -- Движение второго NPC npc2:MoveTo(D.PrimaryPart.Position) npc2.MoveToFinished:Wait() npc2:MoveTo(B.PrimaryPart.Position) npc2.MoveToFinished:Wait() end Согласно скрипту два NPC будут поочередно перемещаться между контрольными точками. Можно добавить в этот процесс движения и координаты игрока. Например, как только игрок появляется в игре, записываются его координаты, которые и используются для движения NPC к нему. Добавляем в начало кода функцию, которая получает координаты игрока: local l -- Функция, которая получает координаты игрока, когда он входит в игру game.Players.PlayeгAdded:Connect(function(Player) Player.CharacterAdded:Connect(function(Character) print(Character.HumanoidRootPart.Position) l=Character.HumanoidRootPart.Position end) end) Осталось вставить в функцию MoveTo переменную l: while true do -- Движение первого NPC npc1:MoveTo(B.PrimaryPart.Position) npc1.MoveToFinished:Wait() npc1.MoveTo(D.PrimaryPart.Position) npc1.MoveToFinished:Wait() 227 -- Движение к точке, где расположился игрок npc1:MoveTo(l) npc1.MoveToFinished:Wait() -- Движение второго NPC npc2:MoveTo(D.PrimaryPart.Position) npc2.MoveToFinished:Wait() npc2.MoveTo(B.PrimaryPart.Position) npc2.MoveToFinished:Wait() -- Движение к точке, где расположился игрок nрс2:MoveTo(l) npc2.MoveToFinished:Wait() end С простым процессом движения разобрались! Теперь изучим вариант поиска пути к цели и движение к ней. Для решения более сложных задач подойдет модуль Pathfinding (Поиск пути). Подробнее о нем — в статье https://developer.Roblox.com/ en-us/articles/Pathfinding. Создадим новый игровой проект, на котором разместим объекты, выполняющие роль NPC, цели и препятствия. Переименуем их в NPC, Wall (Стена), Pole (Столб). РИС. 3.246. ОБХОД ПРЕПЯТСТВИЙ Создадим скрипт в Workspace. Пропишем код для подключения к модулю Pathfinding и созданию переменных для получения свойств NPC и Pole: 228 -- Подключаемся к модулю Pathfinding local PathfindingService = game:GetService("PathfindingService") -- Создаем переменные для NPC и Столба local npc1 = game.Workspace.NPC.Humanoid local k = game.Workspace.Pole Теперь давай создадим путь с помощью функции CreatePath(). Затем надо провести его вычисление, и за это отвечает функция ComputeAsync(), для которой необходимо ввести два параметра: координаты начала и координаты конца пути. Координатами начала выступает положение NPC в пространстве, а координатами конца является расположение столба. Вычисленный путь разбивается на точки, которые формируют траекторию движения NPC. За это отвечает GetWaypoints(): -- Создаем переменную для вычисления пути local path = PathfindingService:CreatePath() -- Вызываем функцию для вычисления траектории пути path:ComputeAsync(npc1.Torso.Position, k.Position) -- Создадим таблицу точек, описывающих траекторию пути local waypoints = path:GetWaypoints() -- Выведем количество данных точек print(#waypoints) -- Loop through waypoints Количество точек можно вывести с помощью функции print (#waypoints). Их можно визуализировать, чтобы увидеть траекторию пути NPC. Для этого создается цикл, который пробегает все точки созданной таблицы waypoints. В цикле с каждым шагом создается светящаяся сфера размером (0.6, 0.6, 0.6), которая будет закреплена и лишена функции препятствия: -- Loop through waypoints for _, waypoint in pairs(waypoints) do local part = Instance.new("Part") part.Shape = "Ball" part.Material = "Neon" part.Size = Vector3.new(0.6, 0.6, 0.6) part.Position = waypoint.Position part.Anchored = true part.CanCollide = false part.Parent = game.Workspace end Если запустить игру, то можно увидеть результат построения траектории. 229 РИС. 3.247. ТРАЕКТОРИЯ ДВИЖЕНИЯ NPC Чтобы наш NPC начал движение, применим функцию MoveTo. С помощью нее NPC будет проходить по каждой точке созданной траектории. Обязательно нужно прописать задержку по времени прохождения каждой точки, как в предыдущем примере. Перемещение и задержку прописываем внутри созданного ранее цикла. Полная программа представлена ниже: -- Подключаемся к модулю Pathfinding local PathfindingService = game:GetService("PathfindingService") -- Создаем переменные для NPC и Столба local npc1 = game.Workspace.NPC.Humanoid local k = game.Workspace.Pole -- Создаем переменную для вычисления пути local path = PathfindingService:CreatePath() -- Вызываем функцию по вычислению траектории пути path:СоmputeAsync(npc1.Torso.Position, k.Position) -- Создадим таблицу точек, описывающих траекторию пути local waypoints = path:GetWaypoints() -- Выведем количество данных точек print(#waypoints) -- Loop through waypoints for _, waypoint in pairs(waypoints) do local part = Instance.new("Part") part.Shape = "Ball" part.Material = "Neon" part.Size = Vector3.new(0.6, 0.6, 0.6) part.Position = waypoint.Position part.Anchored = true part.CanCollide = false part.Parent = game.Workspace npc1:MoveTo(waypoint.Position) npc1.MoveToFinished:Wait() end Запустив игру, ты увидишь процесс движения NPC к цели. 230 Создай пять NPC, которые перемещаются между пятью точками в разном порядке. Создай игру, где NPC движется за движущимся объектом. Создай игру, где нужно просчитывать новый путь, если вдруг на пути появилось препятствие. СТРЕЛЬБА Мало какая игра обходится без стрельбы. В заключительном разделе рассмотрим один из вариантов реализации стрельбы. Создадим новый игровой проект, в котором сделаем игровой объект, имитирующий оружие. Его можно создать из цилиндра и куба, затем наложить красный цвет и применить инструмент Union. Это оружие будет располагаться в инструментах игрока. Если он его активизирует, то при щелчке ЛКМ должно срабатывать событие — стрельба блоком. Чтобы стрельба выглядело реалистично, можно добавить элемент, который будет началом появления пули. Ее можно сделать из сферы и расположить на определенном расстоянии от ствола оружия. Обязательно нужно связать сферу с оружием с помощью Weld из списка Constraints. А чтобы оружие держалось в руках, добавим к нему куб Handle, который тоже нужно прикрепить к оружию инструментом Weld (см. раздел «Создание инвентаря»). РИС. 3.248. СОЗДАНИЕ ОРУЖИЯ Переименуем оружие в Gun, рукоятку в Handle, а элемент для создания пуль — в B. Создадим в папке StarterPack инструмент Tool и переместим в него три созданных нами объекта. Создадим для Tool скрипт. 231 РИС. 3.249. ДОБАВЛЕНИЕ ОРУЖИЯ В ИНСТРУМЕНТЫ Запусти игру и протестируй работу инструмента. Если игровой персонаж держит его так, как показано на рис. 3.250, значит, ты все сделал верно. Не забудь изменить размер. Если оружие в руке держится неправильно, значит, нужно изменить положение Handle (см. раздел «Создание инвентаря»). РИС. 3.250. УДЕРЖАНИЕ ОРУЖИЯ В РУКЕ Приступим к написанию кода в скрипте. Для начала нужно получить сведения об элементах Cun и B. Здесь важны координаты их положения. Далее нужно создать функцию, где мы сделаем деталь, которая будет пулей. Чтобы пуля создавалась в нужной точке, а именно перед оружием, нужны координаты объекта B. Чтобы пуля двигалась с определенной скоростью и в нужном направлении, туда, куда направлен ствол оружия, вычислим положение вектора. Для этого понадобятся координаты начала и конца. Этими координатами обладают Gun и B. Разность конечных координат с начальными и будет образовывать координаты нашего вектора, который мы зададим параметру Velocity. Последним штрихом будет вызов функции при активизации инструмента ЛКМ. Как только инструмент станет активен, по щелчку ЛКМ оружие будет стрелять пулями: 232 -- Получение данных по объектам Gun и В local v1= script.Parent.Gun local v2= script.Parent.В -- Функция, создающая пулю function fire () -- Создание пули local cub = Instance.new("Part", workspace) -- Определение координат вектора направления local х = v2.Position.X-v1.Position.X local у = v2.Position.Y-v1.Position.Y local z = v2.Position.Z-v1.Position.Z -- Задание начального положения пули cub.Position=Vector3.new(v2.Position.X,v2.Position.Y, v2.Position.Z) print(cub.Position) -- Указываем скорость и направление движения пули cub.Velocity= 10*Vector3.new(x,у,z) print ("Создание пули") end -- Вызываем функцию fire() при активизации инструмента ЛКМ script.Parent.Activated:Connect(fire) В результате у тебя должно получиться так, как показано на рис. 3.251. РИС. 3.251. ТЕСТИРОВАНИЕ СТРЕЛЬБЫ Изменим размер и цвет пули, чтобы выстрелы выглядели реалистичней. 233 function fire() -- Создание пули local cub = Instance.new("Part", workspace) -- Размер пули cub.Size=Vector3.new(0.5,0.5,0.5) -- Материал пули Неон cub.Material=Enum.Material.Neon -- Цвет пули красный cub.BrickColor=BrickColor.Red() -- Определение координат вектора направления local х = v2.Position.X-v1.Position.X local у = v2.Position.Y-v1.Position.Y local z = v2.Position.Z-v1.Position.Z -- Задание начального положения пули cub.Position=Vector3.new(v2.Position.X,v2.Position.Y, v2.Position.Z) print(cub.Position) -- Указываем скорость и направление движения пули cub.Velocity= 10*Vector3.new(x,у,z) wait(2) Пуля должна создавать разрушение, поэтому добавим свойство Explosion (Взрыв) (см. раздел «Взрывы и разрушения»). Для примера вызовем взрыв через секунду после создания пули. Через 1 секунду пуля уничтожается и происходит взрыв: wait(1) print(cub.Position) -- Уничтожение пули cub:Destroy() -- Создание взрыва local bomb =Instance.new("Explosion",workspace) bomb.Position= cub.Position bomb.BlastPressure=5 bomb.BlastRadius = 2 print ("Создание пули") end -- Вызываем функцию fire() при активации инструмента ЛКМ script.Parent.Activated:Connect(fire) Запусти игру: результат должен быть таким, как на рис. 3.252. Можно приблизить или отдалить взрыв, изменив параметр Velocity для cub. 234 РИС. 3.252. СТРЕЛЬБА СО ВЗРЫВАМИ Добавь в игру объекты для создания тира и перепиши код так, чтобы взрывом можно было сбивать эти объекты. Создай движущиеся игровые объекты, которые нужно выбить по принципу игры в морской бой. Добавь NPC из Toolbox или создай своего. В игре они должны возрождаться через определенное время и нападать на твоего персонажа. Твой персонаж при этом должен обстреливать противника. ЗАКЛЮЧЕНИЕ Вот мы и подошли к концу! Ты только что прошел базовый курс создания игр в Roblox, и тебе уже по плечу многие игровые механики. Смело создавай свои первые игры и приглашай друзей сыграть в них. Чтобы отточить навыки в языке Lua и создании игр в Roblox, нужно как можно чаще практиковаться. Рекомендую изучать документацию по Roblox API на Lua https:// developer.Roblox.com/en-us/learn-Roblox/coding-scripts. Все представленные проекты можно будет скачать с GitHub по ссылке https://github.com/Antipat/Roblox_LUA. В разработке игр нет предела совершенству. Ты можешь не только выбирать игровой жанр из множества вариантов, но и обращаться к смешанным жанрам. Чем интереснее игра по сюжету, механике, дизайну или новаторским нововведениям, тем она популярнее и тем выше твои опыт и профессионализм как геймдизайнера. Изучение языка Lua и алгоритмов для решения разных задач в программировании откроет тебе дорогу в профессию программиста. Не останавливайся на достигнутом и переходи к изучению других языков. Чем больше ты знаешь языков и алгоритмов, тем выше твой уровень. А программисты нужны и важны почти в любой области. Удачи в достижении твоей цели! ПРЕДМЕТНЫЙ УКАЗАТЕЛЬ 0–9 М Ц 3d Max 129 3d-редактор 129 Цикл 47 A Массив 42 Математические операторы 40 Материал 64 Моделирование в Roblox 111 Аккаунт 9 Анимация 179 Н В Вектор 53 Ветвление 45 Г Глобальная переменная 35 Д Декартова система 53 Диалог 188 З Завершение игры 27 Запуск игры 27 Звуки 214 И Индексы 42 К Ключи анимации 181 Комментарии 68 Л Линейный алгоритм 45 ЛКМ 10 Логический тип данных 38 Локальная переменная 35 Направление вектора 232 О Окно отладки 24 Окно свойств 24 Операторы сравнения 40 A Anchor 71 Anchored 71 Animation 185 Animation Editor 179 Animationid 186 Avatar 10 B Сенсорные кнопки 210 Словарь 42 Список 42 Стрельба 231 Структура вашего проекта 24 BackgroundColor3 175 Ball 137 Baseplate 27 BehaviorType 189 BindAction 209 BlastPressure 152 BlastRadius 152 Blender 129 Block 56 Blog 16 BodyAngularVelocity 85 BodyForce 85 BodyGyro 81 BodyPosition 79 BodyVelocity 84 BottomSurface 136 Break 56 BrickColor 56 Brightness 126 Build Rig 179 Т C П Параметр P 81 Переменные 34 Р Рабочее поле 24 Рабочее пространство Roblox 24 Радиус вектор 53 С Таблица истинности 38 Таблицы 42 Типы данных 34 Триггеры 219 Ф Формат 3d-модели 131 Функция 49 237 ClickDetector 205 ClockTime 96 Collisions 69 Color 57 ComputeAsync 229 Connect 92 Constraints 105 Conversation Distance 189 Coroutine 51 CreatePath 229 Current: Client 204 Current: Server 203 Cylinder 137 H P Handle 161 Help 191 Humanoid 93 HumanoidRootPart 214 D I Parent 110 Part 56 Pathfinding 228 Pcall 201 Permissions 198 PlayerAdded 200 PlayerRemoving 203 Playing 215 Plugins 179 PointLight 125 Position 74 PrimaryPart 223 Print 36 Profile 13 Publish Roblox 197 Purpose 191 Data 64 Data Stores 199 Debug 51 Destroy 95 Device 208 DialogChoice 191 DialogChoiceSelected 194 E Emulation 208 Enable Studio Access to API Services 197 Enemy 191 Enum 66 Explorer 35 Explosion 152 Export 183 Ignore Water 30 ImageButton 177 InitialPrompt 190 Instance 134 InUse 190 Io 51 Ipairs 49 K KeyCode 171 Q L Quest 191 Lighting 96 LocalScript 186 Looped 214 Lua 34 R F M False 38 Fbx 131 Fire 82 Font 169 Frame 178 Friendly 191 Friends 14 FrontSurface 161 Massages 13 Math 51 MaxForce 80 Maya 129 Meshid 132 MeshPart 131 Mesh Rig 179 Move 72 MoveTo 108 MoveToFinished:Wait 222 My Feed 16 G Games 18 Game Settings 197 GetAsync 201 GetMinutesAfterMidnight 97 GetPlayerFromCharter 110 GetWaypoints 229 Gift Cards 17 GoodbyeDialog 190 Group 221 Groups 15 GUI 168 N Negate 120 Neutral 191 Nil 42 NPC 179 Number 39 O Obj 131 Orientation 74 Os 51 238 R15 179 Range 126 ReplicatedStorage 185 ResposeDialog 192 RGB 61 Roblox 8 Roblox Player 18 Roblox Studio 18 Robux 15 Rotate 72 RotVelocity 84, 85 RPG 188 S Scale 63 ScreenGUI 168 Script 35, 58 ScrollingFrame 177 Seat 115 ServerScriptService 76 SetAsync 202 SetMinutesAfterMidnight 97 Shape 137 Shop 191 Sign up 9 Snap to Grid 30, 72 Solid Modeling 101 Spawn 77 StarterPack 159 StarterPlayerScripts 186 String 51 SubtractAsync 141 Surface 135 T Table 51 Terrain Edit 25 Text 168 TextBox 170 TextButton 175 TextLabel 168 TextScaled 169 TextSize 169 Textureld 167 Tone 191 Tonumber 39 Tool 159 Toolbox 123 TopSurface 136 Torso 221 tostring 39 Touched 92 Touchscreen 208 Trade 15 Transparency 113 TriggerDistance 191 TriggerOffset 191 True 38 Type 39 U Udim2 212 Union 101 URL 174 UserDialog 192 UserId 199 V Velocity 84 W Wait 48 Weld 105 Workspace 35 Андрей Корягин Roblox: играй, программируй и создавай свои миры Заведующая редакцией Руководитель проекта Ведущий редактор Литературный редактор Художники Корректоры Верстка Ю. Сергиенко Н. Римицан К. Тульцева А. Потапова В. Мостипан М. Одинокова, Е. Павлович Е. Неволайнен Изготовлено в России. Изготовитель: ООО «Прогресс книга». Место нахождения и фактический адрес: 194044, Россия, г. Санкт-Петербург, Б. Сампсониевский пр., д. 29А, пом. 52. Тел.: +78127037373. Дата изготовления: 02.2021. Наименование: книжная продукция. Срок годности: не ограничен. Налоговая льгота — общероссийский классификатор продукции ОК 034-2014, 58.11.12 — Книги печатные профессиональные, технические и научные. Импортер в Беларусь: ООО «ПИТЕР М», 220020, РБ, г. Минск, ул. Тимирязева, д. 121/3, к. 214, тел./факс: 208 80 01. Подписано в печать 02.02.21. Формат 84х108/16. Бумага офсетная. Усл. п. л. 25,200. Тираж 1700. Заказ ПРОГРАММИРОВАНИЕ ДЛЯ ДЕТЕЙ. УЧИМСЯ СОЗДАВАТЬ САЙТЫ, ПРИЛОЖЕНИЯ И ИГРЫ. HTML, CSS И JAVASCRIPT Дэвид Уитни Думаешь, программировать — это сложно? Вовсе нет! Профессор Бейрстоун и доктор Дэй (и, конечно, Эрнест) играючи научат этому всего за шесть увлекательных приключений! Создавать собственные сайты, игры или приложения с помощью HTML, CSS и JavaScript — это весело и увлекательно. Учитесь вместе с юными программистами из Young Rewired State, создающими технологии будущего (Guardian). КУПИТЬ УЧИМСЯ КОДИТЬ НА JAVASCRIPT Мориц Джереми Ты любишь играть онлайн? Общаться с друзьями через ВКонтакте, Фейсбук и Инстаграм? Смотреть видеоролики на смартфоне? Все, чем ты пользуешься, было придумано обычными людьми, которые когдато решили, что хотят заняться программированием. Умение писать код — это современная суперспособность, отличающая магов от маглов. И логичнее всего начать с изучения языка JavaScript, на котором написано более 90 % всех веб-сайтов. «Учимся кодить на JavaScript» поможет тебе самостоятельно, без помощи родителей и учителей, написать программный код; ведь если говорить начистоту, большинство взрослых слабо представляют себе, как это делается. В книге много задачек и упражнений (с ответами, только чур сразу не подглядывать!). Ответов может быть несколько, главное, чтобы у тебя получился рабочий вариант. Стань круче Илона Маска! Пришло время закатать рукава и приступить к прокачке новой суперспособности! КУПИТЬ ПРОГРАММИРОВАНИЕ ДЛЯ ДЕТЕЙ. ОТ ОСНОВ К СОЗДАНИЮ РОБОТОВ И. Воронин, В. Воронина Нашу жизнь невозможно представить без разно­ образных цифровых устройств. Ноутбуки, планшеты, мобильники и другие гаджеты доступны детям буквально с рождения. «Разум» внедряется в привычные вещи — пылесосы, стиральные машины, холодильники, автомобили. Как не потеряться в этом мире? Как управлять умными вещами? Вероника и Игорь Воронины просто и увлекательно рассказывают о программировании, роботах, передаче зашифрованных посланий и многом другом. Хочешь научиться основам программирования и создать собственного робота, который будет выполнять твои команды? Теперь всё в твоих руках. Изобретателем может стать каждый! Книга выпущена при поддержке фонда «Сколково» и Международной гимназии инновационного центра «Сколково». КУПИТЬ ПРОГРАММИРОВАНИЕ ДЛЯ ДЕТЕЙ. ПЯТЬ САМЫХ КРУТЫХ ИГР НА HTML И JAVASCRIPT Дэвид Уитни Думаешь, программировать — это сложно? Вовсе нет! Научись программировать, создавая игры на HTML и JavaScript. «Приключения Марио» и «Майнкрафт» познакомят с циклами, таймерами, подсчетом очков, работой с графикой и даже искусственным интеллектом. Краткие и простые объяснения позволят легко создать свою игру. В начале каждой главы тебя ждет описание задачи от клуба «Счастливый кот». Тебе все расскажут об игре, которую хотят создать члены клуба. Код каждой игры разбит на небольшие фрагменты. Следуя пошаговым инструкциям, ты сам напишешь игру от начала и до конца. К концу каждой миссии у тебя будет созданная своими руками игра и приобретенные навыки. Поиграй, отдохни, а потом зайди на сайт издательства «Питер» за новыми книгами по программированию. КУПИТЬ