Voltar ao Blog

Defold se torna totalmente 3D: Uma Análise Técnica e Tutorial de Arquitetura

Publicado em 12 de maio de 2026
Defold se torna totalmente 3D: Uma Análise Técnica e Tutorial de Arquitetura

Em resumo

O Defold evoluiu de uma engine 2D minimalista para um ecossistema 3D completo, mantendo sua performance extrema para WebGL e mobile. Este guia técnico detalha a implementação de um custom rendering pipeline via Lua, a criação de câmeras perspectiva e as estratégias de otimização de assets e sincronização de rede para multiplayer. É a escolha ideal para desenvolvedores que buscam binários leves e controle total da arquitetura sem o overhead de engines convencionais.

Todo indie developer conhece o momento em que sua game engine o trai. Você começa com um conceito 2D leve, o scope creep introduz elementos 3D, e de repente o build size da sua engine explode, seus load times rastejam e seus web builds crasham por falta de memória. Engines massivas como Unity e Unreal são fenomenais para fidelidade AAA, mas para desenvolvedores solo que buscam distribuição cross-platform sem fricção — especialmente WebGL e mobile — elas muitas vezes parecem como dirigir um tanque para ir à padaria.

Conheça o Defold. Historicamente aclamado como uma engine 2D ultra-rápida e zero-bloat usada por estúdios para atingir todas as plataformas, de HTML5 ao Nintendo Switch com um único codebase, o Defold oficialmente amadureceu. Agora ele é uma 3D game engine completa. Embora tecnicamente sempre tenha renderizado em um contexto 3D sob o capô (projetando planos flat via uma câmera ortográfica), as atualizações recentes introduziram ferramentas 3D dedicadas, suporte completo a glTF mesh e um workflow otimizado para o desenvolvimento 3D real.

Esta é uma mudança massiva para o ecossistema. Se você procura uma engine que compila em segundos, gera binários de apenas um dígito de megabytes e ainda oferece o poder de renderizar mundos 3D dinâmicos, o Defold é agora um competidor de elite. Neste tutorial de defold 3d game engine e análise arquitetural, vamos mergulhar fundo em como o 3D rendering pipeline do Defold funciona, como scriptar uma câmera 3D customizada do zero e as implicações de backend para o networking de um jogo 3D leve.

Under the Hood: A Realidade do Pipeline 3D do Defold

Para dominar o Defold em 3D, você precisa entender sua filosofia de renderização. O Defold não entrega um PBR (Physically Based Rendering) pipeline pré-configurado out of the box como a Unreal Engine faz. Em vez disso, ele fornece um render script altamente otimizado e data-driven escrito em Lua.

Tudo o que é desenhado na tela no Defold é gerenciado por um render_script. Por padrão, este script é configurado para 2D. Ele define uma matriz de projeção ortográfica, ordena os sprites pelo seu Z-value (depth) e os desenha de trás para frente. Para desbloquear as capacidades 3D do Defold, precisamos reescrever este script para utilizar uma matriz de projeção perspectiva, habilitar hardware depth testing e definir render predicates customizados para nossos modelos 3D.

Este acesso low-level é uma faca de dois gumes. Por um lado, você precisa escrever um pouco de matemática de matrizes. Por outro, você tem controle absoluto sobre seus draw calls, permitindo otimizar o rendering para hardware ultra-low-end de formas que engines monolíticas tornam incrivelmente difíceis.

Arquitetando seu Custom 3D Render Script

Para renderizar modelos 3D reais sem que eles se sobreponham incorretamente baseados na ordem de desenho, precisamos habilitar o depth buffer (Z-buffer). Aqui está um 3D render script base que substitui o pipeline padrão do Defold.

-- main/3d_pipeline.render_script
function init(self)
    -- Define a cor de fundo (RGBA)
    self.clear_color = vmath.vector4(0.1, 0.1, 0.12, 1.0)
    self.clear_buffers = {
        [render.BUFFER_COLOR_BIT] = self.clear_color,
        [render.BUFFER_DEPTH_BIT] = 1.0,
        [render.BUFFER_STENCIL_BIT] = 0
    }

    -- Cria render predicates. 'model' é a tag padrão para meshes 3D
    self.predicates = {
        model = render.predicate({"model"}),
        gui = render.predicate({"gui"}),
        text = render.predicate({"text"})
    }
end

function update(self)
    -- 1. Setup do estado de renderização para o frame
    render.set_depth_mask(true)
    render.set_stencil_mask(0xff)
    render.clear(self.clear_buffers)

    -- 2. Configura a 3D Camera Projection
    local window_width = render.get_window_width()
    local window_height = render.get_window_height()
    if window_width == 0 or window_height == 0 then return end
    
    local aspect_ratio = window_width / window_height
    local fov = math.rad(60) -- 60 graus de Field of View
    local near_z = 0.1
    local far_z = 1000.0
    
    local proj_matrix = vmath.matrix4_perspective(fov, aspect_ratio, near_z, far_z)
    render.set_projection(proj_matrix)

    -- 3. Desenha os modelos 3D com Depth Testing habilitado
    render.set_depth_test(render.COMPARE_LEQUAL)
    render.set_cull_face(render.FACE_BACK)
    render.set_blend_func(render.BLEND_SRC_ALPHA, render.BLEND_ONE_MINUS_SRC_ALPHA)
    
    -- A view matrix é passada via mensagens do nosso script de câmera
    if self.view_matrix then
        render.set_view(self.view_matrix)
        render.draw(self.predicates.model)
    end

    -- 4. Desenha a GUI sobre a cena 3D (Ortográfico)
    render.set_depth_mask(false)
    render.set_depth_test(render.COMPARE_ALWAYS)
    local gui_proj = vmath.matrix4_orthographic(0, window_width, 0, window_height, -1, 1)
    render.set_projection(gui_proj)
    render.set_view(vmath.matrix4())
    
    render.draw(self.predicates.gui)
    render.draw(self.predicates.text)
end

function on_message(self, message_id, message)
    if message_id == hash("set_view_matrix") then
        self.view_matrix = message.matrix
    end
end

Note o quão explicitamente estamos gerenciando o estado aqui. Limpamos o depth buffer, calculamos a matriz perspectiva baseada nas dimensões atuais da janela, forçamos o back-face culling para economizar ciclos de rasterização e, finalmente, alternamos a matriz de projeção de volta para ortográfica antes de renderizar a GUI. Isso te entrega uma arquitetura de renderização de pipeline dividido muito robusta.

Criando um First-Person 3D Camera Controller

Um render script sozinho não mostrará muito sem uma câmera se movendo pelo espaço. O Defold opera fortemente em uma arquitetura de Message Passing. Ao contrário de engines orientadas a objetos onde a câmera pode chamar diretamente transform.Translate(), no Defold, nosso script de câmera calculará sua view matrix e a despachará para o render script que acabamos de escrever.

Vamos construir uma câmera First-Person padrão que gerencia mouse-look (pitch e yaw) e movimentação por teclado (WASD).

-- scripts/camera_controller.script
go.property("mouse_sensitivity", 0.2)
go.property("move_speed", 10.0)

function init(self)
    msg.post(".", "acquire_input_focus")
    self.pitch = 0
    self.yaw = 0
    
    -- Esconde e captura o cursor do mouse
    window.set_mouse_lock(true)
    
    self.forward = vmath.vector3(0, 0, -1)
    self.right = vmath.vector3(1, 0, 0)
    self.up = vmath.vector3(0, 1, 0)
    self.velocity = vmath.vector3(0)
end

function update(self, dt)
    local pos = go.get_position()
    
    -- Aplica a velocidade de movimento
    if vmath.length_sqr(self.velocity) > 0 then
        local move_dir = vmath.normalize(self.velocity)
        pos = pos + move_dir * self.move_speed * dt
        go.set_position(pos)
    end

    -- Calcula a view matrix
    local rotation = go.get_rotation()
    self.forward = vmath.rotate(rotation, vmath.vector3(0, 0, -1))
    local target = pos + self.forward
    
    local view_matrix = vmath.matrix4_look_at(pos, target, self.up)
    
    -- Envia a view matrix calculada para o render pipeline
    msg.post("@render:", "set_view_matrix", { matrix = view_matrix })
    
    -- Reseta a velocidade para o próximo frame
    self.velocity = vmath.vector3(0)
end

function on_input(self, action_id, action)
    if action_id == hash("mouse_moved") then
        self.yaw = self.yaw - action.dx * self.mouse_sensitivity
        self.pitch = self.pitch + action.dy * self.mouse_sensitivity
        
        -- Trava o pitch para evitar que a câmera capote
        self.pitch = math.max(-89, math.min(89, self.pitch))
        
        -- Converte Euler angles para um Quaternion
        local rot_y = vmath.quat_rotation_y(math.rad(self.yaw))
        local rot_x = vmath.quat_rotation_x(math.rad(self.pitch))
        go.set_rotation(rot_y * rot_x)
        
    elseif action_id == hash("move_forward") then
        self.velocity = self.velocity + self.forward
    elseif action_id == hash("move_backward") then
        self.velocity = self.velocity - self.forward
    elseif action_id == hash("move_left") then
        self.right = vmath.cross(self.forward, self.up)
        self.velocity = self.velocity - self.right
    elseif action_id == hash("move_right") then
        self.right = vmath.cross(self.forward, self.up)
        self.velocity = self.velocity + self.right
    end
end

Este script captura os movimentos delta do mouse para ajustar pitch e yaw, convertendo esses Euler angles em uma rotação via Quaternion robusta. Ele então deriva os vetores forward e right diretamente dessa rotação para garantir que pressionar "W" sempre te mova na direção para a qual você está olhando.

Integração de Assets 3D e Shaders Customizados

Com as atualizações recentes do Defold, trazer assets 3D para seu projeto é trivial. A engine suporta nativamente os formatos .gltf e .glb, que se tornaram o padrão da indústria para web e desenvolvimento de jogos leves.

No entanto, renderizar uma mesh requer um material, e materiais requerem shaders. Por padrão, o Defold inclui materiais básicos, mas escrever seus próprios GLSL shaders te dá a distinção visual necessária para se destacar. Vamos escrever um shader unlit textured rápido que é perfeitamente otimizado para targets mobile ou HTML5.

O Vertex Shader (model.vp)

// model.vp
uniform highp mat4 view_proj;
uniform highp mat4 world;

attribute highp vec4 position;
attribute mediump vec2 texcoord0;

varying mediump vec2 var_texcoord0;

void main()
{
    // Calcula a posição final em screen-space do vértice
    vec4 p = view_proj * world * vec4(position.xyz, 1.0);
    var_texcoord0 = texcoord0;
    gl_Position = p;
}

O Fragment Shader (model.fp)

// model.fp
varying mediump vec2 var_texcoord0;
uniform lowp sampler2D texture_sampler;
uniform lowp vec4 tint;

void main()
{
    // Amostra a textura e multiplica por um color tint
    lowp vec4 tex_color = texture2D(texture_sampler, var_texcoord0.xy);
    gl_FragColor = tex_color * tint;
}

No seu arquivo de material do Defold, você mapeia o uniform view_proj para a matriz de view-projection embutida da engine, e o texture_sampler para a textura diffuse da sua mesh. Como esses shaders não calculam iluminação dinâmica ou shadow maps, eles rodam incrivelmente rápido, permitindo manter 60 FPS facilmente em hardware low-end.

Gerenciando Multiplayer 3D e Sincronização de Estado

Quando você transita de um jogo 2D baseado em grid para um ambiente 3D completo, a complexidade da sua arquitetura de networking aumenta exponencialmente. Mover de 2D para 3D significa que sua sincronização de estado agora precisa levar em conta a profundidade do eixo Z, imprecisões de floating-point entre physics engines e rotações completas via quaternions. Se você gerenciar isso mal, acabará com visual stuttering severo — um problema comum que exploramos ao analisar Como Corrigir Desync de Localização de Jogadores em Multiplayer Uefn e Unreal Engine.

Como o Defold é uma engine leve, ele não vem com um sistema de replicação pesado como os RPCs da Unreal Engine. Você é responsável por empacotar seus dados de estado eficientemente.

Depender de REST APIs para sincronizar posições 3D causará um gargalo no seu game loop instantaneamente. Em vez disso, você precisa de conexões persistentes e bidirecionais. Embora nosso guia anterior tenha coberto como Abandonar HTTP Polling: Um Tutorial de WebSockets em Unreal Engine para Backends em Tempo Real, os mesmos princípios arquiteturais se aplicam diretamente às extensões de WebSocket baseadas em Lua do Defold.

Aqui está um exemplo de como você deve serializar dados de transform 3D para minimizar o payload size sobre WebSockets:

-- scripts/network_sync.lua
local json = require "builtins.scripts.json"

function serialize_transform(go_id)
    local pos = go.get_position(go_id)
    local rot = go.get_rotation(go_id)
    
    -- Comprime o payload para o mínimo absoluto de dados necessários
    -- Arredondamos a posição para 2 casas decimais para economizar banda
    local payload = {
        id = hash_to_hex(go_id),
        x = math.floor(pos.x * 100) / 100,
        y = math.floor(pos.y * 100) / 100,
        z = math.floor(pos.z * 100) / 100,
        -- Quaternions exigem todos os 4 componentes para reconstrução precisa
        qx = rot.x,
        qy = rot.y,
        qz = rot.z,
        qw = rot.w
    }
    
    return json.encode(payload)
end

Ao arredondar os floats e empacotar apenas dados essenciais, você evita que seus buffers de rede transbordem durante atualizações rápidas de movimento. Gerenciar esse fluxo é a chave para um jogo cross-platform responsivo.

O Fardo de Infraestrutura do 3D Cross-Platform

Escrever um game client 3D altamente otimizado no Defold é imensamente satisfatório. A engine não atrapalha, compila em milissegundos e deixa você focar estritamente na lógica.

No entanto, no momento em que você decide tornar esse jogo 3D multiplayer, adicionar cloud saves ou implementar leaderboards cross-platform, seu foco muda imediatamente do desenvolvimento do jogo para a orquestração de servidores. Você de repente se vê escrevendo Dockerfiles, configurando clusters de Kubernetes, lutando com instâncias de Redis para estado de sessão e tentando proteger seus gateways de WebSocket contra ataques DDoS.

Construir isso sozinho requer configurar load balancers, database sharding e gerenciamento de certificados SSL — facilmente 4 a 6 semanas de trabalho apenas para ter um protótipo confiável rodando.

Com o horizOn, esses serviços de backend já vêm pré-configurados, permitindo que você lance seu jogo em vez da sua infraestrutura. O horizOn fornece integrações nativas para autenticação de usuários, sincronização de banco de dados em tempo real e lógica server-authoritative, fechando perfeitamente a lacuna para engines leves como o Defold que não vêm com ecossistemas de backend proprietários. Você mantém a velocidade de uma engine minúscula enquanto terceiriza o trabalho pesado da arquitetura de servidor.

Melhores Práticas para Desenvolvimento 3D no Defold

Se você planeja construir um projeto 3D comercialmente viável no Defold, siga rigorosamente estas diretrizes arquiteturais:

  1. Mantenha a Geometria Altamente Otimizada: O Defold é projetado para velocidade. Para manter sua vantagem leve, mantenha a geometria do seu nível abaixo de 100.000 polígonos no total por cena, especialmente se o alvo for HTML5/WebGL. Use baked normal maps em vez de meshes de alta densidade para simular detalhes.
  2. Aproveite Render Predicates para Frustum Culling: O Defold não faz cull automático de objetos que estão fora da visão da câmera no espaço 3D por padrão. Você deve escrever sua própria lógica de frustum culling em Lua, desabilitando dinamicamente os componentes de modelo de objetos fora dos limites para economizar tempo de rasterização.
  3. Consolide Draw Calls via Atlasing: Cada material e textura únicos exigem um draw call separado enviado para a GPU. Combine suas texturas em grandes Texture Atlases. Se 10 modelos 3D diferentes compartilham exatamente o mesmo material e atlas, o Defold pode fazer o batch deles com muito mais eficiência sob o capô.
  4. Pré-calcule Cálculos Matemáticos Complexos: Multiplicações de matrizes e conversões de quaternions são operações caras em Lua. Faça o cache dos seus vetores forward e right e só os recalcule quando a rotação do player realmente mudar, em vez de fazer a matemática pesada incondicionalmente em cada frame.
  5. Desacople a Lógica da Frequência de Render: Sua lógica de jogo (update) pode rodar a 60 FPS, mas seus passos de física ou networking customizados podem rodar a 30 FPS. Interpole suas posições visuais 3D baseadas na velocidade em vez de apenas travá-las no estado mais recente, para garantir um rendering suave em diferentes taxas de atualização de monitores.
  6. Gerencie o Garbage Collection do Lua: Em um ambiente 3D dinâmico, você cria e destrói objetos de vetores e matrizes frequentemente. O Garbage Collector do Lua pode causar picos de frame perceptíveis se não for gerenciado. Reutilize instâncias de vmath.vector3 e vmath.matrix4 sempre que possível, atualizando seus valores internos diretamente em vez de instanciar novas variáveis locais dentro do seu loop de update. Pré-aloque memory pools para projéteis e entidades.
  7. Faça o Bake da Iluminação Externamente: Como a iluminação dinâmica em shaders customizados consome performance rapidamente em dispositivos móveis, faça o bake da sua Global Illumination e Ambient Occlusion diretamente nas texturas usando Blender ou Maya antes de exportar seus modelos glTF. Um shader unlit simples com iluminação lindamente baked sempre terá performance superior a um shader dinâmico complexo em browsers mobile.

Conclusão

A evolução do Defold para uma engine 3D robusta é uma vitória massiva para desenvolvedores independentes. Ele consegue manter seus tempos de compilação absurdamente rápidos e binários minúsculos enquanto oferece as fundações matemáticas e ferramentas necessárias para construir mundos 3D expansivos e envolventes. Ao dominar render scripts customizados, entender operações de matrizes e serializar eficientemente seus dados de rede, você pode criar títulos cross-platform que competem tecnicamente com engines muito maiores e pesadas.

Quando estiver pronto para levar seu client 3D otimizado para o ambiente online e escalar seu multiplayer backend sem a dor de cabeça de gerenciar infraestrutura bruta, experimente o horizOn gratuitamente ou confira a documentação da API para ver quão rápido você pode integrar serviços em tempo real no seu próximo projeto Defold.


Fonte: Defold is Now a Full 3D Game Engine