Как писать мультики

Примечание: в связи с выходом новой версии программы руководство было дополнено.

Не умеете рисовать и лень изучать какие-то программы, но хотите сделать мультик? Вы можете его написать. Всё просто.

character Petya {
  body = gray
  eyes = green
}

scene {
  Petya->right(200)
}

Сначала создаётся персонаж. Имя может быть любым — английским, русским (если не лень переключать раскладку), числом (если не запутаетесь) или вообще каким-нибудь набором символов. Между фигурными скобками задаются свойства персонажа — цвет тела (gray, серый) и цвет глаз (green, зелёный). Цвет можно писать как английском словом (green), так и кодом (#00FF00). Можно для красоты ставить пробелы в начале каждой строки, находящейся внутри блока, можно не ставить. Внутри блока scene создаётся сама анимация, в данном случае персонаж Petya перемещается вправо на 200 пикселей.

character Petya {
}

scene {
  Petya->right(200, 2)
  Petya->left(20, 0.5)
}

Как видите, свойства персонажа можно не указывать — будут установлены значения по умолчанию. Здесь Петя сначала переместится вправо на 200 пикселей за 2 секунды, а затем влево на 20 пикселей за полсекунды. Если количество секунд не указывать, используется свойство персонажа speed, по умолчанию установленное в 250 (вы можете переопределить).

character Petya {
  bodySize = 50   ; размер тела (точнее, радиус шарика)
  eyeSize  = 5    ; размер глаз
  x        = 0    ; координата x в начале мультфильма
  body     = gray ; цвет тела
  eyes     = aqua ; цвет глаз
  speed    = 250  ; скорость (250 пикселей в секунду)
}

scene {
  Petya->right(200, 2)
  Petya->left(20, 0.5)
}

Здесь показаны все имеющиеся у персонажей свойства и их значения по умолчанию, а также комментарии — часть строки после точки с запятой, которая будет проигнорирована программой.

background {
  ratio       = 3       ; отношение
  skyColor    = #9CE8F2 ; цвет неба
  groundColor = #C4EDBD ; цвет земли
}

Здесь и ниже также показаны значения по умолчанию. Небо занимает 1/ratio (здесь — треть) места. При ratio = 1 не отображается земля, при ratio = 0 не отображается небо.

settings {
  fps       = 24      ; кадров в секунду, изменять не рекомендуется
  width     = 1200    ; ширина мультфильма
  height    = 900     ; высота мультфильма
  pageColor = black   ; цвет страницы (на сам мультфильм не влияет; после окончания мультфильма возвращается прежний цвет)
}

А теперь попробуем сделать что-то с двумя персонажами.

character 1 {
}

character 2 {
  body = black
  x    = 500
}

scene {
  1->right(200)
  2->left(200)
}

Здесь сначала персонаж 1 пойдёт направо, а затем персонаж 2 налево. Но что делать, если мы хотим, чтобы они шли друг к другу одновременно? Достаточно добавить пару слов:

character 1 {
}

character 2 {
  body = black
  x    = 500
}

scene {
  together
  1->right(200)
  2->left(200)
  together_end
}

А теперь сделаем общение персонажей при помощи надписей:

character 1 {
}

character 2 {
  body = black
  x    = 500
}

scene {
  text = Привет! Как дела?
  together
  1->right(200)
  2->left(200)
  together_end
  write_clear
}

Над персонажами будет написана сия фраза, а после того, как они встретятся, она исчезнет благодаря команде write_clear. Можно изменять настройки надписей:

write {
  text  = ""     ; текст
  color = black  ; цвет
  x     = 10     ; координата X
  y     = 700    ; координата Y
  size  = 30     ; размер
  font  = Arial  ; шрифт
  style = normal ; normal — обычный, italic — курсив, bold — жирный
}

Координаты x и y — это координаты нижней левой точки текста. Т. е. при x и y, равным 0, текст видно не будет.

И напоследок примерчик чуть посложнее:

character Transaction {
  body = black
  eyes = lime
  x = 50
}

character Systempaw {
  body = gray
  eyes = yellow
  x = 500
}

write {
  color = gray
}

scene {
  Systempaw->left(200, 1)
  text = Ты не захватишь лес, Транзакция.
  wait = 2 ; ничего не делать 2 секунды
}

write {
  color = green
}

scene {
  text = Ты так наивен, Системолап.
}

Вторая версия программы

Всё, что написано выше, для второй версии программы также верно, но есть ряд нововведений.

Petya {
  body = gray
  eyes = green
}
Petya->right(200)

Некоторые слова, как видите, в новой версии можно опускать, сокращая таким образом объём кода. Слово character стало необязательным, и, соответственно, не рекомендуется использовать для имени персонажа ключевые слова (settings, write, camera и др.). Оборачивать действия в блок scene стало также необязательно.

Petya.body = gray
Petya.eyes = green
Petya->right(200)

Этот код делает абсолютно то же, что и код выше. Рекомендуем использовать такую сокращённую запись в том случае, если не требуется указывать более одного-двух свойств.

Big.bodySize = 100
Big->bodySize(1000, 3)->left(800, 3)->down(200, 3)->eyeSize(100, 3)->body(darkred, 3)

Анимировать стало возможно любые свойства любых объектов (не совсем любые, а только числовые и цветовые, но это почти все). Принцип тот же — знак -> указывает, что это анимация, bodySize (первая анимация в примере) указывает анимируемое свойство, 1000 — значение, к которому его нужно привести за 3 секунды. Также у персонажей появилось свойство y и возможность его анимировать через down и up.

Можно также указывать не значение, к которому нужно привести, а разницу с нынешним (только для числовых свойств; для цветовых, разумеется, нельзя):

settings->width(+100)->height(-200)

Здесь ширина увеличится на 100 пикселей и затем высота уменьшится на 200. Кстати, если количество секунд, за которое должна выполниться анимация, не указано, используется либо свойство speed (установлено по умолчанию только у персонажей, но вы можете поставить у других объектов сами), либо анимация выполняется ровно за одну секунду, если оно не установлено.

Big.bodySize = 100
Big->bodySize(1000, 3)->x(-800, 3)->y(-200, 3)->eyeSize(100, 3)->body(darkred, 3)

Показан изменённый код примера выше. Как видите, left, right, down и up — лишь синонимы для более удобного анимирования свойств x и y.

Big.bodySize = 100
repeat 5 {
  Big->bodySize(+50, 1)
  wait = 1
}

Появились циклы. В данном случае код в блоке repeat повторится 5 раз. Очевидно, есть смысл использовать цикл, только если вы используете анимацию или команду wait (которые повторяются).

; Пример 1
Big.bodySize = 100
together
Big->bodySize(250, 3)->body(black, 3)
together_end

; Пример 2
Big.bodySize = 100
Big->bodySize(250, 3)->body(black, 3)

Возможно, это не очевидно сразу, но примеры несколько разные, хоть здесь и анимируется лишь один персонаж. В первом случае персонаж одновременно будет увеличиваться и становиться чёрным, а во втором — сначала увеличится, а потом уже станет чёрным.

1.body = gray
1.x = 0

2.body = green
2.x = 150

3.body = white
3.x = 300

4.body = yellow
4.x = 450

5.body = black
5.x = 600

camera->right(200)

Если передвигать объект camera, будет как бы передвигаться фон. В данном случае — персонажи уйдут налево, и не придётся каждого отдельно анимировать. В следующих версиях, вероятно, появятся предметы и прочие прелести, так что камера будет ещё более нужной.

text = Привет!
write.x = 500
write->x(-100)

У координат текста возможны отрицательные значения. Например, x=-100 при ширине 1200 будет означать, что x=1100. Т. е. текст появится с другой стороны. В этом примере координата x будет анимирована со значения 500 до 400. Но что делать, если нужно анимировать до значения -100? Помимо варианта написать -600, можно написать необязательный по умолчанию знак =:

text = Привет; а ты кто?
write.x = 500
write->x(=-100)

И тут же демонстрируется, помимо решения предыдущей, новая проблема. Слова «; а ты кто?» не отобразятся, т. к. это комментарий. Чтобы точка с запятой не считалось за знак комментария, добавьте перед ней \ (это, кстати, называется экранированием):

text = Привет\; а ты кто?

В новой версии также появились возможности по изменению и анимированию рта персонажа. Для этого есть целых четыре свойства, которые мы рассмотрим по отдельности. Первое, mouth, указывает цвет. Интереснее второе — mouthForm. При чётных значениях рот является линией, при нечётных — кружочком. Это удобно для анимирования разговора:

Big {
  bodySize = 100
  mouth = #37252B
  mouthSize = 0.3
  mouthThickness = 7
}
together
Big->mouthForm(20, 4)->down(40, 4)
together_end
Big.mouth = black
Big.mouthThickness = 1

Здесь же показаны ещё два свойства — mouthSize (по умолчанию значение 0.2) указывает длину линии рта или радиус окружности, mouthThickness — толщину линии (на окружность не влияет).

Напоследок покажем, как можно анимировать прыжок:

Big.bodySize = 100
together
Big->right(200, 0.5)->up(400, 0.5)
together_end
together
Big->right(200, 0.5)->down(400, 0.5)
together_end

Или туда-сюда:

Big.bodySize = 100
repeat 10 {

together
Big->right(200, 0.5)->up(400, 0.5)
together_end
together
Big->right(200, 0.5)->down(400, 0.5)
together_end

together
Big->left(200, 0.5)->up(400, 0.5)
together_end
together
Big->left(200, 0.5)->down(400, 0.5)
together_end

}

Третья версия

Свойство mouthForm стало проще анимировать благодаря появлению синонима:

Big.bodySize = 100
Big.speakSpeed = 5
text = Привет!
Big->speak(5)
; всё равно, что:
; Big->mouthForm(+26, 5)

В speak нужно указывать только кол-во секунд. При этом анимируется свойство mouthForm — оно увеличивается на кол-во секунд * speakSpeed (и если получилось нечётное число, то +1, чтобы после разговора персонаж не стоял с открытым ртом). По умолчанию speakSpeed равно пяти.

В новой версии свойства стали регистронезависимыми, т. е., например, можно писать как speakSpeed, так и speakspeed (или даже sPeAkSpEeD). Всё остальное (объекты, имена персонажей и предметов и т. д.) по-прежнему регистрозависимое.

Также появились предметы, хотя использоваться они могут далеко не только для создания предметов.

item.type cat {
 url = http://catwar.su/cw3/koloroj/0/bazoj/1.png
}
item Ololostar.type = cat

В нижнем левом углу появится котик. Собственно, сначала нужно объявить новый тип предметов — коты. Указав адрес предмета, можно создавать сколько угодно предметов с таким типом.

item.type cat {
 url = http://catwar.su/cw3/koloroj/0/bazoj/1.png
}
item cat1 {
 type = cat
}
item cat2 {
 type = cat
 x = 200
}
item cat3 {
 type = cat
 x = 400
}

Свойства можно указывать всему типу (или даже всем предметам, поменяв свойство объекта item), а можно переопределять у конкретных предметов.

Есть свойство fixed. Если оно равно 1, то предмет не будет передвигаться вместе с камерой (удобно для фонов):

item.type cat {
 url = http://catwar.su/cw3/koloroj/0/bazoj/1.png
}
item 1 {
 type = cat
 fixed = 1
}
item 2 {
 type = cat
 x = 200
}
camera->left(200)

Первый котик, в отличие от второго, застынет.

У предметов есть много свойств, позволяющих вырезать, растягивать и всячески развлекаться с картинками:

item.type cat {
 url = http://catwar.su/cw3/koloroj/0/bazoj/1.png
}
item 1 {
 type = cat
 x = 10 ; координаты предметы
 y = 10
 width = 50 ; ширина и высота предмета
 height = 50
 sx = 10 ; из исходного изображения будет браться область, начиная с 10х10
 sy = 10
 sWidth = 50 ; из исходного изображения будет взят кусок размером 50х50
 sHeight = 50
}

Чтобы это лучше понять, можете поиграться с циферками.

Поддерживается прозрачность, не поддерживаются, увы, анимированные картинки. Однако способ анимировать предметы всё же есть.

Это картинка называется спрайтом. Рисуется несколько кадров, и затем они слепляются в одно изображение (вручную или с помощью онлайн-сервиса). А в самом мультфильме в один момент времени используется первая часть картинки, в другой — вторая.

item.type sprite {
 url = http://savepic.su/4728843.png
}
item Ololo {
 type = sprite
 width = 100
 sWidth = 100
}
repeat 10 {
 wait = 0.5
 item Ololo.sx = 100
 wait = 0.5
 item Ololo.sx = 0
}

Четвёртая версия

Появились, во-первых, более удобные возможности для анимирования своих предметов. Чтобы понять, как это работает, вам нужно внимательно изучить предыдущий пример со спрайтами.

item.type sprite {
 url = http://savepic.su/4728843.png
}
item Ololo {
 type = sprite
 fWidth = 100
}
item Ololo->frame(+20, 10)

Свойство fWidth (буква f обозначает frame, т. е. ширина кадра) меняет одновременно два свойства: width и sWidth. Можно было бы самому задать значения этим свойствам, разницы нет, кроме того, что с fWidth на строчку меньше.

frame, соответственно, анимирует на деле свойство sx. Увеличение frame на единицу — это увеличение sx на sWidth. Если кадров, как здесь, два, то +20 означает, что анимация повторится 10 раз.

Также появилось примитивненькое недо3D. У персонажей, предметов и камеры появилось, помимо x и y, свойство z.

item.type sprite {
 url = http://savepic.su/4728843.png
}
item Ololo {
 type = sprite
 width = 100
 swidth = 100
}
together
item Ololo->frame(+20, 10)->z(+100, 10)
together_end
together
item Ololo->frame(+20, 10)->z(-100, 10)
together_end

Ololo будет сначала 10 секунд удаляться от нас, а затем 10 секунд к нам возвращаться. Аналогично можно было бы передвигать камеру относительно Ololo, а не наоборот.

Пятая версия

Появились звуки. Одновременно может проигрываться сколь угодное количество звуков, только давайте им разные названия (здесь название music).

sound music {
 src = http://адрес_к_файлу_со_звуком/ ; обратите внимание: не url, как у изображений, а src
 loop = 0 ; 0 - проиграть 1 раз; 1 - циклически повторять
 pause = 0 ; если вы захотите остановить проигрывание через некоторое время, поменяйте это свойство на 1
 time = 10 ; начать с десятой секунды
 volume = 1 ; громкость (от 0 до 1, т. е. дробные числа)
}

Есть некоторые сложности: перед началом мультфильма все изображения и звуки сначала загружаются, поэтому если звуки весят много, зрителям вашего творчества придётся ждать некоторое время. К тому же, не все форматы поддерживаются всеми браузерами.

Также появились команды skip и skip_end. Они удобны при создании более или менее продолжительных мультфильмов, когда сделано незначительное изменение и нужно посмотреть, как получилось.

Big.bodySize = 100
skip
Big->body(black, 1) ; эта анимация выполнится мгновенно, несмотря на то что указана продолжительность 1 секунда
skip_end
Big->body(white, 1)