Сегодня поговорим о том, как писать скрипты для Unity Editor. Статья рассчитана на тех, кто уже знаком с Unity3D, что-то успел сделать, но еще не решился попробовать писать скрипты для эдитора.
Если коротко — то в режиме эдитора скриптами можно сделать абсолютно всё тоже самое, что и в режиме игры.
Начнем с простого. Допустим, мы хотим в режиме эдитора создать 10 кубиков и расположить их в линию. Для начала давайте упростим задачу и забудем про эдитор, давайте сделаем так, чтобы эти кубики создавались при старте приложения.
Теперь попробуем выполнить этот код в режиме эдитора, для этого нужно к коду добавить всего лишь один волшебный атрибут [ContextMenu()] к функции Create10Cubes():
так чтобы код выглядел вот так:
Теперь, если мы нажмем правой кнопкой по заголовку скрипта, там появится пункт CreateCubes, при нажатии на который функция точно также будет выполнена.
Важное замечание: функция, помеченная атрибутом ContextMenu, не должна иметь параметров, вернее, если у нее будут параметры, вы не сможете вызвать таким способом.
Лично я применяю такой способ, когда нужно что-то сделать с группой объектов, например, выключить отбрасывание теней у всех детей объекта, у которых в названии встречается «withoutshadow»:
Вобщем способ хорош для одноразовых действий над кучей объектов — быстренько накидали нужный код в отдельном классе, кинули на нужный объект и тут же удалили этот класс к едрене фене.
Теперь давайте решим следующую задачу: мы хотим запечь occlusion culling. Для этого нам необходимо пометить галочкой Occluder Static все объекты, которые бубдут загораживать другие объекты, и галочкой Occludee Static все, которые будут скрываться за Occluder`ами. То есть нам нужно вычленить все статичные объекты, непрозрачным объкетам поставить обе галки (на самом деле все), прозрачным — только Occludee, а Occluder выключить.
Казалось бы, ну что такого, пробежался по сцене ручками — расставил кому нужно галочки — и все. Но проблема в том, что объектов в сцене может быть много и в процессе развития проекта сцена скорее всего будет меняться. Следить самому за всеми этими галочками — с ума можно сойти. Поэтому мы напишем маленький скриптик, который делает это за нас.
Для начала напишем полезный код, который выполняет нашу работу, а далее оформим это в отдельный виззард:
1) Найти интересующие нас объекты в сцене; 2) Расставить нужные галочки.
Оформим код в виде отдельной команды, для того чтобы его можно было вызывать из любого места и он не зависел от того, в каком виззарде он будет вызываться. Внимание: файл, содержащий следующий код, необходимо поместить в папку под названием Editor, это нужно для того, чтобы этот код не попал в основной билд:
Здесь мы предполагаем, что статичные объекты уже до этого каким то образом нашли (скорее всего ручками) и отметили их галочкой Static, а значит, в том числе и BatchingStatic.
Теперь давайте оформим отдельный виззард, чтобы можно было удобно вызывать эту команду:
Тут нам пригодится класс EditorWindow.
На этом пока закончим наш обзор, он получился далеко не полным.
В следующих статьях я планирую описать, как можно создавать кастомные инспекторы для ваших классов, как вмешиваться в процесс импорта ассетов, как можно поставить запекать тени на 20-ти уровнях по очереди и получить скриншоты с результатом себе на почту.
Современные браузеры позволяют создавать игры с полноценной графикой. Рассказываем, как написать простые гонки на JavaScript и HTML5.
Сейчас браузеры дают JavaScript-разработчикам огромное количество возможностей для создания интересных сайтов. Раньше для этого использовался Flash — он был популярен, и на нём было создано бессчётное количество игр, плееров, необычных интерфейсов и так далее. Однако они уже не запустятся ни в одном современном браузере.
Дело в том, что технология Flash тяжеловесна, а также полна уязвимостей, поэтому от неё стали отказываться. Тем более что появилась альтернатива в виде HTML5 — в этой версии появился элемент canvas.
Canvas — это холст, на котором можно рисовать с помощью JS-команд. Его можно использовать для создания анимированных фонов, различных конструкторов и, самое главное, игр.
Из этой статьи вы узнаете, как создать браузерную игру на JavaScript и HTML5. Но прежде рекомендуем ознакомиться с объектно-ориентированным программированием в JS (достаточно понимать, что такое класс, метод и объект). Оно лучше всего подходит для создания игр, потому что позволяет работать с сущностями, а не с абстрактными данными. Однако есть и недостаток: ООП не поддерживается ни в одной из версий Internet Explorer.
Пишет о программировании, в свободное время создает игры. Мечтает открыть свою студию и выпускать ламповые RPG.
Вёрстка страницы с игрой
Для начала нужно создать страницу, на которой будет отображаться холст. Для этого потребуется совсем немного HTML:
Теперь нужно добавить стили:
Обратите внимание, что в HTML элементу canvas были заданы нулевые ширина и высота, в то время как в CSS указано 100%. В этом плане холст ведёт себя как изображение. У него есть фактическое и видимое разрешение.
С помощью стилей меняется видимое разрешение. Однако при этом размеры картинки останутся прежними: она просто растянется или сожмётся. Поэтому фактические ширина и высота будут указаны позже — через скрипт.
Скрипт для игры
Для начала добавим заготовку скрипта для игры:
В этом скрипте есть всё, что необходимо для создания игры: данные (массивы), функции обновления, прорисовки и управления. Остаётся только дополнить это основной логикой. То есть указать, как именно объекты будут себя вести и как будут выводиться на холст.
Логика игры
Во время вызова функции Update () будут меняться состояния игровых объектов. После этого они отрисовываются на canvas с помощью функции Draw (). То есть на самом деле мы не двигаем объекты на холсте — мы рисуем их один раз, потом меняем координаты, стираем старое изображение и выводим объекты с новыми координатами. Всё это происходит так быстро, что создаётся иллюзия движения.
Рассмотрим это на примере дороги.
На холсте выводится вот такое изображение и постепенно двигается вниз. Сразу же следом будет выводиться ещё одна такая же картинка, благодаря чему создастся ощущение бесконечной дороги.
Для этого создадим класс Road:
В массив с фонами добавляются два объекта класса Road:
Теперь можно изменить функцию Update (), чтобы положение изображений менялось с каждым кадром.
Остаётся только добавить вывод этих изображений:
Теперь можно посмотреть, как это работает в игре:
Пора добавить игрока и NPC. Для этого нужно написать класс Car. В нём будет метод Move (), с помощью которого игрок управляет своим автомобилем. Движение NPC будет осуществляться с помощью Update (), в котором просто меняется координата Y.
Пишем бота для онлайн-игры на JavaScript с применением AOP
1. Готовим ингредиенты
Важно! Игра должна работать в браузере, а не в клиенте. Причем не на Flash, а на HTML+JavaScript. На выходе у нас должно получиться расширение для Chrome, которое будет играть вместо нас.
2. Делаем расширение
О том как делается расширение я не буду подробно расписывать. На хабре об этом уже писали, например, тут. Приведу лишь коды, нужных нам файлов. В manifest.json
В строчке «matches»: [ «pernatsk.ru*» ] вам нужно будет указать адрес вашей игры. Файл background.js я использую для случаев, когда хочу инджектить на сайте свой JS кода. Собственно код background.js:
Важно! Если вы не понимаете, что мы делаем в этой единственной функции, то делать бота вам пока рано. Почитайте основы JavaScript. Вся работа у нас будет вестись в файле injected.js Его код пока такой:
Все эти файлы сохраняем в одной папке bot.
3. Первый пуск бота
4. Добавляем AOP
Для работы бота нам потребуются библиотеки. Мой любимый jQuery уже используется на Пернатске, поэтому добавлять его не будет. Добавим плагин AOP for Jquery. По хорошему это стоило запаковать в само расширение в виде отдельного файла, но я ленив. Поэтому просто добавим код bin/aop.pack.js первой строкой в наш injected.js. Проверим, что это работает изменив ai_on
Проверяем, что AOP нормально подключилось. В консоле разработчика теперь будет строчка «jQuery detected!» Сообщение будет только один раз, так как я отключаю совет после первого же срабатывания. Важно! Прочитайте документацию AOP for Jquery, чтобы понять jQuery.aop.after и bot[0].unweave().
5. Зачем мы будем использовать AOP
6. Учим бота первой команде
В injected.js добавим такой код:
По этой команде наша бот-птичка будет лететь в Пернатске за шишками. Код слегка мудренный, так как в Пернатске есть небольшая защита от ботов. Когда вы будете писать свои команды я рекомендую сначала опробовать их работоспособность в console, а уже потом переносить код в редактор. Чтобы протестировать и проверить работу нашей команды запустим в косноле код commands.conessearch() Все работает.
7. Ищем событие на которое должен реагировать бот
Тут есть два метода первый — анализируем код игры. Долго 🙁 Второй метод — воспользоваться AOP, и после всех функций, который срабатывали вывести в лог их имя. Потом выбрать нужные. Меняем ai_on()
Теперь у нас отражаются только те функции, которые еще не отображались. Их полный список мы храним в fnList. После пары минут там будут такие варианты функции для прицепки [«clearInterval», «$», «setTimeout», «timerTick», «serverTimeUpdate», «getComputedStyle», «setInterval», «tutorialArr», «showQ», «showQc», «updateBirdData», «viz», «unviz», «weatherUpdate»] Меняя target и регулярное выражение в method мы можем подобрать ту функцию, которая нам подойдет, чтобы к ней прицепиться. Для примера, я выбираю функцию weatherUpdate теперь каждый раз как будет меняться погода наша птичка будет лететь за шишками.
7. Учим бота реагировать на события
Мы снова меняем код функции ai_on()
Функцияю ai_off нужна, чтобы через консоль выключить бота.
Для многих начинающих игростроевцев, которые уже собирают свою команду, чтобы слепить на коленке очередной шедевр, программирование часто видится жутким монстром, с которым непонятно как бороться. Вроде и в 3D уже рисовать умеют, и в Photoshop кисточкой сноровисто работают, и другие полезные программы неплохо знают. Но как только дело доходит до кода, начинается паника и неразбериха: “ Кто спрограммирует? Кто напишет заветные строчки? А если и напишет, то как в них потом разобраться?! ”.
Данный материал открывает серию статей, в которых мы на простых примерах научим вас читать с листа и писать несложный программный код. Суперкрутыми программистами вы, разумеется, не станете (для этого надо учиться значительно дольше), но иероглифы кода перестанут быть для вас чем-то непонятным, от чего нужно держаться подальше.
В ООП существуют три основных принципа, на которых строятся практически все логические манипуляции с объектами.
Инкапсуляция — объединение внутри объекта данных с методами для их обработки. Например, если рассматривается класс “Лампочка”, то у него должны быть методы “включить свет” и “выключить свет”.
Наследование — создание класса объектов на основе уже существующего класса. При этом создаваемый класс будет не только содержать все данные и методы базового класса, но и обладать своими собственными. Методы базового класса могут быть переопределены. Примеры наследования и переопределения: если класс “Пегас” наследуется от класса “Лошадь”, то у первого появляется новое свойство — “крылья” и соответствующий метод “махать крыльями”. Все остальное у этих двух классов одинаковое. Если мы рассмотрим класс “Русалка”, основанный на классе “Человек”, то в данном случае будет иметь место переопределение свойства “ноги” на “рыбий хвост”, а метод “двигаться” будет вместо движения ног отвечать за перемещение хвоста.
Полиморфизм — что это такое, проще всего рассмотреть на конкретном примере. Рассмотрим два класса объектов — “Пегас” и “Лошадь”. Полиморфизм заключается в том, что мы можем рассматривать любого пегаса как лошадь и, например, у обоих объектов выполнять метод “кушать траву” или “бежать галопом”.
Многие начинающие программисты не различают понятия “класс” и “объект”. Но при этом эти два понятия различаются примерно так же, как и, скажем, чертеж устройства и готовое устройство в магазине. В жизни их спутать трудно.
Создание проекта
К сожалению, в UDE кнопка “Создать новый проект” отсутствует. Поэтому создаем в корневой папке UT2004 файл CreateNewClass.bat с командами:
echo class %pkg% extends //base class>> %pkg%\Classes\%pkg%.uc
Значение переменной pkg — это имя нашего проекта (в данном случае — Megakiller ). Остальные строки генерируют “пустой” скрипт.
Идея модификации
Перед изучением нижеследующей информации настоятельно рекомендуется изучить текстовый блок “ Анатомия Unreal-класса ”.
Внимание! Читая нижеследующие абзацы, смотрите соответствующие строки в листинге кода, приведенном на отдельной странице (номера строк указаны слева от кода).
Класс оружия
Рис. 1. Диалог создания дерева классов и архивов.
Класс альтернативной стрельбы
Рис. 2. Окна с деревом классов и списком архивов.
В начале файла объявляем несколько новых свойств для объектов класса ( строки 2-5 ): визуальный тип взрыва ( ExplosionClass ), визуальный тип следа от взрыва, например на стенах ( ExplosionDecalClass ). Тип луча при альтернативной стрельбе ( BeamEffectClass ) и радиус поражения при взрыве ( DamageRadius ).
Рис. 3. Диалог создания подкласса.
Классы повреждения
Рис. 4. Окно компиляции.
Класс игрового мутатора
— просто лежать на карте ( строка 15 ). Тут нам помогает класс WeaponPickup ( строка 15 ). Подмена осуществляется функцией ReplaceWith ( строка 19 ).
Рис. 5. Сообщение об ошибке и справка.
Дуэли на «Мегакиллерах» иногда превращают обычный бой в феерическое зрелище.
Чернокожий стрелок пытается безуспешно подстрелить «античную» мадам.
Местные леди способны просто-таки заряжать вас энергией!
Каждый Чебурашка мечтает о большой пушке!
Компиляция и тестирование
Для того чтобы вы сразу могли оценить действие мутатора и поиграть в него, мы выкладываем полную версию “Мегакиллера” на наш диск.
Анатомия Unreal-класса
Класс игры имеет унифицированную архитектуру. Структура файла следующая.
— Заголовок, содержащий объявление класса:
class ClassName extends BaseClass config(user); где ClassName — название нашего класса, BaseClass — базовый класс, на основе которого создается новый класс, config(user) — пример параметра в объявлении класса — указывает, что используются конфигурационные параметры из файла User.ini. Все игровые классы имеют в качестве родительского класс Object или любой из его классов-потомков. Лексемы class и extends являются зарезервированными обязательными атрибутами объявления игрового класса. Важно запомнить, что корректным является только тот uc-файл, имя которого совпадает с названием объявленного в нем класса. И в одном uc-файле должен быть описан только один класс.
— Объявления новых переменных (свойств класса) в следующем формате:
PropertyName; или var() SimpleType PropertyName;где PropertyClass — название класса создаваемого свойства, SimpleType — элементарный тип, такой как byte (байтовое значение от 0 до 255), int (знаковый целый тип), bool (логический тип), float (знаковый вещественный тип), string (символьная строка) и т.д. (полный список элементарных типов можно посмотреть в справке UDE), PropertyName — имя нового свойства.
— Объявления новых методов или переопределение унаследованных от базового класса имеют следующий вид:
— Задание параметров (свойств) по умолчанию класса:
defaultproperties где defaultproperties — лексема, указывающая начало блока свойств, Variable1Name — название свойства, представляющего собой некоторый другой объект класса ClassName, содержащегося в u-файле с именем Package. Параметр Variable2Name имеет элементарный тип, поэтому и присваиваемое значение Variable2Value должно иметь тот же тип. Символ «=» означает операцию присвоения значения справа переменной слева. Фигурные скобки обособляют в коде блок присвоений.
Вот и подошел к концу наш первый экскурс в мир игрового программирования. Мы не только сделали интересный мутатор, но и научились читать несложный программный код. В одном из ближайших номеров “Мании” мы продолжим разбираться в азах программирования и, изучив более продвинутые строки кода, создадим более сложный мутатор.
Добрый день, Хабрахабр! Решил написать этот топик на тему скриптов
Что нужно знать?
Но есть способ, на голову выше — использование скриптов.
Решение проблемы
«Окей, для таких дел хватает обычного файла с описанием характеристиков игрока. Но что делать, если в бурно развивающемся проекте почти каждый день приходится немножко изменять логику главного игрока, и, следовательно, много раз компилировать проект?» Хороший вопрос. В этом случае нам на помощь приходят скрипты, держащие именно логику игрока со всеми характеристиками либо какой-либо другой части игры. Естественно, удобнее всего держать, логику игрока в виде кода какого-нибудь языка программирования. Первая мысль — написать свой интерпретатор своего скриптового языка, выкидывается из мозга через несколько секунд. Логика игрока определенно не стоит таких жутких затрат. К счастью, есть специальные библиотеки скриптовых языков для С++, которые принимают на вход текстовый файл и выполняют его.
Об одном таком скриптовом языке Lua пойдет речь.
Как это работает?
Прежде чем начать, важно понимать, как работает скриптовый язык. Дело в том, что в скриптовых языках есть очень мало функций, при наличии конструкций for, while, if, прочих. В основном это функции вывода текста в консоль, математические функции и функции для работы с файлами. Как же тогда можно управлять игроком через скрипты?
Мы в С++-программе делаем какие-либо функции, «регистрируем» их под каким-нибудь именем в скрипте и вызываем в скрипте. То есть если мы зарегистрировали функцию SetPos(x,y) для определения позиции игрока в С++-программе, то, встретив эту функцию в скрипте, «интерпретатор» из библиотеки скриптового языка вызывает эту функцию в С++-программе, естественно, с передачей всех методов. Удивительно, да? 🙂
UPD: Внимание! Один юзер обратился мне с мейлом, что, когда я заливал код, я не полностью устранил все ошибки — habrahabr.ru/post/196272/#comment_6850016 В коде с позволения хабра проникли жучки Замените участки кода вроде
И еще вместо lua_CFunction проскакивает lua_cfunction Спасибо!
Я готов!
Когда вы поняли преимущества скриптовых языков программирования, самое время начать работать! Скачайте из репозитория на гитхабе (низ топика) lib’у и includ’ы Lua, либо возмите их на официальном сайте.
Создаем консольный проект либо Win32 (это неважно) в Visual Studio (у меня стоит версия 2012)
Заходим в Проект->Свойства->Свойства конфигурации->Каталоги VC++ и в «каталоги включения» и «каталоги библиотек» добавьте папку Include и Lib из репозитория соответственно.
Теперь создаем файл main.cpp, пишем в нем:
Как вы догадались, у меня консольное приложение.
Теперь переходим к кодингу
Обещаю, что буду тщательно объяснять каждый момент
У нас за скрипты будет отвечать класс Script. Я буду объявлять и одновременно реализовывать функции в Script.h/.cpp Создаем Script.cpp и пишем в нем
Создаем Script.h и пишем в нем
После 2 строчки и перед #endif мы определяем класс скриптов Этот код пишется для предотвращения взаимного включения файлов. Допустим, что файл Game.h подключает Script.h, а Script.h подключает Game.h — непорядок! А с таким кодом включение выполняется только 1 раз
Теперь пишем внутри этого кода вот это
Первая строчка подключает сам lua.lib из архива. Для чего нужен extern «C»? Дело в том, что lua написан на С и поэтому такой код необходим для подключения библиотек.
Дальше идет подключение хорошо известных многим файлов для работы с консолью
Теперь приступим к определению класса
Самый главный объект библиотеки Lua для C++ — lua_State, он необходим для выполнения скриптов
Дальше идут публичные функции
Эта функция инициализирует lua_State
Его определение в Script.cpp
Первой строчкой мы инициализируем наш lua_State. Потом мы объявляем список «подключенных библиотек». Дело в том, что в «чистом» виде в луа есть только функция print(). Для математических и прочих функций требуется подключать специальные библиотеки и потом вызывать их как math.foo, base.foo, io.foo. Для подключения других библиотек добавьте в lualibs, например, <«math», luaopen_math>. Все названия библиотек начинаются с luaopen_. в конце lialibs должен стоять
Просто используем lua_close()
А эта функция выполняет файл. На вход она принимает название файла, например, «C:\\script.lua». Почему она возвращает int? Просто некоторые скрипты могут содержать return, прерывая работу скрипта и возвращая какое-нибудь значение.
Как вы видите, я выполняю скрипт и возвращаю int. Но возращать функция может не только int, но еще и bool и char*, просто я всегда возвращаю числа (lua_toboolean, lua_tostring)
Теперь мы сделаем функцию, регистрирующую константы (числа, строки, функции)
Мы действуем через шаблоны. Пример вызова функции: Ее определение
Для каждого возможного значения class T мы определяем свои действия. *Капитан* последнее определение — регистрация функции Функции, годные для регистрации, выглядят так:
Где n — количество возвращаемых значений. Если n = 2, то в Луа можно сделать так:
Читайте мануалы по Луа, если были удивлены тем, что одна функция возвращает несколько значений 🙂
Следующая функция создает таблицу для Луа. Если непонятно, что это значит, то тамошная таблица все равно что массив
Следующая функция регистрирует элемент в таблице.
Если вы не знаете Lua, вы, наверное, удивлены тем, что в один массив помещается столько типов? 🙂 На самом деле в элементе таблицы может содержаться еще и таблица, я так никогда не делаю.
Наконец, заполненную таблицу нужно зарегистрировать
Ничего особенного нет
Следующие функции предназначены в основном только для функций типа int foo(lua_State*), которые нужны для регистрации в Луа.
Первая из них — получает количество аргументов
Эта функция нужна, например, для функции Write(), куда можно запихать сколь угодно аргументов, а можно и ни одного Подобную функцию мы реализуем позже
Следующая функция получает аргумент, переданный функции в скрипте
Можно получить все типы, описывавшиеся ранее, кроме таблиц и функций index — это номер аргумента. И первый аргумент начинается с 1.
Наконец, последняя функция, которая возвращает значение в скрипт
Боевой код
Пора что-нибудь сделать! Изменяем main.cpp
Компилируем. Теперь можно приступить к тестированию нашего класса
Помните, я обещал сделать функцию Write? 🙂 Видоизменяем main.cpp
А в папке с проектом создаем файл script.lua
Компилируем и запускаем проект.
Теперь изменяем script.lua
Теперь программа будет выводить по 2 строки («\n» — создание новой строки), ждать нажатия Enter и снова выводить строки.
Экспериментируйте со скриптами!
Вот пример main.cpp с функциями и пример script.lua