Давайте вспомним функции, с которыми мы познакомились на прошлом занятии. Например, draw_line() — она рисует линию.
Если забыли, как они работают, всегда можно заглянуть во встроенную справку Godot — там подробно описано, что к чему.
А чтобы закрепить материал на практике, создадим новый проект и назовём его Custom Drawing Experiment.
Следующий шаг — создать корневой узел для нашей сцены. Самый простой способ: нажмите на иконку 2D-сцена в левом верхнем углу. Альтернативный вариант — вручную добавить узел Node2D через контекстное меню (правой кнопкой мыши → «Добавить узел» → выбрать Node2D).
Переименуем узел Node2D в Primitives. Затем создадим скрипт ex01.gd — именно в нём мы будем использовать функцию draw_line().
Внутри функции _ready() вызовем draw_line() и сразу же переопределим её следующим образом:
draw_line(Vector2(0,0),Vector2(100,100),Color.BLUE);
Сохраним сцену и запустим. Ожидаемо, программа вылетает. Мы немного расстроились, но не страшно если мы не забыли сохранить проект.
Давайте попробуем по-другому: перенесём код draw_line() в функцию _process() и снова запустим. На этот раз программа не вылетает, но… ничего не рисует. Снова расстраиваемся и идём смотреть в консоль Godot. А вот что она нам расскажет.
Ошибка сообщает: все команды рисования должны находиться внутри специальной функции _draw().
func _draw()->void:
draw_line(Vector2(0,0),Vector2(100,100),Color.BLUE);
Этот код работает — он рисует синюю линию.
Теперь попробуем использовать связку функций draw_line() и draw_set_transform().
Для начала заглянем в справку и посмотрим, что из себя представляет draw_set_transform().
Затем добавим эту функцию перед вызовом draw_line().
func _draw()->void:
draw_set_transform(Vector2(100,100));
draw_line(Vector2(0,0),Vector2(100,100),Color.BLUE);
Функция draw_set_transform сдвинет линию на координаты (100, 100). Получается, с её помощью можно смещать начало координат. Вот что об этом пишет официальная справка Godot:
void draw_set_transform(position: Vector2, rotation: float = 0.0, scale: Vector2 = Vector2(1, 1))
Задаёт пользовательское локальное преобразование для отрисовки через компоненты. Всё, что будет отрисовано после этого, будет преобразовано этим преобразованием.
Вы могли заметить, что с помощью draw_set_transform можно не только менять положение, но и поворачивать объекты, а также изменять их масштаб. Давайте используем эту возможность и посмотрим, что из этого выйдет.
func _draw()->void:
draw_set_transform(Vector2(100,100),deg_to_rad(30),Vector2(2,2));
draw_line(Vector2(0,0),Vector2(100,100),Color.BLUE);
Вот что получится: локальные координаты сместятся на (100, 100), линия повернётся на 30 градусов по часовой стрелке и станет в два раза длиннее. Самое удобное — мы можем независимо сдвигать, вращать и масштабировать элементы относительно друг друга.
Напишем ещё немного кода с графическими примитивами. Итак, у нас получилось сделать смещённую линию. Это превосходно. Продолжим эксперименты: будем смещать локальную систему координат и перемещать другие графические элементы.
Краткая справка по новым функциям:
draw_circle() — рисует окружность.
draw_arc() — рисует дугу.
Вот как будет выглядеть следующий код.
func _draw()->void:
draw_set_transform(Vector2(100,100))
draw_line(Vector2(0,0),Vector2(100,100),Color.BLUE)
draw_set_transform(Vector2(200,100))
draw_circle(Vector2(100,100),50,Color.BROWN)
draw_set_transform(Vector2(300,100))
draw_arc(Vector2(100,100),50,0,deg_to_rad(320),10,Color.WHITE)
А теперь посмотрим, что у нас получилось.
Продолжаем разбираться. На очереди у нас функции draw_rect() и draw_set_transform_matrix(). Давайте допишем код и посмотрим, что получится.
func _draw()->void:
draw_set_transform(Vector2(100,100))
draw_line(Vector2(0,0),Vector2(100,100),Color.BLUE)
draw_set_transform(Vector2(200,100))
draw_circle(Vector2(100,100),50,Color.BROWN)
draw_set_transform(Vector2(300,100))
draw_arc(Vector2(100,100),50,0,deg_to_rad(320),10,Color.WHITE)
draw_set_transform(Vector2(450,150))
draw_rect(Rect2(0,0,100,100),Color.AQUA)
draw_set_transform_matrix(Transform2D(deg_to_rad(10),Vector2(600,150)))
draw_rect(Rect2(0,0,100,100),Color.AQUA)
А вот что делает этот код.
Отличия draw_set_transform от draw_set_transform_matrix
draw_set_transform_matrix отличается от draw_set_transform тем, что позволяет использовать класс Transform2D, благодаря чему можно легко вращать изображения. Хотя и в draw_set_transform вращение и масштабирование тоже доступны, но код получается более громоздким — требуется писать больше букв.
Переходим к примитивам
Теперь давайте попробуем нарисовать примитивы. Изучим следующие функции:
draw_primitive()
draw_polygon()
draw_colored_polygon()
draw_polyline()
draw_polyline_colors()
Но сначала добавим табулятор для удобства.
Мы добавили табулятор, создали панели и к одной из них прикрепили скрипт. При запуске получили ошибку — оказалось, что скрипт был привязан к объекту Node2D. Исправили: заменили extends Node2D на extends Panel. Запустили снова — и тут новая проблема: табулятор оказался слишком маленьким, нужно увеличивать его ширину.
Заодно в свойствах TabContainer отключили галочку Clip Tabs. Вот как теперь выглядят наши панели.
Теперь у нас появилось гораздо больше пространства для работы, и мы можем продолжить изучение графических примитивов. Прикрепим к панели Panel2 скрипт ex02.gd, а к панели Panel3 — скрипт ex03.gd.
В скрипте ex02.gd напишем следующий код.
func _draw()->void:
draw_primitive(PackedVector2Array([Vector2(100,100),Vector2(200,200),Vector2(300,100)]),PackedColorArray([Color.WHITE,Color.BLACK,Color.AQUAMARINE]),PackedVector2Array())
Вот что этот код выведет на экран.
Я написал вот такой код.
func _draw()->void:
draw_primitive(PackedVector2Array([Vector2(100,100),Vector2(200,200),Vector2(300,100)]),PackedColorArray([Color.WHITE,Color.BLACK,Color.AQUAMARINE]),PackedVector2Array())
draw_set_transform(Vector2(400,100))
draw_primitive(PackedVector2Array([Vector2(0,0)]),PackedColorArray([Color.GREEN]),PackedVector2Array())
draw_set_transform(Vector2(500,100))
draw_primitive(PackedVector2Array([Vector2(0,0),Vector2(100,100)]),PackedColorArray([Color.WHITE,Color.BLACK]),PackedVector2Array())
draw_set_transform(Vector2(700,100))
draw_primitive(PackedVector2Array([Vector2(0,0),Vector2(100,0),Vector2(100,100),Vector2(0,100)]),PackedColorArray([Color.WHITE,Color.BLACK,Color.DARK_SLATE_GRAY,Color.BLUE_VIOLET]),PackedVector2Array())
А вот что в итоге нарисовал код.
вот полигоны, с которыми я разобрался.
Теперь разберёмся с мешами. В скрипте ex03.gd создадим функцию _draw(). Внутри неё добавим объекты TextMesh и `NoiseTexture2D», а затем напишем следующий код.
extends Panel
var tm = TextMesh.new()
var nt = NoiseTexture2D.new()
func _ready() -> void:
tm.text="hello world"
tm.pixel_size=3
func _draw()->void:
draw_set_transform(Vector2(200,100),0.0,Vector2(1,-1))
draw_mesh(tm,nt)
Здесь мы создаём объекты TextMesh и NoiseTexture2D. В TextMesh вписываем легендарную фразу «Hello, world!», однако она отображается перевёрнутой. Чтобы развернуть её обратно, используем третий параметр функции draw_set_transform() — масштаб (scale). С его помощью переворачиваем текст и выводим меш на экран с помощью функции draw_mesh().
Добавим ещё три панели — Panel4, Panel5 и Panel6. Для них создадим соответствующие скрипты: ex04.gd (для работы с текстурами), ex05.gd (для текста) и ex06.gd (для анимаций). А теперь приведём код, который следует поместить в скрипт ex04.gd.
extends Panel
var tex=load("res://icon.svg")
func _draw()->void:
draw_texture(tex,Vector2(100,100))
Данный скрипт загружает и рисует на экране логотип Godot.
В скрипт ex05.gd добавим следующий код.
extends Panel
func _draw()->void:
draw_char(get_theme_default_font(),Vector2(100,100),"I",16,Color.AQUA)
draw_char(get_theme_default_font(),Vector2(110,100),"a",16,Color.BROWN)
draw_char(get_theme_default_font(),Vector2(120,100),"m",16,Color.DARK_GOLDENROD)
draw_set_transform(Vector2(0,50))
draw_string(get_theme_default_font(),Vector2(100,100),"programmer")
Этот код выведет две строки: первая — разноцветная «I am», вторая — белая «programmer».
И наконец, в скрипт ex06.gd добавим следующий код.
extends Panel
const FRAMES=10.0
const LENGTH=2.0
func _draw()->void:
for i in range(FRAMES):
var begin=LENGTH/FRAMES*i
var end=LENGTH/FRAMES*(i+1)
draw_animation_slice(LENGTH, begin, end)
draw_set_transform(Vector2(100,100),deg_to_rad(i));
draw_polygon(PackedVector2Array([Vector2(100,100),Vector2(150,50),Vector2(200,100),Vector2(200,300),Vector2(100,300)]),PackedColorArray([Color.BLUE,Color.YELLOW,Color.RED,Color.DARK_MAGENTA,Color.CYAN]))
draw_end_animation()
С помощью draw_animation_slice() мы можем рисовать анимированные полигоны.
На этом эксперимент с программой Годот custom drawing in 2d завершён. Мы разобрались, как работает физика в Godot, и выделили из этого упражнения главные блоки для понимания. Спасибо, что были со мной!









