Нашел в доке меши, шейдеры, решил узнать, что это такое на практике, какая-то магия даже на простых примерах.
Интересные моменты:
- точки в координатах должны идти по часовой стрелке A → B → C. Зачем нужно? Для того чтоб GPU понимал какая сторона ФРОНТ/БЕК и не рисовать те стороны, которые не видно, culling, не рендерит невидимые части.
треугольник
треугольник
Рисуем треугольник Mesh | Defold
# ASSETS
- создаем fragment program с именем mesh
- создаем vertex program с именем mesh
- создаем buffer с именем triangle.buffer
- создаем mesh.material
- в meterial выставляем в vertex program наш mesh.vp
- и в fragment program mesh.fp
- tag добавляем model (ля рендер скрипта какой тип)
- vertex constants: создаем две переменных нажимая на плюс
- имя: mtx_proj type: Projection
- имя: mtx_worldview type: Wordview
- vertex space: Local
# OUTLINE
- создаем го с именем triangle
- в этом го именяем scale x,y до 100,100 и позицию на x,y на 300,300
- созаем меш, в свойствах меша:
- в meterial ставим созданный mesh.material
- vertices: ставим созданный triangle.buffer
- position stream: position
- normal stream: position
// mesh.fp
varying mediump vec4 var_color;
void main()
{
gl_FragColor = var_color;
}
// mesh.vp
uniform mediump mat4 mtx_worldview;
uniform mediump mat4 mtx_proj;
attribute mediump vec4 position;
attribute mediump vec4 color0;
varying mediump vec4 var_color;
void main()
{
gl_Position = mtx_proj * mtx_worldview * vec4(position.xyz, 1.0);
var_color = color0;
}
[
{
-- name это имена attribute, точки координат(position) и цвета(color0)
-- увидим в вертексном шейдере
"name": "position",
-- тип чисел, обычно float32
"type": "float32",
-- Сколько чисел описывают ОДНУ вершину.
"count": 3,
-- координаты
-- можно делать простые фигуры через 0,1 и потом масштабировать через go scale или же сразу задавать размеры здесь, например, вместо 1 писать сразу 50
"data": [
-- точки в координатах должны идти по часовой стрелке A → B → C.
-1, -1, 0,
1, -1, 0,
0, 1, 0
]
},
{
"name": "color0",
"type": "float32",
"count": 4,
-- r = красный
-- g = зелёный
-- b = синий
-- a = альфа (прозрачность)
"data": [
0, 1, 0, 1,
1, 0, 0, 1,
0, 0, 1, 1
]
}
]
-1,-1,0 это коориднаты x,y,z
квадрат
Меняем наш triangle.buffer, добавляем два треугольника.
[
{
"name": "position",
"type": "float32",
"count": 3,
"data": [
-- один треугольник
-1, -1, 0,
1, -1, 0,
1, 1, 0,
-- второй
-1,-1,0,
1,1,0,
-1,1,0
]
},
{
"name": "color0",
"type": "float32",
"count": 4,
"data": [
-- цвета для rgba для двух треугольников
0, 1, 0, 1,
1, 0, 0, 1,
0, 0, 1, 1,
0, 1, 0, 1,
0, 0, 1, 1,
1, 1, 0, 1
]
}
]
случайное блуждание
Размер экрана в game.project должен быть 960 на 640
# ASSETS
- добавляем картинку 1 на 1 пиксель с любым цветом
- создаем атлас, добалвяем картинку
- создаем game object
- в него добавляем спрайт
- в спрайте выбираем атлас, наше изображение
- создаем nature.script
# OUTLINE
- создаем го
- добавляем nature.script к скрипту
- создаем фабрику
- в фабрике выбираем Prototype наш созданный game object
в game.project увеличиваем лимиты для обьектов в коллекций COLLECTION: 65534
и у SPRITE: 65534
65534 - лимит для одной коллекции, для превышения новой создаем новую коллекцию
NATURE.SCRIPT
-- случайное число от a до b
local function rand(a,b)
return math.random(a,b)
end
-- первый вариант 1,2,3,4 ход
local function step1(self)
-- выбираем направление
local choice = rand(1,4)
if choice == 1 then
-- ход +1 self.width это размер нашего спрайта в 1 пиксель
self.startX = self.startX + self.width
elseif choice == 2 then
self.startX = self.startX - self.width
elseif choice == 3 then
self.startY = self.startY + self.width
elseif choice == 4 then
self.startY = self.startY - self.width
end
end
-- улучшенный вариант хода
local function step(self)
// по x 0,1,-1
local x = rand(-1,1)
// по y 0,1,-1
local y = rand(-1,1)
self.startX = self.startX + x
self.startY = self.startY + y
end
local function show(self)
local pos = vmath.vector3(self.startX,self.startY,1)
factory.create("#factory", pos)
end
function init(self)
-- для рандома + сброс первых значений
math.randomseed(math.floor(socket.gettime()*1e6))
math.random(); math.random(); math.random();
-- размеры в game project игры
local width = sys.get_config_int("display.width")
local height = sys.get_config_int("display.height")
-- для центра
self.startX = width/2
self.startY = height/2
self.width = 1
self.accum = 0 -- аккумулятор времени
self.value = 0.0001 -- время в секундах для рисовки
end
function update(self, dt)
self.accum = self.accum + dt
while self.accum >= self.value do
-- ходим
step(self)
-- рисуем
show(self)
self.accum = self.accum - self.value
end
end
Стоблики
Размер экрана в game.project должен быть 960 на 640
# ASSETS
- добавляем 64 на 10
- создаем атлас, добалвяем картинку
- создаем game object
- в него добавляем спрайт
- в спрайте выбираем атлас, наше изображение
- в спрайте включаем slice 9 (Size mode: Manual) и Slice 9 ставим 3,3,3,3
- создаем nature.script
# OUTLINE
- создаем го
- добавляем nature.script к скрипту
- создаем фабрику
- в фабрике выбираем Prototype наш созданный game object
-- случайное число от a до b
local function rand(a,b)
return math.random(a,b)
end
local function step(item)
local id = item.id
-- высота текущая
local height = item.height
-- число на которое прибавляем
local value = 1
-- прибавляем новую высоту
local sprite_url = msg.url(nil, id, "sprite")
go.set(sprite_url, "size", vmath.vector3(64, height+value, 0))
-- выравниваем относительно новой высоты pivot
-- иначе уйдет под экран
local p = go.get_position(id)
local pos = vmath.vector3(p.x, height/2, p.z)
item.height = item.height + value
end
function init(self)
-- для рандома + сброс первых значений
math.randomseed(math.floor(socket.gettime()*1e6))
math.random(); math.random(); math.random();
local table = {}
-- стартовый стоблик
local start = vmath.vector3(64/2, 1, 0)
local dx = 64
self.table = {}
for i = 1, 15 do
-- позиция для нового стоблика
local pos = start + vmath.vector3((i-1) * dx, 0, 0)
local id = factory.create("#factory", pos)
-- обьект с id и текущей высотой
local obj = {
id = id,
height = 10
}
self.table[i] = obj
end
self.accum = 0 -- аккумулятор времени
self.value = 0.001 -- в секундах ход
end
function update(self, dt)
self.accum = self.accum + dt
while self.accum >= self.value do
-- выбираем случайный столб от 1 до 15
local r = rand(1,15)
-- прибавляем к нему +1 высоту
step(self.table[r])
self.accum = self.accum - self.value
end
end
Шейдер, создаем блик на 0.1 милисекунд как урон для спрайта
Вот туториал на форуме, здесь исправленный вариант для версии 1.11
Shaders for beginners - The Defoldmine - Defold game engine forum
vertex program - определяет положение вершин
fragment program - красит каждый пиксель
материал - во что они объединяются подробнее - Материалы | Defold
# ASSETS
- берем любой png картинку и закидываем в проект
- создаем main.atlas, кидаем картинку в него
- идем в папку builltins/materials и ищем sprite.vp, sprite.fp и sprite.material и копируем их в папку main с нашей главной коллекцией
- созадем скрипт shader.script
# OUTLINE
- создаем го с именем shader
- добавляем shader.script в него
- добавляем спрайт, выбираем Image: атлас и defold animation наш спрайт
- Material: вставляем наш sprite.material из /main
В sprite.material vertex и fragment program нужно выбрать из нашей папки main
в Fragment Constants добавить переменную blink_effect_trigger и выставить значения 1,1,1,1
Идем в sprite.fp
#version 140
in mediump vec2 var_texcoord0;
out vec4 out_fragColor;
uniform mediump sampler2D texture_sampler;
uniform fs_uniforms
{
mediump vec4 tint;
// отличие от статьи
// теперь переменные fragment constant нужно определять в fs_uniforms
uniform lowp vec4 blink_effect_trigger;
};
void main()
{
// Pre-multiply alpha since all runtime textures already are
mediump vec4 tint_pm = vec4(tint.xyz * tint.w, tint.w);
// обьявляем переменную mediump vec4 color_of_pixel
mediump vec4 color_of_pixel = texture(texture_sampler, var_texcoord0.xy) * tint;
// blink_effect_trigger.r проверяем, если 1 = показывать, 0 - нет
// color_of_pixel.a != 0.0 иначе вместо спрайта будет красить белым весь квадрат
if((blink_effect_trigger.r == 1.0) && (color_of_pixel.a != 0.0)) {
// монжо поменять цвет на любой rgba формат
color_of_pixel = vec4(1.,1.,1.,1.);
}
// отдаем для каждого пикселя
out_fragColor = color_of_pixel;
}
ШЕЙДЕР СКРИПТ
function init(self)
timer.delay(1, true, function() -- каждую секунду
go.set("#sprite", "blink_effect_trigger", vmath.vector4(1)) -- красим в белый
timer.delay(0.1, false, function() -- и после 0.1 секунды
go.set("#sprite", "blink_effect_trigger", vmath.vector4(0)) -- выключаем
end)
end)
end
Готово, спрайт мигает каждую секунду 0.1 секунду в белый цвет





