Что за паркур вы построили в Roblox? Давайте сделаем круче
Каникулы с Roblox Studio
Прочитал на Хабре заметку «Что делать с детьми летом, если ты айтишник?» и взгрустнулось:
во-первых, паяльник — это удел очень малой части «широких слоев» IT-шников
во-вторых, мало какому ребенку интересно сортировать резисторы по цветным колечкам;
в-третьих, паяльник — это ретро, и даже, если елочку нарядили и она весело мигает огоньками, что дальше? Что стало понятнее? Где применить полученные знания и навыки?
И руки потянулись к клавиатуре, чтобы написать пару заметок, предлагающих родителям альтернативные варианты вовлечения детей в IT, кстати, не только в soft, но и в hard.
На возраст детей, о котором идет речь в заметке, есть более интересные и, главное, более перспективные альтернативы, не связанные с риском уронить горячий паяльник на колени или взять его чуть выше рукоятки. Например, Scratch и Roblox. Ну не стыдно ли вам господа IT-шники за страну, глядя на такие вот веселые картинки?
Не странная ли это статистика: когда начнем догонять?
А детям упомянутого возраста Scratch — попроще, да и интереснее, чем сортировка резисторов…
Но, как говорится: «Критикуешь — предлагай!» Предлагаю: «А не замахнуться ли нам на Вильяма Шекспира?» Посмотрите, сколько на Youtube роликов про строительство паркуров в Roblox — грубо говоря, куча! Но, все не то! Давайте сделаем немного круче! Предлагаемый ниже текст взят из нашей с внуком (недавно изданной) книжки и рассчитан на самостоятельное усвоение детьми возраста »10+».
Глава 9. Программируем паркур
Вы научились создавать строительные блоки и задавать им нужные вам свойства. Давайте применим полученные навыки для создания игры.
Разработка паркура
Сначала разложите элементы вашего паркура прямо на Baseplate. Раскрасьте платформы паркура, чтобы они выглядели привлекательнее. Затем, в меню Explorer наведите курсор мыши на Baseplate, нажмите правую кнопку мыши и выберите в выпадающем меню опцию Delete — Baseplate будет удалена, а созданные вами платформы «повиснут» в небе (если, конечно, вы «заякорили» их — иначе, все они упадут в бездну).
Можно, конечно, несколько изменить положение и ориентацию в пространстве отдельных площадок паркура, используя свойства Orientation в окне Properties, но это не слишком увеличит сложность его прохождения. Другое дело, если заставить платформы паркура двигаться прямо в процессе игры. Давайте разбираться, как это сделать. Первое, что нужно знать: в каждой игре Roblox позиция Х=0, Y=0, Z=0 всегда находится в центре игрового пространства и совпадает с местом спавна игрока.
Для определения положении и ориентации любых parts в 3D-пространстве игры можно использовать данные типа Vector3, которые используют те же координаты: X, Y и Z. Поясним это на примере простой программы, скрипт которой нужно вставить, например, в заранее созданный в Workplace объект-платформу obby_p:
1 local cf_part = script.Parent
2 x, y, z = 2,8,2
3 cf_part.Position = Vector3.new (x, y, z)
4 cf_part.Size = Vector3.new (3,0.5,3)
5 cf_part.BrickColor = BrickColor.new (255,0,0)
6 while true do
7 cf_part.Position = Vector3.new (x, y, z)
8 cf_part.Orientation = Vector3.new (x*45, y, z)
9 wait (1)
10 x = x +1
11 end
Поясним код:
в строке 1 создаем переменную cf_part и указываем ее Родителя obby_p;
в строке 2 задаем исходные значения сразу трем переменным;
в строке 3 задаем при помощи Vector3 позицию obby_p (указав значение свойства Position объекта cf_part) — теперь его позиция будет соответствовать тем координатам, которые указаны в строке 2;
в строке 4 задаем размеры cf_part (указав при помощи Vector3 свойству Size размеры 3×0,5×3 шипа);
в строке 5 красим obby_p в красный цвет;
цикл while true в строках 6…11 меняет не только позицию, но и ориентацию obby_p за счет изменения угла ее поворота вокруг оси Х. Ведь через каждую секунду значение переменной Х меняется на »1», а значит, меняется значение координаты Х в свойствах Position и Orientation.
Запустите программу, вы увидите, что obby_p меняет свое местоположение на игровом поле и, одновременно, поворачивается на 45 градусов при каждом перемещении вдоль оси Х. Теперь вы можете, например, используя цикл while, заставить некоторые из платформ паркура качаться то в одну, то в другую сторону, да еще наклоняться, усложняя тем самым прохождение паркура. Продублируйте obby_p и откорректируйте скрипт новой part:
измените ее цвет;
измените ее положение (чтобы parts не мешали друг другу);
измените «содержимое» цикла while true, а именно:
1 local cf_part = script.Parent
2 x, y, z=2,4,2
3 i=0
4 cf_part.Position = Vector3.new (x, y, z)
5 cf_part.Size = Vector3.new (3,0.5,3)
6 cf_part.BrickColor = BrickColor.new (0,200,0)
7 while true do
8 while i<50 do
9 cf_part.Position = cf_part.Position + Vector3.new (0.1,0,0)
10 cf_part.Orientation= cf_part.Orientation + Vector3.new (2,0,0)
11 wait (0.2)
12 i = i +1
13 end
14 i=0
15 while i<50 do
16 cf_part.Position = cf_part.Position + Vector3.new (-0.1,0,0)
17 cf_part.Orientation= cf_part.Orientation + Vector3.new (-2,0,0)
18 wait (0.2)
19 i = i +1
20 end
21 i=0
22 end
Поворот платформы
Запустите игру. Согласитесь, пройти паркур с такими платформами будет гораздо сложнее.
Давайте разберемся с новым скриптом:
мы видим, что в обоих циклах while i<50 переменная i изменяется от »0» до »50»;
— с каждой итерацией (с каждым шагом) цикла положение obby_p по оси Х изменяется либо на »0,1», либо на »-0,1», а угол поворота obby_p вокруг оси Х изменяется на »2» либо на »-2»;
— после каждой итерации программа приостанавливается на время равное 0,2 секунды;
— получается, что значения »0.1» и »-0.1» соответствуют понятию «скорость», а максимальное значение переменной i — понятию времени, затраченного на движение платформы. В результате, путь пройденный obby_p, можно выразить формулой:
S = V*t = 0,1×50 = 5 шипов.
Вы всегда можете рассчитать нужный вам диапазон перемещения платформ по оси Х;
— аналогично, вы можете заранее рассчитать необходимые вам углы поворота платформ;
— кроме того, меняя «скорость» и «шаг» изменения времени (i) мы можем изменять скорость движения и смены ориентации платформ.
А почему мы ограничились изменением положения только по оси Х? Можно, аналогично, менять положение и ориентацию платформ паркура по другим осям. В том числе, одновременно. Обязательно попробуйте это сделать.
Чертово колесо
Но и это еще не все. Вспомните, как движутся кабинки «чертова колеса». А что, если сделать, чтобы и некоторые ваши платформы двигались так же? Давайте, попробуем. Соорудите еще одну платформу, например, part_sin, вставьте в нее скрипт, приведенный ниже и проверьте, как это работает. Вы увидите, что цель достигнута.
Строим «чертово колесо»
1 local part_sin = script.Parent
2 i=0
3 x, y, z = 12,3,12
4 part_sin.Position = Vector3.new (x, y, z)
5 part_sin.Size = Vector3.new (6,0.5,6)
6 part_sin.BrickColor = BrickColor.new (0,0,250)
7 while true do
8 i_rad=math.rad (i)
9 part_sin.Position = part_sin.Position + Vector3.new (math.sin (i_rad)/2, math.cos (i_rad)/2,0)
10 wait (.2)
11 i = i + 10
12 end
Поясним скрипт:
в строке 3 мы изменили исходные координаты платформы;
в строке 5 увеличили размеры платформы;
в строке 6 изменили цвет платформы на синий;
в строке 8 (внутри бесконечного цикла while true) мы преобразуем числовое значение переменной i (которая у нас будет выполнять роль величины угла поворота платформы) в радианы, используя для этого уже знакомую математическую функцию math.rad;
в строке 9 мы (при помощи Vector3.new) наращиваем исходную позицию платформы part_sin на величину, соответствующую:
● по оси координат Х — величине синуса угла i, деленную на »2»;
● по оси координат Y — величине косинуса угла i, деленную на »2». Получается, что переменная i будто бы бесконечно растет, но вы же представляете, что если крутишь что-либо, угол поворота этой вещи всегда меняется только в пределах от »0» до »360» градусов. Так и здесь. Кстати, вы можете попробовать (и обязательно сделайте это) изменить число »10», которое прибавляется к прежнему значению переменной i и число »2», на которое делятся синус и косинус. Меняя эти параметры, вы можете заставить платформу вращаться быстрее или медленнее, увеличивать или уменьшать диаметр круга по которому вращается платформа. Можно не только делить значения синуса и косинуса на какой-то коэффициент, но и умножать. Проверьте. Только поднимите платформу повыше, иначе она будет цеплять за Baseplate и обратите внимание на параметр CanCollide.
Изменяем положение объектов при помощи CFrame
Но, продолжим разбираться с положением и ориентацией parts. Вы уже неоднократно заглядывали в окно Properties строительных блоков и помните, что в этом окне их положение в игровом пространстве описывает параметр, который называется CFrame. Действительно в Lua есть еще один «инструмент», позволяющий задать в скрипте любой part нужное вам положение и ориентацию. Для этого можно использовать выражение:
cf_part.CFrame = CFrame.new (Х, Y, Z) * CFrame.Angles (math.rad (n1), math.rad (n2), math.rad (n3)),
где: X, Y, Z — нужные вам координаты центра obby_p,
Angles — указывает на изменение ориентации obby_p в пространстве;
n1, n2, n3 — углы поворота, соответствующие осям координат X, Y, Z;
math.rad — функция, преобразующая угол из градусов в радианы.
Если же менять ориентацию part в пространстве не нужно, то можно использовать более короткое выражение:
cf_part.CFrame = CFrame.new (Х, Y, Z)
Поставьте в строках 7 и 8 скрипта (первого из ранее созданных obby_p) по два тире (как для вставки комментариев), чтобы эти строки перестали действовать. А вместо них вставьте новую строку кода:
cf_part.CFrame = CFrame.new (x, y, z) * CFrame.Angles (math.rad (x*45), 0, 0)
Запустив программу, вы легко убедитесь, что поведение obby_p нисколько не изменилось. То есть, в данном случае конструкции Vector3 и CFrame действуют полностью аналогично. Теперь, самостоятельно, попробуйте при помощи CFrame вращать платформу part_sin, как в «чертовом колесе».
Что же лучше использовать Position или CFrame для управления перемещениями parts? В описанных выше скриптах мы перемещали простые parts. Но, часто требуется перемещать одновременно группы объектов, скрепленных друг с другом (состоящие из нескольких «сваренных» деталей). Если Position одной из сваренных деталей изменять, эта деталь будет двигаться, но ни одна из соединенных с ней деталей не будет двигаться вместе с ней. Напротив, если изменяется CFrame одной из деталей, любой объект, приваренный к этой детали, также будет двигаться.
Что еще можно предложить, чтобы сделать ваш паркур еще круче? А давайте вырежем в некоторых платформах отверстия, через которые игроки могут провалиться при неудачном прыжке. Вот скрипт такой платформы, перемещающейся, как кабинка «чертового колеса».
Платформа с отверстием-ловушкой
1 local part_sin = script.Parent
2 i=0
3 x, y, z = 12,6,12
4 part_sin.Position = Vector3.new (x, y, z)
5 part_sin.Size = Vector3.new (6,1,6)
6 part_sin.BrickColor = BrickColor.new (0,0,250)
7 part_sin.Anchored = true
8 local part_2 = Instance.new(«Part», workspace)
9 part_2.Shape = «Ball»
10×1, y1, z1 = 12,6,12
11 part_2.Position = Vector3.new (x1, y1, z1)
12 part_2.Size = Vector3.new (3,3,3)
13 part_2.Anchored = true
14 new_Union = part_sin: SubtractAsync({part_2})
15 new_Union.Parent = game.Workspace
16 new_Union.Position = part_sin.Position
17 new_Union.Anchored = true
18 part_sin: Destroy ()
19 part_2: Destroy ()
20 while true do
21 i_rad=math.rad (i)
22 new_Union.Position = new_Union.Position + Vector3.new (math.sin (i_rad)/5, math.cos (i_rad)/5,0)
23 wait (.2)
24 i = i + 3
25 end
Вы видите, что в строках 1…7 нет никаких изменений в сравнении с прежним скриптом, описывающим part_sin. А вот в строке 8 появилось что-то новое. Оказывается, в языке Луа новые (самые разные) объекты можно создавать внутри скрипта с помощью функции Instance.new () — где в скобках нужно указывать тип создаваемого объекта:
именно так в строке 8 (указав тип объекта »Part» и указав его Родителя — workspace) мы создаем объект part_2;
в строке 9 определяем, что формой этого объекта является шар (Ball);
в строке 10 задаем значения трем переменным х1, y1, z1, которые в следующей строке используем в качестве координат, в которые поместим наш новый объект. Обратите внимание: значения координат совпадают со значением координат x, y, z — значит, центры part_sin и part_2 совпадают. В этом случае, можно было бы использовать в строках 4 и 11 одни и те же переменные, но мы решили показать, как быть, если вам будут нужны другие координаты;
в строке 12 задаем размер part_2 равным 3×3 х 3 шипа;
в строке 13 «якорим» об ъект;
в строке 14 создаем объект new_Union, представляющий собой «разность» фигур part_sin и part_2. То есть, вырезаем в платформе part_sin отверстие с диаметром равным диаметру шара part_2. Обратите внимание на фигурные скобки, в которых указан объект part_2. Фигурные скобки означают, что в них может быть указан список. Если бы вам нужно было «вычесть» из part_sin не один, а несколько объектов, вы могли бы указать их в этих фигурных списках через запятую;
в строке 15 указываем Родителя new_Union;
в строке 16 указываем, что положение new_Union в игровом пространстве совпадает с положением part_sin;
строка 17 «якорит» newUnion. Получается, что мы программным способом создали твердотельную модель, и элементы, из которых она состоит на уже не нужны;
в строках 18, 19 уничтожаем ненужные нам part_sin и part_2, используя функцию Destroy ();
строки бесконечного цикла while true 20…25 управляют движением объекта new_Union, точно также, как раньше управляли платформой part_sin. Теперь игрокам будет еще труднее, ведь они могут провалиться сквозь отверстие в платформе.
Преграда, качающаяся из стороны в сторону и мешающая прыжку с платформы на платформу
Согласитесь, можно вставить в паркур не только платформы, но и преграды, мешающие прыжкам игроков. И теперь вы знаете, как это все сделать.» (конец цитаты)
Образец выполнения скриптов в книжке
Если вы дочитали (просмотрели) текст до этого места и вам интересно, о какой книжке идет речь — погуглите «Азбука программирования игр в Roblox Studio». Кстати, в статье на сайте Хабра невозможно воспроизвести тексты скриптов в том виде, в каком они представлены в нашей книге, поэтому прикладываю картинку с иллюстрацией. Примерно также, как это делает Roblox, мы применили в текстах скриптов цветовое выделение — это важно, для упрощения восприятия скриптов в возрасте »10+», на который и ориентирована книжка.
Теперь, если эта заметка вызовет хоть какой-то интерес, осталось написать про альтернативу в сфере hard. Как говаривали древние: Dixi et animam meam salvāvi.