Volver al Blog

Defold se vuelve totalmente 3D: Un Technical Breakdown y Tutorial de Arquitectura

Publicado el 12 de mayo de 2026
Defold se vuelve totalmente 3D: Un Technical Breakdown y Tutorial de Arquitectura

En resumen

Este análisis técnico explora la transformación de Defold en un motor 3D capaz de generar binarios extremadamente ligeros para WebGL y móviles. Se detalla la implementación de un rendering pipeline personalizado mediante Lua y la creación de controladores de cámara basados en matrices de perspectiva y cuaterniones. Finalmente, se ofrecen estrategias para optimizar el networking y el rendimiento mediante el uso de atlas de texturas y gestión eficiente de la memoria en entornos multiplayer.

Todo desarrollador indie conoce el momento en que su motor de juego le traiciona. Empiezas con un concepto 2D ligero, el scope creep introduce elementos 3D y, de repente, el tamaño del build de tu motor explota, los tiempos de carga se ralentizan y tus web builds se quedan sin memoria. Los motores masivos como Unity y Unreal son fenomenales para la fidelidad AAA, pero para desarrolladores en solitario que buscan una distribución cross-platform sin fricciones —especialmente para WebGL y dispositivos móviles— a menudo se sienten como conducir un tanque para ir a comprar el pan.

Aquí entra Defold. Históricamente defendido como un motor 2D ultrarrápido y sin bloat utilizado por estudios para llegar a todas las plataformas, desde HTML5 hasta Nintendo Switch con un único codebase, Defold ha madurado oficialmente. Ahora es un motor de juego 3D totalmente capaz. Aunque técnicamente siempre renderizó en un contexto 3D bajo el capó (proyectando planos mediante una cámara ortográfica), las actualizaciones recientes han introducido herramientas 3D dedicadas, soporte completo para meshes glTF y un workflow optimizado para el desarrollo 3D real.

Este es un cambio masivo para el ecosistema. Si buscas un motor que compile en segundos, genere binarios de pocos megabytes y aun así te de el poder de renderizar mundos 3D dinámicos, Defold es ahora un competidor de primer nivel. En este tutorial de Defold 3D game engine y desglose de arquitectura, vamos a profundizar en cómo funciona el Rendering Pipeline 3D de Defold, cómo programar una cámara 3D personalizada desde cero y las implicaciones de Backend al implementar el networking de un juego 3D ligero.

Under the Hood: La realidad del 3D Rendering Pipeline de Defold

Para entender cómo dominar Defold en 3D, hay que comprender su filosofía de renderizado. Defold no te entrega un PBR (Physically Based Rendering) Pipeline preconfigurado de fábrica como lo hace Unreal Engine. En su lugar, proporciona un Render Script altamente optimizado y orientado a datos escrito en Lua.

Todo lo que se dibuja en pantalla en Defold es gestionado por un render_script. Por defecto, este script está configurado para 2D. Configura una matriz de proyección ortográfica, ordena los sprites por su valor Z (profundidad) y los dibuja de atrás hacia adelante. Para desbloquear las capacidades 3D de Defold, debemos reescribir este script para utilizar una matriz de proyección de perspectiva, habilitar el hardware depth testing y definir Render Predicates personalizados para nuestros modelos 3D.

Este acceso de bajo nivel es un arma de doble filo. Por un lado, tienes que escribir algo de matemática de matrices. Por otro lado, tienes un control absoluto sobre tus Draw Calls, lo que te permite optimizar el renderizado para hardware de gama ultra baja de formas que los motores monolíticos hacen increíblemente difíciles.

Arquitectura de tu propio 3D Render Script

Para renderizar modelos 3D reales sin que se superpongan incorrectamente según el orden de dibujo, necesitamos habilitar el depth buffer (Z-buffer). Aquí tienes un 3D Render Script fundamental que reemplaza el pipeline por defecto de Defold.

-- main/3d_pipeline.render_script
function init(self)
    -- Define the background clear color (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
    }

    -- Create render predicates. 'model' is the default tag for 3D meshes
    self.predicates = {
        model = render.predicate({"model"}),
        gui = render.predicate({"gui"}),
        text = render.predicate({"text"})
    }
end

function update(self)
    -- 1. Setup the rendering state for the frame
    render.set_depth_mask(true)
    render.set_stencil_mask(0xff)
    render.clear(self.clear_buffers)

    -- 2. Configure the 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 degree 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. Draw 3D Models with Depth Testing enabled
    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)
    
    -- The view matrix is passed via messages from our camera script
    if self.view_matrix then
        render.set_view(self.view_matrix)
        render.draw(self.predicates.model)
    end

    -- 4. Draw GUI over the 3D scene (Orthographic)
    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

Observa con qué detalle estamos gestionando el estado. Limpiamos el depth buffer, calculamos la matriz de perspectiva basándonos en las dimensiones actuales de la ventana, forzamos el back-face culling para ahorrar ciclos de rasterización y, finalmente, cambiamos la matriz de proyección de nuevo a ortográfica antes de renderizar la GUI. Esto te proporciona una arquitectura de renderizado de pipeline dividido muy robusta.

Creando un First-Person 3D Camera Controller

Un Render Script por sí solo no servirá de mucho sin una cámara moviéndose por el espacio. Defold funciona principalmente con una arquitectura de Message Passing. A diferencia de los motores orientados a objetos donde la cámara podría llamar directamente a transform.Translate(), en Defold, nuestro script de cámara calculará su View Matrix y la enviará al Render Script que acabamos de escribir.

Vamos a construir un controlador de cámara estándar en primera persona que gestione el mouse-look (pitch y yaw) y el movimiento 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
    
    -- Hide and capture the mouse cursor
    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()
    
    -- Apply movement velocity
    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

    -- Calculate the 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)
    
    -- Send the calculated view matrix to the render pipeline
    msg.post("@render:", "set_view_matrix", { matrix = view_matrix })
    
    -- Reset velocity for the next 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
        
        -- Clamp pitch to prevent the camera from flipping over
        self.pitch = math.max(-89, math.min(89, self.pitch))
        
        -- Convert Euler angles to a 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 los movimientos delta del ratón para ajustar el pitch y el yaw, convirtiendo esos ángulos de Euler en una rotación por Quaternion robusta. Luego deriva los vectores forward y right directamente de esa rotación para asegurar que presionar "W" siempre te mueva en la dirección hacia la que estás mirando.

Integración de 3D Assets y Shaders personalizados

Con las recientes actualizaciones de Defold, importar 3D Assets a tu proyecto es trivial. El motor soporta nativamente los formatos .gltf y .glb, que se han convertido en el estándar de la industria para el desarrollo web y de juegos ligeros.

Sin embargo, renderizar un mesh requiere un material, y los materiales requieren Shaders. Por defecto, Defold incluye materiales básicos, pero escribir tus propios Shaders GLSL te da la distinción visual necesaria para destacar. Vamos a escribir un Shader texturizado unlit rápido que esté perfectamente optimizado para plataformas móviles o HTML5.

El 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()
{
    // Calculate the final screen-space position of the vertex
    vec4 p = view_proj * world * vec4(position.xyz, 1.0);
    var_texcoord0 = texcoord0;
    gl_Position = p;
}

El Fragment Shader (model.fp)

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

void main()
{
    // Sample the texture and multiply by a color tint
    lowp vec4 tex_color = texture2D(texture_sampler, var_texcoord0.xy);
    gl_FragColor = tex_color * tint;
}

En tu archivo de material de Defold, mapeas el uniform view_proj a la matriz view-projection integrada del motor, y el texture_sampler a la diffuse texture de tu mesh. Como estos Shaders no calculan iluminación dinámica ni shadow maps, se ejecutan increíblemente rápido, permitiéndote mantener fácilmente 60 FPS en hardware de gama baja.

Gestión de Multiplayer 3D y State Synchronization

Cuando pasas de un juego 2D basado en grids a un entorno 3D completo, la complejidad de tu arquitectura de networking aumenta exponencialmente. Pasar de 2D a 3D significa que tu State Synchronization ahora tiene que tener en cuenta la profundidad del eje Z, las imprecisiones de coma flotante entre los Physics Engines y rotaciones completas por Quaternion. Si gestionas esto mal, terminarás con un severo visual stuttering, un problema común que exploramos al analizar How To Fix Player Location Desync In Uefn And Unreal Engine Multiplayer.

Debido a que Defold es un motor ligero, no viene con un sistema de replicación pesado e integrado como los RPCs de Unreal Engine. Eres responsable de empaquetar tus datos de estado eficientemente.

Relying en REST APIs para sincronizar posiciones 3D creará un cuello de botella en tu game loop instantáneamente. En su lugar, necesitas conexiones persistentes y bidireccionales. Aunque nuestra guía anterior cubrió cómo Ditch Http Polling An Unreal Engine Websockets Tutorial For Real Time Backends, los mismos principios arquitectónicos se aplican directamente a las extensiones de WebSocket basadas en Lua de Defold.

Aquí tienes un ejemplo de cómo deberías serializar los datos de 3D Transform para minimizar el tamaño del payload 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)
    
    -- Compress the payload to the absolute minimum required data
    -- We round position to 2 decimal places to save bandwidth
    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 require all 4 components for accurate reconstruction
        qx = rot.x,
        qy = rot.y,
        qz = rot.z,
        qw = rot.w
    }
    
    return json.encode(payload)
end

Al redondear los floats y empaquetar solo los datos esenciales, evitas que tus buffers de red se desborden durante actualizaciones rápidas de movimiento. Gestionar este flujo es la clave para un juego cross-platform responsivo.

El peso de la infraestructura en el 3D Cross-Platform

Escribir un cliente de juego 3D altamente optimizado en Defold es inmensamente satisfactorio. El motor no estorba, compila en milisegundos y te permite concentrarte estrictamente en la lógica.

Sin embargo, en el momento en que decides que ese juego 3D sea Multiplayer, añadir cloud saves o implementar leaderboards cross-platform, tu enfoque cambia inmediatamente del desarrollo del juego a la orquestación de servidores. De repente te encuentras escribiendo Dockerfiles, configurando clusters de Kubernetes, lidiando con instancias de Redis para el estado de sesión y tratando de asegurar tus pasarelas WebSocket contra ataques DDoS.

Construir esto por tu cuenta requiere configurar Load Balancers, Database Sharding y gestión de certificados SSL —fácilmente de 4 a 6 semanas de trabajo solo para tener un prototipo fiable funcionando.

Con horizOn, estos servicios de Backend vienen preconfigurados, permitiéndote lanzar tu juego en lugar de tu infraestructura. horizOn proporciona integraciones nativas para autenticación de usuarios, sincronización de bases de datos en tiempo real y lógica autoritativa del servidor, cerrando perfectamente la brecha para motores ligeros como Defold que no incluyen ecosistemas de Backend propietarios. Mantienes la velocidad de un motor cliente diminuto mientras subcontratas el trabajo pesado de la arquitectura del servidor.

Best Practices para el desarrollo 3D en Defold

Si planeas construir un proyecto 3D comercialmente viable en Defold, sigue estrictamente estas directrices arquitectónicas:

  1. Geometría altamente optimizada: Defold está diseñado para la velocidad. Para mantener su ventaja de ligereza, mantén la geometría de tus niveles por debajo de los 100,000 polígonos totales por escena, especialmente si te diriges a HTML5/WebGL. Usa baked normal maps en lugar de meshes de alta densidad para simular detalles.
  2. Usa Render Predicates para Frustum Culling: Defold no descarta automáticamente objetos que están fuera de la vista de la cámara en el espacio 3D de serie. Debes escribir tu propia lógica de Frustum Culling en Lua, desactivando dinámicamente los componentes de modelo de los objetos que están fuera de los límites para ahorrar tiempo de rasterización.
  3. Consolida Draw Calls mediante Atlasing: Cada material y textura único requiere un Draw Call separado enviado a la GPU. Combina tus texturas en grandes Texture Atlases. Si 10 modelos 3D diferentes comparten el mismo material y atlas, Defold puede agruparlos de forma mucho más eficiente bajo el capó.
  4. Precalcula matemáticas complejas: Las multiplicaciones de matrices y conversiones de Quaternions son operaciones costosas en Lua. Cachea tus vectores forward y right y solo recalcúlalos cuando la rotación del jugador cambie realmente, en lugar de hacer los cálculos pesados incondicionalmente en cada frame.
  5. Desacopla la lógica de la frecuencia de renderizado: Tu lógica de juego (update) puede ejecutarse a 60 FPS, pero tus pasos de física personalizada o networking podrían ir a 30 FPS. Interpola tus posiciones visuales 3D basándote en la velocidad en lugar de saltar directamente al último estado para asegurar un renderizado suave en diferentes tasas de refresco de monitor.
  6. Gestiona el Lua Garbage Collection: En un entorno 3D dinámico, crearás y destruirás objetos vectoriales y matrices con frecuencia. El Garbage Collector de Lua puede causar picos de frames notables si no se gestiona. Reutiliza instancias de vmath.vector3 y vmath.matrix4 siempre que sea posible actualizando sus valores internos directamente, en lugar de instanciar nuevas variables locales dentro de tu loop de update. Preasigna memory pools para balas y entidades.
  7. Bakea tu iluminación externamente: Como la iluminación dinámica en Shaders GLSL personalizados consumirá rápidamente tu presupuesto de rendimiento en dispositivos móviles, bakea tu Global Illumination y Ambient Occlusion directamente en tus texturas usando Blender o Maya antes de exportar tus modelos glTF. Un simple Shader unlit con una iluminación bakeada bellamente siempre superará a un Shader dinámico complejo en navegadores web móviles.

Conclusión

La evolución de Defold hacia un robusto motor de juego 3D es una gran victoria para los desarrolladores independientes. Conserva con éxito sus tiempos de compilación ultrarrápidos y sus binarios increíblemente pequeños, al tiempo que ofrece los cimientos matemáticos y las herramientas necesarias para construir mundos 3D expansivos y atractivos. Al dominar los Render Scripts personalizados, entender las operaciones de matrices y serializar eficientemente tus datos de red, puedes construir títulos cross-platform que compiten técnicamente con motores mucho más grandes y pesados.

Cuando estés listo para llevar tu cliente 3D optimizado a la red y escalar tu Backend Multiplayer sin el dolor de cabeza de gestionar la infraestructura pura, prueba horizOn gratis o consulta la documentación de la API para ver qué tan rápido puedes integrar servicios en tiempo real en tu próximo proyecto de Defold.


Fuente: Defold is Now a Full 3D Game Engine