
Прочитал на Хабре заметку “Что делать с детьми летом, если ты айтишник?” и взгрустнулось:
во-первых, паяльник — это удел очень малой части “широких слоев” 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 x1,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.
